Skip to content

Commit 8269e9f

Browse files
committed
[IMP] util/domains: handle expression domains
Before it was impossible to handle domains defined as `context.get('key') and [<dom1>] or [<dom2>]` Example: ``` context.get('default_move_type') in ('out_invoice', 'out_refund', 'out_receipt') and [('sale_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)] or [('purchase_ok', '=', True), '|', ('company_id', '=', False), ('company_id', '=', parent.company_id)] ```
1 parent f4ac22c commit 8269e9f

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

src/base/tests/test_util.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,28 @@ def setUp(self):
5353
self.mock_adapter = mock.Mock()
5454

5555
def test_adapt_renamed_field(self):
56-
domain = [("user_ids.partner_id.user_ids.partner_id", "=", False)]
56+
term = ("user_ids.partner_id.user_ids.partner_id", "=", False)
57+
match_term = ("renamed_user_ids.partner_id.renamed_user_ids.partner_id", "=", False)
58+
5759
Filter = self.env["ir.filters"]
5860
filter1 = Filter.create(
59-
{"name": "Test filter for adapt domain", "model_id": "res.partner", "domain": str(domain)}
61+
{"name": "Test filter for adapt domain", "model_id": "res.partner", "domain": str([term])}
62+
)
63+
assert [term] == ast.literal_eval(filter1.domain)
64+
65+
base_exp = "context.get('context_value') in (1, 2) and [{0}] or ['!', {0}]"
66+
base_exp_fallback = "(((context.get('context_value') in (1, 2)) and [{0}]) or ['!', {0}])"
67+
filter2 = Filter.create(
68+
{"name": "Test filter for adapt domain2", "model_id": "res.partner", "domain": base_exp.format(term)}
6069
)
61-
assert domain == ast.literal_eval(filter1.domain)
70+
6271
util.invalidate(Filter)
6372
util.rename_field(self.cr, "res.partner", "user_ids", "renamed_user_ids")
64-
match_domain = [("renamed_user_ids.partner_id.renamed_user_ids.partner_id", "=", False)]
73+
6574
new_domain = ast.literal_eval(filter1.domain)
75+
self.assertEqual([match_term], new_domain)
6676

67-
self.assertEqual(match_domain, new_domain)
77+
self.assertIn(filter2.domain, [base_exp.format(match_term), base_exp_fallback.format(match_term)])
6878

6979
@parametrize(
7080
[

src/util/domains.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*- coding: utf-8 -*-
2+
import ast
23
import collections
34
import functools
45
import logging
56
import re
7+
import sys
68
import warnings
79

810
import lxml
@@ -34,7 +36,7 @@
3436
from .const import NEARLYWARN
3537
from .helpers import _dashboard_actions, _validate_model, resolve_model_fields_path
3638
from .inherit import for_each_inherit
37-
from .misc import SelfPrintEvalContext, version_gte
39+
from .misc import SelfPrintEvalContext, ast_unparse, literal_replace, version_gte
3840
from .pg import column_exists, get_value_or_en_translation, table_exists
3941
from .records import edit_view
4042

@@ -100,6 +102,8 @@ def is_leaf(leaf):
100102
del expression # unset so it's not used directly
101103

102104

105+
AST_CONSTANT_TYPES = (ast.Constant, ast.Str) if sys.version_info < (3, 12) else (ast.Constant,)
106+
103107
_logger = logging.getLogger(__name__)
104108
DomainField = collections.namedtuple("DomainField", "table domain_column model_select")
105109
"""
@@ -248,6 +252,41 @@ def _replace_path(cr, old, new, src_model, dst_model, path_str):
248252

249253

250254
def _adapt_one_domain(cr, target_model, old, new, model, domain, adapter=None, force_adapt=False):
255+
if not isinstance(domain, basestring):
256+
# defer to the old adapter that replaces invalid terms
257+
return _adapt_one_domain_old(cr, target_model, old, new, model, domain, adapter, force_adapt)
258+
259+
def domain_check(ast_node):
260+
if all(
261+
(
262+
isinstance(term, (ast.Tuple, ast.List))
263+
and len(term.elts) == 3
264+
and isinstance(term.elts[1], AST_CONSTANT_TYPES)
265+
)
266+
or isinstance(term, AST_CONSTANT_TYPES)
267+
for term in ast_node.elts
268+
) and any(isinstance(term, (ast.Tuple, ast.List)) and len(term.elts) == 3 for term in ast_node.elts):
269+
# it looks like a domain, let's adapt it
270+
internal_domain = _adapt_one_domain_old(
271+
cr,
272+
target_model,
273+
old,
274+
new,
275+
model,
276+
ast_unparse(ast_node),
277+
adapter=adapter,
278+
force_adapt=force_adapt,
279+
)
280+
return ast.parse(unicode(internal_domain), mode="eval").body if internal_domain else ast_node
281+
return ast_node
282+
283+
new_domain = literal_replace(domain, {ast.List([literal_replace.WILDCARD], None): domain_check})
284+
if new_domain != domain:
285+
return new_domain
286+
return None
287+
288+
289+
def _adapt_one_domain_old(cr, target_model, old, new, model, domain, adapter=None, force_adapt=False):
251290
if not adapter:
252291
adapter = lambda leaf, _, __: [leaf]
253292

0 commit comments

Comments
 (0)