diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index fdaa3d5e912..ecd196cf532 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -190,11 +190,11 @@ class MrpProduction(models.Model): move_raw_ids = fields.One2many( 'stock.move', 'raw_material_production_id', 'Components', - copy=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, + copy=False, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('scrapped', '=', False)]) move_finished_ids = fields.One2many( 'stock.move', 'production_id', 'Finished Products', - copy=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, + copy=False, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('scrapped', '=', False)]) move_byproduct_ids = fields.One2many('stock.move', compute='_compute_move_byproduct_ids', inverse='_set_move_byproduct_ids') finished_move_line_ids = fields.One2many( @@ -773,6 +773,16 @@ class MrpProduction(models.Model): workorders_to_delete.unlink() return super(MrpProduction, self).unlink() + def copy_data(self, default=None): + default = dict(default or {}) + # covers at least 2 cases: backorders generation (follow default logic for moves copying) + # and copying a done MO via the form (i.e. copy only the non-cancelled moves since no backorder = cancelled finished moves) + if not default or 'move_finished_ids' not in default: + default['move_finished_ids'] = [(0, 0, move.copy_data()[0]) for move in self.move_finished_ids.filtered(lambda m: m.state != 'cancel' and m.product_qty != 0.0)] + if not default or 'move_raw_ids' not in default: + default['move_raw_ids'] = [(0, 0, move.copy_data()[0]) for move in self.move_raw_ids.filtered(lambda m: m.product_qty != 0.0)] + return super(MrpProduction, self).copy_data(default=default) + def action_toggle_is_locked(self): self.ensure_one() self.is_locked = not self.is_locked diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 34343364000..46479ed66d6 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -55,7 +55,7 @@ class TestMrpOrder(TestMrpCommon): }).action_apply_inventory() test_date_planned = Dt.now() - timedelta(days=1) - test_quantity = 2.0 + test_quantity = 3.0 man_order_form = Form(self.env['mrp.production'].with_user(self.user_mrp_user)) man_order_form.product_id = self.product_4 man_order_form.bom_id = self.bom_1 @@ -89,7 +89,7 @@ class TestMrpOrder(TestMrpCommon): # produce product mo_form = Form(man_order) - mo_form.qty_producing = 1.0 + mo_form.qty_producing = 2.0 man_order = mo_form.save() action = man_order.button_mark_done() @@ -101,6 +101,14 @@ class TestMrpOrder(TestMrpCommon): backorder.save().action_close_mo() self.assertEqual(man_order.state, 'done', "Production order should be done.") + # check that copy handles moves correctly + mo_copy = man_order.copy() + self.assertEqual(mo_copy.state, 'draft', "Copied production order should be draft.") + self.assertEqual(len(mo_copy.move_raw_ids), 4, + "Incorrect number of component moves [i.e. all non-0 (even cancelled) moves should be copied].") + self.assertEqual(len(mo_copy.move_finished_ids), 1, "Incorrect number of moves for products to produce [i.e. cancelled moves should not be copied") + self.assertEqual(mo_copy.move_finished_ids.product_uom_qty, 2, "Incorrect qty of products to produce") + def test_production_availability(self): """ Checks the availability of a production order through mutliple calls to `action_assign`. """