[FIX] web,mail: one2many records: multiple views
A record of the basic model may need to be displayed in different views (e.g. a o2m record is displayed in the, e.g., Kanban view inside the main form view, and if clicked, it is also displayed in the form view opened in a dialog). However, the fields present in those views may differ. The previous implementation of fieldsInfo in the record didn't handle that, as it was a dict whose keys were field names. With this commit, the fieldsInfo is dict whose keys are view types, and the values are dicts whose keys are field names (the fields of the corresponding views, with they attrs in the view). This allows to create multiple o2m records in a row in a form view.
This commit is contained in:
@@ -6,11 +6,12 @@ var BasicView = require('web.BasicView');
|
||||
var mailWidgets = ['mail_followers', 'mail_thread', 'mail_activity', 'kanban_activity'];
|
||||
|
||||
BasicView.include({
|
||||
init: function (viewInfo, params) {
|
||||
init: function (viewInfo) {
|
||||
this._super.apply(this, arguments);
|
||||
this.mailFields = {};
|
||||
for (var fieldName in viewInfo.fieldsInfo) {
|
||||
var fieldInfo = viewInfo.fieldsInfo[fieldName];
|
||||
var fieldsInfo = viewInfo.fieldsInfo[this.viewType];
|
||||
for (var fieldName in fieldsInfo) {
|
||||
var fieldInfo = fieldsInfo[fieldName];
|
||||
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
||||
this.mailFields[fieldInfo.widget] = fieldName;
|
||||
fieldInfo.__no_fetch = true;
|
||||
|
||||
@@ -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.fieldsInfo[mailFields.mail_thread].options;
|
||||
var nodeOptions = this.record.fieldsInfo.form[mailFields.mail_thread].options;
|
||||
this.hasLogButton = nodeOptions.display_log_button;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,9 +92,14 @@ var AbstractField = Widget.extend({
|
||||
// such as the type, the comodel (relation), ...
|
||||
this.field = record.fields[name];
|
||||
|
||||
// the 'viewType' is the type of the view in which the field widget is
|
||||
// instantiated. For standalone widgets, a 'default' viewType is set.
|
||||
this.viewType = options.viewType || 'default';
|
||||
|
||||
// the 'attrs' property contains the attributes of the xml 'field' tag,
|
||||
// the inner views...
|
||||
this.attrs = record.fieldsInfo && record.fieldsInfo[name] || {};
|
||||
var fieldsInfo = record.fieldsInfo[this.viewType];
|
||||
this.attrs = fieldsInfo && fieldsInfo[name] || {};
|
||||
|
||||
// modifiers
|
||||
this.modifiers = JSON.parse(this.attrs.modifiers || "{}");
|
||||
|
||||
@@ -99,6 +99,9 @@ var FieldMany2One = AbstractField.extend({
|
||||
quick_create: true,
|
||||
});
|
||||
this.m2o_value = field_utils.format.many2one(this.value);
|
||||
// 'recordParams' is a dict of params used when calling functions
|
||||
// 'getDomain' and 'getContext' on this.record
|
||||
this.recordParams = {fieldName: this.name, viewType: this.viewType};
|
||||
},
|
||||
start: function () {
|
||||
// what is 'this.floating' for? add a comment!
|
||||
@@ -201,7 +204,7 @@ var FieldMany2One = AbstractField.extend({
|
||||
model: this.field.relation,
|
||||
method: 'name_create',
|
||||
args: [name],
|
||||
context: this.record.getContext({fieldName: this.name})
|
||||
context: this.record.getContext(this.recordParams),
|
||||
})
|
||||
.then(function (result) {
|
||||
if (self.mode === "edit") {
|
||||
@@ -261,8 +264,8 @@ var FieldMany2One = AbstractField.extend({
|
||||
var def = $.Deferred();
|
||||
this.orderer.add(def);
|
||||
|
||||
var context = this.record.getContext({fieldName: this.name});
|
||||
var domain = this.record.getDomain({fieldName: this.name});
|
||||
var context = this.record.getContext(this.recordParams);
|
||||
var domain = this.record.getDomain(this.recordParams);
|
||||
|
||||
var blacklisted_ids = this._getSearchBlacklist();
|
||||
if (blacklisted_ids.length > 0) {
|
||||
@@ -356,7 +359,7 @@ var FieldMany2One = AbstractField.extend({
|
||||
new dialogs.SelectCreateDialog(this, _.extend({}, this.nodeOptions, {
|
||||
res_model: this.field.relation,
|
||||
domain: this.record.getDomain({fieldName: this.name}),
|
||||
context: _.extend({}, this.record.getContext({fieldName: this.name}), context || {}),
|
||||
context: _.extend({}, this.record.getContext(this.recordParams), context || {}),
|
||||
title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string,
|
||||
initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined,
|
||||
initial_view: view,
|
||||
@@ -394,7 +397,7 @@ var FieldMany2One = AbstractField.extend({
|
||||
model: this.field.relation,
|
||||
method: 'get_formview_action',
|
||||
args: [[this.value.res_id]],
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
context: this.record.getContext(this.recordParams),
|
||||
})
|
||||
.then(function (action) {
|
||||
self.trigger_up('do_action', {action: action});
|
||||
@@ -410,7 +413,7 @@ var FieldMany2One = AbstractField.extend({
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var context = this.record.getContext({fieldName: this.name});
|
||||
var context = this.record.getContext(this.recordParams);
|
||||
this._rpc({
|
||||
model: this.field.relation,
|
||||
method: 'get_formview_id',
|
||||
@@ -531,6 +534,7 @@ var FieldX2Many = AbstractField.extend({
|
||||
this.isReadonly = this.mode === 'readonly';
|
||||
this.view = this.attrs.views.list || this.attrs.views.kanban;
|
||||
this.activeActions = {};
|
||||
this.recordParams = {fieldName: this.name, viewType: this.viewType};
|
||||
var arch = this.view && this.view.arch;
|
||||
if (arch) {
|
||||
this.activeActions.create = arch.attrs.create ?
|
||||
@@ -780,8 +784,8 @@ var FieldOne2Many = FieldX2Many.extend({
|
||||
*/
|
||||
_openFormDialog: function (params) {
|
||||
this.trigger_up('open_one2many_record', _.extend(params, {
|
||||
domain: this.record.getDomain({fieldName: this.name}),
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
domain: this.record.getDomain(this.recordParams),
|
||||
context: this.record.getContext(this.recordParams),
|
||||
field: this.field,
|
||||
fields_view: this.attrs.views && this.attrs.views.form,
|
||||
viewInfo: this.view,
|
||||
@@ -873,7 +877,7 @@ var FieldMany2Many = FieldX2Many.extend({
|
||||
new dialogs.SelectCreateDialog(this, {
|
||||
res_model: this.field.relation,
|
||||
domain: domain.concat(["!", ["id", "in", this.value.res_ids]]),
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
context: this.record.getContext(this.recordParams),
|
||||
title: _t("Add: ") + this.string,
|
||||
no_create: this.nodeOptions.no_create || !this.activeActions.create,
|
||||
fields_view: this.attrs.views.form,
|
||||
@@ -900,8 +904,8 @@ var FieldMany2Many = FieldX2Many.extend({
|
||||
*/
|
||||
_onOpenRecord: function (event) {
|
||||
_.extend(event.data, {
|
||||
context: this.record.getContext({fieldName: this.name}),
|
||||
domain: this.record.getDomain({fieldName: this.name}),
|
||||
context: this.record.getContext(this.recordParams),
|
||||
domain: this.record.getDomain(this.recordParams),
|
||||
fields_view: this.attrs.views && this.attrs.views.form,
|
||||
on_saved: this.trigger_up.bind(this, 'reload', {db_id: event.data.id}),
|
||||
readonly: this.mode === 'readonly',
|
||||
@@ -976,6 +980,7 @@ var FieldMany2ManyTags = AbstractField.extend({
|
||||
this.many2one = new FieldMany2One(this, this.name, this.record, {
|
||||
idForLabel: this.idForLabel,
|
||||
mode: 'edit',
|
||||
viewType: this.viewType,
|
||||
});
|
||||
// to prevent the M2O to take the value of the M2M
|
||||
this.many2one.value = false;
|
||||
|
||||
@@ -280,6 +280,7 @@ return core.Class.extend({
|
||||
// as _ doesn't behave correctly when there is a length key in the object
|
||||
attrs.views = {};
|
||||
_.each(field.views, function (innerFieldsView, viewType) {
|
||||
viewType = viewType === 'tree' ? 'list' : viewType;
|
||||
innerFieldsView.type = viewType;
|
||||
attrs.views[viewType] = self._processFieldsView(_.extend({}, innerFieldsView));
|
||||
});
|
||||
@@ -317,10 +318,12 @@ return core.Class.extend({
|
||||
display_name: {type: 'char'},
|
||||
//id: {type: 'integer'},
|
||||
};
|
||||
attrs.fieldsInfo = {display_name: {}, id: {}};
|
||||
attrs.fieldsInfo = {};
|
||||
attrs.fieldsInfo.default = {display_name: {}, id: {}};
|
||||
attrs.viewType = 'default';
|
||||
if (attrs.color || 'color') {
|
||||
attrs.relatedFields[attrs.color || 'color'] = {type: 'int'};
|
||||
attrs.fieldsInfo.color = {};
|
||||
attrs.relatedFields[attrs.color || 'color'] = {type: 'integer'};
|
||||
attrs.fieldsInfo.default.color = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,12 +362,14 @@ return core.Class.extend({
|
||||
* @returns {Object} viewInfo
|
||||
*/
|
||||
_processFieldsView: function (viewInfo) {
|
||||
viewInfo.fieldsInfo = this._processFields(viewInfo.type, viewInfo.arch, viewInfo.fields);
|
||||
// by default display fetch display_name and id
|
||||
var viewFields = this._processFields(viewInfo.type, viewInfo.arch, viewInfo.fields);
|
||||
// by default fetch display_name and id
|
||||
if (!viewInfo.fields.display_name) {
|
||||
viewInfo.fields.display_name = {type: 'char'};
|
||||
viewInfo.fieldsInfo.display_name = {};
|
||||
viewFields.display_name = {};
|
||||
}
|
||||
viewInfo.fieldsInfo = {};
|
||||
viewInfo.fieldsInfo[viewInfo.type] = viewFields;
|
||||
utils.deepFreeze(viewInfo.fields);
|
||||
return viewInfo;
|
||||
},
|
||||
|
||||
@@ -23,7 +23,6 @@ odoo.define('web.BasicModel', function (require) {
|
||||
* count: {integer},
|
||||
* data: {Object|Object[]},
|
||||
* domain: {*[]},
|
||||
* fieldNames: {string[]},
|
||||
* fields: {Object},
|
||||
* fieldsInfo: {Object},
|
||||
* getContext: {function},
|
||||
@@ -145,7 +144,7 @@ var BasicModel = AbstractModel.extend({
|
||||
|
||||
/**
|
||||
* Add a default record to a list object. This method actually make a new
|
||||
* record with the makeDefaultRecord method, then add it to the list object.
|
||||
* record with the _makeDefaultRecord method, then add it to the list object.
|
||||
*
|
||||
* @param {string} listID a valid handle for a list object
|
||||
* @param {Object} [options]
|
||||
@@ -160,13 +159,13 @@ var BasicModel = AbstractModel.extend({
|
||||
var position = (options && options.position) || 'top';
|
||||
var params = {
|
||||
context: context,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
fieldNames: list.fieldNames,
|
||||
fields: list.fields,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
parentID: list.id,
|
||||
relationField: list.relationField,
|
||||
viewType: list.viewType,
|
||||
};
|
||||
return this.makeDefaultRecord(list.model, params).then(function (id) {
|
||||
return this._makeDefaultRecord(list.model, params).then(function (id) {
|
||||
list.count++;
|
||||
if (position === 'top') {
|
||||
list.data.unshift(id);
|
||||
@@ -243,11 +242,11 @@ var BasicModel = AbstractModel.extend({
|
||||
record.res_ids.splice(index + 1, 0, res_id);
|
||||
return self.load({
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
modelName: record.model,
|
||||
res_id: res_id,
|
||||
res_ids: record.res_ids.slice(0),
|
||||
viewType: record.viewType,
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -400,8 +399,6 @@ var BasicModel = AbstractModel.extend({
|
||||
*
|
||||
* @param {any} params
|
||||
* @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
|
||||
* @param {string} [params.type] 'record' or 'list'
|
||||
* @param {string} [params.recordID] an ID for an existing resource.
|
||||
@@ -409,197 +406,26 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
load: function (params) {
|
||||
params.type = params.type || (params.res_id !== undefined ? 'record' : 'list');
|
||||
// FIXME: the following seems only to be used by the basic_model_tests
|
||||
// so it should probably be removed and the tests should be adapted
|
||||
params.viewType = params.viewType || 'default';
|
||||
if (!params.fieldsInfo) {
|
||||
var fieldsInfo = {};
|
||||
for (var fieldName in params.fieldNames) {
|
||||
fieldsInfo[params.fieldNames[fieldName]] = {};
|
||||
}
|
||||
params.fieldsInfo = {};
|
||||
params.fieldsInfo[params.viewType] = fieldsInfo;
|
||||
}
|
||||
|
||||
if (params.type === 'record' && params.res_id === undefined) {
|
||||
return this.makeDefaultRecord(params.modelName, params);
|
||||
return this._makeDefaultRecord(params.modelName, params);
|
||||
}
|
||||
var dataPoint = this._makeDataPoint(params);
|
||||
return this._load(dataPoint).then(function () {
|
||||
return dataPoint.id;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* When one needs to create a record from scratch, a not so simple process
|
||||
* needs to be done:
|
||||
* - call the /default_get route to get default values
|
||||
* - fetch all relational data
|
||||
* - apply all onchanges if necessary
|
||||
* - fetch all relational data
|
||||
*
|
||||
* This method tries to optimize the process as much as possible. Also,
|
||||
* it is quite horrible and should be refactored at some point.
|
||||
*
|
||||
* @param {any} params
|
||||
* @param {string} modelName model name
|
||||
* @param {Object} params.context the context for the new record
|
||||
* @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
|
||||
* @param {Object} params.context the context for the new record
|
||||
* @returns {Deferred -> string} resolves to the id for the created resource
|
||||
*/
|
||||
makeDefaultRecord: function (modelName, params) {
|
||||
var self = this;
|
||||
var fields_key = _.without(params.fieldNames, '__last_update');
|
||||
|
||||
return this._rpc({
|
||||
model: modelName,
|
||||
method: 'default_get',
|
||||
args: [fields_key],
|
||||
context: params.context,
|
||||
})
|
||||
.then(function (result) {
|
||||
// fill default values for missing fields
|
||||
for (var i = 0; i < params.fieldNames.length; i++) {
|
||||
var fieldName = params.fieldNames[i];
|
||||
if (!(fieldName in result)) {
|
||||
var field = params.fields[fieldName];
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
result[fieldName] = [];
|
||||
} else {
|
||||
result[fieldName] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var data = {};
|
||||
var record = self._makeDataPoint({
|
||||
modelName: modelName,
|
||||
data: data,
|
||||
fields: params.fields,
|
||||
fieldNames: params.fieldNames,
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
context: params.context,
|
||||
parentID: params.parentID,
|
||||
relationField: params.relationField,
|
||||
res_ids: params.res_ids,
|
||||
});
|
||||
|
||||
var defs = []; // FIXME: remove defs?
|
||||
_.each(params.fieldNames, function (name) {
|
||||
var field = params.fields[name];
|
||||
data[name] = null;
|
||||
record._changes = record._changes || {};
|
||||
if (field.type === 'many2one' && result[name]) {
|
||||
var rec = self._makeDataPoint({
|
||||
context: record.context,
|
||||
data: {id: result[name]},
|
||||
modelName: field.relation,
|
||||
});
|
||||
record._changes[name] = rec.id;
|
||||
} else if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
var fieldInfo = record.fieldsInfo[name];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
|
||||
var x2manyList = self._makeDataPoint({
|
||||
context: record.context,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
limit: fieldInfo.limit,
|
||||
modelName: field.relation,
|
||||
parentID: record.id,
|
||||
rawContext: fieldInfo && fieldInfo.context,
|
||||
relationField: field.relation_field,
|
||||
res_ids: [],
|
||||
static: true,
|
||||
type: 'list',
|
||||
});
|
||||
record._changes[name] = x2manyList.id;
|
||||
var many2ones = {};
|
||||
var r;
|
||||
_.each(result[name], function (value) {
|
||||
if (_.isArray(value)) {
|
||||
// value is a command
|
||||
if (value[0] === 0) {
|
||||
// CREATE
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
});
|
||||
x2manyList._changes.push(r.id);
|
||||
r._changes = value[2];
|
||||
|
||||
// this is necessary so the fields are initialized
|
||||
for (var fieldName in value[2]) {
|
||||
r.data[fieldName] = null;
|
||||
}
|
||||
|
||||
for (var name in r._changes) {
|
||||
if (r.fields[name].type === 'many2one') {
|
||||
var rec = self._makeDataPoint({
|
||||
context: r.context,
|
||||
modelName: r.fields[name].relation,
|
||||
data: {id: r._changes[name]}
|
||||
});
|
||||
r._changes[name] = rec.id;
|
||||
many2ones[name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value[0] === 6) {
|
||||
// REPLACE_WITH
|
||||
x2manyList._changes = [];
|
||||
}
|
||||
} else {
|
||||
// value is an id
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
res_id: value,
|
||||
});
|
||||
if (!x2manyList._changes) {
|
||||
x2manyList._changes = [];
|
||||
}
|
||||
x2manyList._changes.push(r.id);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch many2ones display_name
|
||||
_.each(_.keys(many2ones), function (name) {
|
||||
defs.push(self._fetchNameGets(x2manyList, name));
|
||||
});
|
||||
} else if (field.type === 'date') {
|
||||
// process date: convert into a moment instance
|
||||
record._changes[name] = fieldUtils.parse.date(result[name]);
|
||||
} else if (field.type === 'datetime') {
|
||||
// process datetime: convert into a moment instance
|
||||
record._changes[name] = fieldUtils.parse.datetime(result[name]);
|
||||
} else {
|
||||
record._changes[name] = result[name];
|
||||
}
|
||||
});
|
||||
return $.when.apply($, defs)
|
||||
.then(function () {
|
||||
var shouldApplyOnchange = false;
|
||||
var field;
|
||||
for (var field_name in record.data) {
|
||||
field = record.fields[field_name];
|
||||
if (field.onChange) {
|
||||
shouldApplyOnchange = true;
|
||||
}
|
||||
}
|
||||
if (shouldApplyOnchange) {
|
||||
return self._applyOnChange(record, fields_key);
|
||||
} else {
|
||||
return $.when();
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self._fetchRelationalData(record);
|
||||
})
|
||||
.then(function () {
|
||||
return self._postprocess(record);
|
||||
})
|
||||
.then(function () {
|
||||
return record.id;
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* This helper method is designed to help developpers that want to use a
|
||||
* field widget outside of a view. In that case, we want a way to create
|
||||
@@ -607,7 +433,7 @@ var BasicModel = AbstractModel.extend({
|
||||
*
|
||||
* @param {string} model name of the model
|
||||
* @param {Object[]} fields a description of field properties
|
||||
* @param {Object} [fieldInfo] various field fieldInfo that we want to set
|
||||
* @param {Object} [fieldInfo] various field info that we want to set
|
||||
* @returns {string} the local id for the created resource
|
||||
*/
|
||||
makeRecord: function (model, fields, fieldInfo) {
|
||||
@@ -616,10 +442,15 @@ var BasicModel = AbstractModel.extend({
|
||||
_.each(fields, function (field) {
|
||||
record_fields[field.name] = _.pick(field, 'type', 'relation', 'domain');
|
||||
});
|
||||
var viewInfo = {};
|
||||
viewInfo.default = fieldInfo || _.mapObject(fields, function () {
|
||||
return {};
|
||||
});
|
||||
var record = this._makeDataPoint({
|
||||
modelName: model,
|
||||
fields: record_fields,
|
||||
fieldsInfo: fieldInfo,
|
||||
fieldsInfo: viewInfo,
|
||||
viewType: 'default',
|
||||
});
|
||||
_.each(fields, function (field) {
|
||||
if ('value' in field) {
|
||||
@@ -670,10 +501,10 @@ var BasicModel = AbstractModel.extend({
|
||||
var params = {
|
||||
context: element.context,
|
||||
fieldsInfo: element.fieldsInfo,
|
||||
fieldNames: element.fieldNames,
|
||||
fields: element.fields,
|
||||
viewType: element.viewType,
|
||||
};
|
||||
return this.makeDefaultRecord(element.model, params);
|
||||
return this._makeDefaultRecord(element.model, params);
|
||||
}
|
||||
this.discardChanges(id);
|
||||
}
|
||||
@@ -703,7 +534,7 @@ var BasicModel = AbstractModel.extend({
|
||||
if (element.type === 'record') {
|
||||
element.offset = _.indexOf(element.res_ids, element.res_id);
|
||||
}
|
||||
var loadOptions = _.pick(options, 'fieldNames');
|
||||
var loadOptions = _.pick(options, 'fieldNames', 'viewType');
|
||||
return this._load(element, loadOptions).then(function (result) {
|
||||
return result.id;
|
||||
});
|
||||
@@ -762,7 +593,8 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
|
||||
if (method === 'create') {
|
||||
_.each(record.fieldNames, function (name) {
|
||||
var fieldNames = record.getFieldNames();
|
||||
_.each(fieldNames, function (name) {
|
||||
changes[name] = changes[name] || record.data[name];
|
||||
if (changes[name] === null) {
|
||||
delete changes[name];
|
||||
@@ -799,34 +631,19 @@ var BasicModel = AbstractModel.extend({
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Set fieldNames.
|
||||
* Set fields on a record element (current fields as defaults)
|
||||
* 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.
|
||||
* Completes the fields and fieldsInfo of a dataPoint with the given ones.
|
||||
* It is useful for the cases where a record element is shared between
|
||||
* various views, such as a one2many with a tree and a form view.
|
||||
*
|
||||
* @param {string} recordID a valid element ID
|
||||
* @param {Object} props
|
||||
* @param {Object} props.fieldNames
|
||||
* @param {Object} props.fields
|
||||
* @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.fieldsInfo the previous field fieldInfo
|
||||
* @param {Object} viewInfo
|
||||
* @param {Object} viewInfo.fields
|
||||
* @param {Object} viewInfo.fieldsInfo
|
||||
*/
|
||||
setFieldProps: function (recordID, props) {
|
||||
addFieldsInfo: function (recordID, viewInfo) {
|
||||
var record = this.localData[recordID];
|
||||
var result = {
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
};
|
||||
record.mode = props.mode;
|
||||
record.fieldNames = Object.keys(props.fieldsInfo);
|
||||
record.fields = _.defaults(props.fields, record.fields);
|
||||
record.fieldsInfo = props.fieldsInfo;
|
||||
return result;
|
||||
record.fields = _.defaults(record.fields, viewInfo.fields);
|
||||
record.fieldsInfo = _.defaults(record.fieldsInfo, viewInfo.fieldsInfo);
|
||||
},
|
||||
/**
|
||||
* For list resources, this changes the orderedBy key, then performs the
|
||||
@@ -1029,7 +846,8 @@ var BasicModel = AbstractModel.extend({
|
||||
var commands = this._generateX2ManyCommands(record);
|
||||
|
||||
// compute field values
|
||||
var currentData = _.pick(_.extend(rawData, commands), record.fieldNames);
|
||||
var fieldNames = record.getFieldNames();
|
||||
var currentData = _.pick(_.extend(rawData, commands), fieldNames);
|
||||
if (record.relationField) {
|
||||
// if there is a relation field, this means that record is an elem
|
||||
// in a one2many. The relation field is the corresponding many2one
|
||||
@@ -1099,16 +917,16 @@ var BasicModel = AbstractModel.extend({
|
||||
fields: list.fields,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
modelName: list.model,
|
||||
fieldNames: _.keys(command[2]),
|
||||
viewType: list.viewType,
|
||||
};
|
||||
if (command[0] === 1) {
|
||||
params.res_id = command[1];
|
||||
}
|
||||
rec = self._makeDataPoint(params);
|
||||
var data = command[2];
|
||||
_.each(rec.fieldNames, function (name) {
|
||||
_.each(Object.keys(data), function (name) {
|
||||
var field = rec.fields[name];
|
||||
if (field.type === 'many2one') {
|
||||
if (field && field.type === 'many2one') {
|
||||
var r = self._makeDataPoint({
|
||||
modelName: field.relation,
|
||||
data: {
|
||||
@@ -1161,7 +979,7 @@ 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 fieldInfo = record.fieldsInfo[record.viewType][fieldName];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
var rec;
|
||||
var defs = [];
|
||||
@@ -1179,7 +997,10 @@ var BasicModel = AbstractModel.extend({
|
||||
// list's fields with those in the created record
|
||||
var newRecord = this.localData[command.id];
|
||||
_.defaults(list.fields, newRecord.fields);
|
||||
_.defaults(list.fieldsInfo, newRecord.fieldsInfo);
|
||||
newRecord.fields = list.fields;
|
||||
newRecord.fieldsInfo = list.fieldsInfo;
|
||||
newRecord.viewType = list.viewType;
|
||||
list._changes.push(newRecord.id);
|
||||
break;
|
||||
case 'ADD_M2M':
|
||||
@@ -1193,9 +1014,10 @@ var BasicModel = AbstractModel.extend({
|
||||
rec = self._makeDataPoint({
|
||||
context: record.context,
|
||||
modelName: field.relation,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: view ? view.fields : fieldInfo.relatedFields,
|
||||
fieldsInfo: view ? view.fieldsInfo : fieldInfo.fieldsInfo,
|
||||
res_id: d.id,
|
||||
viewType: view ? view.type : fieldInfo.viewType,
|
||||
});
|
||||
list_records[d.id] = rec;
|
||||
list._changes.push(rec.id);
|
||||
@@ -1205,15 +1027,16 @@ var BasicModel = AbstractModel.extend({
|
||||
// _changes so this is a very specific case)
|
||||
// this could be optimized by registering the fetched records in the list's _cache
|
||||
// so that if a record is removed and then re-added, it won't be fetched twice
|
||||
var fieldNames = list.getFieldNames();
|
||||
var def = this._rpc({
|
||||
model: list.model,
|
||||
method: 'read',
|
||||
args: [_.pluck(data, 'id'), list.fieldNames]
|
||||
args: [_.pluck(data, 'id'), fieldNames]
|
||||
})
|
||||
.then(function (records) {
|
||||
_.each(records, function (record) {
|
||||
list_records[record.id].data = record;
|
||||
self._parseServerData(list.fieldNames, list.fields, record);
|
||||
self._parseServerData(fieldNames, list.fields, record);
|
||||
});
|
||||
});
|
||||
defs.push(def);
|
||||
@@ -1272,12 +1095,12 @@ var BasicModel = AbstractModel.extend({
|
||||
_buildOnchangeSpecs: function (record) {
|
||||
// TODO: replace this function by some generic tree function in utils
|
||||
var specs = {};
|
||||
_.each(record.fieldNames, function (name) {
|
||||
_.each(record.getFieldNames(), function (name) {
|
||||
var field = record.fields[name];
|
||||
var fieldInfo = record.fieldsInfo[name];
|
||||
var fieldInfo = record.fieldsInfo[record.viewType][name];
|
||||
specs[name] = (field.onChange) || "";
|
||||
_.each(fieldInfo.views, function (view) {
|
||||
_.each(view.fieldsInfo, function (field, subname) {
|
||||
_.each(view.fieldsInfo[view.type], function (field, subname) {
|
||||
specs[name + '.' + subname] = (view.fields[subname].onChange) || "";
|
||||
});
|
||||
});
|
||||
@@ -1335,13 +1158,18 @@ var BasicModel = AbstractModel.extend({
|
||||
* For a given resource of type 'record', fetch all data.
|
||||
*
|
||||
* @param {Object} record local resource
|
||||
* @param {string[]} [fieldNames] the list of fields to fetch. If not given,
|
||||
* fetch all the fields in record.fieldNames (+ display_name)
|
||||
* @param {Object} [options]
|
||||
* @param {string[]} [options.fieldNames] the list of fields to fetch. If
|
||||
* not given, fetch all the fields in record.fieldNames (+ display_name)
|
||||
* @param {string} [optinos.viewType] the type of view for which the record
|
||||
* is fetched (usefull to load the adequate fields), by defaults, uses
|
||||
* record.viewType
|
||||
* @returns {Deferred -> Object} resolves to the record
|
||||
*/
|
||||
_fetchRecord: function (record, fieldNames) {
|
||||
_fetchRecord: function (record, options) {
|
||||
var self = this;
|
||||
fieldNames = fieldNames || _.uniq(record.fieldNames.concat(['display_name']));
|
||||
var fieldNames = options && options.fieldNames ||
|
||||
_.uniq(record.getFieldNames().concat(['display_name']));
|
||||
return this._rpc({
|
||||
model: record.model,
|
||||
method: 'read',
|
||||
@@ -1356,7 +1184,7 @@ var BasicModel = AbstractModel.extend({
|
||||
self._parseServerData(fieldNames, record.fields, record.data);
|
||||
})
|
||||
.then(function () {
|
||||
return self._fetchX2Manys(record, fieldNames).then(function () {
|
||||
return self._fetchX2Manys(record, options).then(function () {
|
||||
return self._postprocess(record);
|
||||
});
|
||||
});
|
||||
@@ -1369,7 +1197,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* batching the requests if possible (for example, if 3 many2ones are in
|
||||
* relation on the same model, then we can probably fetch them in one rpc)
|
||||
*
|
||||
* This method is currently only called by makeDefaultRecord, it should be
|
||||
* This method is currently only called by _makeDefaultRecord, it should be
|
||||
* called by the onchange methods at some point.
|
||||
*
|
||||
* @todo fix bug: returns a list of deferred, not a deferred
|
||||
@@ -1382,7 +1210,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var toBeFetched = [];
|
||||
|
||||
// find all many2one related records to be fetched
|
||||
_.each(record.fieldNames, function (name) {
|
||||
_.each(record.getFieldNames(), function (name) {
|
||||
var field = record.fields[name];
|
||||
if (field.type === 'many2one') {
|
||||
var localId = (record._changes && record._changes[name]) || record.data[name];
|
||||
@@ -1417,8 +1245,8 @@ var BasicModel = AbstractModel.extend({
|
||||
_fetchSpecialData: function (record) {
|
||||
var self = this;
|
||||
var fieldNames = [];
|
||||
return $.when.apply($, _.map(record.fieldNames, function (name) {
|
||||
var fieldInfo = record.fieldsInfo[name] || {};
|
||||
return $.when.apply($, _.map(record.getFieldNames(), function (name) {
|
||||
var fieldInfo = record.fieldsInfo[record.viewType][name] || {};
|
||||
var Widget = fieldInfo.Widget;
|
||||
if (Widget && Widget.prototype.specialData) {
|
||||
return self[Widget.prototype.specialData](record, name).then(function (data) {
|
||||
@@ -1551,7 +1379,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* the undefined value.
|
||||
*/
|
||||
_fetchSpecialStatus: function (record, fieldName) {
|
||||
var foldField = record.fieldsInfo[fieldName].options.fold_field;
|
||||
var foldField = record.fieldsInfo[record.viewType][fieldName].options.fold_field;
|
||||
var fieldsToRead = foldField ? [foldField] : [];
|
||||
return this._fetchSpecialMany2ones(record, fieldName, fieldsToRead).then(function (m2os) {
|
||||
_.each(m2os, function (m2o) {
|
||||
@@ -1575,7 +1403,7 @@ var BasicModel = AbstractModel.extend({
|
||||
_fetchSpecialDomain: function (record, fieldName) {
|
||||
var context = record.getContext({fieldName: fieldName});
|
||||
|
||||
var domainModel = record.fieldsInfo[fieldName].options.model;
|
||||
var domainModel = record.fieldsInfo[record.viewType][fieldName].options.model;
|
||||
if (record.data.hasOwnProperty(domainModel)) {
|
||||
domainModel = record._changes && record._changes[domainModel] || record.data[domainModel];
|
||||
}
|
||||
@@ -1642,24 +1470,31 @@ var BasicModel = AbstractModel.extend({
|
||||
* useless rpcs.
|
||||
*
|
||||
* @param {Object} record local resource
|
||||
* @param {string[]} [fieldNames] list of fields to fetch
|
||||
* @param {Object} [options]
|
||||
* @param {string[]} [options.fieldNames] the list of fields to fetch.
|
||||
* If not given, fetch all the fields in record.fieldNames
|
||||
* @param {string} [options.viewType] the type of view for which the main
|
||||
* record is fetched (useful to load the adequate fields), by defaults,
|
||||
* uses record.viewType
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_fetchX2Manys: function (record, fieldNames) {
|
||||
_fetchX2Manys: function (record, options) {
|
||||
var self = this;
|
||||
var defs = [];
|
||||
fieldNames = fieldNames || record.fieldNames;
|
||||
var fieldNames = options && options.fieldNames || record.getFieldNames();
|
||||
var viewType = options && options.viewType || record.viewType;
|
||||
_.each(fieldNames, function (fieldName) {
|
||||
var field = record.fields[fieldName];
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
var fieldInfo = record.fieldsInfo[fieldName];
|
||||
var fieldInfo = record.fieldsInfo[viewType][fieldName];
|
||||
var rawContext = fieldInfo && fieldInfo.context;
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
var fieldsInfo = view ? view.fieldsInfo : (fieldInfo.fieldsInfo || {});
|
||||
var ids = record.data[fieldName] || [];
|
||||
var list = self._makeDataPoint({
|
||||
count: ids.length,
|
||||
fieldsInfo: fieldInfo.fieldsInfo || view && view.fieldsInfo,
|
||||
fields: fieldInfo.relatedFields || view && view.fields,
|
||||
fieldsInfo: fieldsInfo,
|
||||
fields: view ? view.fields : fieldInfo.relatedFields,
|
||||
limit: fieldInfo.limit,
|
||||
modelName: field.relation,
|
||||
res_ids: ids,
|
||||
@@ -1668,6 +1503,7 @@ var BasicModel = AbstractModel.extend({
|
||||
parentID: record.id,
|
||||
rawContext: rawContext,
|
||||
relationField: field.relation_field,
|
||||
viewType: view ? view.type : fieldInfo.viewType,
|
||||
});
|
||||
record.data[fieldName] = list.id;
|
||||
if (!fieldInfo.__no_fetch) {
|
||||
@@ -1691,7 +1527,7 @@ var BasicModel = AbstractModel.extend({
|
||||
_fetchX2ManyBatched: function (list, fieldName) {
|
||||
var self = this;
|
||||
var field = list.fields[fieldName];
|
||||
var fieldInfo = list.fieldsInfo[fieldName];
|
||||
var fieldInfo = list.fieldsInfo[list.viewType][fieldName];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
|
||||
// step 1: collect ids
|
||||
@@ -1755,10 +1591,11 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_fetchX2ManysBatched: function (list) {
|
||||
var defs = [];
|
||||
for (var i = 0; i < list.fieldNames.length; i++) {
|
||||
var field = list.fields[list.fieldNames[i]];
|
||||
var fieldNames = list.getFieldNames();
|
||||
for (var i = 0; i < fieldNames.length; i++) {
|
||||
var field = list.fields[fieldNames[i]];
|
||||
if (field.type === 'many2many' || field.type === 'one2many') {
|
||||
defs.push(this._fetchX2ManyBatched(list, list.fieldNames[i]));
|
||||
defs.push(this._fetchX2ManyBatched(list, fieldNames[i]));
|
||||
}
|
||||
}
|
||||
return $.when.apply($, defs);
|
||||
@@ -1853,7 +1690,8 @@ var BasicModel = AbstractModel.extend({
|
||||
context.set_eval_context(this._getEvalContext(element));
|
||||
|
||||
if (options && options.fieldName) {
|
||||
var fieldInfo = element.fieldsInfo[options.fieldName];
|
||||
var viewType = options.viewType || element.viewType;
|
||||
var fieldInfo = element.fieldsInfo[viewType][options.fieldName];
|
||||
if (fieldInfo && fieldInfo.context) {
|
||||
context.add(fieldInfo.context);
|
||||
} else {
|
||||
@@ -1885,7 +1723,8 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_getDomain: function (element, options) {
|
||||
if (options && options.fieldName) {
|
||||
var fieldInfo = element.fieldsInfo[options.fieldName];
|
||||
var viewType = options.viewType || element.viewType;
|
||||
var fieldInfo = element.fieldsInfo[viewType][options.fieldName];
|
||||
if (fieldInfo && fieldInfo.domain) {
|
||||
return Domain.prototype.stringToArray(
|
||||
fieldInfo.domain,
|
||||
@@ -1930,6 +1769,16 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
return evalContext;
|
||||
},
|
||||
/**
|
||||
* Returns the list of field names of the given element according to its
|
||||
* default view type.
|
||||
*
|
||||
* @param {Object} element an element from the localData
|
||||
* @returns {string[]} the list of field names
|
||||
*/
|
||||
_getFieldNames: function (element) {
|
||||
return Object.keys(element.fieldsInfo[element.viewType] || {});
|
||||
},
|
||||
/**
|
||||
* Helper method for the load entry point.
|
||||
*
|
||||
@@ -1942,7 +1791,7 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_load: function (dataPoint, options) {
|
||||
if (dataPoint.type === 'record') {
|
||||
return this._fetchRecord(dataPoint, options && options.fieldNames);
|
||||
return this._fetchRecord(dataPoint, options);
|
||||
}
|
||||
if (dataPoint.type === 'list' && dataPoint.groupedBy.length) {
|
||||
return this._readGroup(dataPoint);
|
||||
@@ -1979,13 +1828,6 @@ 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,
|
||||
@@ -1994,9 +1836,8 @@ var BasicModel = AbstractModel.extend({
|
||||
count: params.count || res_ids.length,
|
||||
data: params.data || (type === 'record' ? {} : []),
|
||||
domain: params.domain || [],
|
||||
fieldNames: params.fieldNames || Object.keys(params.fieldsInfo),
|
||||
fields: fields,
|
||||
fieldsInfo: params.fieldsInfo || {},
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
groupedBy: params.groupedBy || [],
|
||||
id: _.uniqueId(params.modelName + '_'),
|
||||
isOpen: params.isOpen,
|
||||
@@ -2015,15 +1856,207 @@ var BasicModel = AbstractModel.extend({
|
||||
static: params.static || false,
|
||||
type: type, // 'record' | 'list'
|
||||
value: value,
|
||||
viewType: params.viewType,
|
||||
};
|
||||
|
||||
dataPoint.getContext = this._getContext.bind(this, dataPoint);
|
||||
dataPoint.getDomain = this._getDomain.bind(this, dataPoint);
|
||||
dataPoint.getFieldNames = this._getFieldNames.bind(this, dataPoint);
|
||||
|
||||
this.localData[dataPoint.id] = dataPoint;
|
||||
|
||||
return dataPoint;
|
||||
},
|
||||
/**
|
||||
* When one needs to create a record from scratch, a not so simple process
|
||||
* needs to be done:
|
||||
* - call the /default_get route to get default values
|
||||
* - fetch all relational data
|
||||
* - apply all onchanges if necessary
|
||||
* - fetch all relational data
|
||||
*
|
||||
* This method tries to optimize the process as much as possible. Also,
|
||||
* it is quite horrible and should be refactored at some point.
|
||||
*
|
||||
* @private
|
||||
* @param {any} params
|
||||
* @param {string} modelName model name
|
||||
* @param {Object} params.context the context for the new record
|
||||
* @param {Object} params.fieldsInfo contains the fieldInfo of each view,
|
||||
* for each field
|
||||
* @param {Object} params.fields contains the description of each field
|
||||
* @param {Object} params.context the context for the new record
|
||||
* @param {string} params.viewType the key in fieldsInfo of the fields to load
|
||||
* @returns {Deferred -> string} resolves to the id for the created resource
|
||||
*/
|
||||
_makeDefaultRecord: function (modelName, params) {
|
||||
var self = this;
|
||||
var fieldNames = Object.keys(params.fieldsInfo[params.viewType]);
|
||||
var fields_key = _.without(fieldNames, '__last_update');
|
||||
|
||||
return this._rpc({
|
||||
model: modelName,
|
||||
method: 'default_get',
|
||||
args: [fields_key],
|
||||
context: params.context,
|
||||
})
|
||||
.then(function (result) {
|
||||
// fill default values for missing fields
|
||||
for (var i = 0; i < fieldNames.length; i++) {
|
||||
var fieldName = fieldNames[i];
|
||||
if (!(fieldName in result)) {
|
||||
var field = params.fields[fieldName];
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
result[fieldName] = [];
|
||||
} else {
|
||||
result[fieldName] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var data = {};
|
||||
var record = self._makeDataPoint({
|
||||
modelName: modelName,
|
||||
data: data,
|
||||
fields: params.fields,
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
context: params.context,
|
||||
parentID: params.parentID,
|
||||
relationField: params.relationField,
|
||||
res_ids: params.res_ids,
|
||||
viewType: params.viewType,
|
||||
});
|
||||
|
||||
var defs = []; // FIXME: remove defs?
|
||||
_.each(fieldNames, function (name) {
|
||||
var field = params.fields[name];
|
||||
data[name] = null;
|
||||
record._changes = record._changes || {};
|
||||
if (field.type === 'many2one' && result[name]) {
|
||||
var rec = self._makeDataPoint({
|
||||
context: record.context,
|
||||
data: {id: result[name]},
|
||||
modelName: field.relation,
|
||||
});
|
||||
record._changes[name] = rec.id;
|
||||
} else if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
var fieldInfo = record.fieldsInfo[record.viewType][name];
|
||||
var view = fieldInfo.views && fieldInfo.views[fieldInfo.mode];
|
||||
var fieldsInfo = view ? view.fieldsInfo : fieldInfo.fieldsInfo;
|
||||
var fields = view ? view.fields : fieldInfo.relatedFields;
|
||||
var viewType = view ? view.type : fieldInfo.viewType;
|
||||
|
||||
var x2manyList = self._makeDataPoint({
|
||||
context: record.context,
|
||||
fieldsInfo: fieldsInfo,
|
||||
fields: fields,
|
||||
limit: fieldInfo.limit,
|
||||
modelName: field.relation,
|
||||
parentID: record.id,
|
||||
rawContext: fieldInfo && fieldInfo.context,
|
||||
relationField: field.relation_field,
|
||||
res_ids: [],
|
||||
static: true,
|
||||
type: 'list',
|
||||
viewType: viewType,
|
||||
});
|
||||
record._changes[name] = x2manyList.id;
|
||||
var many2ones = {};
|
||||
var r;
|
||||
_.each(result[name], function (value) {
|
||||
if (_.isArray(value)) {
|
||||
// value is a command
|
||||
if (value[0] === 0) {
|
||||
// CREATE
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fieldsInfo: fieldsInfo,
|
||||
fields: fields,
|
||||
viewType: viewType,
|
||||
});
|
||||
x2manyList._changes.push(r.id);
|
||||
r._changes = value[2];
|
||||
|
||||
// this is necessary so the fields are initialized
|
||||
for (var fieldName in value[2]) {
|
||||
r.data[fieldName] = null;
|
||||
}
|
||||
|
||||
for (var name in r._changes) {
|
||||
if (r.fields[name].type === 'many2one') {
|
||||
var rec = self._makeDataPoint({
|
||||
context: r.context,
|
||||
modelName: r.fields[name].relation,
|
||||
data: {id: r._changes[name]}
|
||||
});
|
||||
r._changes[name] = rec.id;
|
||||
many2ones[name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value[0] === 6) {
|
||||
// REPLACE_WITH
|
||||
x2manyList._changes = [];
|
||||
}
|
||||
} else {
|
||||
// value is an id
|
||||
r = self._makeDataPoint({
|
||||
modelName: x2manyList.model,
|
||||
context: x2manyList.context,
|
||||
fieldsInfo: fieldsInfo,
|
||||
fields: fields,
|
||||
res_id: value,
|
||||
viewType: viewType,
|
||||
});
|
||||
if (!x2manyList._changes) {
|
||||
x2manyList._changes = [];
|
||||
}
|
||||
x2manyList._changes.push(r.id);
|
||||
}
|
||||
});
|
||||
|
||||
// fetch many2ones display_name
|
||||
_.each(_.keys(many2ones), function (name) {
|
||||
defs.push(self._fetchNameGets(x2manyList, name));
|
||||
});
|
||||
} else if (field.type === 'date') {
|
||||
// process date: convert into a moment instance
|
||||
record._changes[name] = fieldUtils.parse.date(result[name]);
|
||||
} else if (field.type === 'datetime') {
|
||||
// process datetime: convert into a moment instance
|
||||
record._changes[name] = fieldUtils.parse.datetime(result[name]);
|
||||
} else {
|
||||
record._changes[name] = result[name];
|
||||
}
|
||||
});
|
||||
return $.when.apply($, defs)
|
||||
.then(function () {
|
||||
var shouldApplyOnchange = false;
|
||||
var field;
|
||||
for (var field_name in record.data) {
|
||||
field = record.fields[field_name];
|
||||
if (field.onChange) {
|
||||
shouldApplyOnchange = true;
|
||||
}
|
||||
}
|
||||
if (shouldApplyOnchange) {
|
||||
return self._applyOnChange(record, fields_key);
|
||||
} else {
|
||||
return $.when();
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return self._fetchRelationalData(record);
|
||||
})
|
||||
.then(function () {
|
||||
return self._postprocess(record);
|
||||
})
|
||||
.then(function () {
|
||||
return record.id;
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* parse the server values to javascript framwork
|
||||
*
|
||||
@@ -2070,7 +2103,7 @@ var BasicModel = AbstractModel.extend({
|
||||
* quite a lot of computations to determine what needs to be fetched. This
|
||||
* method is doing that.
|
||||
*
|
||||
* @see _fetchRecord @see makeDefaultRecord
|
||||
* @see _fetchRecord @see _makeDefaultRecord
|
||||
*
|
||||
* @param {any} record
|
||||
* @returns {Deferred -> Object} resolves to the finished resource
|
||||
@@ -2078,9 +2111,9 @@ var BasicModel = AbstractModel.extend({
|
||||
_postprocess: function (record) {
|
||||
var self = this;
|
||||
var defs = [];
|
||||
_.each(record.fieldNames, function (name) {
|
||||
_.each(record.getFieldNames(), function (name) {
|
||||
var field = record.fields[name];
|
||||
var fieldInfo = record.fieldsInfo[name] || {};
|
||||
var fieldInfo = record.fieldsInfo[record.viewType][name] || {};
|
||||
var options = fieldInfo.options || {};
|
||||
if (options.always_reload) {
|
||||
if (record.fields[name].type === 'many2one' && record.data[name]) {
|
||||
@@ -2114,7 +2147,7 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_readGroup: function (list) {
|
||||
var self = this;
|
||||
var fields = _.uniq(list.fieldNames.concat(list.groupedBy));
|
||||
var fields = _.uniq(list.getFieldNames().concat(list.groupedBy));
|
||||
return this._rpc({
|
||||
model: list.model,
|
||||
method: 'read_group',
|
||||
@@ -2146,7 +2179,6 @@ var BasicModel = AbstractModel.extend({
|
||||
domain: group.__domain,
|
||||
context: list.context,
|
||||
fields: list.fields,
|
||||
fieldNames: list.fieldNames,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
value: group[rawGroupBy],
|
||||
aggregateValues: aggregateValues,
|
||||
@@ -2155,6 +2187,7 @@ var BasicModel = AbstractModel.extend({
|
||||
limit: list.limit,
|
||||
openGroupByDefault: list.openGroupByDefault,
|
||||
type: 'list',
|
||||
viewType: list.viewType,
|
||||
});
|
||||
list.data.push(newGroup.id);
|
||||
list.count += newGroup.count;
|
||||
@@ -2201,6 +2234,7 @@ var BasicModel = AbstractModel.extend({
|
||||
var ids = [];
|
||||
var missingIds = [];
|
||||
var upper_bound = list.limit ? Math.min(list.offset + list.limit, list.count) : list.count;
|
||||
var fieldNames = list.getFieldNames();
|
||||
for (var i = list.offset; i < upper_bound; i++) {
|
||||
var id = list.res_ids[i];
|
||||
ids.push(id);
|
||||
@@ -2212,7 +2246,7 @@ var BasicModel = AbstractModel.extend({
|
||||
def = this._rpc({
|
||||
model: list.model,
|
||||
method: 'read',
|
||||
args: [missingIds, list.fieldNames],
|
||||
args: [missingIds, fieldNames],
|
||||
context: {} // FIXME
|
||||
});
|
||||
} else {
|
||||
@@ -2228,14 +2262,14 @@ var BasicModel = AbstractModel.extend({
|
||||
dataPoint = self._makeDataPoint({
|
||||
data: _.findWhere(records, {id: id}),
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
fieldNames: list.fieldNames,
|
||||
fields: list.fields,
|
||||
modelName: list.model,
|
||||
parentID: list.id,
|
||||
viewType: list.viewType,
|
||||
});
|
||||
|
||||
// add many2one records
|
||||
self._parseServerData(list.fieldNames, dataPoint.fields, dataPoint.data);
|
||||
self._parseServerData(fieldNames, dataPoint.fields, dataPoint.data);
|
||||
list._cache[id] = dataPoint.id;
|
||||
}
|
||||
list.data.push(dataPoint.id);
|
||||
@@ -2272,10 +2306,11 @@ var BasicModel = AbstractModel.extend({
|
||||
*/
|
||||
_searchReadUngroupedList: function (list) {
|
||||
var self = this;
|
||||
var fieldNames = list.getFieldNames();
|
||||
return this._rpc({
|
||||
route: '/web/dataset/search_read',
|
||||
model: list.model,
|
||||
fields: list.fieldNames,
|
||||
fields: fieldNames,
|
||||
domain: list.domain || [],
|
||||
limit: list.limit,
|
||||
offset: list.offset,
|
||||
@@ -2287,14 +2322,14 @@ var BasicModel = AbstractModel.extend({
|
||||
var dataPoint = self._makeDataPoint({
|
||||
data: record,
|
||||
fields: list.fields,
|
||||
fieldNames: list.fieldNames,
|
||||
fieldsInfo: list.fieldsInfo,
|
||||
modelName: list.model,
|
||||
parentID: list.id,
|
||||
viewType: list.viewType,
|
||||
});
|
||||
|
||||
// add many2one records
|
||||
self._parseServerData(list.fieldNames, dataPoint.fields, dataPoint.data);
|
||||
self._parseServerData(fieldNames, dataPoint.fields, dataPoint.data);
|
||||
return dataPoint.id;
|
||||
});
|
||||
list.data = data;
|
||||
|
||||
@@ -64,7 +64,7 @@ return AbstractRenderer.extend({
|
||||
*/
|
||||
_getFieldValues: function (record) {
|
||||
var result = {};
|
||||
_.each(record.fieldNames, function (name) {
|
||||
_.each(record.getFieldNames(), function (name) {
|
||||
var field = record.fields[name];
|
||||
var value = record.data[name];
|
||||
result[name] = value;
|
||||
|
||||
@@ -40,9 +40,9 @@ var BasicView = AbstractView.extend({
|
||||
'action_buttons' in params ? params.action_buttons : true;
|
||||
|
||||
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.loadParams.viewType = this.viewType;
|
||||
this.recordID = params.recordID;
|
||||
},
|
||||
|
||||
@@ -60,9 +60,14 @@ var BasicView = AbstractView.extend({
|
||||
_loadData: function () {
|
||||
if (this.recordID) {
|
||||
var record = this.model.get(this.recordID);
|
||||
var fieldNames = _.difference(record.fieldNames, Object.keys(record.data));
|
||||
var viewType = this.viewType;
|
||||
var viewFields = Object.keys(record.fieldsInfo[viewType]);
|
||||
var fieldNames = _.difference(viewFields, Object.keys(record.data));
|
||||
if (fieldNames.length && !this.model.isNew(record.id)) {
|
||||
return this.model.reload(this.recordID, {fieldNames: fieldNames});
|
||||
return this.model.reload(this.recordID, {
|
||||
fieldNames: fieldNames,
|
||||
viewType: viewType,
|
||||
});
|
||||
} else {
|
||||
return $.when(this.recordID);
|
||||
}
|
||||
|
||||
@@ -92,17 +92,19 @@ var FormController = BasicController.extend({
|
||||
createRecord: function () {
|
||||
var self = this;
|
||||
var record = this.model.get(this.handle, {raw: true});
|
||||
return this.model.makeDefaultRecord(this.modelName, {
|
||||
context: this.context,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
fieldNames: record.fieldNames,
|
||||
fields: record.fields,
|
||||
res_ids: record.res_ids,
|
||||
}).then(function (handle) {
|
||||
self.handle = handle;
|
||||
self._updateEnv();
|
||||
self._toEditMode();
|
||||
});
|
||||
return this.model.load({
|
||||
context: this.context,
|
||||
fields: record.fields,
|
||||
fieldsInfo: record.fieldsInfo,
|
||||
modelName: this.modelName,
|
||||
res_ids: record.res_ids,
|
||||
type: 'record',
|
||||
viewType: 'form',
|
||||
}).then(function (handle) {
|
||||
self.handle = handle;
|
||||
self._updateEnv();
|
||||
self._toEditMode();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns the current res_id, wrapped in a list. This is only used by the
|
||||
@@ -512,32 +514,25 @@ var FormController = BasicController.extend({
|
||||
* shouldn't be saved in DB when the user clicks on 'Save' in the dialog,
|
||||
* but later on when he clicks on 'Save' in the main form view. For this to
|
||||
* work correctly, the main model and the local id of the opened record must
|
||||
* be given to the dialog, which will update the list of fields of the
|
||||
* record with the one of the form view, and this list of fields will be
|
||||
* reset when the related record is saved (or when the dialog is closed).
|
||||
* be given to the dialog, which will complete the viewInfo of the record
|
||||
* with the one of the form view.
|
||||
*
|
||||
* @private
|
||||
* @param {OdooEvent} event
|
||||
*/
|
||||
_onOpenOne2ManyRecord: function (event) {
|
||||
var self = this;
|
||||
var data = event.data;
|
||||
var record;
|
||||
if (data.id) {
|
||||
record = this.model.get(data.id, {raw: true});
|
||||
}
|
||||
// reset the list of fields when the user clicks on 'Save' in the modal
|
||||
var on_saved = function (record) {
|
||||
self.model.setFieldProps(record.id, data.viewInfo);
|
||||
data.on_saved(record);
|
||||
};
|
||||
|
||||
var dialog = new dialogs.FormViewDialog(this, {
|
||||
new dialogs.FormViewDialog(this, {
|
||||
context: data.context,
|
||||
domain: data.domain,
|
||||
fields_view: data.fields_view,
|
||||
model: this.model,
|
||||
on_saved: on_saved,
|
||||
on_saved: event.data.on_saved,
|
||||
parentID: this.handle,
|
||||
readonly: data.readonly,
|
||||
recordID: record && record.id,
|
||||
@@ -546,16 +541,6 @@ var FormController = BasicController.extend({
|
||||
shouldSaveLocally: true,
|
||||
title: (record ? _t("Open:") : _t("Create")) + data.field.string,
|
||||
}).open();
|
||||
|
||||
// reset the list of fields when the user closes the modal (this is
|
||||
// necessary if he didn't save, but this is not sufficient if he saved
|
||||
// because in this case, a (re-)rendering of the related record is
|
||||
// done in the kanban/list o2m field before the dialog is closed)
|
||||
dialog.on('closed', this, function () {
|
||||
if (record) {
|
||||
this.model.setFieldProps(record.id, data.viewInfo);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Open an existing record in a form view dialog
|
||||
|
||||
@@ -301,8 +301,9 @@ return BasicRenderer.extend({
|
||||
mode: is_readonly ? 'readonly' : this.mode,
|
||||
required: is_required,
|
||||
idForLabel: this._getIDForLabel(name),
|
||||
viewType: 'form',
|
||||
};
|
||||
var attrs = this.state.fieldsInfo[name];
|
||||
var attrs = this.state.fieldsInfo.form[name];
|
||||
var widget = new attrs.Widget(this, name, this.state, options);
|
||||
this.widgets.push(widget);
|
||||
var def = widget.__widgetRenderAndInsert(function () {});
|
||||
|
||||
@@ -18,7 +18,7 @@ var FormView = BasicView.extend({
|
||||
icon: 'fa-edit',
|
||||
multi_record: false,
|
||||
searchable: false,
|
||||
|
||||
viewType: 'form',
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -55,7 +55,7 @@ var FormView = BasicView.extend({
|
||||
var defs = [];
|
||||
var fields = this.loadParams.fields;
|
||||
|
||||
_.each(self.loadParams.fieldsInfo, function (attrs, fieldName) {
|
||||
_.each(self.loadParams.fieldsInfo.form, function (attrs, fieldName) {
|
||||
var field = fields[fieldName];
|
||||
if (field.type !== 'one2many' && field.type !== 'many2many') {
|
||||
return;
|
||||
|
||||
@@ -46,7 +46,7 @@ var KanbanColumn = Widget.extend({
|
||||
// todo: handle group_by_m2o (nameget)
|
||||
this.title = value || _t('Undefined');
|
||||
this.folded = !data.isOpen;
|
||||
this.has_active_field = _.contains(data.fieldNames, 'active');
|
||||
this.has_active_field = 'active' in data.fields;
|
||||
this.size = data.count;
|
||||
this.values = data.values;
|
||||
this.fields = data.fields;
|
||||
|
||||
@@ -27,8 +27,8 @@ var KanbanModel = BasicModel.extend({
|
||||
res_id: resId,
|
||||
modelName: group.model,
|
||||
fields: group.fields,
|
||||
fieldNames: group.fieldNames,
|
||||
fieldsInfo: group.fieldsInfo,
|
||||
viewType: group.viewType,
|
||||
});
|
||||
group.data.unshift(new_record);
|
||||
group.count++;
|
||||
@@ -65,13 +65,14 @@ var KanbanModel = BasicModel.extend({
|
||||
context: parent.context,
|
||||
domain: parent.domain.concat([[groupBy,"=",result[0]]]),
|
||||
fields: parent.fields,
|
||||
fieldNames: parent.fieldNames,
|
||||
fieldsInfo: parent.fieldsInfo,
|
||||
groupedBy: parent.groupedBy,
|
||||
isOpen: true,
|
||||
limit: parent.limit,
|
||||
openGroupByDefault: true,
|
||||
orderedBy: parent.orderedBy,
|
||||
value: result,
|
||||
viewType: parent.viewType,
|
||||
});
|
||||
|
||||
// newGroup.is_open = true;
|
||||
|
||||
@@ -27,7 +27,7 @@ var KanbanRecord = Widget.extend({
|
||||
this._super(parent);
|
||||
|
||||
this.fields = state.fields;
|
||||
this.fieldsInfo = state.fieldsInfo;
|
||||
this.fieldsInfo = state.fieldsInfo.kanban;
|
||||
this.modelName = state.model;
|
||||
|
||||
this.options = options;
|
||||
@@ -359,7 +359,7 @@ var KanbanRecord = Widget.extend({
|
||||
_transformRecord: function (recordData) {
|
||||
var self = this;
|
||||
var new_record = {};
|
||||
_.each(this.state.fieldNames, function (name) {
|
||||
_.each(this.state.getFieldNames(), function (name) {
|
||||
var value = recordData[name];
|
||||
var r = _.clone(self.fields[name] || {});
|
||||
|
||||
|
||||
@@ -94,7 +94,10 @@ var KanbanRenderer = BasicRenderer.extend({
|
||||
transformQwebTemplate(templates, state.fields);
|
||||
this.qweb.add_template(utils.json_node_to_xml(templates));
|
||||
|
||||
this.recordOptions = _.extend({}, params.record_options, { qweb: this.qweb });
|
||||
this.recordOptions = _.extend({}, params.record_options, {
|
||||
qweb: this.qweb,
|
||||
viewType: 'kanban',
|
||||
});
|
||||
this.columnOptions = _.extend({}, params.column_options, { qweb: this.qweb });
|
||||
},
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ var KanbanView = BasicView.extend({
|
||||
Controller: KanbanController,
|
||||
Renderer: KanbanRenderer,
|
||||
},
|
||||
viewType: 'kanban',
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ ListRenderer.include({
|
||||
if (field.readonly) {
|
||||
return true;
|
||||
}
|
||||
var Widget = this.state.fieldsInfo[node.attrs.name].Widget;
|
||||
var Widget = this.state.fieldsInfo.list[node.attrs.name].Widget;
|
||||
if (Widget && Widget.prototype.readonly) {
|
||||
return true;
|
||||
}
|
||||
@@ -268,8 +268,10 @@ ListRenderer.include({
|
||||
return;
|
||||
}
|
||||
var $td = this._findTd($row, colIndex);
|
||||
var widget = new this.state.fieldsInfo[name].Widget(this, name, record, {
|
||||
mode: 'edit'
|
||||
var Widget = this.state.fieldsInfo.list[name].Widget;
|
||||
var widget = new Widget(this, name, record, {
|
||||
mode: 'edit',
|
||||
viewType: 'list',
|
||||
});
|
||||
if (widget.replace_element) {
|
||||
$td.empty();
|
||||
|
||||
@@ -284,8 +284,10 @@ var ListRenderer = BasicRenderer.extend({
|
||||
}
|
||||
var name = node.attrs.name;
|
||||
if (node.attrs.widget) {
|
||||
var widget = new this.state.fieldsInfo[name].Widget(this, name, record, {
|
||||
var Widget = this.state.fieldsInfo.list[name].Widget;
|
||||
var widget = new Widget(this, name, record, {
|
||||
mode: 'readonly',
|
||||
viewType: 'list',
|
||||
});
|
||||
widget.appendTo($td);
|
||||
$td.addClass('o_' + node.attrs.widget + '_cell');
|
||||
@@ -504,7 +506,7 @@ var ListRenderer = BasicRenderer.extend({
|
||||
}
|
||||
var description;
|
||||
if (node.attrs.widget) {
|
||||
description = this.state.fieldsInfo[name].Widget.prototype.description;
|
||||
description = this.state.fieldsInfo.list[name].Widget.prototype.description;
|
||||
}
|
||||
if (description === undefined) {
|
||||
description = node.attrs.string || field.string;
|
||||
|
||||
@@ -24,6 +24,7 @@ var ListView = BasicView.extend({
|
||||
Renderer: ListRenderer,
|
||||
Controller: ListController,
|
||||
}),
|
||||
viewType: 'list',
|
||||
/**
|
||||
* @override
|
||||
*
|
||||
|
||||
@@ -160,7 +160,7 @@ var FormViewDialog = ViewDialog.extend({
|
||||
|
||||
fields_view_def.then(function (viewInfo) {
|
||||
if (self.recordID) {
|
||||
self.model.setFieldProps(self.recordID, viewInfo);
|
||||
self.model.addFieldsInfo(self.recordID, viewInfo);
|
||||
}
|
||||
var formview = new FormView(viewInfo, {
|
||||
modelName: self.res_model,
|
||||
|
||||
@@ -949,6 +949,8 @@ QUnit.module('relational_fields', {
|
||||
'</kanban>' +
|
||||
'<form string="Partners">' +
|
||||
'<field name="display_name"/>' +
|
||||
// foo will be in the form but not in the kanban
|
||||
'<field name="foo"/>' +
|
||||
'</form>' +
|
||||
'</field>' +
|
||||
'</form>',
|
||||
|
||||
@@ -335,19 +335,12 @@ function addMockEnvironment(widget, params) {
|
||||
* @param {Object} params This object will be given to addMockEnvironment, so
|
||||
* any parameters from that method applies
|
||||
* @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.fieldsInfo] map of field names to field attributes
|
||||
* @returns {Model}
|
||||
*/
|
||||
function createModel(params) {
|
||||
var widget = new Widget();
|
||||
|
||||
var model = new params.Model(widget, {
|
||||
fieldsInfo: params.fieldsInfo,
|
||||
fieldNames: params.fieldNames,
|
||||
fields: params.fields,
|
||||
});
|
||||
var model = new params.Model(widget);
|
||||
|
||||
addMockEnvironment(widget, params);
|
||||
|
||||
|
||||
@@ -309,11 +309,15 @@ QUnit.module('Views', {
|
||||
assert.expect(3);
|
||||
|
||||
this.params.res_id = 1;
|
||||
this.params.fieldNames = ['category'];
|
||||
this.params.fieldsInfo = {category: {
|
||||
relatedFields: {display_name: {type: "char"}},
|
||||
fieldsInfo: {display_name: {}},
|
||||
}};
|
||||
this.params.fieldsInfo = {
|
||||
default: {
|
||||
category: {
|
||||
fieldsInfo: {default: {display_name: {}}},
|
||||
relatedFields: {display_name: {type: "char"}},
|
||||
viewType: 'default',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -336,11 +340,15 @@ QUnit.module('Views', {
|
||||
QUnit.test('can use command add and get many2many value with date field', function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
this.params.fieldNames = ['category'];
|
||||
this.params.fieldsInfo = {category: {
|
||||
relatedFields: {date: {type: "date"}},
|
||||
fieldsInfo: {date: {}},
|
||||
}};
|
||||
this.params.fieldsInfo = {
|
||||
default: {
|
||||
category: {
|
||||
fieldsInfo: {default: {date: {}}},
|
||||
relatedFields: {date: {type: "date"}},
|
||||
viewType: 'default',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -424,16 +432,17 @@ QUnit.module('Views', {
|
||||
QUnit.test('can make a default_record, no onchange', function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
this.params.context = {};
|
||||
this.params.fieldNames = ['product_id', 'category', 'product_ids'];
|
||||
this.params.res_id = undefined;
|
||||
this.params.context = {};
|
||||
this.params.type = 'record';
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
data: this.data,
|
||||
});
|
||||
|
||||
model.makeDefaultRecord('partner', this.params).then(function (resultID) {
|
||||
model.load(this.params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.product_id, false, "m2o default value should be false");
|
||||
assert.deepEqual(record.data.product_ids.data, [], "o2m default should be []");
|
||||
@@ -449,9 +458,10 @@ QUnit.module('Views', {
|
||||
// of 'false', which does not work with the mockserver mockOnChange method.
|
||||
this.data.partner.onchanges.product_id = true;
|
||||
|
||||
this.params.context = {};
|
||||
this.params.fieldNames = ['product_id'];
|
||||
this.params.res_id = undefined;
|
||||
this.params.context = {};
|
||||
this.params.type = 'record';
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -464,7 +474,7 @@ QUnit.module('Views', {
|
||||
},
|
||||
});
|
||||
|
||||
model.makeDefaultRecord('partner', this.params).then(function (resultID) {
|
||||
model.load(this.params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.product_id, false, "m2o default value should be false");
|
||||
});
|
||||
@@ -493,9 +503,11 @@ QUnit.module('Views', {
|
||||
context: {},
|
||||
fieldNames: ['other_product_id', 'product_id'],
|
||||
fields: fields,
|
||||
modelName: 'partner',
|
||||
type: 'record',
|
||||
};
|
||||
|
||||
model.makeDefaultRecord('partner', params).then(function (resultID) {
|
||||
model.load(params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.product_id.data.display_name, "xphone",
|
||||
"should have fetched correct name");
|
||||
@@ -675,6 +687,8 @@ QUnit.module('Views', {
|
||||
assert.expect(4);
|
||||
|
||||
this.params.fieldNames = ['product_ids'];
|
||||
this.params.res_id = undefined;
|
||||
this.params.type = 'record';
|
||||
|
||||
var id;
|
||||
var model = createModel({
|
||||
@@ -696,7 +710,7 @@ QUnit.module('Views', {
|
||||
},
|
||||
});
|
||||
|
||||
model.makeDefaultRecord('partner', this.params).then(function (resultID) {
|
||||
model.load(this.params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
model.save(record.id, {reload: false});
|
||||
record = model.get(resultID);
|
||||
@@ -759,6 +773,8 @@ QUnit.module('Views', {
|
||||
};
|
||||
|
||||
this.params.fieldNames = ['total', 'product_ids'];
|
||||
this.params.res_id = undefined;
|
||||
this.params.type = 'record';
|
||||
|
||||
var model = createModel({
|
||||
Model: BasicModel,
|
||||
@@ -772,7 +788,7 @@ QUnit.module('Views', {
|
||||
},
|
||||
});
|
||||
|
||||
model.makeDefaultRecord('partner', this.params).then(function (resultID) {
|
||||
model.load(this.params).then(function (resultID) {
|
||||
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.product_ids.data.length, 0,
|
||||
@@ -868,9 +884,10 @@ QUnit.module('Views', {
|
||||
fieldNames: ['date'],
|
||||
fields: this.data.partner.fields,
|
||||
modelName: 'partner',
|
||||
type: 'record',
|
||||
};
|
||||
|
||||
model.makeDefaultRecord('partner', params).then(function (resultID) {
|
||||
model.load(params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.strictEqual(record.data.date, false, "date default value should be false");
|
||||
});
|
||||
@@ -891,9 +908,10 @@ QUnit.module('Views', {
|
||||
fieldNames: ['category'],
|
||||
fields: this.data.partner.fields,
|
||||
modelName: 'partner',
|
||||
type: 'record',
|
||||
};
|
||||
|
||||
model.makeDefaultRecord('partner', params).then(function (resultID) {
|
||||
model.load(params).then(function (resultID) {
|
||||
var record = model.get(resultID);
|
||||
assert.ok(_.isEqual(record.data.category.res_ids, [12, 14]),
|
||||
"category field should have correct default value");
|
||||
@@ -943,10 +961,13 @@ QUnit.module('Views', {
|
||||
});
|
||||
|
||||
this.params.fieldsInfo = {
|
||||
product_id: {
|
||||
context: "{'hello2': 'world', 'test2': foo}",
|
||||
domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]",
|
||||
},
|
||||
default: {
|
||||
foo: {},
|
||||
product_id: {
|
||||
context: "{'hello2': 'world', 'test2': foo}",
|
||||
domain: "[['hello2', 'like', 'world'], ['test2', 'like', foo]]",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
model.load(this.params).then(function (resultID) {
|
||||
|
||||
Reference in New Issue
Block a user