From 14036869c7b7eebea339d51f0b8abbe143e244b1 Mon Sep 17 00:00:00 2001 From: Alvaro Fuentes Date: Sun, 23 May 2021 18:56:09 +0000 Subject: [PATCH] [FIX] core: fix filtered_domain for hierarchical terms The method filtered_domain() is broken for domains with hierarchical terms ('child_of'/'parent_of'). To see *one* of the ways the implementation is broken, let `A` be a model with `parent_id` pointing to `A`, and `a1` a record of model `A` without parent (`a1.parent_id` is `False`), then this fails: assert a1 in a1.filtered_domain([("parent_id", "child_of", a1.id)]) The reason it fails is that on https://github.com/odoo/odoo/blob/f5519586d214a9b34ad24683a7f97c47802a3bad/odoo/models.py#L5377-L5380 `data` is empty since `a1` has no parent, thus https://github.com/odoo/odoo/blob/f5519586d214a9b34ad24683a7f97c47802a3bad/odoo/models.py#L5403-L5404 fails, therefore the result of `filtered_domain` is empty. Note: the implementation of the hierarchical operators is full of quirks that are hard to emulate otherwise than by reusing the original code. As a consequence, the current implementation may be broken in more than one way. Let's see another way the implementation is broken: let `B` be a model without a `parent_id` field and with a `friend_id` field pointing to `B`, and let `b1` be a record of model `B`. Then b1.filtered_domain([("friend_id", "child_of", b1.id)]) throws an exception of the form shown below: ValueError: Invalid field 'parent_id' in leaf " --- odoo/addons/base/tests/test_expression.py | 11 +++++++++++ odoo/models.py | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index b0dc30d3ec1..505194ff64f 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -115,6 +115,17 @@ class TestExpression(SavepointCaseWithUserDemo): test('not ilike', 'B', ['0', 'a']) test('not like', 'AB', ['0', 'a', 'b', 'a b']) + def test_09_hierarchy_filtered_domain(self): + Partner = self.env['res.partner'] + p = Partner.create({'name': 'dummy'}) + + # hierarchy without parent + self.assertFalse(p.parent_id) + p2 = self._search(Partner, [('parent_id', 'child_of', p.id)], [('id', '=', p.id)]) + self.assertEqual(p2, p) + p3 = self._search(Partner, [('parent_id', 'parent_of', p.id)], [('id', '=', p.id)]) + self.assertEqual(p3, p) + def test_10_hierarchy_in_m2m(self): Partner = self.env['res.partner'] Category = self.env['res.partner.category'] diff --git a/odoo/models.py b/odoo/models.py index 17f82685ad8..e56f7a4c279 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -5374,6 +5374,9 @@ Fields: result.append(self.browse()) else: (key, comparator, value) = d + if comparator in ('child_of', 'parent_of'): + result.append(self.search([('id', 'in', self.ids), d])) + continue if key.endswith('.id'): key = key[:-3] if key == 'id': @@ -5390,9 +5393,6 @@ Fields: records_ids = OrderedSet() for rec in self: data = rec.mapped(key) - if comparator in ('child_of', 'parent_of'): - value = data.search([(data._parent_name, comparator, value)]).ids - comparator = 'in' if isinstance(data, BaseModel): v = value if (isinstance(value, list) or isinstance(value, tuple)) and len(value):