diff --git a/mrp_workorder_sequence/__init__.py b/mrp_workorder_sequence/__init__.py index 44f9fd7c1..bb1838349 100644 --- a/mrp_workorder_sequence/__init__.py +++ b/mrp_workorder_sequence/__init__.py @@ -1,3 +1,4 @@ # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). from . import models +from .hooks import post_init_hook diff --git a/mrp_workorder_sequence/__manifest__.py b/mrp_workorder_sequence/__manifest__.py index 598dbd09c..b69046b61 100644 --- a/mrp_workorder_sequence/__manifest__.py +++ b/mrp_workorder_sequence/__manifest__.py @@ -14,4 +14,5 @@ "depends": ["mrp"], "data": ["views/mrp_workorder_view.xml"], "installable": True, + "post_init_hook": "post_init_hook", } diff --git a/mrp_workorder_sequence/hooks.py b/mrp_workorder_sequence/hooks.py new file mode 100644 index 000000000..52e2f89e4 --- /dev/null +++ b/mrp_workorder_sequence/hooks.py @@ -0,0 +1,11 @@ +# Copyright 2022 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import SUPERUSER_ID, api, tools + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + all_workorders = env["mrp.workorder"].search([], order="production_id ASC, id ASC") + for _, workorders in tools.groupby(all_workorders, lambda w: w.production_id): + for seq, wo in enumerate(workorders, 1): + wo.sequence = seq diff --git a/mrp_workorder_sequence/models/mrp_production.py b/mrp_workorder_sequence/models/mrp_production.py index a631994d0..c2531fe65 100644 --- a/mrp_workorder_sequence/models/mrp_production.py +++ b/mrp_workorder_sequence/models/mrp_production.py @@ -9,12 +9,15 @@ class MrpProduction(models.Model): def _reset_work_order_sequence(self): for rec in self: - current_sequence = 1 - for work in rec.workorder_ids: - work.sequence = current_sequence - current_sequence += 1 + for current_seq, work in enumerate(rec.workorder_ids, 1): + work.sequence = current_seq def _create_workorder(self): - res = super()._create_workorder() + # Bypass sequence assignation on create and make sure there is no gap + # using _reset_work_order_sequence + res = super( + MrpProduction, + self.with_context(_bypass_sequence_assignation_on_create=True), + )._create_workorder() self._reset_work_order_sequence() return res diff --git a/mrp_workorder_sequence/models/mrp_workorder.py b/mrp_workorder_sequence/models/mrp_workorder.py index e350595a5..03e7c8036 100644 --- a/mrp_workorder_sequence/models/mrp_workorder.py +++ b/mrp_workorder_sequence/models/mrp_workorder.py @@ -1,10 +1,42 @@ # Copyright 2019-20 ForgeFlow S.L. (https://www.forgeflow.com) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from odoo import fields, models +from odoo import api, fields, models class MrpWorkOrder(models.Model): _inherit = "mrp.workorder" + _order = "production_id, sequence, id" sequence = fields.Integer() + + def _assign_sequence_on_create(self, values_list): + """Assign sequence number for manually added operations""" + new_wos_production_ids_without_seq = { + vals["production_id"] for vals in values_list if not vals.get("sequence") + } + if new_wos_production_ids_without_seq: + max_seq_by_production = self.read_group( + [("production_id", "in", list(new_wos_production_ids_without_seq))], + ["sequence:max", "production_id"], + ["production_id"], + ) + max_seq_by_prod_id = { + res["production_id"][0]: res["sequence"] + for res in max_seq_by_production + } + for values in values_list: + prod_id = values["production_id"] + values_seq = values.get("sequence") + max_seq = max_seq_by_prod_id.setdefault(prod_id, 0) + if values_seq and values_seq > max_seq: + max_seq_by_prod_id[prod_id] = values_seq + continue + max_seq_by_prod_id[prod_id] += 1 + values["sequence"] = max_seq_by_prod_id[prod_id] + + @api.model_create_multi + def create(self, values_list): + if not self.env.context.get("_bypass_sequence_assignation_on_create"): + self._assign_sequence_on_create(values_list) + return super().create(values_list) diff --git a/mrp_workorder_sequence/readme/CONTRIBUTORS.rst b/mrp_workorder_sequence/readme/CONTRIBUTORS.rst index f70560233..60325ea80 100644 --- a/mrp_workorder_sequence/readme/CONTRIBUTORS.rst +++ b/mrp_workorder_sequence/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * Lois Rilo * Pimolnat Suntian * Christopher Ormaza +* Akim Juillerat diff --git a/mrp_workorder_sequence/tests/test_mrp_workorder_sequence.py b/mrp_workorder_sequence/tests/test_mrp_workorder_sequence.py index c8a434a34..b4edfe3c9 100644 --- a/mrp_workorder_sequence/tests/test_mrp_workorder_sequence.py +++ b/mrp_workorder_sequence/tests/test_mrp_workorder_sequence.py @@ -1,15 +1,19 @@ # Copyright 2022 ForgeFlow S.L. (https://www.forgeflow.com) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). -from odoo import Command +from odoo import Command, fields from odoo.tests import Form from odoo.addons.mrp.tests.common import TestMrpCommon class TestMrpWorkorderSequence(TestMrpCommon): - def test_mrp_workorder_sequence(self): - self.env["mrp.bom"].create( + def setUp(self): + super().setUp() + self._create_bom() + + def _create_bom(self): + return self.env["mrp.bom"].create( { "product_tmpl_id": self.product_7_template.id, "product_uom_id": self.uom_unit.id, @@ -104,8 +108,95 @@ class TestMrpWorkorderSequence(TestMrpCommon): ], } ) + + def _create_order(self, product): mrp_order_form = Form(self.env["mrp.production"]) - mrp_order_form.product_id = self.product_7_3 + mrp_order_form.product_id = product + return mrp_order_form.save() + + def test_mrp_workorder_sequence_new_production(self): + mrp_order = self._create_order(self.product_7_1) + self.assertEqual(len(mrp_order.workorder_ids), 2) + for seq, workorder in enumerate(mrp_order.workorder_ids, 1): + self.assertEqual(workorder.sequence, seq) + + def test_mrp_workorder_sequence_new_production_new_workorder(self): + mrp_order = self._create_order(self.product_7_1) + self.assertEqual(len(mrp_order.workorder_ids), 2) + max_sequence = max(mrp_order.workorder_ids.mapped("sequence")) + mrp_order_form = Form(mrp_order) + with mrp_order_form.workorder_ids.new() as wo_form: + wo_form.name = "Extra operation" + wo_form.workcenter_id = self.workcenter_1 mrp_order = mrp_order_form.save() - for workorder in mrp_order.workorder_ids: - self.assertEqual(workorder.sequence, workorder.operation_id.sequence) + self.assertEqual(len(mrp_order.workorder_ids), 3) + last_wo = fields.first(mrp_order.workorder_ids.sorted(reverse=True)) + self.assertEqual(last_wo.sequence, max_sequence + 1) + + def test_mrp_workorder_create_multi(self): + """ + Test automatic sequence assignation through create override + + * WO 1: - each added operations without sequence defined + get the next sequence after existing WOs + * WO 2: - first added operation without sequence + get the next sequence after existing WOs + - second added operation with sequence defined stays unchanged + * WO 3: - first added operation with sequence defined stays unchanged + - second added operation without sequence defined + get the next sequence from previous operation created + """ + first_mrp_order = self._create_order(self.product_7_1) + second_mrp_order = self._create_order(self.product_7_1) + third_mrp_order = self._create_order(self.product_7_1) + create_values = [ + { + "name": "Extra WO 1.1", + "production_id": first_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "product_uom_id": self.product_7_1.uom_id.id, + }, + { + "name": "Extra WO 1.2", + "production_id": first_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "product_uom_id": self.product_7_1.uom_id.id, + }, + { + "name": "Extra WO 2.1", + "production_id": second_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "product_uom_id": self.product_7_1.uom_id.id, + }, + { + "name": "Extra WO 2.2", + "production_id": second_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "product_uom_id": self.product_7_1.uom_id.id, + "sequence": 6, + }, + { + "name": "Extra WO 3.1", + "production_id": third_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "sequence": 4, + "product_uom_id": self.product_7_1.uom_id.id, + }, + { + "name": "Extra WO 3.2", + "production_id": third_mrp_order.id, + "workcenter_id": self.workcenter_1.id, + "product_uom_id": self.product_7_1.uom_id.id, + }, + ] + created_wos = self.env["mrp.workorder"].create(create_values) + expected_res = { + "Extra WO 1.1": 3, + "Extra WO 1.2": 4, + "Extra WO 2.1": 3, + "Extra WO 2.2": 6, + "Extra WO 3.1": 4, + "Extra WO 3.2": 5, + } + for wo in created_wos: + self.assertEqual(wo.sequence, expected_res[wo.name])