From b2d66d0c1468b9d321d9ffa5782e370e92525a43 Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Wed, 15 Mar 2017 15:19:46 +0100 Subject: [PATCH] [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. --- addons/web/static/src/js/pyeval.js | 47 +++++++++++++++++++++++++----- addons/web/static/test/evals.js | 5 ++-- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js index c132b8365fb..a651c7d8bfa 100644 --- a/addons/web/static/src/js/pyeval.js +++ b/addons/web/static/src/js/pyeval.js @@ -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); }); diff --git a/addons/web/static/test/evals.js b/addons/web/static/test/evals.js index ff97086b019..8dd9a62497e 100644 --- a/addons/web/static/test/evals.js +++ b/addons/web/static/test/evals.js @@ -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],