[REF] web: change internal representation of fields and view meta datas
* Data manager load_view returns a viewInfo object * ViewInfo contains: the arch, fields and fieldsInfo objects, and some extra informations * The fields come from the server and are frozen, without any view attributes (depend of the model). * The fieldsInfo contains every view attributes and informations.
This commit is contained in:
committed by
Aaron Bohy
parent
e1eea38f2f
commit
eaa66d1156
@@ -50,7 +50,7 @@ function jsonp(form, attributes, callback) {
|
||||
|
||||
// Mixins that enable the 'Import' feature
|
||||
var ImportViewMixin = {
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
var importEnabled = 'import_enabled' in params ? params.import_enabled : true;
|
||||
// if true, the 'Import' button will be visible
|
||||
this.controllerParams.importEnabled = importEnabled;
|
||||
|
||||
@@ -6,28 +6,19 @@ var BasicView = require('web.BasicView');
|
||||
var mailWidgets = ['mail_followers', 'mail_thread', 'mail_activity', 'kanban_activity'];
|
||||
|
||||
BasicView.include({
|
||||
init: function () {
|
||||
this.mailFields = {};
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.mailFields = {};
|
||||
for (var fieldName in viewInfo.fieldsInfo) {
|
||||
var fieldInfo = viewInfo.fieldsInfo[fieldName];
|
||||
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
||||
this.mailFields[fieldInfo.widget] = fieldName;
|
||||
fieldInfo.__no_fetch = true;
|
||||
}
|
||||
}
|
||||
this.rendererParams.activeActions = this.controllerParams.activeActions;
|
||||
this.rendererParams.mailFields = this.mailFields;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @param {Object} field - the field properties
|
||||
* @param {Object} attrs - the field attributes (from the xml)
|
||||
*/
|
||||
_processField: function (field, attrs) {
|
||||
if (_.contains(mailWidgets, attrs.widget)) {
|
||||
this.mailFields[attrs.widget] = attrs.name;
|
||||
field.__no_fetch = true;
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ var Chatter = Widget.extend(chat_mixin, {
|
||||
}
|
||||
if (mailFields.mail_thread) {
|
||||
this.fields.thread = new ThreadField(this, mailFields.mail_thread, record, options);
|
||||
var nodeOptions = this.record.fieldAttrs[mailFields.mail_thread].options;
|
||||
var nodeOptions = this.record.fieldsInfo[mailFields.mail_thread].options;
|
||||
this.hasLogButton = nodeOptions.display_log_button;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -348,7 +348,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
params.mode = params.initial_mode || 'edit';
|
||||
}
|
||||
|
||||
view = new View(arch, view.fields_view.fields, params);
|
||||
view = new View(view.fields_view, params);
|
||||
return view.getController(this).then(function(controller) {
|
||||
controller.on('history_back', this, function() {
|
||||
if (self.action_manager) self.action_manager.trigger('history_back');
|
||||
|
||||
@@ -429,6 +429,33 @@ var utils = {
|
||||
});
|
||||
return str;
|
||||
},
|
||||
/**
|
||||
* Visit a tree of objects, where each children are in an attribute 'children'.
|
||||
* For each children, we call the callback function given in arguments.
|
||||
*
|
||||
* @param {Object} tree an object describing a tree structure
|
||||
* @param {function} f a callback
|
||||
*/
|
||||
traverse: function (tree, f) {
|
||||
if (f(tree)) {
|
||||
_.each(tree.children, function (c) { utils.traverse(c, f); });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Visit a tree of objects and freeze all
|
||||
*
|
||||
* @param {Object} obj
|
||||
*/
|
||||
deepFreeze: function (obj) {
|
||||
var propNames = Object.getOwnPropertyNames(obj);
|
||||
propNames.forEach(function(name) {
|
||||
var prop = obj[name];
|
||||
if (typeof prop == 'object' && prop !== null)
|
||||
utils.deepFreeze(prop);
|
||||
});
|
||||
return Object.freeze(obj);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
return utils;
|
||||
|
||||
@@ -88,8 +88,9 @@ var AbstractField = Widget.extend({
|
||||
// such as the type, the comodel (relation), ...
|
||||
this.field = record.fields[name];
|
||||
|
||||
// the 'attrs' property contains the attributes of the xml 'field' tag
|
||||
this.attrs = record.fieldAttrs[name] || {};
|
||||
// the 'attrs' property contains the attributes of the xml 'field' tag,
|
||||
// the inner views...
|
||||
this.attrs = record.fieldsInfo && record.fieldsInfo[name] || {};
|
||||
|
||||
// modifiers
|
||||
this.modifiers = JSON.parse(this.attrs.modifiers || "{}");
|
||||
|
||||
@@ -20,6 +20,8 @@ var session = require('web.session');
|
||||
var time = require('web.time');
|
||||
var utils = require('web.utils');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Formatting
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -191,10 +193,13 @@ function formatMany2one(value) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatMany2Many(value) {
|
||||
var names = _.map(value.data, function (p) {
|
||||
return p.data.display_name;
|
||||
});
|
||||
return names.join(', ');
|
||||
if (value.data.length === 0) {
|
||||
return value.data.length + _t('0 records');
|
||||
} else if (value.data.length === 1) {
|
||||
return value.data.length + _t('1 record');
|
||||
} else {
|
||||
return value.data.length + _t(' records');
|
||||
}
|
||||
}
|
||||
|
||||
function formatMonetary(value, field, options) {
|
||||
|
||||
@@ -520,7 +520,7 @@ var FieldX2Many = AbstractField.extend({
|
||||
this._super.apply(this, arguments);
|
||||
this.operations = [];
|
||||
this.isReadonly = this.mode === 'readonly';
|
||||
this.view = this.field.views.tree || this.field.views.kanban;
|
||||
this.view = this.attrs.views.list || this.attrs.views.kanban;
|
||||
this.activeActions = {};
|
||||
var arch = this.view && this.view.arch;
|
||||
if (arch) {
|
||||
@@ -704,7 +704,7 @@ var FieldX2Many = AbstractField.extend({
|
||||
_.extend(event.data, {
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
domain: this.record.getDomain({fieldName: this.name}),
|
||||
form_view: this.field.views && this.field.views.form,
|
||||
form_view: this.attrs.views && this.attrs.views.form,
|
||||
readonly: this.mode === 'readonly',
|
||||
string: this.string,
|
||||
});
|
||||
@@ -799,6 +799,7 @@ var FieldOne2Many = FieldX2Many.extend({
|
||||
domain: this.record.getDomain({fieldName: this.name}),
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
field: this.field,
|
||||
attrs: this.attrs,
|
||||
on_save: function (record) {
|
||||
this._setValue({
|
||||
operation: 'ADD',
|
||||
@@ -867,7 +868,7 @@ var FieldMany2Many = FieldX2Many.extend({
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
title: _t("Add: ") + this.string,
|
||||
no_create: this.nodeOptions.no_create || !this.activeActions.create,
|
||||
fields_view: this.field.views.form,
|
||||
fields_view: this.attrs.views.form,
|
||||
on_selected: function (res_ids) {
|
||||
var new_ids = _.difference(res_ids, self.value.res_ids);
|
||||
if (new_ids.length) {
|
||||
@@ -911,6 +912,8 @@ var FieldMany2ManyTags = AbstractField.extend({
|
||||
'keydown .o_form_field_many2one input': '_onKeyDown',
|
||||
}),
|
||||
|
||||
fetchSubFields: true,
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
odoo.define('web.DataManager', function (require) {
|
||||
"use strict";
|
||||
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var pyeval = require('web.pyeval');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
|
||||
@@ -112,7 +115,13 @@ return core.Class.extend({
|
||||
}
|
||||
|
||||
return this._cache.views[key].then(function (views) {
|
||||
return $.extend(true, {}, views);
|
||||
return _.mapObject(views, function (view, viewType) {
|
||||
return _.extend(view, self._processFieldsView({
|
||||
type: viewType,
|
||||
arch: view.arch,
|
||||
fields: view.fields,
|
||||
}));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -224,6 +233,149 @@ return core.Class.extend({
|
||||
_invalidate: function (cache, key) {
|
||||
delete cache[key];
|
||||
},
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Process a field node, in particular, put a flag on the field to give
|
||||
* special directives to the BasicModel.
|
||||
*
|
||||
* @param {string} viewType
|
||||
* @param {Object} field - the field properties
|
||||
* @param {Object} attrs - the field attributes (from the xml)
|
||||
* @returns {Object} attrs
|
||||
*/
|
||||
_processField: function (viewType, field, attrs) {
|
||||
var self = this;
|
||||
attrs.Widget = this._getFieldWidgetClass(viewType, field, attrs);
|
||||
|
||||
if (!_.isObject(attrs.options)) { // parent arch could have already been processed (TODO this should not happen)
|
||||
attrs.options = attrs.options ? pyeval.py_eval(attrs.options) : {};
|
||||
}
|
||||
|
||||
if (attrs.on_change && !field.onChange) {
|
||||
field.onChange = "1";
|
||||
}
|
||||
|
||||
if (field.views) {
|
||||
// process the inner fields_view as well to find the fields they use.
|
||||
// register those fields' description directly on the view.
|
||||
// for those inner views, the list of all fields isn't necessary, so
|
||||
// basically the field_names will be the keys of the fields obj.
|
||||
// don't use _ to iterate on fields in case there is a 'length' field,
|
||||
// as _ doesn't behave correctly when there is a length key in the object
|
||||
attrs.views = {};
|
||||
_.each(field.views, function (innerFieldsView, viewType) {
|
||||
innerFieldsView.type = viewType;
|
||||
attrs.views[viewType] = self._processFieldsView(_.extend({}, innerFieldsView));
|
||||
});
|
||||
delete field.views;
|
||||
}
|
||||
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
if (attrs.Widget.prototype.useSubview) {
|
||||
if (!attrs.views) {
|
||||
attrs.views = {};
|
||||
}
|
||||
var mode = attrs.mode;
|
||||
if (!mode) {
|
||||
if (attrs.views.tree && attrs.views.kanban) {
|
||||
mode = 'tree';
|
||||
} else if (!attrs.views.tree && attrs.views.kanban) {
|
||||
mode = 'kanban';
|
||||
} else {
|
||||
mode = 'tree,kanban';
|
||||
}
|
||||
} else {
|
||||
mode = 'tree,kanban';
|
||||
}
|
||||
if (mode.indexOf(',') !== -1) {
|
||||
mode = config.device.size_class !== config.device.SIZES.XS ? 'tree' : 'kanban';
|
||||
}
|
||||
if (mode === 'tree') {
|
||||
mode = 'list';
|
||||
if (!attrs.views.list && attrs.views.tree) {
|
||||
attrs.views.list = attrs.views.tree;
|
||||
}
|
||||
}
|
||||
attrs.mode = mode;
|
||||
}
|
||||
if (attrs.Widget.prototype.fetchSubFields) {
|
||||
attrs.relatedFields = {
|
||||
display_name: {type: 'char'},
|
||||
//id: {type: 'integer'},
|
||||
};
|
||||
attrs.fieldsInfo = {display_name: {}, id: {}};
|
||||
if (attrs.color || 'color') {
|
||||
attrs.relatedFields[attrs.color || 'color'] = {type: 'int'};
|
||||
attrs.fieldsInfo.color = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
},
|
||||
/**
|
||||
* Visit all nodes in the arch field and process each fields
|
||||
*
|
||||
* @param {string} viewType
|
||||
* @param {Object} arch
|
||||
* @param {Object} fields
|
||||
* @returns {Object} fieldsInfo
|
||||
*/
|
||||
_processFields: function (viewType, arch, fields) {
|
||||
var self = this;
|
||||
var fieldsInfo = Object.create(null);
|
||||
utils.traverse(arch, function (node) {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (node.tag === 'field') {
|
||||
fieldsInfo[node.attrs.name] = self._processField(viewType,
|
||||
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
|
||||
return false;
|
||||
}
|
||||
return node.tag !== 'arch';
|
||||
});
|
||||
return fieldsInfo;
|
||||
},
|
||||
/**
|
||||
* Visit all nodes in the arch field and process each fields and inner views
|
||||
*
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} viewInfo.arch
|
||||
* @param {Object} viewInfo.fields
|
||||
* @returns {Object} viewInfo
|
||||
*/
|
||||
_processFieldsView: function (viewInfo) {
|
||||
viewInfo.fieldsInfo = this._processFields(viewInfo.type, viewInfo.arch, viewInfo.fields);
|
||||
// by default display fetch display_name and id
|
||||
if (!viewInfo.fields.display_name) {
|
||||
viewInfo.fields.display_name = {type: 'char'};
|
||||
viewInfo.fieldsInfo.display_name = {};
|
||||
}
|
||||
utils.deepFreeze(viewInfo.fields);
|
||||
return viewInfo;
|
||||
},
|
||||
/**
|
||||
* Returns the AbstractField specialization that should be used for the
|
||||
* given field informations. If there is no mentioned specific widget to
|
||||
* use, determine one according the field type.
|
||||
*
|
||||
* @param {string} viewType
|
||||
* @param {Object} field
|
||||
* @param {Object} attrs
|
||||
* @returns {function|null} AbstractField specialization Class
|
||||
*/
|
||||
_getFieldWidgetClass: function (viewType, field, attrs) {
|
||||
var Widget;
|
||||
if (attrs.widget) {
|
||||
Widget = fieldRegistry.getAny([viewType + "." + attrs.widget, attrs.widget]);
|
||||
if (!Widget) {
|
||||
console.warn("Missing widget: ", attrs.widget, " for field", attrs.name, "of type", field.type);
|
||||
}
|
||||
}
|
||||
return Widget || fieldRegistry.getAny([viewType + "." + field.type, field.type, "abstract"]);
|
||||
},
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -60,8 +60,10 @@ var AbstractView = Class.extend({
|
||||
*
|
||||
* @constructs AbstractView
|
||||
*
|
||||
* @param {any} arch
|
||||
* @param {any} fields
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} viewInfo.arch
|
||||
* @param {Object} viewInfo.fields
|
||||
* @param {Object} viewInfo.fieldsInfo
|
||||
* @param {Object} params
|
||||
* @param {string} params.modelName The actual model name
|
||||
* @param {Object} params.context
|
||||
@@ -72,17 +74,17 @@ var AbstractView = Class.extend({
|
||||
* @param {number[]} [params.ids]
|
||||
* @param {string} [params.action.help]
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this.rendererParams = {
|
||||
arch: arch
|
||||
arch: viewInfo.arch,
|
||||
};
|
||||
|
||||
this.controllerParams = {
|
||||
modelName: params.modelName,
|
||||
activeActions: {
|
||||
edit: arch.attrs.edit ? JSON.parse(arch.attrs.edit) : true,
|
||||
create: arch.attrs.create ? JSON.parse(arch.attrs.create) : true,
|
||||
delete: arch.attrs.delete ? JSON.parse(arch.attrs.delete) : true,
|
||||
edit: viewInfo.arch.attrs.edit ? JSON.parse(viewInfo.arch.attrs.edit) : true,
|
||||
create: viewInfo.arch.attrs.create ? JSON.parse(viewInfo.arch.attrs.create) : true,
|
||||
delete: viewInfo.arch.attrs.delete ? JSON.parse(viewInfo.arch.attrs.delete) : true,
|
||||
},
|
||||
noContentHelp: params.action && params.action.help,
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ odoo.define('web.BasicModel', function (require) {
|
||||
* domain: {*[]},
|
||||
* fieldNames: {string[]},
|
||||
* fields: {Object},
|
||||
* fieldAttrs: {Object},
|
||||
* fieldsInfo: {Object},
|
||||
* getContext: {function},
|
||||
* getDomain: {function},
|
||||
* groupedBy: {string[]},
|
||||
@@ -160,7 +160,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var position = (options && options.position) || 'top';
|
||||
var params = {
|
||||
context: context,
|
||||
fieldAttrs: list.fieldAttrs,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
fieldNames: list.fieldNames,
|
||||
fields: list.fields,
|
||||
parentID: list.id,
|
||||
@@ -238,7 +238,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var index = record.res_ids.indexOf(record.res_id);
|
||||
record.res_ids.splice(index + 1, 0, res_id);
|
||||
return self.load({
|
||||
fieldAttrs: record.fieldAttrs,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
modelName: record.model,
|
||||
@@ -395,7 +395,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* @todo document all params
|
||||
*
|
||||
* @param {any} params
|
||||
* @param {Object} [params.fieldAttrs={}] contains the attrs of each field
|
||||
* @param {Object} [params.fieldsInfo={}] contains the fieldInfo of each field
|
||||
* @param {Array} [params.fieldNames] the name of fields to load, the list
|
||||
* of all fields by default
|
||||
* @param {Object} params.fields contains the description of each field
|
||||
@@ -428,7 +428,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* @param {any} params
|
||||
* @param {string} modelName model name
|
||||
* @param {Object} params.context the context for the new record
|
||||
* @param {Object} [params.fieldAttrs={}] contains the attrs of each field
|
||||
* @param {Object} [params.fieldsInfo={}] contains the fieldInfo of each field
|
||||
* @param {Array} params.fieldNames the name of fields to load, the list
|
||||
* of all fields by default
|
||||
* @param {Object} params.fields contains the description of each field
|
||||
@@ -463,7 +463,7 @@ var BasicModel = AbstractModel.extend({
|
||||
data: data,
|
||||
fields: params.fields,
|
||||
fieldNames: params.fieldNames,
|
||||
fieldAttrs: params.fieldAttrs,
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
context: params.context,
|
||||
parentID: params.parentID,
|
||||
relationField: params.relationField,
|
||||
@@ -483,15 +483,17 @@ var BasicModel = AbstractModel.extend({
|
||||
});
|
||||
record._changes[name] = rec.id;
|
||||
} else if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
var attrs = record.fieldAttrs[name];
|
||||
var fieldInfo = record.fieldsInfo[name];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
|
||||
var x2manyList = self._makeDataPoint({
|
||||
context: record.context,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fields: field.relatedFields,
|
||||
limit: field.limit,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
limit: fieldInfo.limit,
|
||||
modelName: field.relation,
|
||||
parentID: record.id,
|
||||
rawContext: attrs && attrs.context,
|
||||
rawContext: fieldInfo && fieldInfo.context,
|
||||
relationField: field.relation_field,
|
||||
res_ids: [],
|
||||
static: true,
|
||||
@@ -508,8 +510,8 @@ var BasicModel = AbstractModel.extend({
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fields: field.relatedFields,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
});
|
||||
x2manyList._changes.push(r.id);
|
||||
r._changes = value[2];
|
||||
@@ -540,8 +542,8 @@ var BasicModel = AbstractModel.extend({
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fields: field.relatedFields,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
res_id: value,
|
||||
});
|
||||
if (!x2manyList._changes) {
|
||||
@@ -599,10 +601,10 @@ var BasicModel = AbstractModel.extend({
|
||||
*
|
||||
* @param {string} model name of the model
|
||||
* @param {Object[]} fields a description of field properties
|
||||
* @param {Object} [attrs] various field attrs that we want to set
|
||||
* @param {Object} [fieldInfo] various field fieldInfo that we want to set
|
||||
* @returns {string} the local id for the created resource
|
||||
*/
|
||||
makeRecord: function (model, fields, attrs) {
|
||||
makeRecord: function (model, fields, fieldInfo) {
|
||||
var self = this;
|
||||
var record_fields = {};
|
||||
_.each(fields, function (field) {
|
||||
@@ -611,7 +613,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var record = this._makeDataPoint({
|
||||
modelName: model,
|
||||
fields: record_fields,
|
||||
fieldAttrs: attrs,
|
||||
fieldsInfo: fieldInfo,
|
||||
});
|
||||
_.each(fields, function (field) {
|
||||
if ('value' in field) {
|
||||
@@ -704,7 +706,7 @@ var BasicModel = AbstractModel.extend({
|
||||
if ('currentId' in options && !options.currentId) {
|
||||
var params = {
|
||||
context: element.context,
|
||||
fieldAttrs: element.fieldAttrs,
|
||||
fieldsInfo: element.fieldsInfo,
|
||||
fieldNames: element.fieldNames,
|
||||
fields: element.fields,
|
||||
};
|
||||
@@ -836,7 +838,7 @@ var BasicModel = AbstractModel.extend({
|
||||
/**
|
||||
* Set fieldNames.
|
||||
* Set fields on a record element (current fields as defaults)
|
||||
* Set fieldattrs.
|
||||
* Set fieldsInfo.
|
||||
* It is mostly useful for the cases where a record element is shared
|
||||
* between various views, such as a one2many with a tree and a form view.
|
||||
*
|
||||
@@ -844,22 +846,23 @@ var BasicModel = AbstractModel.extend({
|
||||
* @param {Object} props
|
||||
* @param {Object} props.fieldNames
|
||||
* @param {Object} props.fields
|
||||
* @param {Object} props.fieldAttrs
|
||||
* @param {Object} props.fieldsInfo
|
||||
* @return {Object}
|
||||
* @return {Object} result.fieldNames the previous field names
|
||||
* @return {Object} result.fields the previous description fields
|
||||
* @return {Object} result.fieldattrs the previous field attrs
|
||||
* @return {Object} result.fieldsInfo the previous field fieldInfo
|
||||
*/
|
||||
setFieldProps: function (recordID, props) {
|
||||
var record = this.localData[recordID];
|
||||
var result = {
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
fieldAttrs: record.fieldAttrs,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
};
|
||||
record.fieldNames = props.fieldNames;
|
||||
record.mode = props.mode;
|
||||
record.fieldNames = Object.keys(props.fieldsInfo);
|
||||
record.fields = _.defaults(props.fields, record.fields);
|
||||
record.fieldAttrs = props.fieldAttrs;
|
||||
record.fieldsInfo = props.fieldsInfo;
|
||||
return result;
|
||||
},
|
||||
/**
|
||||
@@ -979,6 +982,7 @@ var BasicModel = AbstractModel.extend({
|
||||
context: record.context,
|
||||
data: rel_data,
|
||||
fields: {},
|
||||
fieldsInfo: {},
|
||||
modelName: record.fields[fieldName].relation,
|
||||
});
|
||||
record._changes[fieldName] = rec.id;
|
||||
@@ -1063,7 +1067,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var params = {
|
||||
context: context,
|
||||
fields: list.fields,
|
||||
fieldAttrs: list.fieldAttrs,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
modelName: list.model,
|
||||
fieldNames: _.keys(command[2]),
|
||||
};
|
||||
@@ -1128,6 +1132,8 @@ var BasicModel = AbstractModel.extend({
|
||||
var self = this;
|
||||
var list = this.localData[record._changes[fieldName] || record.data[fieldName]];
|
||||
var field = record.fields[fieldName];
|
||||
var fieldInfo = record.fieldsInfo[fieldName];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
var rec;
|
||||
var defs = [];
|
||||
list._changes = list._changes || list.data;
|
||||
@@ -1158,8 +1164,8 @@ var BasicModel = AbstractModel.extend({
|
||||
rec = self._makeDataPoint({
|
||||
context: record.context,
|
||||
modelName: field.relation,
|
||||
fields: field.relatedFields,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
res_id: d.id,
|
||||
});
|
||||
list_records[d.id] = rec;
|
||||
@@ -1237,9 +1243,10 @@ var BasicModel = AbstractModel.extend({
|
||||
var specs = {};
|
||||
_.each(record.fieldNames, function (name) {
|
||||
var field = record.fields[name];
|
||||
var fieldInfo = record.fieldsInfo[name];
|
||||
specs[name] = (field.onChange) || "";
|
||||
_.each(field.views, function (view) {
|
||||
_.each(view.fieldAttrs, function (field, subname) {
|
||||
_.each(fieldInfo.views, function (view) {
|
||||
_.each(view.fieldsInfo, function (field, subname) {
|
||||
specs[name + '.' + subname] = (view.fields[subname].onChange) || "";
|
||||
});
|
||||
});
|
||||
@@ -1374,8 +1381,8 @@ var BasicModel = AbstractModel.extend({
|
||||
var self = this;
|
||||
var fieldNames = [];
|
||||
return $.when.apply($, _.map(record.fieldNames, function (name) {
|
||||
var attrs = record.fieldAttrs[name] || {};
|
||||
var Widget = attrs.Widget;
|
||||
var fieldInfo = record.fieldsInfo[name] || {};
|
||||
var Widget = fieldInfo.Widget;
|
||||
if (Widget && Widget.prototype.specialData) {
|
||||
return self[Widget.prototype.specialData](record, name).then(function (data) {
|
||||
if (data === undefined) {
|
||||
@@ -1501,7 +1508,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* the undefined value.
|
||||
*/
|
||||
_fetchSpecialStatus: function (record, fieldName) {
|
||||
var foldField = record.fieldAttrs[fieldName].options.fold_field;
|
||||
var foldField = record.fieldsInfo[fieldName].options.fold_field;
|
||||
var fieldsToRead = foldField ? [foldField] : [];
|
||||
return this._fetchSpecialMany2ones(record, fieldName, fieldsToRead).then(function (m2os) {
|
||||
_.each(m2os, function (m2o) {
|
||||
@@ -1525,7 +1532,7 @@ var BasicModel = AbstractModel.extend({
|
||||
_fetchSpecialDomain: function (record, fieldName) {
|
||||
var context = record.getContext({fieldName: fieldName});
|
||||
|
||||
var domainModel = record.fieldAttrs[fieldName].options.model;
|
||||
var domainModel = record.fieldsInfo[fieldName].options.model;
|
||||
if (record.data.hasOwnProperty(domainModel)) {
|
||||
domainModel = record._changes && record._changes[domainModel] || record.data[domainModel];
|
||||
}
|
||||
@@ -1600,14 +1607,15 @@ var BasicModel = AbstractModel.extend({
|
||||
_.each(fieldNames, function (fieldName) {
|
||||
var field = record.fields[fieldName];
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
var attrs = record.fieldAttrs[fieldName];
|
||||
var rawContext = attrs && attrs.context;
|
||||
var fieldInfo = record.fieldsInfo[fieldName];
|
||||
var rawContext = fieldInfo && fieldInfo.context;
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
var ids = record.data[fieldName] || [];
|
||||
var list = self._makeDataPoint({
|
||||
count: ids.length,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fields: field.relatedFields,
|
||||
limit: field.limit,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
limit: fieldInfo.limit,
|
||||
modelName: field.relation,
|
||||
res_ids: ids,
|
||||
static: true,
|
||||
@@ -1617,7 +1625,7 @@ var BasicModel = AbstractModel.extend({
|
||||
relationField: field.relation_field,
|
||||
});
|
||||
record.data[fieldName] = list.id;
|
||||
if (!field.__no_fetch) {
|
||||
if (!fieldInfo.__no_fetch) {
|
||||
var def = self._readUngroupedList(list).then(function () {
|
||||
return self._fetchX2ManysBatched(list);
|
||||
});
|
||||
@@ -1638,6 +1646,8 @@ var BasicModel = AbstractModel.extend({
|
||||
_fetchX2ManyBatched: function (list, fieldName) {
|
||||
var self = this;
|
||||
var field = list.fields[fieldName];
|
||||
var fieldInfo = list.fieldsInfo[fieldName];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
|
||||
// step 1: collect ids
|
||||
var ids = [];
|
||||
@@ -1645,8 +1655,8 @@ var BasicModel = AbstractModel.extend({
|
||||
var record = self.localData[dataPoint];
|
||||
ids = _.unique(ids.concat(record.data[fieldName] || []));
|
||||
var m2mList = self._makeDataPoint({
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fields: field.relatedFields,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
modelName: field.relation,
|
||||
res_ids: record.data[fieldName],
|
||||
static: true,
|
||||
@@ -1655,13 +1665,13 @@ var BasicModel = AbstractModel.extend({
|
||||
record.data[fieldName] = m2mList.id;
|
||||
});
|
||||
|
||||
if (!ids.length || field.__no_fetch) {
|
||||
if (!ids.length || fieldInfo.__no_fetch) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
// step 2: fetch data from server
|
||||
return this._rpc(field.relation, 'read')
|
||||
.args([ids, _.keys(field.relatedFields)])
|
||||
.args([ids, _.keys(fieldInfo.relatedFields)])
|
||||
.withContext({}) // FIXME
|
||||
.exec()
|
||||
.then(function (results) {
|
||||
@@ -1670,8 +1680,8 @@ var BasicModel = AbstractModel.extend({
|
||||
return self._makeDataPoint({
|
||||
modelName: field.relation,
|
||||
data: result,
|
||||
fields: field.relatedFields,
|
||||
fieldAttrs: field.fieldAttrs,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1796,9 +1806,9 @@ var BasicModel = AbstractModel.extend({
|
||||
context.set_eval_context(this._getEvalContext(element));
|
||||
|
||||
if (options && options.fieldName) {
|
||||
var attrs = element.fieldAttrs[options.fieldName];
|
||||
if (attrs && attrs.context) {
|
||||
context.add(attrs.context);
|
||||
var fieldInfo = element.fieldsInfo[options.fieldName];
|
||||
if (fieldInfo && fieldInfo.context) {
|
||||
context.add(fieldInfo.context);
|
||||
} else {
|
||||
var fieldParams = element.fields[options.fieldName];
|
||||
if (fieldParams.context) {
|
||||
@@ -1828,10 +1838,10 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_getDomain: function (element, options) {
|
||||
if (options && options.fieldName) {
|
||||
var attrs = element.fieldAttrs[options.fieldName];
|
||||
if (attrs && attrs.domain) {
|
||||
var fieldInfo = element.fieldsInfo[options.fieldName];
|
||||
if (fieldInfo && fieldInfo.domain) {
|
||||
return Domain.prototype.stringToArray(
|
||||
attrs.domain,
|
||||
fieldInfo.domain,
|
||||
this._getEvalContext(element)
|
||||
);
|
||||
}
|
||||
@@ -1899,7 +1909,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* the resource in the localData object.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {Object} [params.fieldAttrs={}] contains the attrs of each field
|
||||
* @param {Object} [params.fieldsInfo={}] contains the fieldInfo of each field
|
||||
* @param {Array} [params.fieldNames] the name of fields to load, the list
|
||||
* of all fields by default
|
||||
* @param {Object} params.fields contains the description of each field
|
||||
@@ -1907,7 +1917,6 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_makeDataPoint: function (params) {
|
||||
var type = params.type || ('domain' in params && 'list') || 'record';
|
||||
|
||||
var res_id, value;
|
||||
var res_ids = params.res_ids || [];
|
||||
if (type === 'record') {
|
||||
@@ -1923,6 +1932,13 @@ var BasicModel = AbstractModel.extend({
|
||||
id: {type: 'integer'},
|
||||
}, params.fields);
|
||||
|
||||
if (!params.fieldsInfo) {
|
||||
params.fieldsInfo = {};
|
||||
for (var fieldName in fields) {
|
||||
params.fieldsInfo[fieldName] = {};
|
||||
}
|
||||
}
|
||||
|
||||
var dataPoint = {
|
||||
_cache: type === 'list' ? {} : undefined,
|
||||
_changes: null,
|
||||
@@ -1931,9 +1947,9 @@ var BasicModel = AbstractModel.extend({
|
||||
count: params.count || res_ids.length,
|
||||
data: params.data || (type === 'record' ? {} : []),
|
||||
domain: params.domain || [],
|
||||
fieldNames: params.fieldNames || Object.keys(fields),
|
||||
fieldNames: params.fieldNames || Object.keys(params.fieldsInfo),
|
||||
fields: fields,
|
||||
fieldAttrs: params.fieldAttrs || {},
|
||||
fieldsInfo: params.fieldsInfo || {},
|
||||
groupedBy: params.groupedBy || [],
|
||||
id: _.uniqueId(params.modelName + '_'),
|
||||
isOpen: params.isOpen,
|
||||
@@ -1979,6 +1995,10 @@ var BasicModel = AbstractModel.extend({
|
||||
// the many2one value is of the form [id, display_name]
|
||||
var r = self._makeDataPoint({
|
||||
modelName: field.relation,
|
||||
fields: {
|
||||
display_name: {type: 'char'},
|
||||
id: {type: 'integer'},
|
||||
},
|
||||
data: {
|
||||
display_name: val[1],
|
||||
id: val[0],
|
||||
@@ -2013,8 +2033,8 @@ var BasicModel = AbstractModel.extend({
|
||||
var defs = [];
|
||||
_.each(record.fieldNames, function (name) {
|
||||
var field = record.fields[name];
|
||||
var attrs = record.fieldAttrs[name] || {};
|
||||
var options = attrs.options || {};
|
||||
var fieldInfo = record.fieldsInfo[name] || {};
|
||||
var options = fieldInfo.options || {};
|
||||
if (options.always_reload) {
|
||||
if (record.fields[name].type === 'many2one' && record.data[name]) {
|
||||
var element = self.localData[record.data[name]];
|
||||
@@ -2076,7 +2096,7 @@ var BasicModel = AbstractModel.extend({
|
||||
context: list.context,
|
||||
fields: list.fields,
|
||||
fieldNames: list.fieldNames,
|
||||
fieldAttrs: list.fieldAttrs,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
value: group[rawGroupBy],
|
||||
aggregateValues: aggregateValues,
|
||||
groupedBy: list.groupedBy.slice(1),
|
||||
@@ -2152,7 +2172,7 @@ var BasicModel = AbstractModel.extend({
|
||||
} else {
|
||||
dataPoint = self._makeDataPoint({
|
||||
data: _.findWhere(records, {id: id}),
|
||||
fieldAttrs: list.fieldAttrs,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
fieldNames: list.fieldNames,
|
||||
fields: list.fields,
|
||||
modelName: list.model,
|
||||
@@ -2211,7 +2231,7 @@ var BasicModel = AbstractModel.extend({
|
||||
data: record,
|
||||
fields: list.fields,
|
||||
fieldNames: list.fieldNames,
|
||||
fieldAttrs: list.fieldAttrs,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
modelName: list.model,
|
||||
parentID: list.id,
|
||||
});
|
||||
|
||||
@@ -13,23 +13,6 @@ odoo.define('web.BasicView', function (require) {
|
||||
var AbstractView = require('web.AbstractView');
|
||||
var BasicController = require('web.BasicController');
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var pyeval = require('web.pyeval');
|
||||
|
||||
/**
|
||||
* Visit a tree of objects, where each children are in an attribute 'children'.
|
||||
* For each children, we call the callback function given in arguments.
|
||||
*
|
||||
* @todo move this somewhere in web.utils
|
||||
*
|
||||
* @param {Object} tree an object describing a tree structure
|
||||
* @param {function} f a callback
|
||||
*/
|
||||
function traverse(tree, f) {
|
||||
if (f(tree)) {
|
||||
_.each(tree.children, function (c) { traverse(c, f); });
|
||||
}
|
||||
}
|
||||
|
||||
var BasicView = AbstractView.extend({
|
||||
config: _.extend({}, AbstractView.prototype.config, {
|
||||
@@ -45,51 +28,21 @@ var BasicView = AbstractView.extend({
|
||||
* augmented with their attrs and some flags if they require a
|
||||
* particular handling.
|
||||
*
|
||||
* @param {Object} arch
|
||||
* @param {Object} fields
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} params
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var result = this._processFieldsView(arch, fields);
|
||||
var fieldAttrs = result.fieldAttrs;
|
||||
var viewFields = result.view_fields;
|
||||
var fieldNames = _.keys(viewFields);
|
||||
fields = _.defaults(viewFields, fields);
|
||||
var self = this;
|
||||
// process the inner fields_view as well to find the fields they use.
|
||||
// register those fields' description directly on the view.
|
||||
// for those inner views, the list of all fields isn't necessary, so
|
||||
// basically the field_names will be the keys of the fields obj.
|
||||
// don't use _ to iterate on fields in case there is a 'length' field,
|
||||
// as _ doesn't behave correctly when there is a length key in the object
|
||||
for (var fieldName in fields) {
|
||||
var field = fields[fieldName];
|
||||
_.each(field.views, function (innerFieldsView) {
|
||||
result = self._processFieldsView(innerFieldsView.arch, innerFieldsView.fields);
|
||||
innerFieldsView.fieldAttrs = result.fieldAttrs;
|
||||
innerFieldsView.fields = result.view_fields;
|
||||
});
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
field.relatedFields = {
|
||||
color: {type: 'int'},
|
||||
display_name: {type: 'char'},
|
||||
id: {type: 'integer'},
|
||||
};
|
||||
field.fieldAttrs = {};
|
||||
}
|
||||
}
|
||||
|
||||
this.controllerParams.confirmOnDelete = true;
|
||||
this.controllerParams.archiveEnabled = 'active' in fields;
|
||||
this.controllerParams.archiveEnabled = 'active' in viewInfo.fields;
|
||||
this.controllerParams.hasButtons =
|
||||
'action_buttons' in params ? params.action_buttons : true;
|
||||
|
||||
this.loadParams.fieldAttrs = fieldAttrs;
|
||||
this.loadParams.fieldNames = fieldNames;
|
||||
this.loadParams.fields = fields;
|
||||
this.loadParams.limit = parseInt(arch.attrs.limit, 10) || params.limit;
|
||||
this.loadParams.fieldsInfo = viewInfo.fieldsInfo;
|
||||
this.loadParams.fieldNames = _.keys(viewInfo.fieldsInfo);
|
||||
this.loadParams.fields = viewInfo.fields;
|
||||
this.loadParams.limit = parseInt(viewInfo.arch.attrs.limit, 10) || params.limit;
|
||||
this.recordID = params.recordID;
|
||||
},
|
||||
|
||||
@@ -116,76 +69,6 @@ var BasicView = AbstractView.extend({
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
/**
|
||||
* Visit all nodes in the arch field and process each fields
|
||||
*
|
||||
* @param {Object} arch
|
||||
* @param {Object} fields
|
||||
* @returns {Object} An object with 2 keys: view_fields and fieldAttrs
|
||||
*/
|
||||
_processFieldsView: function (arch, fields) {
|
||||
var self = this;
|
||||
var viewFields = Object.create(null);
|
||||
var fieldAttrs = Object.create(null);
|
||||
traverse(arch, function (node) {
|
||||
if (typeof node === 'string') {
|
||||
return false;
|
||||
}
|
||||
if (node.tag === 'field') {
|
||||
var attrs = node.attrs ? _.clone(node.attrs) : {};
|
||||
var field = _.clone(fields[node.attrs.name]);
|
||||
self._processField(field, attrs);
|
||||
viewFields[node.attrs.name] = field;
|
||||
fieldAttrs[node.attrs.name] = attrs;
|
||||
return false;
|
||||
}
|
||||
return node.tag !== 'arch';
|
||||
});
|
||||
return {
|
||||
view_fields: viewFields,
|
||||
fieldAttrs: fieldAttrs,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Process a field node, in particular, put a flag on the field to give
|
||||
* special directives to the BasicModel.
|
||||
*
|
||||
* @param {Object} field - the field properties
|
||||
* @param {Object} attrs - the field attributes (from the xml)
|
||||
*/
|
||||
_processField: function (field, attrs) {
|
||||
attrs.Widget = this._getFieldWidgetClass(field, attrs);
|
||||
|
||||
if (!_.isObject(attrs.options)) { // parent arch could have already been processed (TODO this should not happen)
|
||||
attrs.options = attrs.options ? pyeval.py_eval(attrs.options) : {};
|
||||
}
|
||||
|
||||
if (attrs.on_change) {
|
||||
field.onChange = "1";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the AbstractField specialization that should be used for the
|
||||
* given field informations. If there is no mentioned specific widget to
|
||||
* use, determine one according the field type.
|
||||
*
|
||||
* @param {Object} field
|
||||
* @param {Object} attrs
|
||||
* @returns {function|null} AbstractField specialization Class
|
||||
*/
|
||||
_getFieldWidgetClass: function (field, attrs) {
|
||||
var viewName = this.rendererParams.arch.tag;
|
||||
|
||||
var Widget;
|
||||
if (attrs.widget) {
|
||||
Widget = fieldRegistry.getAny([viewName + "." + attrs.widget, attrs.widget]);
|
||||
if (!Widget) {
|
||||
console.warn("Missing widget: ", attrs.widget, " for field", attrs.name, "of type", field.type);
|
||||
}
|
||||
}
|
||||
return Widget || fieldRegistry.getAny([viewName + "." + field.type, field.type, "abstract"]);
|
||||
},
|
||||
});
|
||||
|
||||
return BasicView;
|
||||
|
||||
@@ -151,6 +151,7 @@ return AbstractModel.extend({
|
||||
this.modelName = params.modelName;
|
||||
this.fields = params.fields;
|
||||
this.fieldNames = params.fieldNames;
|
||||
this.fieldsInfo = params.fieldsInfo;
|
||||
this.mapping = params.mapping;
|
||||
this.mode = params.mode; // one of month, week or day
|
||||
this.scales = params.scales; // one of month, week or day
|
||||
|
||||
@@ -26,8 +26,10 @@ var CalendarView = AbstractView.extend({
|
||||
Controller: CalendarController,
|
||||
Renderer: CalendarRenderer,
|
||||
},
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
var arch = viewInfo.arch;
|
||||
var fields = viewInfo.fields;
|
||||
var attrs = arch.attrs;
|
||||
|
||||
if (!attrs.date_start) {
|
||||
@@ -121,6 +123,7 @@ var CalendarView = AbstractView.extend({
|
||||
this.loadParams.fieldNames = _.uniq(fieldNames);
|
||||
this.loadParams.mapping = mapping;
|
||||
this.loadParams.fields = fields;
|
||||
this.loadParams.fieldsInfo = viewInfo.fieldsInfo;
|
||||
this.loadParams.editable = !this.controllerParams.read_only_mode && !fields[mapping.date_start].readonly;
|
||||
this.loadParams.eventLimit = eventLimit;
|
||||
this.loadParams.field_color = attrs.color;
|
||||
|
||||
@@ -66,7 +66,7 @@ var FormController = BasicController.extend({
|
||||
var record = this.model.get(this.handle, {raw: true});
|
||||
return this.model.makeDefaultRecord(this.modelName, {
|
||||
context: this.context,
|
||||
fieldAttrs: record.fieldAttrs,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
res_ids: record.res_ids,
|
||||
@@ -408,6 +408,7 @@ var FormController = BasicController.extend({
|
||||
*/
|
||||
_onAddOne2ManyRecord: function (event) {
|
||||
var field = event.data.field;
|
||||
var attrs = event.data.attrs;
|
||||
new dialogs.FormViewDialog(this, {
|
||||
res_model: field.relation,
|
||||
shouldSaveLocally: true,
|
||||
@@ -416,7 +417,7 @@ var FormController = BasicController.extend({
|
||||
on_save: event.data.on_save,
|
||||
title: _t('Create: ') + field.string,
|
||||
initial_view: 'form',
|
||||
fields_view: field.views ? field.views.form : undefined,
|
||||
fields_view: attrs.views ? attrs.views.form : undefined,
|
||||
form_view_options: {'not_interactible_on_create': true},
|
||||
model: this.model,
|
||||
parentID: this.handle,
|
||||
@@ -520,7 +521,7 @@ var FormController = BasicController.extend({
|
||||
var res_id = record.res_id;
|
||||
var fieldsViewDef;
|
||||
if (event.data.form_view) {
|
||||
fieldsViewDef = $.when(event.data.form_view);
|
||||
fieldsViewDef = $.when({form: event.data.form_view});
|
||||
} else {
|
||||
fieldsViewDef = this.loadViews(model, new Context(event.data.context), [[null, 'form']], {});
|
||||
}
|
||||
|
||||
@@ -268,7 +268,8 @@ return BasicRenderer.extend({
|
||||
required: is_required,
|
||||
idForLabel: this._getIDForLabel(name),
|
||||
};
|
||||
var widget = new this.state.fieldAttrs[name].Widget(this, name, this.state, options);
|
||||
var attrs = this.state.fieldsInfo[name];
|
||||
var widget = new attrs.Widget(this, name, this.state, options);
|
||||
this.widgets.push(widget);
|
||||
var def = widget.__widgetRenderAndInsert(function () {});
|
||||
if (def.state() === 'pending') {
|
||||
|
||||
@@ -5,7 +5,6 @@ var BasicView = require('web.BasicView');
|
||||
var FormRenderer = require('web.FormRenderer');
|
||||
var FormController = require('web.FormController');
|
||||
var Context = require('web.Context');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
|
||||
var _lt = core._lt;
|
||||
@@ -23,7 +22,7 @@ var FormView = BasicView.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var mode = params.mode || (params.currentId ? 'readonly' : 'edit');
|
||||
@@ -51,45 +50,23 @@ var FormView = BasicView.extend({
|
||||
var defs = [];
|
||||
var fields = this.loadParams.fields;
|
||||
|
||||
_.each(self.loadParams.fieldAttrs, function (attrs, fieldName) {
|
||||
_.each(self.loadParams.fieldsInfo, function (attrs, fieldName) {
|
||||
var field = fields[fieldName];
|
||||
if (field.type !== 'one2many' && field.type !== 'many2many') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (attrs.Widget.prototype.useSubview && !(attrs.invisible && JSON.parse(attrs.invisible))) {
|
||||
var mode = attrs.mode;
|
||||
if (!mode) {
|
||||
if (field.views.tree && field.views.kanban) {
|
||||
mode = 'tree';
|
||||
} else if (!field.views.tree && field.views.kanban) {
|
||||
mode = 'kanban';
|
||||
} else {
|
||||
mode = 'tree,kanban';
|
||||
}
|
||||
}
|
||||
if (mode.indexOf(',') !== -1) {
|
||||
mode = config.device.size_class !== config.device.SIZES.XS ? 'tree' : 'kanban';
|
||||
}
|
||||
if (field.views[mode]) {
|
||||
field.relatedFields = field.views[mode].fields;
|
||||
field.fieldAttrs = field.views[mode].fieldAttrs;
|
||||
field.limit = mode === "tree" ? 80 : 40;
|
||||
return;
|
||||
}
|
||||
attrs.limit = attrs.mode === "tree" ? 80 : 40;
|
||||
|
||||
if (attrs.Widget.prototype.useSubview && !(attrs.invisible && JSON.parse(attrs.invisible)) && !attrs.views[attrs.mode]) {
|
||||
defs.push(parent.loadViews(
|
||||
field.relation,
|
||||
new Context(self.loadParams.context),
|
||||
[[null, mode === 'tree' ? 'list' : mode]],
|
||||
[[null, attrs.mode === 'tree' ? 'list' : attrs.mode]],
|
||||
{})
|
||||
.then(function (views) {
|
||||
for (var viewName in views) {
|
||||
var view = views[viewName];
|
||||
var fieldView = self._processFieldsView(view.arch, view.fields);
|
||||
field.views[viewName === 'list' ? 'tree' : viewName] = view;
|
||||
field.relatedFields = fieldView.view_fields;
|
||||
field.fieldAttrs = fieldView.fieldAttrs;
|
||||
view.fieldAttrs = fieldView.fieldAttrs;
|
||||
attrs.views[viewName] = views[viewName];
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -27,13 +27,13 @@ var GraphView = AbstractView.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function (arch, fields) {
|
||||
init: function (viewInfo) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var initialMeasure = '__count__';
|
||||
var initialGroupBys = [];
|
||||
fields.__count__ = {string: _t("Count"), type: "integer"};
|
||||
arch.children.forEach(function (field) {
|
||||
viewInfo.fields = _.defaults({__count__: {string: _t("Count"), type: "integer"}}, viewInfo.fields);
|
||||
viewInfo.arch.children.forEach(function (field) {
|
||||
var name = field.attrs.name;
|
||||
if (field.attrs.interval) {
|
||||
name += ':' + field.attrs.interval;
|
||||
@@ -46,7 +46,7 @@ var GraphView = AbstractView.extend({
|
||||
});
|
||||
|
||||
var measures = {__count__: {string: _t("Count"), type: "integer"}};
|
||||
_.each(fields, function (field, name) {
|
||||
_.each(viewInfo.fields, function (field, name) {
|
||||
if (name !== 'id' && field.store === true) {
|
||||
if (field.type === 'integer' || field.type === 'float' || field.type === 'monetary') {
|
||||
measures[name] = field;
|
||||
@@ -55,12 +55,12 @@ var GraphView = AbstractView.extend({
|
||||
});
|
||||
|
||||
this.controllerParams.measures = measures;
|
||||
this.rendererParams.stacked = arch.attrs.stacked !== "False";
|
||||
this.rendererParams.stacked = viewInfo.arch.attrs.stacked !== "False";
|
||||
|
||||
this.loadParams.initialMode = arch.attrs.type || 'bar';
|
||||
this.loadParams.initialMode = viewInfo.arch.attrs.type || 'bar';
|
||||
this.loadParams.initialMeasure = initialMeasure;
|
||||
this.loadParams.initialGroupBys = initialGroupBys;
|
||||
this.loadParams.fields = fields;
|
||||
this.loadParams.fields = viewInfo.fields;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ var KanbanModel = BasicModel.extend({
|
||||
modelName: group.model,
|
||||
fields: group.fields,
|
||||
fieldNames: group.fieldNames,
|
||||
fieldAttrs: group.fieldAttrs,
|
||||
fieldsInfo: group.fieldsInfo,
|
||||
});
|
||||
group.data.unshift(new_record);
|
||||
group.count++;
|
||||
|
||||
@@ -27,7 +27,7 @@ var KanbanRecord = Widget.extend({
|
||||
this._super(parent);
|
||||
|
||||
this.fields = state.fields;
|
||||
this.fieldAttrs = state.fieldAttrs;
|
||||
this.fieldsInfo = state.fieldsInfo;
|
||||
this.modelName = state.model;
|
||||
|
||||
this.options = options;
|
||||
@@ -83,7 +83,7 @@ var KanbanRecord = Widget.extend({
|
||||
var widget = self.subWidgets[field_name];
|
||||
if (!widget) {
|
||||
// the widget doesn't exist yet, so instanciate it
|
||||
var Widget = self.fieldAttrs[field_name].Widget;
|
||||
var Widget = self.fieldsInfo[field_name].Widget;
|
||||
if (Widget) {
|
||||
// some field's attrs might be record dependent (they start with
|
||||
// 't-att-') and should thus be evaluated, which is done by qweb
|
||||
@@ -93,14 +93,14 @@ var KanbanRecord = Widget.extend({
|
||||
// that dict being shared between records, we don't modify it
|
||||
// in place
|
||||
var attrs = Object.create(null);
|
||||
_.each(self.fieldAttrs[field_name], function (value, key) {
|
||||
_.each(self.fieldsInfo[field_name], function (value, key) {
|
||||
if (_.str.startsWith(key, 't-att-')) {
|
||||
key = key.slice(6);
|
||||
value = $field.attr(key);
|
||||
}
|
||||
attrs[key] = value;
|
||||
});
|
||||
self.fieldAttrs[field_name] = attrs;
|
||||
self.fieldsInfo[field_name] = attrs;
|
||||
|
||||
widget = new Widget(self, field_name, self.state, self.options);
|
||||
self.subWidgets[field_name] = widget;
|
||||
@@ -276,14 +276,14 @@ var KanbanRecord = Widget.extend({
|
||||
*/
|
||||
_setFieldDisplay: function (widget, fieldName) {
|
||||
// attribute display
|
||||
if (this.fieldAttrs[fieldName].display === 'right') {
|
||||
if (this.fieldsInfo[fieldName].display === 'right') {
|
||||
widget.$el.addClass('pull-right');
|
||||
} else if (this.fieldAttrs[fieldName].display === 'full') {
|
||||
} else if (this.fieldsInfo[fieldName].display === 'full') {
|
||||
widget.$el.addClass('o_text_block');
|
||||
}
|
||||
|
||||
// attribute bold
|
||||
if (this.fieldAttrs[fieldName].bold) {
|
||||
if (this.fieldsInfo[fieldName].bold) {
|
||||
widget.$el.addClass('o_text_bold');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,9 +20,10 @@ var KanbanView = BasicView.extend({
|
||||
Controller: KanbanController,
|
||||
Renderer: KanbanRenderer,
|
||||
},
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var arch = viewInfo.arch;
|
||||
var activeActions = this.controllerParams.activeActions;
|
||||
activeActions = _.extend(activeActions, {
|
||||
group_edit: arch.attrs.group_edit ? JSON.parse(arch.attrs.group_edit) : true,
|
||||
|
||||
@@ -175,7 +175,7 @@ ListRenderer.include({
|
||||
if (field.readonly) {
|
||||
return true;
|
||||
}
|
||||
var Widget = this.state.fieldAttrs[node.attrs.name].Widget;
|
||||
var Widget = this.state.fieldsInfo[node.attrs.name].Widget;
|
||||
if (Widget && Widget.prototype.readonly) {
|
||||
return true;
|
||||
}
|
||||
@@ -268,7 +268,7 @@ ListRenderer.include({
|
||||
return;
|
||||
}
|
||||
var $td = this._findTd($row, colIndex);
|
||||
var widget = new this.state.fieldAttrs[name].Widget(this, name, record, {
|
||||
var widget = new this.state.fieldsInfo[name].Widget(this, name, record, {
|
||||
mode: 'edit'
|
||||
});
|
||||
if (widget.replace_element) {
|
||||
|
||||
@@ -285,7 +285,7 @@ var ListRenderer = BasicRenderer.extend({
|
||||
}
|
||||
var name = node.attrs.name;
|
||||
if (node.attrs.widget) {
|
||||
var widget = new this.state.fieldAttrs[name].Widget(this, name, record, {
|
||||
var widget = new this.state.fieldsInfo[name].Widget(this, name, record, {
|
||||
mode: 'readonly',
|
||||
});
|
||||
widget.appendTo($td);
|
||||
@@ -505,7 +505,7 @@ var ListRenderer = BasicRenderer.extend({
|
||||
}
|
||||
var description;
|
||||
if (node.attrs.widget) {
|
||||
description = this.state.fieldAttrs[name].Widget.prototype.description;
|
||||
description = this.state.fieldsInfo[name].Widget.prototype.description;
|
||||
}
|
||||
if (description === undefined) {
|
||||
description = node.attrs.string || field.string;
|
||||
|
||||
@@ -27,15 +27,15 @@ var ListView = BasicView.extend({
|
||||
/**
|
||||
* @override
|
||||
*
|
||||
* @param {Object} arch
|
||||
* @param {Object} fields
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} params
|
||||
* @param {boolean} params.sidebar
|
||||
* @param {boolean} [params.hasSelectors=true]
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var arch = viewInfo.arch;
|
||||
this.controllerParams.editable = arch.attrs.editable;
|
||||
this.controllerParams.hasSidebar = params.sidebar;
|
||||
this.controllerParams.noContentHelp = params.action && params.action.help;
|
||||
|
||||
@@ -31,9 +31,13 @@ var PivotView = AbstractView.extend({
|
||||
* @override
|
||||
* @param {Object} params
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
var arch = viewInfo.arch;
|
||||
var fields = _.extend({
|
||||
__count: {string: _t("Count"), type: "integer"}
|
||||
}, viewInfo.fields);
|
||||
var activeMeasures = [];
|
||||
var colGroupBys = [];
|
||||
var rowGroupBys = [];
|
||||
@@ -82,7 +86,6 @@ var PivotView = AbstractView.extend({
|
||||
if ((!activeMeasures.length) || arch.attrs.display_quantity) {
|
||||
activeMeasures.push('__count');
|
||||
}
|
||||
fields.__count = {string: _t("Count"), type: "integer"};
|
||||
|
||||
this.loadParams.measures = activeMeasures;
|
||||
this.loadParams.colGroupBys = colGroupBys;
|
||||
|
||||
@@ -137,7 +137,7 @@ var FormViewDialog = ViewDialog.extend({
|
||||
this._super(parent, options);
|
||||
},
|
||||
/**
|
||||
* If we changed some fieldAttrs, we need to restore them now.
|
||||
* If we changed some fieldsInfo, we need to restore them now.
|
||||
*/
|
||||
destroy: function () {
|
||||
if (this.recordID && this.previousFieldProps) {
|
||||
@@ -166,15 +166,11 @@ var FormViewDialog = ViewDialog.extend({
|
||||
fields_view_def = this.loadFieldView(this.dataset, this.options.view_id, 'form');
|
||||
}
|
||||
|
||||
fields_view_def.then(function (fields_view) {
|
||||
fields_view_def.then(function (viewInfo) {
|
||||
if (self.recordID) {
|
||||
self.previousFieldProps = self.model.setFieldProps(self.recordID, {
|
||||
fieldNames: Object.keys(fields_view.fields),
|
||||
fields: fields_view.fields,
|
||||
fieldAttrs: fields_view.fieldAttrs
|
||||
});
|
||||
self.previousFieldProps = self.model.setFieldProps(self.recordID, viewInfo);
|
||||
}
|
||||
var formview = new FormView(fields_view.arch, fields_view.fields, {
|
||||
var formview = new FormView(viewInfo, {
|
||||
modelName: self.res_model,
|
||||
context: self.context,
|
||||
ids: self.res_id ? [self.res_id] : [],
|
||||
@@ -337,7 +333,7 @@ var SelectCreateDialog = ViewDialog.extend({
|
||||
|
||||
return $.when(searchDef).then(function (searchResult) {
|
||||
// Set the list view
|
||||
var listView = new ListView(fields_views.list.arch, fields_views.list.fields, _.extend({
|
||||
var listView = new ListView(fields_views.list, _.extend({
|
||||
context: searchResult.context,
|
||||
domain: searchResult.domain,
|
||||
groupBy: searchResult.groupBy,
|
||||
|
||||
@@ -383,7 +383,7 @@ var DomainLeaf = DomainNode.extend({
|
||||
this.displayValue = this.value;
|
||||
try {
|
||||
var f = this.fieldSelector.selectedField;
|
||||
if (!f.relation) { // TODO in this case, the value should be m2o input, etc...
|
||||
if (f && !f.relation) { // TODO in this case, the value should be m2o input, etc...
|
||||
this.displayValue = field_utils.format[f.type](this.value, f);
|
||||
}
|
||||
} catch (err) {/**/}
|
||||
|
||||
@@ -1395,7 +1395,7 @@ QUnit.module('relational_fields', {
|
||||
rpcCount++;
|
||||
if (args.method === 'write') {
|
||||
assert.deepEqual(args.args[1].p, [[0, false, {
|
||||
int_field: 123, product_id: 41
|
||||
display_name: false, int_field: 123, product_id: 41
|
||||
}]]);
|
||||
}
|
||||
return this._super(route, args);
|
||||
@@ -2104,6 +2104,71 @@ QUnit.module('relational_fields', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many (who contains a one2many) with tree view and without form view', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
// avoid error in _postprocess
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<group>' +
|
||||
'<field name="turtles">' +
|
||||
'<tree>' +
|
||||
'<field name="partner_ids"/>' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</group>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
archs: {
|
||||
"turtle,false,form": '<form string="Turtles"><field name="turtle_foo"/></form>',
|
||||
},
|
||||
});
|
||||
|
||||
form.$('.o_data_row:first').click();
|
||||
|
||||
assert.strictEqual($('.modal .o_form_field[name="turtle_foo"]').text(), 'blip',
|
||||
'should open the modal and display the form field');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('one2many (who contains display_name) with tree view and without form view', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
// avoid error in _fetchX2Manys
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form string="Partners">' +
|
||||
'<group>' +
|
||||
'<field name="turtles">' +
|
||||
'<tree>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</tree>' +
|
||||
'</field>' +
|
||||
'</group>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
archs: {
|
||||
"turtle,false,form": '<form string="Turtles"><field name="turtle_foo"/></form>',
|
||||
},
|
||||
});
|
||||
|
||||
form.$('.o_data_row:first').click();
|
||||
|
||||
assert.strictEqual($('.modal .o_form_field[name="turtle_foo"]').text(), 'blip',
|
||||
'should open the modal and display the form field');
|
||||
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.module('FieldMany2Many');
|
||||
|
||||
QUnit.test('many2many kanban: edition', function (assert) {
|
||||
|
||||
@@ -15,6 +15,8 @@ var core = require('web.core');
|
||||
var session = require('web.session');
|
||||
var MockServer = require('web.MockServer');
|
||||
var Widget = require('web.Widget');
|
||||
var DataManager = require('web.DataManager');
|
||||
|
||||
|
||||
/**
|
||||
* intercepts an event bubbling up the widget hierarchy. The event intercepted
|
||||
@@ -131,7 +133,13 @@ function createAsyncView(params) {
|
||||
|
||||
_.extend(viewOptions, params.viewOptions);
|
||||
|
||||
var view = new params.View(fields_view.arch, fields_view.fields, viewOptions);
|
||||
var viewInfo = DataManager.prototype._processFieldsView({
|
||||
type: fields_view.arch.tag,
|
||||
arch: fields_view.arch,
|
||||
fields: fields_view.fields,
|
||||
});
|
||||
|
||||
var view = new params.View(viewInfo, viewOptions);
|
||||
|
||||
$('#qunit-fixture').on('DOMNodeInserted.removeSRC', function () {
|
||||
removeSrcAttribute($(this), widget);
|
||||
@@ -298,7 +306,16 @@ function addMockEnvironment(widget, params) {
|
||||
arch = arch && (_.isObject(arch) ? arch : mockServer.fieldsViewGet(arch, model).arch);
|
||||
res[view_type] = { arch: arch, fields: params.data[model].fields };
|
||||
});
|
||||
event.data.on_success(res);
|
||||
|
||||
var views = _.mapObject(res, function (view, viewType) {
|
||||
return DataManager.prototype._processFieldsView({
|
||||
type: viewType,
|
||||
arch: view.arch,
|
||||
fields: view.fields,
|
||||
});
|
||||
});
|
||||
|
||||
event.data.on_success(views);
|
||||
});
|
||||
|
||||
intercept(widget, "get_session", function (event) {
|
||||
@@ -323,14 +340,14 @@ function addMockEnvironment(widget, params) {
|
||||
* @param {Class} params.Model the model class to use
|
||||
* @param {string[]} [params.fieldNames] the fields given to the model
|
||||
* @param {Object[]} [params.fields] map of field names to field description
|
||||
* @param {Object[]} [params.fieldAttrs] map of field names to field attributes
|
||||
* @param {Object[]} [params.fieldsInfo] map of field names to field attributes
|
||||
* @returns {Model}
|
||||
*/
|
||||
function createModel(params) {
|
||||
var widget = new Widget();
|
||||
|
||||
var model = new params.Model(widget, {
|
||||
fieldAttrs: params.fieldAttrs || {},
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
fieldNames: params.fieldNames,
|
||||
fields: params.fields,
|
||||
});
|
||||
|
||||
@@ -310,6 +310,10 @@ QUnit.module('Views', {
|
||||
|
||||
this.params.res_id = 1;
|
||||
this.params.fieldNames = ['category'];
|
||||
this.params.fieldsInfo = {category: {
|
||||
relatedFields: {display_name: {type: "char"}},
|
||||
fieldsInfo: {display_name: {}},
|
||||
}};
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -319,9 +323,9 @@ QUnit.module('Views', {
|
||||
model.load(this.params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.category.data[0].res_id, 12,
|
||||
"should have loaded many2omany res_ids");
|
||||
"should have loaded many2many res_ids");
|
||||
assert.strictEqual(record.data.category.data[0].data.display_name, "gold",
|
||||
"should have loaded many2omany display_name");
|
||||
"should have loaded many2many display_name");
|
||||
record = model.get(resultID, {raw: true});
|
||||
assert.deepEqual(record.data.category, [12],
|
||||
"with option raw, category should only return ids");
|
||||
@@ -333,6 +337,10 @@ QUnit.module('Views', {
|
||||
assert.expect(2);
|
||||
|
||||
this.params.fieldNames = ['category'];
|
||||
this.params.fieldsInfo = {category: {
|
||||
relatedFields: {date: {type: "date"}},
|
||||
fieldsInfo: {date: {}},
|
||||
}};
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -909,7 +917,7 @@ QUnit.module('Views', {
|
||||
data: this.data,
|
||||
});
|
||||
|
||||
this.params.fieldAttrs = {
|
||||
this.params.fieldsInfo = {
|
||||
product_id: {
|
||||
context: "{'hello2': 'world', 'test2': foo}",
|
||||
domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]",
|
||||
|
||||
@@ -764,7 +764,7 @@ QUnit.module('Views', {
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === "onchange") {
|
||||
assert.deepEqual(args.args[3],
|
||||
{"foo": "1", "p": "", "p.bar": "", "p.product_id": "", "timmy": "", "timmy.name": ""},
|
||||
{"foo": "1", "p": "", "p.bar": "", "p.display_name": "", "p.product_id": "", "timmy": "", "timmy.name": ""},
|
||||
"should send only the fields used in the views");
|
||||
}
|
||||
return this._super(route, args);
|
||||
@@ -1750,7 +1750,7 @@ QUnit.module('Views', {
|
||||
QUnit.test('attrs are properly transmitted to new records', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// this test checks that the fieldAttrs have been transmitted to the
|
||||
// this test checks that the fieldsInfo have been transmitted to the
|
||||
// load function when creating a new record
|
||||
|
||||
var terminology = {
|
||||
|
||||
@@ -371,7 +371,7 @@ QUnit.module('Views', {
|
||||
arch: '<kanban class="o_kanban_test">' +
|
||||
'<templates><t t-name="kanban-box">' +
|
||||
'<div>' +
|
||||
'<div class="o_kanban_tags_section"><field name="category_ids"/></div>' +
|
||||
'<div class="o_kanban_tags_section"><field name="category_ids" widget="many2many_tags"/></div>' +
|
||||
'<field name="foo"/>' +
|
||||
'</div>' +
|
||||
'</t></templates>' +
|
||||
@@ -379,11 +379,11 @@ QUnit.module('Views', {
|
||||
mockRPC: function () {
|
||||
count++;
|
||||
return this._super.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var $first_record = kanban.$('.o_kanban_record:first()');
|
||||
assert.strictEqual($first_record.find('.o_form_field_many2manytags span').length, 2,
|
||||
assert.strictEqual($first_record.find('.o_form_field_many2manytags .badge').length, 2,
|
||||
'first record should contain 2 tags');
|
||||
assert.ok($first_record.find('span:first()').hasClass('o_tag_color_2'),
|
||||
'first tag should have color 2');
|
||||
|
||||
@@ -621,10 +621,10 @@ QUnit.module('Views', {
|
||||
mockRPC: function (route, args) {
|
||||
rpcCount++;
|
||||
return this._super(route, args);
|
||||
}
|
||||
},
|
||||
});
|
||||
assert.strictEqual(rpcCount, 2, "should have done 2 rpcs: 1 searchread and 1 read for m2m");
|
||||
assert.ok(list.$('td:contains(Value 1, Value 2, Value 3)').length,
|
||||
assert.ok(list.$('td:contains(3 records)').length,
|
||||
"should have a td with correct formatted value");
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
@@ -28,13 +28,13 @@ var DiagramView = BasicView.extend({
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
* @param {Object} arch
|
||||
* @param {Object} fields
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} params
|
||||
*/
|
||||
init: function (arch, fields, params) {
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
var self = this;
|
||||
var arch = viewInfo.arch;
|
||||
// Compute additional data for diagram model
|
||||
function toTitleCase(str) {
|
||||
return str.replace(/\w\S*/g, function (txt) {
|
||||
|
||||
Reference in New Issue
Block a user