From 3d1151245fd8466773436f7c284530f2424dfd26 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 9 Aug 2023 17:37:46 +0200 Subject: [PATCH] edi: refactor model rules backward compat --- .../migrations/14.0.1.19.0/post-migrate.py | 1 + edi_oca/models/edi_exchange_type.py | 134 ++++++++++++++++++ edi_oca/tests/__init__.py | 1 + .../test_exchange_type_deprecated_fields.py | 43 ++++++ edi_oca/views/edi_exchange_type_views.xml | 33 +++++ 5 files changed, 212 insertions(+) create mode 100644 edi_oca/tests/test_exchange_type_deprecated_fields.py diff --git a/edi_oca/migrations/14.0.1.19.0/post-migrate.py b/edi_oca/migrations/14.0.1.19.0/post-migrate.py index 95a944823d9..84e1007bff6 100644 --- a/edi_oca/migrations/14.0.1.19.0/post-migrate.py +++ b/edi_oca/migrations/14.0.1.19.0/post-migrate.py @@ -29,6 +29,7 @@ def migrate(cr, version): kind = "form_btn" if item.pop("form_btn", False) else "custom" vals = dict(item, name="Default", kind=kind) model.create(vals) + item.type_id.button_wipe_deprecated_rule_fields() cr.execute("DROP TABLE exc_type_model_rel_bkp") _logger.info("edi.exchange.type.rule created") diff --git a/edi_oca/models/edi_exchange_type.py b/edi_oca/models/edi_exchange_type.py index aeaef7ed5a0..db09a741983 100644 --- a/edi_oca/models/edi_exchange_type.py +++ b/edi_oca/models/edi_exchange_type.py @@ -116,6 +116,28 @@ class EDIExchangeType(models.Model): inverse_name="type_id", help="Rules to handle exchanges and UI automatically", ) + # Deprecated fields for rules - begin + # These fields have been deprecated in + # https://github.com/OCA/edi/pull/797 + # but are kept for backward compat. + # If you can stop using them now. + # Anyway, annoying warning messages will be logged. + # See inverse methods. + # NOTE: old configurations are migrated automatically on upgrade + # Yet, if you have data files they might be broken + # if we delete these fields. + model_ids = fields.Many2many( + "ir.model", inverse="_inverse_deprecated_rules_model_ids" + ) + enable_domain = fields.Char(inverse="_inverse_deprecated_rules_enable_domain") + enable_snippet = fields.Char(inverse="_inverse_deprecated_rules_enable_snippet") + model_manual_btn = fields.Boolean( + inverse="_inverse_deprecated_rules_model_manual_btn" + ) + deprecated_rule_fields_still_used = fields.Boolean( + compute="_compute_deprecated_rule_fields_still_used" + ) + # Deprecated fields for rules - end quick_exec = fields.Boolean( string="Quick execution", help="When active, records of this type will be processed immediately " @@ -226,3 +248,115 @@ def is_partner_enabled(self, partner): if exc_type.partner_ids: return partner.id in exc_type.partner_ids.ids return True + + # API to support deprecated model rules fields - begin + def _inverse_deprecated_rules_warning(self): + _fields = ", ".join( + ["model_ids", "enable_domain", "enable_snippet", "model_manual_btn"] + ) + _logger.warning( + "The fields %s are deprecated, " + "please stop using them in favor of edi.exchange.type.rule", + _fields, + ) + + def _inverse_deprecated_rules_model_ids(self): + if self.env.context.get("deprecated_rule_fields_bypass_inverse"): + return + self._inverse_deprecated_rules_warning() + for rec in self: + for model in rec.model_ids: + rule = rec._get_rule_by_model(model) + if not rule: + _logger.warning( + "New rule for %s created from deprecated `model_ids`", + model.model, + ) + rec.rule_ids += rec._inverse_deprecated_rules_create(model) + rules_to_delete = rec.rule_ids.browse() + for rule in rec.rule_ids: + if rule.model_id not in rec.model_ids: + _logger.warning( + "Rule for %s deleted from deprecated `model_ids`", + rule.model_id.model, + ) + rules_to_delete |= rule + rules_to_delete.unlink() + + def _inverse_deprecated_rules_enable_domain(self): + if self.env.context.get("deprecated_rule_fields_bypass_inverse"): + return + self._inverse_deprecated_rules_warning() + for rec in self: + for model in rec.model_ids: + rule = rec._get_rule_by_model(model) + if rule: + _logger.warning( + "Rule for %s domain updated from deprecated `enable_domain`", + model.model, + ) + rule.enable_domain = rec.enable_domain + + def _inverse_deprecated_rules_enable_snippet(self): + if self.env.context.get("deprecated_rule_fields_bypass_inverse"): + return + self._inverse_deprecated_rules_warning() + for rec in self: + for model in rec.model_ids: + rule = rec._get_rule_by_model(model) + if rule: + _logger.warning( + "Rule for %s snippet updated from deprecated `enable_snippet`", + model.model, + ) + rule.enable_snippet = rec.enable_snippet + + def _inverse_deprecated_rules_model_manual_btn(self): + if self.env.context.get("deprecated_rule_fields_bypass_inverse"): + return + self._inverse_deprecated_rules_warning() + for rec in self: + for model in rec.model_ids: + rule = rec._get_rule_by_model(model) + if rule: + _logger.warning( + "Rule for %s btn updated from deprecated `model_manual_btn`", + model.model, + ) + rule.kind = "form_btn" if self.model_manual_btn else "custom" + + def _get_rule_by_model(self, model): + return self.rule_ids.filtered(lambda x: x.model_id == model) + + def _inverse_deprecated_rules_create(self, model): + kind = "form_btn" if self.model_manual_btn else "custom" + vals = { + "type_id": self.id, + "model_id": model.id, + "kind": kind, + "name": "Default", + "enable_snippet": self.enable_snippet, + "enable_domain": self.enable_domain, + } + return self.rule_ids.create(vals) + + @api.depends("model_ids", "enable_domain", "enable_snippet", "model_manual_btn") + def _compute_deprecated_rule_fields_still_used(self): + for rec in self: + rec.deprecated_rule_fields_still_used = ( + rec._deprecated_rule_fields_still_used() + ) + + def _deprecated_rule_fields_still_used(self): + for fname in ("model_ids", "enable_snippet", "enable_domain"): + if self[fname]: + return True + + def button_wipe_deprecated_rule_fields(self): + _fields = ["model_ids", "enable_domain", "enable_snippet", "model_manual_btn"] + deprecated_vals = {}.fromkeys(_fields, None) + self.with_context(deprecated_rule_fields_bypass_inverse=True).write( + deprecated_vals + ) + + # API to support deprecated model rules fields - end diff --git a/edi_oca/tests/__init__.py b/edi_oca/tests/__init__.py index 3f4193ce031..30692700a2a 100644 --- a/edi_oca/tests/__init__.py +++ b/edi_oca/tests/__init__.py @@ -12,3 +12,4 @@ from . import test_edi_backend_cron from . import test_security from . import test_quick_exec +from . import test_exchange_type_deprecated_fields diff --git a/edi_oca/tests/test_exchange_type_deprecated_fields.py b/edi_oca/tests/test_exchange_type_deprecated_fields.py new file mode 100644 index 00000000000..dee571bd947 --- /dev/null +++ b/edi_oca/tests/test_exchange_type_deprecated_fields.py @@ -0,0 +1,43 @@ +# Copyright 2023 Camptocamp SA (https://www.camptocamp.com). +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +from .common import EDIBackendCommonTestCase + + +class EDIExchangeTypeDeprecatedFieldsTestCase(EDIBackendCommonTestCase): + def test_inverse(self): + typ = self.exchange_type_out + self.assertFalse(typ.rule_ids) + self.assertFalse(typ.deprecated_rule_fields_still_used) + typ.model_ids += self.env["ir.model"]._get("res.partner") + self.assertTrue(typ.deprecated_rule_fields_still_used) + self.assertEqual(typ.rule_ids.mapped("model_id.model"), ["res.partner"]) + typ.model_ids += self.env["ir.model"]._get("res.groups") + self.assertEqual( + typ.rule_ids.mapped("model_id.model"), ["res.partner", "res.groups"] + ) + typ.enable_domain = "[(1, '=', 1)]" + self.assertRecordValues( + typ.rule_ids, [{"enable_domain": typ.enable_domain, "kind": "custom"}] * 2 + ) + typ.model_manual_btn = True + self.assertRecordValues( + typ.rule_ids, [{"enable_domain": typ.enable_domain, "kind": "form_btn"}] * 2 + ) + typ.model_ids -= self.env["ir.model"]._get("res.groups") + self.assertEqual(typ.rule_ids.mapped("model_id.model"), ["res.partner"]) + typ.model_ids = False + self.assertFalse(typ.rule_ids) + + def test_btn(self): + typ = self.exchange_type_out + typ.model_ids += self.env["ir.model"]._get("res.partner") + typ.enable_domain = "[(1, '=', 1)]" + typ.button_wipe_deprecated_rule_fields() + self.assertFalse(typ.model_ids) + self.assertFalse(typ.enable_domain) + self.assertFalse(typ.enable_snippet) + # Rules are kept + self.assertEqual(typ.rule_ids.mapped("model_id.model"), ["res.partner"]) diff --git a/edi_oca/views/edi_exchange_type_views.xml b/edi_oca/views/edi_exchange_type_views.xml index c3ed3abe4dc..a210c935cdb 100644 --- a/edi_oca/views/edi_exchange_type_views.xml +++ b/edi_oca/views/edi_exchange_type_views.xml @@ -50,6 +50,7 @@ + + + + + + + + + +