changeset ee23aca64b26 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset&node=ee23aca64b26
description:
        Use specific search domain for reference field searches

        issue9997
        review324781002
diffstat:

 trytond/tests/test_tools.py       |  59 ++++++++++++++++++++++++++++++++++++--
 trytond/tools/domain_inversion.py |  57 +++++++++++++++++++++++++++++++++---
 2 files changed, 106 insertions(+), 10 deletions(-)

diffs (145 lines):

diff -r dd0bb6655e6d -r ee23aca64b26 trytond/tests/test_tools.py
--- a/trytond/tests/test_tools.py       Mon Apr 12 20:39:23 2021 +0200
+++ b/trytond/tests/test_tools.py       Mon Apr 12 20:54:03 2021 +0200
@@ -739,16 +739,67 @@
         domain = [['x', 'like', 'A%']]
         self.assertEqual(
             prepare_reference_domain(domain, 'x'),
-            [['x', 'like', 'A%']])
+            [[]])
 
-        domain = [['x.y', 'like', 'A%', 'model']]
+        domain = [['x', '=', 'A']]
         self.assertEqual(
-            prepare_reference_domain(domain, 'x'), [['y', 'like', 'A%']])
+            prepare_reference_domain(domain, 'x'),
+            [[]])
 
         domain = [['x.y', 'child_of', [1], 'model', 'parent']]
         self.assertEqual(
             prepare_reference_domain(domain, 'x'),
-            [['y', 'child_of', [1], 'parent']])
+            [['x.y', 'child_of', [1], 'model', 'parent']])
+
+        domain = [['x.y', 'like', 'A%', 'model']]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['x.y', 'like', 'A%', 'model']])
+
+        domain = [['x', '=', 'model,1']]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['x.id', '=', 1, 'model']])
+
+        domain = [['x', '!=', 'model,1']]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['x.id', '!=', 1, 'model']])
+
+        domain = [['x', '=', 'model,%']]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['x.id', '!=', None, 'model']])
+
+        domain = [['x', '!=', 'model,%']]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['x', 'not like', 'model,%']])
+
+        domain = [['x', 'in',
+                ['model_a,1', 'model_b,%', 'model_c,3', 'model_a,2']]]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['OR',
+                ['x.id', 'in', [1, 2], 'model_a'],
+                ['x.id', '!=', None, 'model_b'],
+                ['x.id', 'in', [3], 'model_c'],
+                ]])
+
+        domain = [['x', 'not in',
+                ['model_a,1', 'model_b,%', 'model_c,3', 'model_a,2']]]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [['AND',
+                ['x.id', 'not in', [1, 2], 'model_a'],
+                ['x', 'not like', 'model_b,%'],
+                ['x.id', 'not in', [3], 'model_c'],
+                ]])
+
+        domain = [['x', 'in', ['model_a,1', 'foo']]]
+        self.assertEqual(
+            prepare_reference_domain(domain, 'x'),
+            [[]])
 
     def test_extract_models(self):
         domain = [['x', 'like', 'A%']]
diff -r dd0bb6655e6d -r ee23aca64b26 trytond/tools/domain_inversion.py
--- a/trytond/tools/domain_inversion.py Mon Apr 12 20:39:23 2021 +0200
+++ b/trytond/tools/domain_inversion.py Mon Apr 12 20:54:03 2021 +0200
@@ -170,15 +170,60 @@
 
 def prepare_reference_domain(domain, reference):
     "convert domain to replace reference fields by their local part"
+
+    def value2reference(value):
+        model, ref_id = None, None
+        if isinstance(value, str) and ',' in value:
+            model, ref_id = value.split(',', 1)
+            if ref_id != '%':
+                try:
+                    ref_id = int(ref_id)
+                except ValueError:
+                    model, ref_id = None, value
+        elif (isinstance(value, (list, tuple))
+                and len(value) == 2
+                and isinstance(value[0], str)
+                and (isinstance(value[1], int) or value[1] == '%')):
+            model, ref_id = value
+        else:
+            ref_id = value
+        return model, ref_id
+
     if domain in ('AND', 'OR'):
         return domain
     elif is_leaf(domain):
-        # When a Reference field is using the dotted notation the model
-        # specified must be removed from the clause
-        if domain[0].count('.') and len(domain) > 3:
-            local_name, target_name = domain[0].split('.', 1)
-            if local_name == reference:
-                return [target_name] + list(domain[1:3] + domain[4:])
+        if domain[0] == reference:
+            if domain[1] in {'=', '!='}:
+                model, ref_id = value2reference(domain[2])
+                if model is not None:
+                    if ref_id == '%':
+                        if domain[1] == '=':
+                            return [reference + '.id', '!=', None, model]
+                        else:
+                            return [reference, 'not like', domain[2]]
+                    return [reference + '.id', domain[1], ref_id, model]
+            elif domain[1] in {'in', 'not in'}:
+                model_values = {}
+                for value in domain[2]:
+                    model, ref_id = value2reference(value)
+                    if model is None:
+                        break
+                    model_values.setdefault(model, []).append(ref_id)
+                else:
+                    new_domain = ['OR'] if domain[1] == 'in' else ['AND']
+                    for model, ref_ids in model_values.items():
+                        if '%' in ref_ids:
+                            if domain[1] == 'in':
+                                new_domain.append(
+                                    [reference + '.id', '!=', None, model])
+                            else:
+                                new_domain.append(
+                                    [reference, 'not like', model + ',%'])
+                        else:
+                            new_domain.append(
+                                [reference + '.id', domain[1], ref_ids, model])
+                    return new_domain
+            return []
         return domain
     else:
         return [prepare_reference_domain(d, reference) for d in domain]

Reply via email to