+
diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py
index 1f574c1d24e..59bafc27487 100644
--- a/addons/mrp/models/mrp_production.py
+++ b/addons/mrp/models/mrp_production.py
@@ -549,8 +549,10 @@ class MrpProduction(models.Model):
consume_move_lines = moves_to_do.mapped('active_move_line_ids')
for moveline in moves_to_finish.mapped('active_move_line_ids'):
if moveline.move_id.has_tracking != 'none':
+ if any([not ml.lot_produced_id for ml in consume_move_lines]):
+ raise UserError(_('You can not consume without telling for which lot you consumed it'))
# Link all movelines in the consumed with same lot_produced_id false or the correct lot_produced_id
- filtered_lines = consume_move_lines.filtered(lambda x: x.lot_produced_id == moveline.lot_id or not x.lot_produced_id)
+ filtered_lines = consume_move_lines.filtered(lambda x: x.lot_produced_id == moveline.lot_id)
moveline.write({'consume_line_ids': [(6, 0, [x for x in filtered_lines.ids])]})
else:
# Link with everything
diff --git a/addons/mrp/models/mrp_workorder.py b/addons/mrp/models/mrp_workorder.py
index 18329432edd..61e7865d5cc 100644
--- a/addons/mrp/models/mrp_workorder.py
+++ b/addons/mrp/models/mrp_workorder.py
@@ -294,11 +294,15 @@ class MrpWorkorder(models.Model):
# For each untracked component without any 'temporary' move lines,
# (the new workorder tablet view allows registering consumed quantities for untracked components)
# we assume that only the theoretical quantity was used
- raw_moves = self.move_raw_ids.filtered(lambda x: (x.has_tracking == 'none') and (x.state not in ('done', 'cancel')) and x.bom_line_id)
- for move in raw_moves:
- if move.unit_factor and not move.move_line_ids.filtered(lambda m: not m.done_wo):
+ for move in self.move_raw_ids:
+ if move.has_tracking == 'none' and (move.state not in ('done', 'cancel')) and move.bom_line_id\
+ and move.unit_factor and not move.move_line_ids.filtered(lambda ml: not ml.done_wo):
rounding = move.product_uom.rounding
- move.quantity_done += float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
+ if self.product_id.tracking != 'none':
+ qty_to_add = float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
+ move._generate_consumed_move_line(qty_to_add, self.final_lot_id)
+ else:
+ move.quantity_done += float_round(self.qty_producing * move.unit_factor, precision_rounding=rounding)
# Transfer quantities from temporary to final move lots or make them final
for move_line in self.active_move_line_ids:
diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py
index 966e3365b6c..4eb94b2379a 100644
--- a/addons/mrp/models/stock_move.py
+++ b/addons/mrp/models/stock_move.py
@@ -68,6 +68,7 @@ class StockMove(models.Model):
help='Technical Field to order moves')
needs_lots = fields.Boolean('Tracking', compute='_compute_needs_lots')
order_finished_lot_ids = fields.Many2many('stock.production.lot', compute='_compute_order_finished_lot_ids')
+ finished_lots_exist = fields.Boolean('Finished Lots Exist', compute='_compute_order_finished_lot_ids')
@api.depends('active_move_line_ids.qty_done', 'active_move_line_ids.product_uom_id')
def _compute_done_quantity(self):
@@ -76,8 +77,13 @@ class StockMove(models.Model):
@api.depends('raw_material_production_id.move_finished_ids.move_line_ids.lot_id')
def _compute_order_finished_lot_ids(self):
for move in self:
- if move.product_id.tracking != 'none' and move.raw_material_production_id:
- move.order_finished_lot_ids = move.raw_material_production_id.move_finished_ids.mapped('move_line_ids.lot_id').ids
+ if move.raw_material_production_id.move_finished_ids:
+ finished_lots_ids = move.raw_material_production_id.move_finished_ids.mapped('move_line_ids.lot_id').ids
+ if finished_lots_ids:
+ move.order_finished_lot_ids = finished_lots_ids
+ move.finished_lots_exist = True
+ else:
+ move.finished_lots_exist = False
@api.depends('product_id.tracking')
def _compute_needs_lots(self):
@@ -178,6 +184,37 @@ class StockMove(models.Model):
})
return self.env['stock.move']
+ def _generate_consumed_move_line(self, qty_to_add, final_lot, lot=False):
+ if lot:
+ ml = self.move_line_ids.filtered(lambda ml: ml.lot_id == lot and not ml.lot_produced_id)
+ else:
+ ml = self.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_produced_id)
+ if ml:
+ new_quantity_done = (ml.qty_done + qty_to_add)
+ if new_quantity_done >= ml.product_uom_qty:
+ ml.write({'qty_done': new_quantity_done, 'lot_produced_id': final_lot.id})
+ else:
+ new_qty_reserved = ml.product_uom_qty - new_quantity_done
+ default = {'product_uom_qty': new_quantity_done,
+ 'qty_done': new_quantity_done,
+ 'lot_produced_id': final_lot.id}
+ ml.copy(default=default)
+ ml.with_context(bypass_reservation_update=True).write({'product_uom_qty': new_qty_reserved, 'qty_done': 0})
+ else:
+ vals = {
+ 'move_id': self.id,
+ 'product_id': self.product_id.id,
+ 'location_id': self.location_id.id,
+ 'location_dest_id': self.location_dest_id.id,
+ 'product_uom_qty': 0,
+ 'product_uom_id': self.product_uom.id,
+ 'qty_done': qty_to_add,
+ 'lot_produced_id': final_lot.id,
+ }
+ if lot:
+ vals.update({'lot_id': lot.id})
+ self.env['stock.move.line'].create(vals)
+
class PushedFlow(models.Model):
_inherit = "stock.location.path"
diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py
index 8c53593ab0f..0c98b64b386 100644
--- a/addons/mrp/tests/test_unbuild.py
+++ b/addons/mrp/tests/test_unbuild.py
@@ -269,8 +269,6 @@ class TestUnbuild(TestMrpCommon):
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 100, lot_id=lot_1)
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5, lot_id=lot_2)
mo.action_assign()
- for ml in mo.move_raw_ids.mapped('move_line_ids'):
- ml.qty_done = ml.product_qty
produce_wizard = self.env['mrp.product.produce'].with_context({
'active_id': mo.id,
@@ -279,6 +277,8 @@ class TestUnbuild(TestMrpCommon):
'product_qty': 5.0,
'lot_id': lot_final.id,
})
+ for pl in produce_wizard.produce_line_ids:
+ pl.qty_done = pl.qty_to_consume
produce_wizard.do_produce()
mo.button_mark_done()
@@ -399,3 +399,57 @@ class TestUnbuild(TestMrpCommon):
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_1), 1, 'You should have get your product with lot 1 in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_2), 3, 'You should have the 3 basic product for lot 2 in stock')
self.assertEqual(self.env['stock.quant']._get_available_quantity(p2, self.stock_location, lot_id=lot_3), 2, 'You should have get one product back for lot 3')
+
+
+ def test_production_links_with_non_tracked_lots(self):
+ """ This test produces an MO in two times and checks that the move lines are linked in a correct way
+ """
+ mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='lot', tracking_base_1='none', tracking_base_2='lot')
+ lot_1 = self.env['stock.production.lot'].create({
+ 'name': 'lot_1',
+ 'product_id': p2.id,
+ })
+
+ self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot_1)
+ lot_finished_1 = self.env['stock.production.lot'].create({
+ 'name': 'lot_finished_1',
+ 'product_id': p_final.id,
+ })
+
+ produce_wizard = self.env['mrp.product.produce'].with_context({
+ 'active_id': mo.id,
+ 'active_ids': [mo.id],
+ }).create({
+ 'product_qty': 3.0,
+ 'lot_id': lot_finished_1.id,
+ })
+
+ produce_wizard.produce_line_ids[0].lot_id = lot_1.id
+ produce_wizard.do_produce()
+
+ lot_2 = self.env['stock.production.lot'].create({
+ 'name': 'lot_2',
+ 'product_id': p2.id,
+ })
+
+ self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 4, lot_id=lot_2)
+ lot_finished_2 = self.env['stock.production.lot'].create({
+ 'name': 'lot_finished_2',
+ 'product_id': p_final.id,
+ })
+
+ produce_wizard = self.env['mrp.product.produce'].with_context({
+ 'active_id': mo.id,
+ 'active_ids': [mo.id],
+ }).create({
+ 'product_qty': 2.0,
+ 'lot_id': lot_finished_2.id,
+ })
+
+ produce_wizard.produce_line_ids[0].lot_id = lot_2.id
+ produce_wizard.do_produce()
+ mo.button_mark_done()
+ ml = mo.finished_move_line_ids[0].consume_line_ids.filtered(lambda m: m.product_id == p1 and m.lot_produced_id == lot_finished_1)
+ self.assertEqual(ml.qty_done, 12.0, 'Should have consumed 12 for the first lot')
+ ml = mo.finished_move_line_ids[1].consume_line_ids.filtered(lambda m: m.product_id == p1 and m.lot_produced_id == lot_finished_2)
+ self.assertEqual(ml.qty_done, 8.0, 'Should have consumed 8 for the second lot')
\ No newline at end of file
diff --git a/addons/mrp/views/stock_move_views.xml b/addons/mrp/views/stock_move_views.xml
index 9a1e0970836..879f576f3f9 100644
--- a/addons/mrp/views/stock_move_views.xml
+++ b/addons/mrp/views/stock_move_views.xml
@@ -26,7 +26,7 @@
-
+
/
@@ -41,11 +41,12 @@
+
-
+
-
+
diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py
index dd8d0863524..eef65170bb6 100644
--- a/addons/mrp/wizard/mrp_product_produce.py
+++ b/addons/mrp/wizard/mrp_product_produce.py
@@ -42,7 +42,7 @@ class MrpProductProduce(models.TransientModel):
for move_line in move.move_line_ids:
if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
break
- if float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
+ if move_line.lot_produced_id or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
continue
to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
lines.append({
@@ -96,7 +96,11 @@ class MrpProductProduce(models.TransientModel):
# TODO currently not possible to guess if the user updated quantity by hand or automatically by the produce wizard.
if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel') and move.unit_factor:
rounding = move.product_uom.rounding
- move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
+ if self.product_id.tracking != 'none':
+ qty_to_add = float_round(quantity * move.unit_factor, precision_rounding=rounding)
+ move._generate_consumed_move_line(qty_to_add, self.lot_id)
+ else:
+ move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
for move in self.production_id.move_finished_ids:
if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel'):
rounding = move.product_uom.rounding
@@ -132,7 +136,7 @@ class MrpProductProduce(models.TransientModel):
'product_uom_id': produce_move.product_uom.id,
'qty_done': self.product_qty,
'lot_id': self.lot_id.id,
- 'location_id': produce_move.location_id.id,
+ 'location_id': produce_move.location_id.id,
'location_dest_id': produce_move.location_dest_id.id,
}
self.env['stock.move.line'].create(vals)
@@ -150,39 +154,16 @@ class MrpProductProduce(models.TransientModel):
# create a move and put it in there
order = self.production_id
pl.move_id = self.env['stock.move'].create({
- 'name': order.name,
- 'product_id': pl.product_id.id,
- 'product_uom': pl.product_uom_id.id,
- 'location_id': order.location_src_id.id,
- 'location_dest_id': self.product_id.property_stock_production.id,
- 'raw_material_production_id': order.id,
- 'origin': order.name,
- 'group_id': order.procurement_group_id.id,
- 'state': 'confirmed',
- })
- ml = pl.move_id.move_line_ids.filtered(lambda ml: ml.lot_id == pl.lot_id and not ml.lot_produced_id)
- if ml:
- if (ml.qty_done + pl.qty_done) >= ml.product_uom_qty:
- ml.write({'qty_done': ml.qty_done + pl.qty_done, 'lot_produced_id': self.lot_id.id})
- else:
- new_qty_todo = ml.product_uom_qty - (ml.qty_done + pl.qty_done)
- default = {'product_uom_qty': ml.qty_done + pl.qty_done,
- 'qty_done': ml.qty_done + pl.qty_done,
- 'lot_produced_id': self.lot_id.id}
- ml.copy(default=default)
- ml.with_context(bypass_reservation_update=True).write({'product_uom_qty': new_qty_todo, 'qty_done': 0})
- else:
- self.env['stock.move.line'].create({
- 'move_id': pl.move_id.id,
- 'product_id': pl.product_id.id,
- 'location_id': pl.move_id.location_id.id,
- 'location_dest_id': pl.move_id.location_dest_id.id,
- 'product_uom_qty': 0,
- 'product_uom_id': pl.product_uom_id.id,
- 'qty_done': pl.qty_done,
- 'lot_id': pl.lot_id.id,
- 'lot_produced_id': self.lot_id.id,
- })
+ 'name': order.name,
+ 'product_id': pl.product_id.id,
+ 'product_uom': pl.product_uom_id.id,
+ 'location_id': order.location_src_id.id,
+ 'location_dest_id': self.product_id.property_stock_production.id,
+ 'raw_material_production_id': order.id,
+ 'group_id': order.procurement_group_id.id,
+ 'origin': order.name,
+ 'state': 'confirmed'})
+ pl.move_id._generate_consumed_move_line(pl.qty_done, self.lot_id, lot=pl.lot_id)
return True