diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 56b87b7e61d..656156a9cde 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -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.") diff --git a/addons/mrp/wizard/mrp_product_produce.py b/addons/mrp/wizard/mrp_product_produce.py index d854e667822..077c62f04b1 100644 --- a/addons/mrp/wizard/mrp_product_produce.py +++ b/addons/mrp/wizard/mrp_product_produce.py @@ -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'))