[FIX] web: properly combine domains with "OR" in CompoundDomain

The CompoundDomain class allows to regroup several domains with an
implicit "AND"; these domains can be either a string, an array or
another CompoundDomain. A CompoundDomain can then be converted to
an array thanks to pyeval.js.
For example, if a CompoundDomain is initialized with `[A, B]` and
`[C]`, the array conversion gave `[A, B, C]` (which is expected).

However, a hackish method was used with CompoundDomain. If one of the
domain of a CompoundDomain is equal to `["|"]` (an array with the
OR operator in it), the two next subdomains were supposed to be joined
by a OR operator. Indeed, for the case of a CompoundDomain initialized
with `["|"]`, `[A]` and `[B]`, the array conversion gave `["|", A, B]`
(which is expected). However, if initialized with `["|"]`, `[A, B]` and
`[C]`, the array conversion gave `["|", A, B, C]` which is very wrong
as what was expected is `["|", "&", A, B, C]`. The problem is that the
given `[A, B]` contains implicit "&" operators.

This commit fixes the problem by normalizing only if the CompoundDomain
starts with a ["|"] or ["!"] array which is the standard odoo case.
This allows to limit breaking custom code (e.g we want a simple "AND"
of `[A]` and `[B]` to stay `[A, B]`, not become `["&", A, B]`).

The commit also modifies a test so that it checks that the problem is
properly solved.
This commit is contained in:
qsm-odoo 2017-03-15 15:19:46 +01:00
parent 874c7aa2dd
commit b2d66d0c14
2 changed files with 42 additions and 10 deletions

View File

@ -852,29 +852,60 @@
var eval_domains = function (domains, evaluation_context) {
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
var result_domain = [];
// Normalize only if the first domain is the array ["|"] or ["!"]
var need_normalization = (
domains.length > 0
&& domains[0].length === 1
&& (domains[0][0] === "|" || domains[0][0] === "!")
);
_(domains).each(function (domain) {
if (_.isString(domain)) {
// wrap raw strings in domain
domain = { __ref: 'domain', __debug: domain };
}
var domain_array_to_combine;
switch(domain.__ref) {
case 'domain':
evaluation_context.context = evaluation_context;
result_domain.push.apply(
result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context));
break;
case 'compound_domain':
var eval_context = eval_contexts([domain.__eval_context]);
result_domain.push.apply(
result_domain, eval_domains(
domain.__domains, _.extend(
{}, evaluation_context, eval_context)));
domain_array_to_combine = eval_domains(
domain.__domains,
_.extend({}, evaluation_context, eval_context)
);
break;
default:
result_domain.push.apply(result_domain, domain);
domain_array_to_combine = domain;
}
if (need_normalization) {
domain_array_to_combine = get_normalized_domain(domain_array_to_combine);
}
result_domain.push.apply(result_domain, domain_array_to_combine);
});
return result_domain;
/**
* Returns a normalized copy of the given domain array. Normalization is
* is making the implicit "&" at the start of the domain explicit, e.g.
* [A, B, C] would become ["&", "&", A, B, C].
*
* @param {Array} domain_array
* @returns {Array} normalized copy of the given array
*/
function get_normalized_domain(domain_array) {
var expected = 1; // Holds the number of expected domain expressions
_.each(domain_array, function (item) {
if (item === "&" || item === "|") {
expected++;
} else if (item !== "!") {
expected--;
}
});
var new_explicit_ands = _.times(-expected, _.constant("&"));
return new_explicit_ands.concat(domain_array);
}
};
var eval_groupbys = function (contexts, evaluation_context) {
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
@ -995,7 +1026,7 @@
instance.web._t("Local evaluation failure\n%s\n\n%s"),
e.message, JSON.stringify(source))
}
}};
}};
}
d.resolve(result);
}, 0); });

View File

@ -500,7 +500,7 @@ openerp.testing.section('eval.edc.nonliterals', {
test('domain with time', {asserts: 1}, function (instance) {
return instance.edc([
[['type', '=', 'contract']],
{ "__domains": [["|"], [["state", "in", ["open", "draft"]]], [["state", "=", "pending"]]],
{ "__domains": [["|"], [["state", "in", ["open", "draft"]]], [["type", "=", "contract"], ["state", "=", "pending"]]],
"__eval_context": null,
"__ref": "compound_domain"
},
@ -513,7 +513,8 @@ openerp.testing.section('eval.edc.nonliterals', {
deepEqual(result.domain, [
["type", "=", "contract"],
"|", ["state", "in", ["open", "draft"]],
["state", "=", "pending"],
"&", ["type", "=", "contract"],
["state", "=", "pending"],
"|",
"&", ["date", "!=", false],
["date", "<=", today],