diff options
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_model.js')
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 221 |
1 files changed, 152 insertions, 69 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 f6c2021f2e8..cdb8bb61f7c 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -41,6 +41,8 @@ odoo.define('web.BasicModel', function (require) { * relationField: {string}, * res_id: {integer|null}, * res_ids: {integer[]}, + * specialData: {Object}, + * _specialDataCache: {Object}, * static: {boolean}, * type: {string} 'record' | 'list' * value: ?, @@ -133,14 +135,6 @@ var BasicModel = AbstractModel.extend({ // save is performed. this.mutex = new concurrency.Mutex(); - // this dictionary is used to hold all many2one informations for widget - // selection. - this.many2ones = {}; - - // this dictionary is used to hold all many2many informations for widget - // many2many_checkboxes. - this.many2manys = {}; - this.localData = Object.create(null); this._super.apply(this, arguments); }, @@ -330,16 +324,6 @@ var BasicModel = AbstractModel.extend({ element.data[fieldName] = this.get(element.data[fieldName]) || []; } } - - // weird way to get out many2ones for widget=selection - if (field.__fetch_selection) { - field.__selection_information = self.many2ones[field.relation]; - } - - // weird way to get out many2manys for widget=many2many_checkboxes - if (field.__fetch_many2manys) { - field.__many2many_information = self.many2manys[field.relation]; - } } // this is not strictly necessary, but it hides some implementation @@ -697,7 +681,11 @@ var BasicModel = AbstractModel.extend({ delete record._changes[name]; } }); - return fieldNames; + return self._fetchSpecialData(record).then(function (fieldNames2) { + // Return the names of the fields that changed (onchange or + // associated special data change) + return _.union(fieldNames, fieldNames2); + }); }); }); }, @@ -1372,6 +1360,147 @@ var BasicModel = AbstractModel.extend({ return $.when.apply($, _.map(groups, this._fetchMany2OneGroup.bind(this))); }, /** + * Check the AbstractField specializations that are (will be) used by the + * given record and fetch the special data they will need. Special data are + * data that the rendering of the record won't need if it was not using + * particular widgets (example of these can be found at the methods which + * start with _fetchSpecial). + * + * @param {Object} record - an element from the localData + * @returns {Deferred<Array>} + * The deferred is resolved with an array containing the names of + * the field whose special data has been changed. + */ + _fetchSpecialData: function (record) { + var self = this; + var fieldNames = []; + return $.when.apply($, _.map(record.fieldNames, function (name) { + var attrs = record.fieldAttrs[name] || {}; + var Widget = attrs.Widget; + if (Widget && Widget.prototype.specialData) { + return self[Widget.prototype.specialData](record, name).then(function (data) { + if (data === undefined) { + return; + } + record.specialData[name] = data; + fieldNames.push(name); + }); + } + })).then(function () { + return fieldNames; + }); + }, + /** + * Fetches all the m2o records associated to the given fieldName. If the + * given fieldName is not a m2o field, nothing is done. + * + * @param {Object} record - an element from the localData + * @param {Object} fieldName - the name of the field + * @param {string[]} [fieldsToRead] - the m2os fields to read (id and + * display_name are automatic). + * @returns {Deferred<any>} + * The deferred is resolved with the fetched special data. If this + * data is the same as the previously fetched one (for the given + * parameters), no RPC is done and the deferred is resolved with + * the undefined value. + */ + _fetchSpecialMany2ones: function (record, fieldName, fieldsToRead) { + var field = record.fields[fieldName]; + if (field.type !== "many2one") { + return $.when(); + } + + var context = record.getContext({fieldName: fieldName}); + var domain = record.getDomain({fieldName: fieldName}); + if (domain.length) { + var localID = record.data[fieldName]; + var element = this.localData[localID]; + domain = ["|", ["id", "=", element.data.id]].concat(domain); + } + + // avoid rpc if not necessary + var specialDataCache = {context: context, domain: domain}; + if (_.isEqual(record._specialDataCache[fieldName], specialDataCache)) { + return $.when(); + } + record._specialDataCache[fieldName] = specialDataCache; + + var self = this; + return this._rpc(field.relation, 'search_read') + .withFields(["id"].concat(fieldsToRead || [])) + .withContext(context) + .withDomain(domain) + .exec() + .then(function (result) { + var ids = _.pluck(result.records, 'id'); + return self._rpc(field.relation, 'name_get') + .args([ids]) + .withContext(context) + .exec() + .then(function (name_gets) { + _.each(result.records, function (rec) { + var name_get = _.find(name_gets, function (n) { + return n[0] === rec.id; + }); + rec.display_name = name_get[1]; + }); + return result.records; + }); + }); + }, + /** + * Fetches all the relation records associated to the given fieldName. If + * the given fieldName is not a relational field, nothing is done. + * + * @param {Object} record - an element from the localData + * @param {Object} fieldName - the name of the field + * @returns {Deferred<any>} + * The deferred is resolved with the fetched special data. If this + * data is the same as the previously fetched one (for the given + * parameters), no RPC is done and the deferred is resolved with + * the undefined value. + */ + _fetchSpecialRelation: function (record, fieldName) { + var field = record.fields[fieldName]; + if (!_.contains(["many2one", "many2many", "one2many"], field.type)) { + return $.when(); + } + + var context = record.getContext({fieldName: fieldName}); + var domain = record.getDomain({fieldName: fieldName}); + + // avoid rpc if not necessary + var specialDataCache = {context: context, domain: domain}; + if (_.isEqual(record._specialDataCache[fieldName], specialDataCache)) { + return $.when(); + } + record._specialDataCache[fieldName] = specialDataCache; + + return this._rpc(field.relation, "name_search") + .args(["", domain]) + .withContext(context) + .exec(); + }, + /** + * Fetches all the m2o records associated to the given fieldName. If the + * given fieldName is not a m2o field, nothing is done. The difference with + * _fetchSpecialMany2ones is that the field given by options.fold_field is + * also fetched. + * + * @param {Object} record - an element from the localData + * @param {Object} fieldName - the name of the field + * @returns {Deferred<any>} + * The deferred is resolved with the fetched special data. If this + * data is the same as the previously fetched one (for the given + * parameters), no RPC is done and the deferred is resolved with + * the undefined value. + */ + _fetchSpecialStatus: function (record, fieldName) { + var foldField = record.fieldAttrs[fieldName].options.fold_field; + var fieldsToRead = foldField ? [foldField] : []; + return this._fetchSpecialMany2ones(record, fieldName, fieldsToRead); + }, + /** * Fetch all data in a ungrouped list * * @param {Object} list a valid resource object @@ -1767,6 +1896,8 @@ var BasicModel = AbstractModel.extend({ relationField: params.relationField, res_id: res_id, res_ids: res_ids, + specialData: {}, + _specialDataCache: {}, static: params.static || false, type: type, // 'record' | 'list' value: value, @@ -1833,38 +1964,6 @@ var BasicModel = AbstractModel.extend({ var field = record.fields[name]; var attrs = record.fieldAttrs[name] || {}; var options = attrs.options || {}; - if (field.__fetch_status && !field.__status_information) { - var field_values = _.mapObject(record.data, function (val, key) { - var fieldType = record.fields[key].type; - if (fieldType === 'many2one') { - var element = self.localData[val]; - return element && element.data.id || false; - } - return val; - }); - var domain = Domain.prototype.stringToArray(field.domain || [], field_values); - var fetch_status_information = self._rpc(field.relation, 'search_read') - .withFields(['id'].concat(options.fold_field ? [options.fold_field] : [])) - .withDomain(domain) - .exec() - .then(function (result) { - var ids = _.pluck(result.records, 'id'); - return self._rpc(field.relation, 'name_get') - .args([ids]) - .withContext(self._getContext(record, {fieldName: name})) - .exec() - .then(function (name_gets) { - _.each(result.records, function (record) { - var name_get = _.find(name_gets, function (n) { - return n[0] === record.id; - }); - record.display_name = name_get[1]; - }); - field.__status_information = result.records; - }); - }); - defs.push(fetch_status_information); - } if (options.always_reload) { if (record.fields[name].type === 'many2one' && record.data[name]) { var element = self.localData[record.data[name]]; @@ -1877,26 +1976,10 @@ var BasicModel = AbstractModel.extend({ })); } } - if (field.__fetch_selection && !self.many2ones[field.relation]) { - var fetchSelection = self._rpc(field.relation, 'name_search') - .args(['', field.domain]) - .exec() - .then(function (result) { - self.many2ones[field.relation] = result; - }); - defs.push(fetchSelection); - } - if (field.__fetch_many2manys) { - var fetchMany2Manys = self._rpc(field.relation, 'name_search') - .args(['', field.domain]) - .exec() - .then(function (result) { - self.many2manys[field.relation] = result; - }); - defs.push(fetchMany2Manys); - } }); + defs.push(this._fetchSpecialData(record)); + return $.when.apply($, defs).then(function () { return record; }); |
