The function remove_domain_leaf is currently used for project burdown chart. Due to an increasing need of such a function in multiple modules for various functional cases, it has been decided to: - Move this function in 'resource' that is imported in all the modules where this function is needed. - Implement extensive unit test to ensure that it address corner cases. - Refactor the function to address the corner cases it was not addressing until now. The refactored function 'filter_domain_leaf' is used to transform a given domain to a new domain using only the leaves that verify a given check (more precisely, the leaves whose first element verify this check). To perform this transformation, the leaves that do not verify this check are considered as undetermined. All the logical operators dealing with undetermined leaves are ignored, which means: - AND(leaf, ?) = leaf - OR(leaf, ?) = leaf - AND(? , ?) = ? - OR(?, ?) = ? - NOT(?) = ? If the result of the operation is undetermined, it is returned as an empty domain ([]). closes odoo/odoo#105470 Related: odoo/enterprise#33657 Related: odoo/upgrade#4221 Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
44 lines
2.0 KiB
Python
44 lines
2.0 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.osv.expression import normalize_domain, NOT_OPERATOR, DOMAIN_OPERATORS
|
|
|
|
def filter_domain_leaf(domain, leaf_check):
|
|
"""
|
|
filter_domain_lead only keep the leaves of a domain that verify a given check. Logical operators that involves
|
|
a leaf that is undetermined (because it does not pass the check) are ignored.
|
|
|
|
each operator is a logic gate:
|
|
- '&' and '|' take two entries and can be ignored if one of them (or the two of them) is undetermined
|
|
-'!' takes one entry and can be ignored if this entry is undetermined
|
|
|
|
params:
|
|
- domain: the domain that needs to be filtered
|
|
- leaf_check: the function that the field used in the leaf needs to verify to keep the leaf
|
|
returns: The filtered version of the domain
|
|
"""
|
|
def _filter_domain_leaf_recursive(domain, leaf_check, operator=False):
|
|
"""
|
|
return domain, rest_domain -> rest_domain should be empty if the operation is finished
|
|
"""
|
|
if len(domain) == 0:
|
|
return ([], [])
|
|
if not operator:
|
|
first_elem = domain[0]
|
|
if first_elem not in DOMAIN_OPERATORS: #End of a current leaf
|
|
return ([], domain[1:]) if not leaf_check(first_elem[0]) else ([first_elem], domain[1:])
|
|
operator = first_elem
|
|
domain = domain[1:]
|
|
|
|
leaf_1, rest_domain = _filter_domain_leaf_recursive(domain, leaf_check)
|
|
if operator == NOT_OPERATOR:
|
|
return ([operator, *leaf_1], rest_domain) if leaf_1 else ([], rest_domain)
|
|
leaf_2, rest_domain = _filter_domain_leaf_recursive(rest_domain, leaf_check)
|
|
if leaf_1 == [] or leaf_2 == []:
|
|
return ((leaf_1 or leaf_2), rest_domain)
|
|
return ([operator, *leaf_1, *leaf_2], rest_domain)
|
|
|
|
domain = normalize_domain(domain)
|
|
operator = domain[0] if len(domain) > 1 else False
|
|
domain = domain[1:] if len(domain) > 1 else domain
|
|
return _filter_domain_leaf_recursive(domain, leaf_check, operator=operator)[0]
|