From d90571b85f6128674e9f6e185b0845733cd829d8 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Tue, 7 Feb 2023 17:50:41 +0100 Subject: [PATCH] Create module mrp_workorder_lock_planning --- mrp_workorder_lock_planning/README.rst | 1 + mrp_workorder_lock_planning/__init__.py | 1 + mrp_workorder_lock_planning/__manifest__.py | 21 +++ .../models/__init__.py | 1 + .../models/mrp_workorder.py | 45 +++++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 4 + mrp_workorder_lock_planning/tests/__init__.py | 0 .../test_mrp_workorder_lock_date_planned.py | 37 ++++++ .../views/mrp_workorder.xml | 121 ++++++++++++++++++ .../odoo/addons/mrp_workorder_lock_planning | 1 + setup/mrp_workorder_lock_planning/setup.py | 6 + 12 files changed, 239 insertions(+) create mode 100644 mrp_workorder_lock_planning/README.rst create mode 100644 mrp_workorder_lock_planning/__init__.py create mode 100644 mrp_workorder_lock_planning/__manifest__.py create mode 100644 mrp_workorder_lock_planning/models/__init__.py create mode 100644 mrp_workorder_lock_planning/models/mrp_workorder.py create mode 100644 mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst create mode 100644 mrp_workorder_lock_planning/readme/DESCRIPTION.rst create mode 100644 mrp_workorder_lock_planning/tests/__init__.py create mode 100644 mrp_workorder_lock_planning/tests/test_mrp_workorder_lock_date_planned.py create mode 100644 mrp_workorder_lock_planning/views/mrp_workorder.xml create mode 120000 setup/mrp_workorder_lock_planning/odoo/addons/mrp_workorder_lock_planning create mode 100644 setup/mrp_workorder_lock_planning/setup.py diff --git a/mrp_workorder_lock_planning/README.rst b/mrp_workorder_lock_planning/README.rst new file mode 100644 index 000000000..7093cd388 --- /dev/null +++ b/mrp_workorder_lock_planning/README.rst @@ -0,0 +1 @@ +to be generated diff --git a/mrp_workorder_lock_planning/__init__.py b/mrp_workorder_lock_planning/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/mrp_workorder_lock_planning/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/mrp_workorder_lock_planning/__manifest__.py b/mrp_workorder_lock_planning/__manifest__.py new file mode 100644 index 000000000..0be3c7c7b --- /dev/null +++ b/mrp_workorder_lock_planning/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +{ + "name": "MRP Workorder Lock Planning", + "summary": "Lock the planning of a MRP workorder to avoid rescheduling", + "version": "15.0.1.0.0", + "development_status": "Beta", + "category": "Manufacturing/Manufacturing", + "website": "https://github.com/OCA/manufacture", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["grindtildeath"], + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "mrp", + ], + "data": [ + "views/mrp_workorder.xml", + ], +} diff --git a/mrp_workorder_lock_planning/models/__init__.py b/mrp_workorder_lock_planning/models/__init__.py new file mode 100644 index 000000000..ddc78464d --- /dev/null +++ b/mrp_workorder_lock_planning/models/__init__.py @@ -0,0 +1 @@ +from . import mrp_workorder diff --git a/mrp_workorder_lock_planning/models/mrp_workorder.py b/mrp_workorder_lock_planning/models/mrp_workorder.py new file mode 100644 index 000000000..2b3821b24 --- /dev/null +++ b/mrp_workorder_lock_planning/models/mrp_workorder.py @@ -0,0 +1,45 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class MrpWorkorder(models.Model): + _inherit = "mrp.workorder" + + lock_planning = fields.Boolean( + help="If set, any change to the planning will be restricted", + readonly=True, + ) + + @api.constrains("lock_planning") + def _check_lock_planning(self): + for workorder in self: + if not workorder.lock_planning: + continue + if not (workorder.date_planned_start and workorder.date_planned_finished): + raise ValidationError( + _("Cannot lock planning of workorder if workorder is not planned") + ) + + @api.constrains( + "date_planned_start", + "date_planned_finished", + "duration_expected", + "workcenter_id", + ) + def _check_lock_planning_fields(self): + for workorder in self: + if not workorder.lock_planning: + continue + raise ValidationError( + _( + "Workorder is locked and modification of Scheduled dates, " + "Expected duration or Workcenter is not allowed. " + "Please unlock the planning first." + ) + ) + + def toggle_lock_planning(self): + self.ensure_one() + self.lock_planning = not self.lock_planning diff --git a/mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst b/mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..e31e2f0c4 --- /dev/null +++ b/mrp_workorder_lock_planning/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Akim Juillerat diff --git a/mrp_workorder_lock_planning/readme/DESCRIPTION.rst b/mrp_workorder_lock_planning/readme/DESCRIPTION.rst new file mode 100644 index 000000000..68526e03d --- /dev/null +++ b/mrp_workorder_lock_planning/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module allows to lock the planning of workorders to avoid +any unintended rescheduling. + +A Lock/Unlock button will be displayed once a workorder is planned. diff --git a/mrp_workorder_lock_planning/tests/__init__.py b/mrp_workorder_lock_planning/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mrp_workorder_lock_planning/tests/test_mrp_workorder_lock_date_planned.py b/mrp_workorder_lock_planning/tests/test_mrp_workorder_lock_date_planned.py new file mode 100644 index 000000000..6c7e1b569 --- /dev/null +++ b/mrp_workorder_lock_planning/tests/test_mrp_workorder_lock_date_planned.py @@ -0,0 +1,37 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.exceptions import ValidationError +from odoo.tests import Form, TransactionCase + + +class TestMrpWorkorderLockDatePlanned(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.bom_with_routing = cls.env.ref("mrp.mrp_bom_drawer_rout") + cls.product_with_routing = ( + cls.bom_with_routing.product_tmpl_id.product_variant_id + ) + + @classmethod + def _create_manufacturing_order(cls, qty=1.0): + mo_form = Form(cls.env["mrp.production"]) + mo_form.product_id = cls.product_with_routing + mo_form.bom_id = cls.bom_with_routing + mo_form.product_qty = qty + return mo_form.save() + + def test_lock_planning_cannot_lock(self): + mo = self._create_manufacturing_order() + mo.action_confirm() + with self.assertRaisesRegex(ValidationError, "Cannot lock planning"): + mo.workorder_ids[0].toggle_lock_planning() + + def test_lock_planning_is_locked(self): + mo = self._create_manufacturing_order() + mo.action_confirm() + mo.button_plan() + mo.workorder_ids[0].toggle_lock_planning() + self.assertTrue(mo.workorder_ids[0].lock_planning) + with self.assertRaisesRegex(ValidationError, "Workorder is locked"): + mo.button_unplan() diff --git a/mrp_workorder_lock_planning/views/mrp_workorder.xml b/mrp_workorder_lock_planning/views/mrp_workorder.xml new file mode 100644 index 000000000..1be77fab7 --- /dev/null +++ b/mrp_workorder_lock_planning/views/mrp_workorder.xml @@ -0,0 +1,121 @@ + + + + mrp.production.work.order.tree.editable.inherit + mrp.workorder + + + + +