summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/core/domain.js21
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js68
-rw-r--r--addons/web/static/src/js/services/data_manager.js20
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js7
-rw-r--r--addons/web/static/src/js/views/basic/basic_renderer.js7
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer.js2
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js44
7 files changed, 146 insertions, 23 deletions
diff --git a/addons/web/static/src/js/core/domain.js b/addons/web/static/src/js/core/domain.js
index 7b873926727..75aaf0105ed 100644
--- a/addons/web/static/src/js/core/domain.js
+++ b/addons/web/static/src/js/core/domain.js
@@ -51,13 +51,30 @@ var Domain = collections.Tree.extend({
} else if (_.isArray(this._data)) {
// The domain is a [name, operator, value] entity
// First check if we have the field value in the field values set
- if (!(this._data[0] in values)) {
+ // and if the first part of the domain contains 'parent.field'
+ // get the value from the parent record.
+ var isParentField = false;
+ var fieldName = this._data[0];
+ // We split the domain first part and check if it's a match
+ // for the syntax 'parent.field'.
+ var parentField = this._data[0].split('.');
+ if ('parent' in values && parentField.length === 2) {
+ fieldName = parentField[1];
+ isParentField = parentField[0] === 'parent' &&
+ fieldName in values.parent;
+ }
+ if (!(this._data[0] in values) && !(isParentField)) {
throw new Error(_.str.sprintf(
"Unknown field %s in domain",
this._data[0]
));
}
- var fieldValue = values[this._data[0]];
+ var fieldValue;
+ if (!isParentField) {
+ fieldValue = values[fieldName];
+ } else {
+ fieldValue = values.parent[fieldName];
+ }
switch (this._data[1]) {
case "=":
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 2851f31c673..dada7653e3b 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -604,6 +604,11 @@ var FieldX2Many = AbstractField.extend({
toggle_column_order: '_onToggleColumnOrder',
}),
+ // We need to trigger the reset on every changes to be aware of the parent changes
+ // and then evaluate the 'column_invisible' modifier in case a evaluated value
+ // changed.
+ resetOnAnyFieldChange: true,
+
/**
* useSubview is used in form view to load view of the related model of the x2many field
*/
@@ -629,6 +634,9 @@ var FieldX2Many = AbstractField.extend({
true;
this.editable = arch.attrs.editable;
}
+ if (this.attrs.columnInvisibleFields) {
+ this._processColumnInvisibleFields();
+ }
},
/**
* @override
@@ -673,10 +681,20 @@ var FieldX2Many = AbstractField.extend({
* @override
* @param {Object} record
* @param {OdooEvent} [ev] an event that triggered the reset action
+ * @param {Boolean} [fieldChanged] if true, the widget field has changed
* @returns {Deferred}
*/
- reset: function (record, ev) {
- if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
+ reset: function (record, ev, fieldChanged) {
+ // If 'fieldChanged' is false, it means that the reset was triggered by
+ // the 'resetOnAnyFieldChange' mechanism. If it is the case the
+ // modifiers are evaluated and if there is no change in the modifiers
+ // values, the reset is skipped.
+ if (!fieldChanged) {
+ var newEval = this._evalColumnInvisibleFields();
+ if (_.isEqual(this.currentColInvisibleFields, newEval)) {
+ return $.when();
+ }
+ } else if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
var command = ev.data.changes[this.name];
// Here, we only consider 'UPDATE' commands with data, which occur
// with editable list view. In order to keep the current line in
@@ -701,6 +719,20 @@ var FieldX2Many = AbstractField.extend({
//--------------------------------------------------------------------------
/**
+ * Evaluates the 'column_invisible' modifier for the parent record.
+ *
+ * @return {Object} Object containing fieldName as key and the evaluated
+ * column_invisible modifier
+ */
+ _evalColumnInvisibleFields: function () {
+ var self = this;
+ return _.mapObject(this.columnInvisibleFields, function (domains) {
+ return self.record.evalModifiers({
+ column_invisible: domains,
+ }).column_invisible;
+ });
+ },
+ /**
* Instanciates or updates the adequate renderer.
*
* @override
@@ -713,19 +745,25 @@ var FieldX2Many = AbstractField.extend({
}
if (this.renderer) {
this.renderer.updateState(this.value, {});
- this.pager.updateState({ size: this.value.count });
+ this.currentColInvisibleFields = this._evalColumnInvisibleFields();
+ this.pager.updateState({
+ size: this.value.count,
+ columnInvisibleFields: this.currentColInvisibleFields,
+ });
return $.when();
}
var arch = this.view.arch;
var viewType;
if (arch.tag === 'tree') {
viewType = 'list';
+ this.currentColInvisibleFields = this._evalColumnInvisibleFields();
this.renderer = new ListRenderer(this, this.value, {
arch: arch,
mode: this.mode,
addCreateLine: !this.isReadonly && this.activeActions.create,
addTrashIcon: !this.isReadonly && this.activeActions.delete,
viewType: viewType,
+ columnInvisibleFields: this.currentColInvisibleFields,
});
}
if (arch.tag === 'kanban') {
@@ -826,6 +864,30 @@ var FieldX2Many = AbstractField.extend({
}
return def;
},
+ /**
+ * Parses the 'columnInvisibleFields' attribute to search for the domains
+ * containing the key 'parent'. If there are such domains, the string
+ * 'parent.field' is replaced with 'field' in order to be evaluated
+ * with the right field name in the parent context.
+ *
+ * @private
+ */
+ _processColumnInvisibleFields: function () {
+ var columnInvisibleFields = {};
+ _.each(this.attrs.columnInvisibleFields, function (domains, fieldName) {
+ if (_.isArray(domains)) {
+ columnInvisibleFields[fieldName] = _.map(domains, function (domain) {
+ // We check if the domain is an array to avoid processing
+ // the '|' and '&' cases
+ if (_.isArray(domain)) {
+ return [domain[0].split('.')[1]].concat(domain.slice(1));
+ }
+ return domain;
+ });
+ }
+ });
+ this.columnInvisibleFields = columnInvisibleFields;
+ },
//--------------------------------------------------------------------------
// Handlers
diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js
index 22c7e86ba7d..48a0e2676ae 100644
--- a/addons/web/static/src/js/services/data_manager.js
+++ b/addons/web/static/src/js/services/data_manager.js
@@ -283,15 +283,15 @@ return core.Class.extend({
// the invisible attribute of a field is supposed to be static ("1" in
// general), but not totally as it may use keys of the context
// ("context.get('some_key')"). It is evaluated server-side, and the
- // result is put inside the modifiers as a value of the '(tree_)invisible'
+ // result is put inside the modifiers as a value of the '(column_)invisible'
// key, and the raw value is left in the invisible attribute (it is used
// in debug mode for informational purposes).
// this should change, for instance the server might set the evaluated
// value in invisible, which could then be seen as static by the client,
// and add another key in debug mode containing the raw value.
- // for now, we do an hack to detect if the value is static and retrieve
- // it from the modifiers,
- if (attrs.invisible && attrs.modifiers.match('"(?:tree_)?invisible": ?true')) {
+ // for now, we look inside the modifiers and consider the value only if
+ // it is static (=== true),
+ if (attrs.modifiers.invisible === true || attrs.modifiers.column_invisible === true) {
attrs.__no_fetch = true;
}
@@ -357,6 +357,15 @@ return core.Class.extend({
attrs.orderedBy = [{name: handleField.attrs.name, asc: true}];
}
}
+
+ attrs.columnInvisibleFields = {};
+ _.each(view.arch.children, function (child) {
+ if (child.attrs && child.attrs.modifiers) {
+ attrs.columnInvisibleFields[child.attrs.name] =
+ child.attrs.modifiers.column_invisible || false;
+ }
+ });
+
// detect editables list has they behave differently with respect
// to the sorting (changes are not sorted directly)
if (mode === 'list' && view.arch.attrs.editable) {
@@ -396,6 +405,9 @@ return core.Class.extend({
if (typeof node === 'string') {
return false;
}
+ if (!_.isObject(node.attrs.modifiers)) {
+ node.attrs.modifiers = node.attrs.modifiers ? JSON.parse(node.attrs.modifiers) : {};
+ }
if (node.tag === 'field') {
fieldsInfo[node.attrs.name] = self._processField(viewType,
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
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 d85f55ddf36..6500fb43b85 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1689,6 +1689,9 @@ var BasicModel = AbstractModel.extend({
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);
}
@@ -2847,7 +2850,7 @@ var BasicModel = AbstractModel.extend({
_isFieldProtected: function (record, fieldName, viewType) {
var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName];
if (fieldInfo) {
- var rawModifiers = JSON.parse(fieldInfo.modifiers || "{}");
+ var rawModifiers = fieldInfo.modifiers || {};
var modifiers = this._evalModifiers(record, rawModifiers);
return modifiers.readonly && !fieldInfo.force_save;
} else {
@@ -2899,7 +2902,7 @@ var BasicModel = AbstractModel.extend({
_.each(element.getFieldNames(), function (fieldName) {
var field = element.fields[fieldName];
var fieldInfo = element.fieldsInfo[element.viewType][fieldName];
- var rawModifiers = JSON.parse(fieldInfo.modifiers || "{}");
+ var rawModifiers = fieldInfo.modifiers || {};
var modifiers = self._evalModifiers(record, rawModifiers);
if (modifiers.required && !self._isFieldSet(recordData[fieldName], field.type)) {
isValid = false;
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 93dd1f4fb73..d335999582e 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -101,8 +101,9 @@ var BasicRenderer = AbstractRenderer.extend({
// which are configured to always be reset on any change
var resetWidgets = [];
_.each(this.allFieldWidgets[id], function (widget) {
- if (_.contains(fields, widget.name) || widget.resetOnAnyFieldChange) {
- defs.push(widget.reset(record, ev));
+ var fieldChanged = _.contains(fields, widget.name);
+ if (fieldChanged || widget.resetOnAnyFieldChange) {
+ defs.push(widget.reset(record, ev, fieldChanged));
resetWidgets.push(widget);
}
});
@@ -406,7 +407,7 @@ var BasicRenderer = AbstractRenderer.extend({
// If not, check the modifiers to see if it needs registration
var modifiersData = this._getModifiersData(node);
if (!modifiersData) {
- var modifiers = JSON.parse(node.attrs.modifiers || "{}"); // FIXME parsed multiple times (record switching, no modifiers, ...)
+ var modifiers = node.attrs.modifiers || {};
modifiersData = {
node: node,
modifiers: modifiers,
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js
index b46d1f7c5fd..c9239d97d03 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js
@@ -38,7 +38,7 @@ function qwebAddIf(node, condition) {
function transformQwebTemplate(node, fields) {
// Process modifiers
if (node.tag && node.attrs.modifiers) {
- var modifiers = JSON.parse(node.attrs.modifiers || "{}");
+ var modifiers = node.attrs.modifiers || {};
if (modifiers.invisible) {
qwebAddIf(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible)));
}
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index 75633629e5e..3ba80210714 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -47,16 +47,9 @@ var ListRenderer = BasicRenderer.extend({
*/
init: function (parent, state, params) {
this._super.apply(this, arguments);
- var self = this;
this.hasHandle = false;
this.handleField = 'sequence';
- this.columns = _.reject(this.arch.children, function (c) {
- if (c.attrs.widget === 'handle') {
- self.hasHandle = true;
- self.handleField = c.attrs.name;
- }
- return !!JSON.parse(c.attrs.modifiers || "{}").tree_invisible;
- });
+ this._processColumns(params.columnInvisibleFields || {});
this.rowDecorations = _.chain(this.arch.attrs)
.pick(function (value, key) {
return DECORATIONS.indexOf(key) >= 0;
@@ -69,6 +62,18 @@ var ListRenderer = BasicRenderer.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ updateState: function (state, params) {
+ this._processColumns(params.columnInvisibleFields || {});
+ return this._super.apply(this, arguments);
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -161,6 +166,29 @@ var ListRenderer = BasicRenderer.extend({
return this.hasSelectors ? n+1 : n;
},
/**
+ * Removes the columns which should be invisible.
+ *
+ * @param {Object} columnInvisibleFields contains the column invisible modifier values
+ */
+ _processColumns: function (columnInvisibleFields) {
+ var self = this;
+ self.hasHandle = false;
+ self.handleField = null;
+ this.columns = _.reject(this.arch.children, function (c) {
+ var reject = c.attrs.modifiers.column_invisible;
+ // If there is an evaluated domain for the field we override the node
+ // attribute to have the evaluated modifier value.
+ if (c.attrs.name in columnInvisibleFields) {
+ reject = columnInvisibleFields[c.attrs.name];
+ }
+ if (!reject && c.attrs.widget === 'handle') {
+ self.hasHandle = true;
+ self.handleField = c.attrs.name;
+ }
+ return reject;
+ });
+ },
+ /**
* Render a list of <td>, with aggregates if available. It can be displayed
* in the footer, or for each open groups.
*