[IMP] web: x2m list: conditionnaly hide a column
This commit introduces a new way to hide a column in a x2m list view. The attribute 'tree_invisible' can be add in the field attrs in the x2m view definition. This attribute can use the 'parent' key to make a reference to the parent record (e.g. 'parent.id').
This commit is contained in:
@@ -32,7 +32,7 @@ class GoogleDrive(models.Model):
|
|||||||
for node in doc.xpath("//field"):
|
for node in doc.xpath("//field"):
|
||||||
if node.get('modifiers'):
|
if node.get('modifiers'):
|
||||||
modifiers = json.loads(node.get('modifiers'))
|
modifiers = json.loads(node.get('modifiers'))
|
||||||
if not modifiers.get('invisible') and not modifiers.get('tree_invisible'):
|
if not modifiers.get('invisible') and not modifiers.get('column_invisible'):
|
||||||
display_fields.append(node.get('name'))
|
display_fields.append(node.get('name'))
|
||||||
fields = " ".join(display_fields)
|
fields = " ".join(display_fields)
|
||||||
domain = domain.replace("'", r"\'").replace('"', "'")
|
domain = domain.replace("'", r"\'").replace('"', "'")
|
||||||
|
|||||||
@@ -51,13 +51,30 @@ var Domain = collections.Tree.extend({
|
|||||||
} else if (_.isArray(this._data)) {
|
} else if (_.isArray(this._data)) {
|
||||||
// The domain is a [name, operator, value] entity
|
// The domain is a [name, operator, value] entity
|
||||||
// First check if we have the field value in the field values set
|
// 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(
|
throw new Error(_.str.sprintf(
|
||||||
"Unknown field %s in domain",
|
"Unknown field %s in domain",
|
||||||
this._data[0]
|
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]) {
|
switch (this._data[1]) {
|
||||||
case "=":
|
case "=":
|
||||||
|
|||||||
@@ -604,6 +604,11 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
toggle_column_order: '_onToggleColumnOrder',
|
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
|
* 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;
|
true;
|
||||||
this.editable = arch.attrs.editable;
|
this.editable = arch.attrs.editable;
|
||||||
}
|
}
|
||||||
|
if (this.attrs.columnInvisibleFields) {
|
||||||
|
this._processColumnInvisibleFields();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
@@ -673,10 +681,20 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
* @override
|
* @override
|
||||||
* @param {Object} record
|
* @param {Object} record
|
||||||
* @param {OdooEvent} [ev] an event that triggered the reset action
|
* @param {OdooEvent} [ev] an event that triggered the reset action
|
||||||
|
* @param {Boolean} [fieldChanged] if true, the widget field has changed
|
||||||
* @returns {Deferred}
|
* @returns {Deferred}
|
||||||
*/
|
*/
|
||||||
reset: function (record, ev) {
|
reset: function (record, ev, fieldChanged) {
|
||||||
if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
|
// 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];
|
var command = ev.data.changes[this.name];
|
||||||
// Here, we only consider 'UPDATE' commands with data, which occur
|
// Here, we only consider 'UPDATE' commands with data, which occur
|
||||||
// with editable list view. In order to keep the current line in
|
// with editable list view. In order to keep the current line in
|
||||||
@@ -700,6 +718,20 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Instanciates or updates the adequate renderer.
|
||||||
*
|
*
|
||||||
@@ -713,19 +745,25 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
}
|
}
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
this.renderer.updateState(this.value, {});
|
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();
|
return $.when();
|
||||||
}
|
}
|
||||||
var arch = this.view.arch;
|
var arch = this.view.arch;
|
||||||
var viewType;
|
var viewType;
|
||||||
if (arch.tag === 'tree') {
|
if (arch.tag === 'tree') {
|
||||||
viewType = 'list';
|
viewType = 'list';
|
||||||
|
this.currentColInvisibleFields = this._evalColumnInvisibleFields();
|
||||||
this.renderer = new ListRenderer(this, this.value, {
|
this.renderer = new ListRenderer(this, this.value, {
|
||||||
arch: arch,
|
arch: arch,
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
addCreateLine: !this.isReadonly && this.activeActions.create,
|
addCreateLine: !this.isReadonly && this.activeActions.create,
|
||||||
addTrashIcon: !this.isReadonly && this.activeActions.delete,
|
addTrashIcon: !this.isReadonly && this.activeActions.delete,
|
||||||
viewType: viewType,
|
viewType: viewType,
|
||||||
|
columnInvisibleFields: this.currentColInvisibleFields,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (arch.tag === 'kanban') {
|
if (arch.tag === 'kanban') {
|
||||||
@@ -826,6 +864,30 @@ var FieldX2Many = AbstractField.extend({
|
|||||||
}
|
}
|
||||||
return def;
|
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
|
// Handlers
|
||||||
|
|||||||
@@ -283,15 +283,15 @@ return core.Class.extend({
|
|||||||
// the invisible attribute of a field is supposed to be static ("1" in
|
// 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
|
// general), but not totally as it may use keys of the context
|
||||||
// ("context.get('some_key')"). It is evaluated server-side, and the
|
// ("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
|
// key, and the raw value is left in the invisible attribute (it is used
|
||||||
// in debug mode for informational purposes).
|
// in debug mode for informational purposes).
|
||||||
// this should change, for instance the server might set the evaluated
|
// this should change, for instance the server might set the evaluated
|
||||||
// value in invisible, which could then be seen as static by the client,
|
// value in invisible, which could then be seen as static by the client,
|
||||||
// and add another key in debug mode containing the raw value.
|
// 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
|
// for now, we look inside the modifiers and consider the value only if
|
||||||
// it from the modifiers,
|
// it is static (=== true),
|
||||||
if (attrs.invisible && attrs.modifiers.match('"(?:tree_)?invisible": ?true')) {
|
if (attrs.modifiers.invisible === true || attrs.modifiers.column_invisible === true) {
|
||||||
attrs.__no_fetch = true;
|
attrs.__no_fetch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,6 +357,15 @@ return core.Class.extend({
|
|||||||
attrs.orderedBy = [{name: handleField.attrs.name, asc: true}];
|
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
|
// detect editables list has they behave differently with respect
|
||||||
// to the sorting (changes are not sorted directly)
|
// to the sorting (changes are not sorted directly)
|
||||||
if (mode === 'list' && view.arch.attrs.editable) {
|
if (mode === 'list' && view.arch.attrs.editable) {
|
||||||
@@ -396,6 +405,9 @@ return core.Class.extend({
|
|||||||
if (typeof node === 'string') {
|
if (typeof node === 'string') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!_.isObject(node.attrs.modifiers)) {
|
||||||
|
node.attrs.modifiers = node.attrs.modifiers ? JSON.parse(node.attrs.modifiers) : {};
|
||||||
|
}
|
||||||
if (node.tag === 'field') {
|
if (node.tag === 'field') {
|
||||||
fieldsInfo[node.attrs.name] = self._processField(viewType,
|
fieldsInfo[node.attrs.name] = self._processField(viewType,
|
||||||
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
|
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
|
||||||
|
|||||||
@@ -1689,6 +1689,9 @@ var BasicModel = AbstractModel.extend({
|
|||||||
if ('invisible' in modifiers) {
|
if ('invisible' in modifiers) {
|
||||||
result.invisible = evalModifier(modifiers.invisible);
|
result.invisible = evalModifier(modifiers.invisible);
|
||||||
}
|
}
|
||||||
|
if ('column_invisible' in modifiers) {
|
||||||
|
result.column_invisible = evalModifier(modifiers.column_invisible);
|
||||||
|
}
|
||||||
if ('readonly' in modifiers) {
|
if ('readonly' in modifiers) {
|
||||||
result.readonly = evalModifier(modifiers.readonly);
|
result.readonly = evalModifier(modifiers.readonly);
|
||||||
}
|
}
|
||||||
@@ -2847,7 +2850,7 @@ var BasicModel = AbstractModel.extend({
|
|||||||
_isFieldProtected: function (record, fieldName, viewType) {
|
_isFieldProtected: function (record, fieldName, viewType) {
|
||||||
var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName];
|
var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName];
|
||||||
if (fieldInfo) {
|
if (fieldInfo) {
|
||||||
var rawModifiers = JSON.parse(fieldInfo.modifiers || "{}");
|
var rawModifiers = fieldInfo.modifiers || {};
|
||||||
var modifiers = this._evalModifiers(record, rawModifiers);
|
var modifiers = this._evalModifiers(record, rawModifiers);
|
||||||
return modifiers.readonly && !fieldInfo.force_save;
|
return modifiers.readonly && !fieldInfo.force_save;
|
||||||
} else {
|
} else {
|
||||||
@@ -2899,7 +2902,7 @@ var BasicModel = AbstractModel.extend({
|
|||||||
_.each(element.getFieldNames(), function (fieldName) {
|
_.each(element.getFieldNames(), function (fieldName) {
|
||||||
var field = element.fields[fieldName];
|
var field = element.fields[fieldName];
|
||||||
var fieldInfo = element.fieldsInfo[element.viewType][fieldName];
|
var fieldInfo = element.fieldsInfo[element.viewType][fieldName];
|
||||||
var rawModifiers = JSON.parse(fieldInfo.modifiers || "{}");
|
var rawModifiers = fieldInfo.modifiers || {};
|
||||||
var modifiers = self._evalModifiers(record, rawModifiers);
|
var modifiers = self._evalModifiers(record, rawModifiers);
|
||||||
if (modifiers.required && !self._isFieldSet(recordData[fieldName], field.type)) {
|
if (modifiers.required && !self._isFieldSet(recordData[fieldName], field.type)) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
|
|||||||
@@ -101,8 +101,9 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
// which are configured to always be reset on any change
|
// which are configured to always be reset on any change
|
||||||
var resetWidgets = [];
|
var resetWidgets = [];
|
||||||
_.each(this.allFieldWidgets[id], function (widget) {
|
_.each(this.allFieldWidgets[id], function (widget) {
|
||||||
if (_.contains(fields, widget.name) || widget.resetOnAnyFieldChange) {
|
var fieldChanged = _.contains(fields, widget.name);
|
||||||
defs.push(widget.reset(record, ev));
|
if (fieldChanged || widget.resetOnAnyFieldChange) {
|
||||||
|
defs.push(widget.reset(record, ev, fieldChanged));
|
||||||
resetWidgets.push(widget);
|
resetWidgets.push(widget);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -406,7 +407,7 @@ var BasicRenderer = AbstractRenderer.extend({
|
|||||||
// If not, check the modifiers to see if it needs registration
|
// If not, check the modifiers to see if it needs registration
|
||||||
var modifiersData = this._getModifiersData(node);
|
var modifiersData = this._getModifiersData(node);
|
||||||
if (!modifiersData) {
|
if (!modifiersData) {
|
||||||
var modifiers = JSON.parse(node.attrs.modifiers || "{}"); // FIXME parsed multiple times (record switching, no modifiers, ...)
|
var modifiers = node.attrs.modifiers || {};
|
||||||
modifiersData = {
|
modifiersData = {
|
||||||
node: node,
|
node: node,
|
||||||
modifiers: modifiers,
|
modifiers: modifiers,
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function qwebAddIf(node, condition) {
|
|||||||
function transformQwebTemplate(node, fields) {
|
function transformQwebTemplate(node, fields) {
|
||||||
// Process modifiers
|
// Process modifiers
|
||||||
if (node.tag && node.attrs.modifiers) {
|
if (node.tag && node.attrs.modifiers) {
|
||||||
var modifiers = JSON.parse(node.attrs.modifiers || "{}");
|
var modifiers = node.attrs.modifiers || {};
|
||||||
if (modifiers.invisible) {
|
if (modifiers.invisible) {
|
||||||
qwebAddIf(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible)));
|
qwebAddIf(node, _.str.sprintf("!kanban_compute_domain(%s)", JSON.stringify(modifiers.invisible)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,16 +47,9 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
*/
|
*/
|
||||||
init: function (parent, state, params) {
|
init: function (parent, state, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
var self = this;
|
|
||||||
this.hasHandle = false;
|
this.hasHandle = false;
|
||||||
this.handleField = 'sequence';
|
this.handleField = 'sequence';
|
||||||
this.columns = _.reject(this.arch.children, function (c) {
|
this._processColumns(params.columnInvisibleFields || {});
|
||||||
if (c.attrs.widget === 'handle') {
|
|
||||||
self.hasHandle = true;
|
|
||||||
self.handleField = c.attrs.name;
|
|
||||||
}
|
|
||||||
return !!JSON.parse(c.attrs.modifiers || "{}").tree_invisible;
|
|
||||||
});
|
|
||||||
this.rowDecorations = _.chain(this.arch.attrs)
|
this.rowDecorations = _.chain(this.arch.attrs)
|
||||||
.pick(function (value, key) {
|
.pick(function (value, key) {
|
||||||
return DECORATIONS.indexOf(key) >= 0;
|
return DECORATIONS.indexOf(key) >= 0;
|
||||||
@@ -68,6 +61,18 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
this.pagers = []; // instantiated pagers (only for grouped lists)
|
this.pagers = []; // instantiated pagers (only for grouped lists)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Public
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
updateState: function (state, params) {
|
||||||
|
this._processColumns(params.columnInvisibleFields || {});
|
||||||
|
return this._super.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -160,6 +165,29 @@ var ListRenderer = BasicRenderer.extend({
|
|||||||
var n = this.columns.length;
|
var n = this.columns.length;
|
||||||
return this.hasSelectors ? n+1 : n;
|
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
|
* Render a list of <td>, with aggregates if available. It can be displayed
|
||||||
* in the footer, or for each open groups.
|
* in the footer, or for each open groups.
|
||||||
|
|||||||
@@ -8351,6 +8351,40 @@ QUnit.module('relational_fields', {
|
|||||||
form.destroy();
|
form.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test('one2many invisible depends on parent field', function (assert) {
|
||||||
|
assert.expect(2);
|
||||||
|
|
||||||
|
this.data.partner.records[0].p = [2];
|
||||||
|
var form = createView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'partner',
|
||||||
|
data: this.data,
|
||||||
|
arch:'<form string="Partners">' +
|
||||||
|
'<sheet>' +
|
||||||
|
'<notebook>' +
|
||||||
|
'<page string="Partner page">' +
|
||||||
|
'<field name="bar"/>' +
|
||||||
|
'<field name="p">' +
|
||||||
|
'<tree>' +
|
||||||
|
'<field name="foo"/>' +
|
||||||
|
'<field name="bar" attrs="{\'column_invisible\': [(\'parent.bar\', \'=\', False)]}"/>' +
|
||||||
|
'</tree>' +
|
||||||
|
'</field>' +
|
||||||
|
'</page>' +
|
||||||
|
'</notebook>' +
|
||||||
|
'</sheet>' +
|
||||||
|
'</form>',
|
||||||
|
res_id: 1,
|
||||||
|
});
|
||||||
|
assert.strictEqual(form.$('th').length, 2,
|
||||||
|
"should be 2 columns in the one2many");
|
||||||
|
form.$buttons.find('.o_form_button_edit').click();
|
||||||
|
form.$('.o_field_boolean[name="bar"] input').click();
|
||||||
|
assert.strictEqual(form.$('th').length, 1,
|
||||||
|
"should be 1 column after the value change");
|
||||||
|
form.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ var MockServer = Class.extend({
|
|||||||
var pyevalContext = window.py.dict.fromJSON(context || {});
|
var pyevalContext = window.py.dict.fromJSON(context || {});
|
||||||
var v = pyeval.py_eval(node.attrs[a], {context: pyevalContext}) ? true: false;
|
var v = pyeval.py_eval(node.attrs[a], {context: pyevalContext}) ? true: false;
|
||||||
if (inTreeView && a === 'invisible') {
|
if (inTreeView && a === 'invisible') {
|
||||||
modifiers['tree_invisible'] = v;
|
modifiers['column_invisible'] = v;
|
||||||
} else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) {
|
} else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) {
|
||||||
modifiers[a] = v;
|
modifiers[a] = v;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1504,7 +1504,9 @@ QUnit.module('Views', {
|
|||||||
default: {
|
default: {
|
||||||
foo: {},
|
foo: {},
|
||||||
bar: {
|
bar: {
|
||||||
modifiers:"{\"readonly\": true}",
|
modifiers: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1524,7 +1526,6 @@ QUnit.module('Views', {
|
|||||||
return this._super(route, args);
|
return this._super(route, args);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
model.load(this.params).then(function (resultID) {
|
model.load(this.params).then(function (resultID) {
|
||||||
var record = model.get(resultID);
|
var record = model.get(resultID);
|
||||||
assert.strictEqual(record.data.bar, 2,
|
assert.strictEqual(record.data.bar, 2,
|
||||||
@@ -1568,7 +1569,9 @@ QUnit.module('Views', {
|
|||||||
default: {
|
default: {
|
||||||
foo: {},
|
foo: {},
|
||||||
bar: {
|
bar: {
|
||||||
modifiers:"{\"readonly\": true}",
|
modifiers: {
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
force_save: true,
|
force_save: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -78,7 +78,7 @@ def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False
|
|||||||
if in_tree_view and a == 'invisible':
|
if in_tree_view and a == 'invisible':
|
||||||
# Invisible in a tree view has a specific meaning, make it a
|
# Invisible in a tree view has a specific meaning, make it a
|
||||||
# new key in the modifiers attribute.
|
# new key in the modifiers attribute.
|
||||||
modifiers['tree_invisible'] = v
|
modifiers['column_invisible'] = v
|
||||||
elif v or (a not in modifiers or not isinstance(modifiers[a], list)):
|
elif v or (a not in modifiers or not isinstance(modifiers[a], list)):
|
||||||
# Don't set the attribute to False if a dynamic value was
|
# Don't set the attribute to False if a dynamic value was
|
||||||
# provided (i.e. a domain from attrs or states).
|
# provided (i.e. a domain from attrs or states).
|
||||||
@@ -106,14 +106,14 @@ def setup_modifiers(node, field=None, context=None, in_tree_view=False):
|
|||||||
:type node: lxml.etree._Element
|
:type node: lxml.etree._Element
|
||||||
:param dict field: field descriptor corresponding to the provided node
|
:param dict field: field descriptor corresponding to the provided node
|
||||||
:param dict context: execution context used to evaluate node attributes
|
:param dict context: execution context used to evaluate node attributes
|
||||||
:param bool in_tree_view: triggers the ``tree_invisible`` code
|
:param bool in_tree_view: triggers the ``column_invisible`` code
|
||||||
path (separate from ``invisible``): in
|
path (separate from ``invisible``): in
|
||||||
tree view there are two levels of
|
tree view there are two levels of
|
||||||
invisibility, cell content (a column is
|
invisibility, cell content (a column is
|
||||||
present but the cell itself is not
|
present but the cell itself is not
|
||||||
displayed) with ``invisible`` and column
|
displayed) with ``invisible`` and column
|
||||||
invisibility (the whole column is
|
invisibility (the whole column is
|
||||||
hidden) with ``tree_invisible``.
|
hidden) with ``column_invisible``.
|
||||||
:returns: nothing
|
:returns: nothing
|
||||||
"""
|
"""
|
||||||
modifiers = {}
|
modifiers = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user