summaryrefslogtreecommitdiff
path: root/addons/web/static
diff options
context:
space:
mode:
authorMartin Geubelle <mge@odoo.com>2017-04-05 13:23:31 +0200
committerAaron Bohy <aab@odoo.com>2017-04-11 19:44:38 +0200
commit0cb500594627c4cbe94d4e8c86d69f74ca031f35 (patch)
treed5a22a28bb4b9857a7d775ce48badd1966cbf24b /addons/web/static
parentb6ce7b136f561f156fb9ed1cc8694da86aff466e (diff)
[FIX] web, *: basic model: makeRecord with X2manys
`makeRecord` is now asynchronous as it will load data for relational fields (X2manys and many2ones if `display_name` is not fetched). This change has implied a refactoring in all `makeRecord` occurences to adapt with the returned deferred.
Diffstat (limited to 'addons/web/static')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js96
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_renderer.js32
-rw-r--r--addons/web/static/test/views/basic_model_tests.js155
3 files changed, 242 insertions, 41 deletions
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 1d511b1eb34..7badafa43c5 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -451,39 +451,92 @@ var BasicModel = AbstractModel.extend({
*/
makeRecord: function (model, fields, fieldInfo) {
var self = this;
+ var defs = [];
var record_fields = {};
_.each(fields, function (field) {
record_fields[field.name] = _.pick(field, 'type', 'relation', 'domain');
});
- var viewInfo = {};
- viewInfo.default = fieldInfo || _.mapObject(fields, function () {
- return {};
+ fieldInfo = fieldInfo || {};
+ var fieldsInfo = {};
+ fieldsInfo.default = {};
+ _.each(fields, function (field) {
+ fieldsInfo.default[field.name] = fieldInfo[field.name] || {};
});
var record = this._makeDataPoint({
modelName: model,
fields: record_fields,
- fieldsInfo: viewInfo,
+ fieldsInfo: fieldsInfo,
viewType: 'default',
});
_.each(fields, function (field) {
- if ('value' in field) {
- if (field.type === 'many2one') {
- var id = field.value[0];
- var display_name = field.value[1];
- var relatedRecord = self._makeDataPoint({
+ var dataPoint;
+ if (field.type === 'many2one') {
+ if (field.value) {
+ var id = _.isArray(field.value) ? field.value[0] : field.value;
+ var display_name = _.isArray(field.value) ? field.value[1] : undefined;
+ dataPoint = self._makeDataPoint({
modelName: field.relation,
data: {
id: id,
display_name: display_name,
- }
+ },
});
- record.data[field.name] = relatedRecord.id;
+ record.data[field.name] = dataPoint.id;
+ if (display_name === undefined) {
+ defs.push(self._fetchNameGet(dataPoint));
+ }
+ }
+ } else if (field.type === 'one2many' || field.type === 'many2many') {
+ var relatedFieldsInfo = {};
+ relatedFieldsInfo.default = {};
+ _.each(field.fields, function (field) {
+ relatedFieldsInfo.default[field.name] = {};
+ });
+ var dpParams = {
+ fieldsInfo: relatedFieldsInfo,
+ modelName: field.relation,
+ static: true,
+ type: 'list',
+ viewType: 'default',
+ };
+ var needLoad = false;
+ // As value, you could either pass:
+ // - a list of ids related to the record
+ // - a list of object
+ // We only need to load the datapoint in the first case.
+ if (field.value && field.value.length) {
+ if (_.isObject(field.value[0])) {
+ dpParams.res_ids = _.pluck(field.value, 'id');
+ dataPoint = self._makeDataPoint(dpParams);
+ _.each(field.value, function (data) {
+ var recordDP = self._makeDataPoint({
+ data: data,
+ modelName: field.relation,
+ type: 'record',
+ });
+ dataPoint.data.push(recordDP.id);
+ });
+ } else {
+ dpParams.res_ids = field.value;
+ dataPoint = self._makeDataPoint(dpParams);
+ needLoad = true;
+ }
} else {
- record.data[field.name] = field.value;
+ dpParams.res_ids = [];
+ dataPoint = self._makeDataPoint(dpParams);
}
+
+ if (needLoad) {
+ defs.push(self._load(dataPoint));
+ }
+ record.data[field.name] = dataPoint.id;
+ } else if (field.value) {
+ record.data[field.name] = field.value;
}
});
- return record.id;
+ return $.when.apply($, defs).then(function () {
+ return record.id;
+ });
},
/**
* This is an extremely important method. All changes in any field go
@@ -832,6 +885,7 @@ var BasicModel = AbstractModel.extend({
var def;
if (rel_data.display_name === undefined) {
var field = record.fields[fieldName];
+ // TODO: refactor this to use _fetchNameGet
def = this._rpc({
model: field.relation,
method: 'name_get',
@@ -1154,6 +1208,22 @@ var BasicModel = AbstractModel.extend({
});
});
},
+ /**
+ * Fetch name_get for a record datapoint.
+ *
+ * @param {Object} dataPoint
+ * @returns {Deferred}
+ */
+ _fetchNameGet: function (dataPoint) {
+ return this._rpc({
+ model: dataPoint.model,
+ method: 'name_get',
+ args: [dataPoint.res_id],
+ context: dataPoint.getContext(),
+ }).then(function (result) {
+ dataPoint.data.display_name = result[0][1];
+ });
+ },
_fetchNameGets: function (list, fieldName) {
var self = this;
var model;
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index 84f11b687be..fc76d42544f 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -48,6 +48,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
FieldManagerMixin.init.call(this);
this.title = options.title;
+ this.fields = options.fields;
this.fieldName = options.fieldName;
this.write_model = options.write_model;
this.write_field = options.write_field;
@@ -56,20 +57,29 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
this.filters = options.filters;
this.label = options.label;
this.getColor = options.getColor;
+ },
+ willStart: function () {
+ var self = this;
+ var defs = [this._super.apply(this, arguments)];
if (this.write_model || this.write_field) {
- this.many2one = new SidebarFilterM2O(this,
- this.write_field,
- this.model.get(this.model.makeRecord(this.write_model, [{
- name: this.write_field,
- relation: options.fields[this.fieldName].relation,
- type: 'many2one',
- }])),
- {
- mode: 'edit',
- can_create: false,
- });
+ var def = this.model.makeRecord(this.write_model, [{
+ name: this.write_field,
+ relation: this.fields[this.fieldName].relation,
+ type: 'many2one',
+ }]).then(function (recordID) {
+ self.many2one = new SidebarFilterM2O(self,
+ self.write_field,
+ self.model.get(recordID),
+ {
+ mode: 'edit',
+ can_create: false,
+ });
+ });
+ defs.push(def);
}
+ return $.when.apply($, defs);
+
},
start: function () {
this._super();
diff --git a/addons/web/static/test/views/basic_model_tests.js b/addons/web/static/test/views/basic_model_tests.js
index b15998c36dd..15947103e6e 100644
--- a/addons/web/static/test/views/basic_model_tests.js
+++ b/addons/web/static/test/views/basic_model_tests.js
@@ -747,19 +747,18 @@ QUnit.module('Views', {
var record = model.get(resultID, {raw: true});
assert.deepEqual(record.data.product_ids, [37], "should have correct initial value");
- var relatedRecordID = model.makeRecord('product', [{
+ model.makeRecord('product', [{
name: 'name',
string: "Product Name",
type: "char",
value: "xpod"
}
- ]);
-
- model.notifyChanges(record.id, {
- product_ids: {operation: "ADD", id: relatedRecordID}
+ ]).then(function (relatedRecordID) {
+ model.notifyChanges(record.id, {
+ product_ids: {operation: "ADD", id: relatedRecordID}
+ });
+ model.save(record.id);
});
-
- model.save(record.id);
});
model.destroy();
});
@@ -794,22 +793,23 @@ QUnit.module('Views', {
assert.strictEqual(record.data.product_ids.data.length, 0,
"one2many should start with a list of length 0");
- var relatedRecordID = model.makeRecord('product', [{
+ model.makeRecord('product', [{
name: 'name',
string: "Product Name",
type: "char",
value: "xpod"
}
- ]);
- model.notifyChanges(resultID, {
- product_ids: {operation: "ADD", id: relatedRecordID}
+ ]).then(function (relatedRecordID) {
+ model.notifyChanges(resultID, {
+ product_ids: {operation: "ADD", id: relatedRecordID}
+ });
+
+ record = model.get(resultID);
+ assert.strictEqual(record.data.product_ids.data.length, 1,
+ "one2many should be a list of length 1");
+ assert.strictEqual(record.data.product_ids.data[0].data.name, "xpod",
+ "one2many should have correct data");
});
-
- record = model.get(resultID);
- assert.strictEqual(record.data.product_ids.data.length, 1,
- "one2many should be a list of length 1");
- assert.strictEqual(record.data.product_ids.data[0].data.name, "xpod",
- "one2many should have correct data");
});
model.destroy();
});
@@ -1096,4 +1096,125 @@ QUnit.module('Views', {
model.destroy();
});
+ QUnit.test('call makeRecord with a pre-fetched many2one field', function (assert) {
+ assert.expect(3);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_id',
+ relation: 'partner',
+ type: 'many2one',
+ value: [1, 'first partner'],
+ }], {
+ partner_id: {
+ options: {
+ no_open: true,
+ },
+ },
+ }).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_id, {options: {no_open: true}},
+ "makeRecord should have generated the fieldsInfo");
+ assert.deepEqual(record.data.partner_id.data, {id: 1, display_name: 'first partner'},
+ "many2one should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('call makeRecord with a many2many field', function (assert) {
+ assert.expect(5);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_ids',
+ fields: [{
+ name: 'id',
+ type: 'integer',
+ }, {
+ name: 'display_name',
+ type: 'char',
+ }],
+ relation: 'partner',
+ type: 'many2many',
+ value: [1, 2],
+ }]).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_ids, {},
+ "makeRecord should have generated the fieldsInfo");
+ assert.strictEqual(record.data.partner_ids.count, 2,
+ "there should be 2 elements in the many2many");
+ assert.strictEqual(record.data.partner_ids.data.length, 2,
+ "many2many should be a list of length 2");
+ assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'},
+ "many2many should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 1, "makeRecord should have done 1 rpc");
+ });
+ model.destroy();
+ });
+
+ QUnit.test('call makeRecord with a pre-fetched many2many field', function (assert) {
+ assert.expect(5);
+ var rpcCount = 0;
+
+ var model = createModel({
+ Model: BasicModel,
+ data: this.data,
+ mockRPC: function (route, args) {
+ rpcCount++;
+ return this._super(route, args);
+ },
+ });
+
+ model.makeRecord('coucou', [{
+ name: 'partner_ids',
+ fields: [{
+ name: 'id',
+ type: 'integer',
+ }, {
+ name: 'display_name',
+ type: 'char',
+ }],
+ relation: 'partner',
+ type: 'many2many',
+ value: [{
+ id: 1,
+ display_name: "first partner",
+ }, {
+ id: 2,
+ display_name: "second partner",
+ }],
+ }]).then(function (recordID) {
+ var record = model.get(recordID);
+ assert.deepEqual(record.fieldsInfo.default.partner_ids, {},
+ "makeRecord should have generated the fieldsInfo");
+ assert.strictEqual(record.data.partner_ids.count, 2,
+ "there should be 2 elements in the many2many");
+ assert.strictEqual(record.data.partner_ids.data.length, 2,
+ "many2many should be a list of length 2");
+ assert.deepEqual(record.data.partner_ids.data[0].data, {id: 1, display_name: 'first partner'},
+ "many2many should contain the partner with id 1");
+ assert.strictEqual(rpcCount, 0, "makeRecord should not have done any rpc");
+ });
+ model.destroy();
+ });
+
});});