From 8852dc2fb9816d28120fcf7fe6cc8984a7b99b44 Mon Sep 17 00:00:00 2001 From: Vraj Panchal Date: Thu, 17 Aug 2023 16:55:51 +0530 Subject: [PATCH] [FIX] mrp: update workcenter with BOM Before this commit ================= While updating the Bill of Materials (BOM), the work center and operation name remained unchanged in the manufacturing order. When Archiving/Unarchiving the operation doesn't show the "Update BoM" button in the appropriate MO. Steps to Produce ================ - Create a BOM1 with operations - Create a MO with BOM1 and confirm MO - Goto BOM and change the workcenter and operation name - In MO Click on the 'update BoM' button - Now you see the operation name and workcenter are not updated in MO After this commit ================= With this commit the work center and operation name get updated with the update BOM click. For Archiving/Unarchiving the operation results to the display of the "Update BoM" button in the appropriate MO. Directly editing an operation will also cause the "Update BoM" button to appear task-3461985 closes odoo/odoo#138256 X-original-commit: c343ebf0a28f6404f7ad6fc892c4a42edd2bbbe0 Signed-off-by: Tiffany Chang (tic) --- addons/mrp/models/mrp_bom.py | 34 ++++++++++++---------- addons/mrp/models/mrp_production.py | 8 ++++++ addons/mrp/models/mrp_routing.py | 17 +++++++++++ addons/mrp/tests/test_bom.py | 44 +++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 15 deletions(-) diff --git a/addons/mrp/models/mrp_bom.py b/addons/mrp/models/mrp_bom.py index 32884606161..7c15be50981 100644 --- a/addons/mrp/models/mrp_bom.py +++ b/addons/mrp/models/mrp_bom.py @@ -244,22 +244,9 @@ class MrpBom(models.Model): def write(self, vals): res = super().write(vals) - relevant_fields = ['bom_line_ids', 'byproduct_ids', 'product_tmpl_id', 'product_id', 'product_qty', 'operation_ids'] + relevant_fields = ['bom_line_ids', 'byproduct_ids', 'product_tmpl_id', 'product_id', 'product_qty'] if any(field_name in vals for field_name in relevant_fields): - # Searches for MOs using these BoMs to notify them that their BoM has been updated. - list_of_domain_by_bom = [] - for bom in self: - domain_by_products = [('product_id', 'in', bom.product_tmpl_id.product_variant_ids.ids)] - if bom.product_id: - domain_by_products = [('product_id', '=', bom.product_id.id)] - domain_for_confirmed_mo = AND([[('state', '=', 'confirmed')], domain_by_products]) - # Avoid confirmed MOs if the BoM's product was changed. - domain_by_states = OR([[('state', '=', 'draft')], domain_for_confirmed_mo]) - list_of_domain_by_bom.append(AND([[('bom_id', '=', bom.id)], domain_by_states])) - domain = OR(list_of_domain_by_bom) - productions = self.env['mrp.production'].search(domain) - if productions: - productions.is_outdated_bom = True + self._set_outdated_bom_in_productions() if 'sequence' in vals and self and self[-1].id == self._prefetch_ids[-1]: self.browse(self._prefetch_ids)._check_bom_cycle() return res @@ -428,6 +415,23 @@ class MrpBom(models.Model): 'template': '/mrp/static/xls/mrp_bom.xls' }] + def _set_outdated_bom_in_productions(self): + # Searches for MOs using these BoMs to notify them that their BoM has been updated. + list_of_domain_by_bom = [] + for bom in self: + domain_by_products = [('product_id', 'in', bom.product_tmpl_id.product_variant_ids.ids)] + if bom.product_id: + domain_by_products = [('product_id', '=', bom.product_id.id)] + domain_for_confirmed_mo = AND([[('state', '=', 'confirmed')], domain_by_products]) + # Avoid confirmed MOs if the BoM's product was changed. + domain_by_states = OR([[('state', '=', 'draft')], domain_for_confirmed_mo]) + list_of_domain_by_bom.append(AND([[('bom_id', '=', bom.id)], domain_by_states])) + if list_of_domain_by_bom: + domain = OR(list_of_domain_by_bom) + productions = self.env['mrp.production'].search(domain) + if productions: + productions.is_outdated_bom = True + class MrpBomLine(models.Model): _name = 'mrp.bom.line' diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index e7ba82f861b..a9d780c5d30 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -2314,6 +2314,13 @@ class MrpProduction(models.Model): break if operation and workorder.operation_id != operation: workorder.operation_id = operation + elif operation and workorder.operation_id == operation: + if workorder.workcenter_id != operation.workcenter_id: + workorder.workcenter_id = operation.workcenter_id + if workorder.name != operation.name: + workorder.name = operation.name + elif workorder.operation_id and workorder.operation_id not in operations_by_id: + workorders_to_unlink |= workorder # Creates a workorder for each remaining operation. workorders_values = [] for operation in operations_by_id.values(): @@ -2403,6 +2410,7 @@ class MrpProduction(models.Model): moves_to_unlink._action_cancel() moves_to_unlink.unlink() + workorders_to_unlink.unlink() self.bom_id = bom @api.model diff --git a/addons/mrp/models/mrp_routing.py b/addons/mrp/models/mrp_routing.py index 5e217cb5ee6..a68cc26f3d4 100644 --- a/addons/mrp/models/mrp_routing.py +++ b/addons/mrp/models/mrp_routing.py @@ -105,10 +105,27 @@ class MrpRoutingWorkcenter(models.Model): if not self._check_m2m_recursion('blocked_by_operation_ids'): raise ValidationError(_("You cannot create cyclic dependency.")) + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) + res.bom_id._set_outdated_bom_in_productions() + return res + + def write(self, vals): + res = super().write(vals) + self.bom_id._set_outdated_bom_in_productions() + return res + def action_archive(self): res = super().action_archive() bom_lines = self.env['mrp.bom.line'].search([('operation_id', 'in', self.ids)]) bom_lines.write({'operation_id': False}) + self.bom_id._set_outdated_bom_in_productions() + return res + + def action_unarchive(self): + res = super().action_unarchive() + self.bom_id._set_outdated_bom_in_productions() return res def copy_to_bom(self): diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index 4df985d063b..4ede513ae4c 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -1955,3 +1955,47 @@ class TestBoM(TestMrpCommon): # Check that component 1 is set again and component 2 is removed self.assertEqual(mo_form.move_raw_ids._records[0]['product_id'], self.product_1.id) self.assertEqual(len(mo_form.move_raw_ids._records), 1) + + def test_update_operations(self): + """Update the operations in BoM which reflects the changes in Manufacturing Order""" + + mo_form = Form(self.env['mrp.production'].with_user(self.user_mrp_user)) + mo_form.product_id = self.product_7_1 + mo_form.product_qty = 1.0 + mo_form.bom_id = self.bom_2 + mo = mo_form.save() + mo.action_confirm() + + self.bom_2.operation_ids.write({ + 'name': 'Painting', + 'workcenter_id': self.workcenter_2.id + }) + self.assertTrue(mo.is_outdated_bom) + + mo.action_update_bom() + self.assertEqual(self.bom_2.operation_ids.name, mo.workorder_ids.name) + self.assertEqual(self.bom_2.operation_ids.workcenter_id, mo.workorder_ids.workcenter_id) + + def test_archive_operations(self): + """Archive the operation in BoM and update the BoM in MO + Unarchive the operation which will reflect in BoM and MO""" + + mo_form = Form(self.env['mrp.production'].with_user(self.user_mrp_user)) + + mo_form.product_id = self.product_7_1 + mo_form.product_qty = 1.0 + mo_form.bom_id = self.bom_2 + mo_order = mo_form.save() + mo_order.action_confirm() + + operation_ids = self.bom_2.operation_ids.ids + self.bom_2.operation_ids.action_archive() + self.assertTrue(mo_order.is_outdated_bom) + + mo_order.action_update_bom() + self.assertEqual(len(mo_order.workorder_ids), 0) + + self.env['mrp.routing.workcenter'].browse(operation_ids).action_unarchive() + self.assertTrue(mo_order.is_outdated_bom) + mo_order.action_update_bom() + self.assertEqual(len(mo_order.workorder_ids), 1)