[FIX] mrp: raw materials and multi-location
- Create a BOM for Product P with the following component: Product C, 5.0 units - Make some stock for C: 2 units in location 1 3 units in location 2 - Create a MO for P, reserve. When trying to produce an error arise: > Cannot set the done quantity from this stock move, work directly with > the move lines. If the user tries to unlock the MO in order to set the done quantity on the move lines, the same error will arise when trying to produce. In the specific scope of the production, we set the quantity done on the move line instead of directly on the move, to avoid this limitation. opw-1871241 Co-authored-by: Arnold Moyaux <arm@odoo.com>
This commit is contained in:
committed by
Nicolas Martinelli
co-authored by
Arnold Moyaux
parent
412c471e78
commit
f0028bc837
@@ -578,3 +578,42 @@ class TestMrpOrder(TestMrpCommon):
|
||||
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
|
||||
|
||||
def test_product_produce_4(self):
|
||||
""" Possibility to produce with a given raw material in multiple locations. """
|
||||
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(qty_final=1, qty_base_1=5)
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_shelf_1, 2)
|
||||
self.env['stock.quant']._update_available_quantity(p1, self.stock_shelf_2, 3)
|
||||
self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 1)
|
||||
|
||||
mo.action_assign()
|
||||
ml_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1).mapped('move_line_ids')
|
||||
ml_p2 = mo.move_raw_ids.filtered(lambda x: x.product_id == p2).mapped('move_line_ids')
|
||||
self.assertEqual(len(ml_p1), 2)
|
||||
self.assertEqual(len(ml_p2), 1)
|
||||
|
||||
# Add some quantity already done to force an extra move line to be created
|
||||
ml_p1[0].qty_done = 1.0
|
||||
|
||||
# Produce baby!
|
||||
product_produce = self.env['mrp.product.produce'].with_context({
|
||||
'active_id': mo.id,
|
||||
'active_ids': [mo.id],
|
||||
}).create({
|
||||
'product_qty': 1.0,
|
||||
})
|
||||
product_produce.do_produce()
|
||||
|
||||
ml_p1 = mo.move_raw_ids.filtered(lambda x: x.product_id == p1).mapped('move_line_ids')
|
||||
self.assertEqual(len(ml_p1), 3)
|
||||
for ml in ml_p1:
|
||||
self.assertIn(ml.qty_done, [1.0, 2.0, 3.0], 'Quantity done should be 1.0, 2.0 or 3.0')
|
||||
self.assertEqual(sum(ml_p1.mapped('qty_done')), 6.0, 'Total qty consumed should be 6.0')
|
||||
self.assertEqual(sum(ml_p1.mapped('product_uom_qty')), 5.0, 'Total qty reserved should be 5.0')
|
||||
|
||||
mo.button_mark_done()
|
||||
self.assertEqual(mo.state, 'done', "Production order should be in done state.")
|
||||
|
||||
@@ -100,8 +100,10 @@ class MrpProductProduce(models.TransientModel):
|
||||
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:
|
||||
elif len(move._get_move_lines()) < 2:
|
||||
move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
|
||||
else:
|
||||
self._set_quantity_done(move, quantity * move.unit_factor)
|
||||
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
|
||||
@@ -118,6 +120,40 @@ class MrpProductProduce(models.TransientModel):
|
||||
})
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def _set_quantity_done(self, move, qty):
|
||||
"""
|
||||
Set the given quantity as quantity done on the move through the move lines. The method is
|
||||
able to handle move lines with a different UoM than the move (but honestly, this would be
|
||||
looking for trouble...).
|
||||
@param move: stock.move
|
||||
@param qty: quantity in the UoM of move.product_uom
|
||||
"""
|
||||
for ml in move.move_line_ids:
|
||||
# Convert move line qty into move uom
|
||||
ml_qty = ml.product_uom_qty - ml.qty_done
|
||||
if ml.product_uom_id != move.product_uom:
|
||||
ml_qty = ml.product_uom_id._compute_quantity(ml_qty, move.product_uom, round=False)
|
||||
|
||||
taken_qty = min(qty, ml_qty)
|
||||
# Convert taken qty into move line uom
|
||||
if ml.product_uom_id != move.product_uom:
|
||||
taken_qty = move.product_uom._compute_quantity(ml_qty, ml.product_uom_id, round=False)
|
||||
|
||||
# Assign qty_done and explicitly round to make sure there is no inconsistency between
|
||||
# ml.qty_done and qty.
|
||||
taken_qty = float_round(taken_qty, precision_rounding=ml.product_uom_id.rounding)
|
||||
ml.qty_done += taken_qty
|
||||
if ml.product_uom_id != move.product_uom:
|
||||
taken_qty = ml.product_uom_id._compute_quantity(ml_qty, move.product_uom, round=False)
|
||||
qty -= taken_qty
|
||||
|
||||
if float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
|
||||
break
|
||||
if float_compare(qty, 0.0, precision_rounding=move.product_uom.rounding) > 0:
|
||||
vals = move._prepare_move_line_vals(quantity=0)
|
||||
vals['qty_done'] = qty
|
||||
ml = self.env['stock.move.line'].create(vals)
|
||||
|
||||
@api.multi
|
||||
def check_finished_move_lots(self):
|
||||
produce_move = self.production_id.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel'))
|
||||
|
||||
Reference in New Issue
Block a user