diff options
| author | Martin Geubelle <mge@odoo.com> | 2017-04-05 13:23:31 +0200 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2017-04-11 19:44:38 +0200 |
| commit | 0cb500594627c4cbe94d4e8c86d69f74ca031f35 (patch) | |
| tree | d5a22a28bb4b9857a7d775ce48badd1966cbf24b /addons/web/static | |
| parent | b6ce7b136f561f156fb9ed1cc8694da86aff466e (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.js | 96 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/calendar/calendar_renderer.js | 32 | ||||
| -rw-r--r-- | addons/web/static/test/views/basic_model_tests.js | 155 |
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(); + }); + });}); |
