diff --git a/maintenance_plan/README.rst b/maintenance_plan/README.rst index b83d5a21b..c51980b3f 100644 --- a/maintenance_plan/README.rst +++ b/maintenance_plan/README.rst @@ -47,10 +47,10 @@ Despite Odoo's built-in recurrence functionality included in the maintenance requests themselves (since 17.0), this module could be needed for some reasons: -- Odoo implementation is javascript only -- Some companies already work with the existing "Maintenance Plan" - workflow -- Some modules are depending on this one +- Odoo implementation is javascript only +- Some companies already work with the existing "Maintenance Plan" + workflow +- Some modules are depending on this one For reference, this module was initially set deprecated when migrated to 17.0: @@ -131,16 +131,17 @@ Authors Contributors ------------ -- Akim Juillerat -- Matteo Mazzoni -- David Alonso -- AdriĆ  Gil Sorribes -- Jordi Ballester Alomar -- Lois Rilo -- Enric Tobella -- Alexei Rivera -- Yann Papouin -- Yannick Payot +- Akim Juillerat +- Matteo Mazzoni +- David Alonso +- AdriĆ  Gil Sorribes +- Jordi Ballester Alomar +- Lois Rilo +- Enric Tobella +- Alexei Rivera +- Yann Papouin +- Yannick Payot +- Carlos Espinosa Maintainers ----------- diff --git a/maintenance_plan/__manifest__.py b/maintenance_plan/__manifest__.py index 925451ac9..259c477ef 100644 --- a/maintenance_plan/__manifest__.py +++ b/maintenance_plan/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Maintenance Plan", "summary": "Extends preventive maintenance planning", - "version": "19.0.1.0.0", + "version": "19.0.1.0.1", "author": "Camptocamp SA, ForgeFlow, Odoo Community Association (OCA)", "license": "AGPL-3", "category": "Maintenance", diff --git a/maintenance_plan/hooks.py b/maintenance_plan/hooks.py index cdc9b3664..ef89e7fc3 100644 --- a/maintenance_plan/hooks.py +++ b/maintenance_plan/hooks.py @@ -1,47 +1,34 @@ # Copyright 2017 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - import logging -from odoo.exceptions import UserError +_logger = logging.getLogger(__name__) def post_init_hook(env): - logging.getLogger("odoo.addons.maintenance_plan").info( - "Migrating existing preventive maintenance" - ) + """Create a maintenance plan for equipments that had an expected MTBF. + Odoo 19 removed ``maintenance.equipment.next_action_date`` and + ``maintenance.equipment.mtbf``; the original migration relied on both and + raised ``AttributeError`` on install. We now derive the plan interval from + ``expected_mtbf`` (expressed in days) and no longer try to tag a specific + preventive request by its (now inexistent) next action date. + """ + _logger.info("Migrating existing preventive maintenance") equipments = env["maintenance.equipment"].search([("expected_mtbf", "!=", False)]) - - if equipments: - maintenance_kind = env["maintenance.kind"].create( - {"name": "Install", "active": True} - ) - - for equipment in equipments: - request = equipment.maintenance_ids.filtered( - lambda r, equipment=equipment: r.maintenance_type == "preventive" - and not r.stage_id.done - and r.request_date == equipment.next_action_date - ) - if len(request) > 1: - raise UserError( - env._( - "You have multiple preventive maintenance requests on " - "equipment %(name)s next action date (%(date)s). " - "Please leave only one preventive request on the " - "date of equipment's next action to install the module.", - name=equipment.name, - date=equipment.next_action_date, - ) - ) - elif len(request) == 1: - request.write({"maintenance_kind_id": maintenance_kind.id}) - env["maintenance.plan"].create( - { - "equipment_id": equipment.id, - "maintenance_kind_id": maintenance_kind.id, - "duration": equipment.mtbf, - "interval": equipment.expected_mtbf, - } - ) + if not equipments: + return + maintenance_kind = env["maintenance.kind"].create( + {"name": "Install", "active": True} + ) + env["maintenance.plan"].create( + [ + { + "equipment_id": equipment.id, + "maintenance_kind_id": maintenance_kind.id, + "interval": equipment.expected_mtbf, + "interval_step": "day", + } + for equipment in equipments + ] + ) diff --git a/maintenance_plan/readme/CONTRIBUTORS.md b/maintenance_plan/readme/CONTRIBUTORS.md index 0f23ccebe..a732aa1f1 100644 --- a/maintenance_plan/readme/CONTRIBUTORS.md +++ b/maintenance_plan/readme/CONTRIBUTORS.md @@ -8,3 +8,4 @@ - Alexei Rivera \<\> - Yann Papouin \<\> - Yannick Payot \<\> +- Carlos Espinosa \<\> diff --git a/maintenance_plan/static/description/index.html b/maintenance_plan/static/description/index.html index a27d24874..ec92b37be 100644 --- a/maintenance_plan/static/description/index.html +++ b/maintenance_plan/static/description/index.html @@ -481,6 +481,7 @@

Contributors

  • Alexei Rivera <arivera@archeti.com>
  • Yann Papouin <ypa@decgroupe.com>
  • Yannick Payot <yannick.payot@acsone.eu>
  • +
  • Carlos Espinosa <carlos.espinosa@ffp.mx>
  • diff --git a/maintenance_plan/tests/__init__.py b/maintenance_plan/tests/__init__.py index def774ebc..808fd70d4 100644 --- a/maintenance_plan/tests/__init__.py +++ b/maintenance_plan/tests/__init__.py @@ -1,2 +1,3 @@ from . import test_maintenance_plan from . import test_maintenance_plan_domain +from . import test_post_init_hook diff --git a/maintenance_plan/tests/test_post_init_hook.py b/maintenance_plan/tests/test_post_init_hook.py new file mode 100644 index 000000000..f0341585e --- /dev/null +++ b/maintenance_plan/tests/test_post_init_hook.py @@ -0,0 +1,42 @@ +# Copyright 2017 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests.common import tagged + +from odoo.addons.base.tests.common import BaseCommon + +from ..hooks import post_init_hook + + +@tagged("post_install", "-at_install") +class TestPostInitHook(BaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.equipment_obj = cls.env["maintenance.equipment"] + cls.plan_obj = cls.env["maintenance.plan"] + + def test_post_init_hook_creates_plan_from_expected_mtbf(self): + """A plan is created for each equipment with an expected MTBF.""" + equipment = self.equipment_obj.create( + {"name": "Equipment with MTBF", "expected_mtbf": 30} + ) + self.assertFalse(equipment.maintenance_plan_ids) + + post_init_hook(self.env) + + plans = self.plan_obj.search([("equipment_id", "=", equipment.id)]) + self.assertEqual(len(plans), 1) + plan = plans + # Interval is derived from expected_mtbf, expressed in days. + self.assertEqual(plan.interval, 30) + self.assertEqual(plan.interval_step, "day") + self.assertEqual(plan.maintenance_kind_id.name, "Install") + + def test_post_init_hook_ignores_equipment_without_mtbf(self): + """Equipment without an expected MTBF gets no plan from the hook.""" + equipment = self.equipment_obj.create({"name": "Equipment without MTBF"}) + + post_init_hook(self.env) + + plans = self.plan_obj.search([("equipment_id", "=", equipment.id)]) + self.assertFalse(plans)