diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml
index 7d5116f93fc..47321ee5ad0 100644
--- a/addons/account/views/account_move_views.xml
+++ b/addons/account/views/account_move_views.xml
@@ -634,7 +634,7 @@
placeholder="Terms"
attrs="{'invisible': [('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/>
or
+ attrs="{'invisible': [ '|', '|', ('state', '!=', 'draft'), ('invoice_payment_term_id', '!=', False), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"> or
@@ -1024,7 +1024,7 @@
diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml
index 77592e57e53..78e01b03510 100644
--- a/addons/account/views/account_view.xml
+++ b/addons/account/views/account_view.xml
@@ -612,12 +612,12 @@ action = model.setting_init_bank_account_action()
-
+
-
+
diff --git a/addons/hr/views/res_users.xml b/addons/hr/views/res_users.xml
index 17c7f58cd1e..2caff4a2b82 100644
--- a/addons/hr/views/res_users.xml
+++ b/addons/hr/views/res_users.xml
@@ -55,7 +55,7 @@
class="oe_stat_button"
disabled="1"
invisible="context.get('from_my_profile', False)"
- attrs="{'invisible': ['|', ('hr_presence_state', '=', 'absent')]}">
+ attrs="{'invisible': [('hr_presence_state', '=', 'absent')]}">
diff --git a/addons/hr_attendance/views/hr_employee_view.xml b/addons/hr_attendance/views/hr_employee_view.xml
index 665a8ed0a98..abfc22d1493 100644
--- a/addons/hr_attendance/views/hr_employee_view.xml
+++ b/addons/hr_attendance/views/hr_employee_view.xml
@@ -63,7 +63,7 @@
- {'invisible': ['|', '|', ('hr_presence_state', '=', 'absent'), ('attendance_state', '=', 'checked_in')]}
+ {'invisible': ['|', ('hr_presence_state', '=', 'absent'), ('attendance_state', '=', 'checked_in')]}
diff --git a/addons/mail/wizard/mail_compose_message_view.xml b/addons/mail/wizard/mail_compose_message_view.xml
index cee9c5a7d58..1e9b0a693ff 100644
--- a/addons/mail/wizard/mail_compose_message_view.xml
+++ b/addons/mail/wizard/mail_compose_message_view.xml
@@ -60,7 +60,7 @@
+ attrs="{'invisible':[('composition_mode', '!=', 'mass_post')]}"/>
- {'invisible':['|', ('type', 'not in', ['product', 'consu'])]}
+ {'invisible': [('type', 'not in', ['product', 'consu'])]}
diff --git a/addons/web/static/src/js/core/domain.js b/addons/web/static/src/js/core/domain.js
index b54dd54d0b6..0db9847645d 100644
--- a/addons/web/static/src/js/core/domain.js
+++ b/addons/web/static/src/js/core/domain.js
@@ -383,8 +383,10 @@ var Domain = collections.Tree.extend({
* to normalize (! will be normalized in-place)
* @returns {Array} the normalized JS prefix-array representation of the
* given domain
+ * @throws {Error} if the domain is invalid and can't be normalised
*/
normalizeArray: function (domain) {
+ if (domain.length === 0) { return domain; }
var expected = 1;
_.each(domain, function (item) {
if (item === "&" || item === "|") {
@@ -395,6 +397,11 @@ var Domain = collections.Tree.extend({
});
if (expected < 0) {
domain.unshift.apply(domain, _.times(Math.abs(expected), _.constant("&")));
+ } else if (expected > 0) {
+ throw new Error(_.str.sprintf(
+ "invalid domain %s (missing %d segment(s))",
+ JSON.stringify(domain), expected
+ ));
}
return domain;
},
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 1ed8c36bb98..532af65744c 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -2259,31 +2259,27 @@ var BasicModel = AbstractModel.extend({
* context.
* @param {Object} modifiers
* @returns {Object}
+ * @throws {Error} if one of the modifier domains is invalid
*/
_evalModifiers: function (element, modifiers) {
- var result = {};
- var self = this;
- var evalContext;
- function evalModifier(mod) {
+ let evalContext = null;
+ const evaluated = {};
+ for (const k of ['invisible', 'column_invisible', 'readonly', 'required']) {
+ const mod = modifiers[k];
if (mod === undefined || mod === false || mod === true) {
- return !!mod;
+ if (k in modifiers) {
+ evaluated[k] = !!mod;
+ }
+ continue;
+ }
+ try {
+ evalContext = evalContext || this._getEvalContext(element);
+ evaluated[k] = new Domain(mod, evalContext).compute(evalContext);
+ } catch (e) {
+ throw new Error(_.str.sprintf('for modifier "%s": %s', k, e.message));
}
- evalContext = evalContext || self._getEvalContext(element);
- return new Domain(mod, evalContext).compute(evalContext);
}
- if ('invisible' in modifiers) {
- result.invisible = evalModifier(modifiers.invisible);
- }
- if ('column_invisible' in modifiers) {
- result.column_invisible = evalModifier(modifiers.column_invisible);
- }
- if ('readonly' in modifiers) {
- result.readonly = evalModifier(modifiers.readonly);
- }
- if ('required' in modifiers) {
- result.required = evalModifier(modifiers.required);
- }
- return result;
+ return evaluated;
},
/**
* Fetch all name_gets for the many2ones in a group
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 046d10a741f..ba407b766c1 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -547,6 +547,7 @@ var BasicRenderer = AbstractRenderer.extend(WidgetAdapterMixin, {
* value (if not given, it is set to this.mode, the mode of the renderer)
* @returns {Object} for code efficiency, returns the last evaluated
* modifiers for the given node and record.
+ * @throws {Error} if one of the modifier domains is not valid
*/
_registerModifiers: function (node, record, element, options) {
options = options || {};
@@ -575,7 +576,15 @@ var BasicRenderer = AbstractRenderer.extend(WidgetAdapterMixin, {
// Evaluate if necessary
if (!modifiersData.evaluatedModifiers[record.id]) {
- modifiersData.evaluatedModifiers[record.id] = record.evalModifiers(modifiersData.modifiers);
+ try {
+ modifiersData.evaluatedModifiers[record.id] = record.evalModifiers(modifiersData.modifiers);
+ } catch (e) {
+ throw new Error(_.str.sprintf(
+ "While parsing modifiers for %s%s: %s",
+ node.tag, node.tag === 'field' ? ' ' + node.attrs.name : '',
+ e.message
+ ));
+ }
}
// Element might not be given yet (a second call to the function can
@@ -597,7 +606,7 @@ var BasicRenderer = AbstractRenderer.extend(WidgetAdapterMixin, {
}
modifiersData.elementsByRecord[record.id].push(newElement);
- this._applyModifiers(modifiersData, record, newElement, options);
+ this._applyModifiers(modifiersData, record, newElement);
}
return modifiersData.evaluatedModifiers[record.id];
diff --git a/addons/web/static/tests/core/domain_tests.js b/addons/web/static/tests/core/domain_tests.js
index a9c7ac48ece..e9ee5f80cf6 100644
--- a/addons/web/static/tests/core/domain_tests.js
+++ b/addons/web/static/tests/core/domain_tests.js
@@ -7,6 +7,11 @@ QUnit.module('core', {}, function () {
QUnit.module('domain');
+ QUnit.test("empty", function (assert) {
+ assert.expect(1);
+ assert.ok(new Domain([]).compute({}));
+ });
+
QUnit.test("basic", function (assert) {
assert.expect(3);
@@ -59,6 +64,22 @@ QUnit.module('core', {}, function () {
assert.notOk(new Domain(0).compute({}));
});
+ QUnit.test("invalid domains should not succeed", function (assert) {
+ assert.expect(3);
+ assert.throws(
+ () => new Domain(['|', ['hr_presence_state', '=', 'absent']]),
+ /invalid domain .* \(missing 1 segment/
+ );
+ assert.throws(
+ () => new Domain(['|', '|', ['hr_presence_state', '=', 'absent'], ['attendance_state', '=', 'checked_in']]),
+ /invalid domain .* \(missing 1 segment/
+ );
+ assert.throws(
+ () => new Domain(['&', ['composition_mode', '!=', 'mass_post']]),
+ /invalid domain .* \(missing 1 segment/
+ );
+ });
+
QUnit.test("domain <=> condition", function (assert) {
assert.expect(3);