From 10812562082dc363825e86f1b6762102d6b825ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Houz=C3=A9fa=20Abbasbhay?= Date: Fri, 15 Mar 2024 12:14:39 +0100 Subject: [PATCH] [FIX] base_tier_validation: Field merge in view Previous implementation was losing fields in some cases; new one properly merges dicts/tuples. This fixes odd "Missing field string information" errors in form-embedded lists. This cset also removes an obsolete `base_model_name` context key no longer used in Odoo 16. This cset adds a `test_get_view` test which checks for that `need_validation` field; it was failing with the previous impl. Fixes #825. --- .../models/tier_validation.py | 26 +++++------ base_tier_validation/readme/CONTRIBUTORS.rst | 3 ++ base_tier_validation/tests/common.py | 18 ++++++++ .../tests/test_tier_validation.py | 44 +++++-------------- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/base_tier_validation/models/tier_validation.py b/base_tier_validation/models/tier_validation.py index 5204c70fed..54b2a7de26 100644 --- a/base_tier_validation/models/tier_validation.py +++ b/base_tier_validation/models/tier_validation.py @@ -713,12 +713,7 @@ def _get_tier_validation_readonly_domain(self): @api.model def get_view(self, view_id=None, view_type="form", **options): res = super().get_view(view_id=view_id, view_type=view_type, **options) - View = self.env["ir.ui.view"] - - # Override context for postprocessing - if view_id and res.get("base_model", self._name) != self._name: - View = View.with_context(base_model_name=res["base_model"]) if view_type == "form" and not self._tier_validation_manual_config: doc = etree.XML(res["arch"]) params = { @@ -726,7 +721,7 @@ def get_view(self, view_id=None, view_type="form", **options): "state_operator": "not in", "state_value": self._state_from, } - all_models = res["models"].copy() + all_models = res["models"].copy() # {modelname(str) ➔ fields(tuple)} for node in doc.xpath(self._tier_validation_buttons_xpath): # By default, after the last button of the header # _add_tier_validation_buttons process @@ -735,6 +730,7 @@ def get_view(self, view_id=None, view_type="form", **options): new_node = etree.fromstring(new_arch) for new_element in new_node: node.addnext(new_element) + _merge_view_fields(all_models, new_models) for node in doc.xpath("/form/sheet"): # _add_tier_validation_label process new_node = self._add_tier_validation_label(node, params) @@ -742,18 +738,13 @@ def get_view(self, view_id=None, view_type="form", **options): new_node = etree.fromstring(new_arch) for new_element in new_node: node.addprevious(new_element) + _merge_view_fields(all_models, new_models) # _add_tier_validation_reviews process new_node = self._add_tier_validation_reviews(node, params) new_arch, new_models = View.postprocess_and_fields(new_node, self._name) - for model in new_models: - if model in all_models: - continue - if model not in res["models"]: - all_models[model] = new_models[model] - else: - all_models[model] = res["models"][model] new_node = etree.fromstring(new_arch) node.append(new_node) + _merge_view_fields(all_models, new_models) excepted_fields = self._get_all_validation_exceptions() for node in doc.xpath("//field[@name][not(ancestor::field)]"): if node.attrib.get("name") in excepted_fields: @@ -772,3 +763,12 @@ def get_view(self, view_id=None, view_type="form", **options): res["arch"] = etree.tostring(doc) res["models"] = frozendict(all_models) return res + + +def _merge_view_fields(all_models: dict, new_models: dict): + """Merge new_models into all_models. Both are {modelname(str) ➔ fields(tuple)}.""" + for model, view_fields in new_models.items(): + if model in all_models: + all_models[model] = tuple(set(all_models[model]) | set(view_fields)) + else: + all_models[model] = tuple(view_fields) diff --git a/base_tier_validation/readme/CONTRIBUTORS.rst b/base_tier_validation/readme/CONTRIBUTORS.rst index a89b027cf0..7723eafc69 100644 --- a/base_tier_validation/readme/CONTRIBUTORS.rst +++ b/base_tier_validation/readme/CONTRIBUTORS.rst @@ -8,3 +8,6 @@ * Evan Soh * Manuel Regidor * Eduardo de Miguel +* `XCG Consulting `_: + + * Houzéfa Abbasbhay diff --git a/base_tier_validation/tests/common.py b/base_tier_validation/tests/common.py index de951cbf7b..7803c31036 100644 --- a/base_tier_validation/tests/common.py +++ b/base_tier_validation/tests/common.py @@ -57,6 +57,24 @@ def setUpClass(cls): } ) + # Define views to avoid automatic views with all fields. + for model in cls.test_model._name, cls.test_model_2._name: + cls.env["ir.ui.view"].create( + { + "model": model, + "name": f"Demo view for {model}", + "arch": """
+
+
+ + + +
""", + } + ) + # Create users: group_ids = cls.env.ref("base.group_system").ids cls.test_user_1 = cls.env["res.users"].create( diff --git a/base_tier_validation/tests/test_tier_validation.py b/base_tier_validation/tests/test_tier_validation.py index 1ea79580c8..40f278b688 100644 --- a/base_tier_validation/tests/test_tier_validation.py +++ b/base_tier_validation/tests/test_tier_validation.py @@ -909,23 +909,6 @@ def test_25_change_field_exception_validation(self): @tagged("at_install") class TierTierValidationView(CommonTierValidation): def test_view_manual(self): - # We need to add a view in order to ensure that an automatic view with all - # fields is not created - self.env["ir.ui.view"].create( - { - "model": self.test_record._name, - "name": "Demo view", - "arch": """
-
-
- - - -
""", - } - ) with Form(self.test_record) as f: self.assertNotIn("review_ids", f._values) form = etree.fromstring(f._view["arch"]) @@ -934,26 +917,19 @@ def test_view_manual(self): self.assertFalse(form.xpath("//button[@name='request_validation']")) def test_view_automatic(self): - # We need to add a view in order to ensure that an automatic view with all - # fields is not created - self.env["ir.ui.view"].create( - { - "model": self.test_record_2._name, - "name": "Demo view", - "arch": """
-
-
- - - -
""", - } - ) with Form(self.test_record_2) as f: self.assertIn("review_ids", f._values) form = etree.fromstring(f._view["arch"]) self.assertTrue(form.xpath("//field[@name='review_ids']")) self.assertTrue(form.xpath("//field[@name='can_review']")) self.assertTrue(form.xpath("//button[@name='request_validation']")) + + def test_get_view(self): + view = self.test_record_2.get_view() + model = "tier.validation.tester2" + self.assertEqual(view["model"], model) + self.assertEqual(view["models"].keys(), {model, "tier.review"}) + self.assertIn("id", view["models"][model]) + self.assertIn("need_validation", view["models"][model]) + self.assertIn("next_review", view["models"][model]) + self.assertIn("review_ids", view["models"][model])