[FIX] mrp: generate move_line with same lot
Usecase to reproduce: - Set a product with the same lot in different locations - Create a MO that will uses theses products and that require more than one location - Produce it. -> Traceback Expected singleton: stock.move.line It happens because product produce only expect one move line by lot. Howeever it could exist more than one move line if the product for a specific lot are shared among multiple under location. This commit try to fill the move line with the correct quantity one by one and add the extra in a new move line.
This commit is contained in:
@@ -190,12 +190,18 @@ class StockMove(models.Model):
|
||||
|
||||
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)
|
||||
move_lines = 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:
|
||||
move_lines = self.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_produced_id)
|
||||
for ml in move_lines:
|
||||
rounding = ml.product_uom_id.rounding
|
||||
if float_compare(qty_to_add, 0, precision_rounding=rounding) <= 0:
|
||||
break
|
||||
quantity_to_process = min(qty_to_add, ml.product_uom_qty - ml.qty_done)
|
||||
qty_to_add -= quantity_to_process
|
||||
|
||||
new_quantity_done = (ml.qty_done + quantity_to_process)
|
||||
if float_compare(new_quantity_done, ml.product_uom_qty, precision_rounding=rounding) >= 0:
|
||||
ml.write({'qty_done': new_quantity_done, 'lot_produced_id': final_lot.id})
|
||||
else:
|
||||
new_qty_reserved = ml.product_uom_qty - new_quantity_done
|
||||
@@ -204,7 +210,8 @@ class StockMove(models.Model):
|
||||
'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:
|
||||
|
||||
if float_compare(qty_to_add, 0, precision_rounding=self.product_uom.rounding) > 0:
|
||||
vals = {
|
||||
'move_id': self.id,
|
||||
'product_id': self.product_id.id,
|
||||
|
||||
@@ -276,7 +276,7 @@ class TestMrpOrder(TestMrpCommon):
|
||||
'final_lot_id': finished_lot.id,
|
||||
'qty_producing': 48
|
||||
})
|
||||
|
||||
|
||||
kit_wo.record_production()
|
||||
|
||||
self.assertEqual(kit_wo.state, 'done', "Workorder should be in done state.")
|
||||
@@ -431,9 +431,9 @@ class TestMrpOrder(TestMrpCommon):
|
||||
""" In previous versions we had rounding and efficiency fields. We check if we can still do the same, but with only the rounding on the UoM"""
|
||||
self.product_6.uom_id.rounding = 1.0
|
||||
bom_eff = self.env['mrp.bom'].create({'product_id': self.product_6.id,
|
||||
'product_tmpl_id': self.product_6.product_tmpl_id.id,
|
||||
'product_qty': 1,
|
||||
'product_uom_id': self.product_6.uom_id.id,
|
||||
'product_tmpl_id': self.product_6.product_tmpl_id.id,
|
||||
'product_qty': 1,
|
||||
'product_uom_id': self.product_6.uom_id.id,
|
||||
'type': 'normal',
|
||||
'bom_line_ids': [
|
||||
(0, 0, {'product_id': self.product_2.id, 'product_qty': 2.03}),
|
||||
@@ -447,7 +447,7 @@ class TestMrpOrder(TestMrpCommon):
|
||||
#Check the production order has the right quantities
|
||||
self.assertEqual(production.move_raw_ids[0].product_qty, 41, 'The quantity should be rounded up')
|
||||
self.assertEqual(production.move_raw_ids[1].product_qty, 84, 'The quantity should be rounded up')
|
||||
|
||||
|
||||
# produce product
|
||||
produce_wizard = self.env['mrp.product.produce'].with_context({
|
||||
'active_id': production.id,
|
||||
@@ -516,3 +516,65 @@ class TestMrpOrder(TestMrpCommon):
|
||||
}).create({})
|
||||
self.assertEqual(len(product_produce.produce_line_ids), 1, 'You should have 1 produce lines since one has already be consumed.')
|
||||
self.assertEqual(product_produce.produce_line_ids[0].lot_id, remaining_lot, 'Wrong lot proposed.')
|
||||
|
||||
def test_product_produce_3(self):
|
||||
""" Check that line are created when the consumed products are
|
||||
tracked by serial and the lot proposed are correct. """
|
||||
self.stock_location = self.env.ref('stock.stock_location_stock')
|
||||
self.stock_shelf_1 = self.env.ref('stock.stock_location_components')
|
||||
self.stock_shelf_2 = self.env.ref('stock.stock_location_14')
|
||||
mo, _, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot', qty_base_1=10, qty_final=1)
|
||||
self.assertEqual(len(mo), 1, 'MO should have been created')
|
||||
|
||||
first_lot_for_p1 = self.env['stock.production.lot'].create({
|
||||
'name': 'lot1',
|
||||
'product_id': p1.id,
|
||||
})
|
||||
second_lot_for_p1 = self.env['stock.production.lot'].create({
|
||||
'name': 'lot2',
|
||||
'product_id': p1.id,
|
||||
})
|
||||
|
||||
final_product_lot = self.env['stock.production.lot'].create({
|
||||
'name': 'lot1',
|
||||
'product_id': p_final.id,
|
||||
})
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_shelf_1, 3, lot_id=first_lot_for_p1)
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_shelf_2, 3, lot_id=first_lot_for_p1)
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 8, lot_id=second_lot_for_p1)
|
||||
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5)
|
||||
|
||||
mo.action_assign()
|
||||
product_produce = self.env['mrp.product.produce'].with_context({
|
||||
'active_id': mo.id,
|
||||
'active_ids': [mo.id],
|
||||
}).create({
|
||||
'product_qty': 1.0,
|
||||
'lot_id': final_product_lot.id,
|
||||
})
|
||||
# product 1 lot 1 shelf1
|
||||
# product 1 lot 1 shelf2
|
||||
# product 1 lot 2
|
||||
self.assertEqual(len(product_produce.produce_line_ids), 3, 'You should have 3 produce lines. lot 1 shelf_1, lot 1 shelf_2, lot2')
|
||||
|
||||
for produce_line in product_produce.produce_line_ids:
|
||||
produce_line.qty_done = produce_line.qty_to_consume + 1
|
||||
product_produce.do_produce()
|
||||
|
||||
move_1 = mo.move_raw_ids.filtered(lambda m: m.product_id == p1)
|
||||
# qty_done/product_uom_qty lot
|
||||
# 3/3 lot 1 shelf 1
|
||||
# 1/1 lot 1 shelf 2
|
||||
# 2/2 lot 1 shelf 2
|
||||
# 2/0 lot 1 other
|
||||
# 5/4 lot 2
|
||||
ml_to_shelf_1 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_1)
|
||||
ml_to_shelf_2 = move_1.move_line_ids.filtered(lambda ml: ml.lot_id == first_lot_for_p1 and ml.location_id == self.stock_shelf_2)
|
||||
|
||||
self.assertEqual(sum(ml_to_shelf_1.mapped('qty_done')), 3.0, '3 units should be took from shelf1 as reserved.')
|
||||
self.assertEqual(sum(ml_to_shelf_2.mapped('qty_done')), 3.0, '3 units should be took from shelf2 as reserved.')
|
||||
self.assertEqual(move_1.quantity_done, 13, 'You should have used the tem units.')
|
||||
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
|
||||
|
||||
Reference in New Issue
Block a user