diff options
| author | qsm-odoo <qsm@odoo.com> | 2017-02-28 11:48:00 +0100 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2017-04-11 19:44:38 +0200 |
| commit | 5804ed100ec790a36c5ff30a217914950cd78098 (patch) | |
| tree | a9f7ecf403cc8682c52ecce354ba082e9dee1a7c /addons/web/static/src/js | |
| parent | e5740270568404d71a3bc093074a085c9c2eded8 (diff) | |
[IMP] web, *: compute contexts and domains from datapoint
This commit tries to refactore the management of contexts and domains
in the webclient.
- Get rid of CompoundDomain: use standard array manipulation to
join multiple domains or use the Domain class (to be improved)
- Get rid of data.js build_domain and build_context methods
- Link this.record to the data point from the basic model for all
fields (not only relational fields)
- Add a getContext and a getDomain function to the basic model
data point (localData element) so that AbstractField implementations
can use them thanks to their this.record element.
- Remove the autocontext binding for field RPCs (see FieldManagerMixin)
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/_deprecated/data.js | 72 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/search_inputs.js | 39 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/context.js | 8 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/domain.js | 42 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/pyeval.js | 8 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/abstract_field.js | 15 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/relational_fields.js | 100 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 99 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/field_manager_mixin.js | 35 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_controller.js | 6 |
11 files changed, 184 insertions, 244 deletions
diff --git a/addons/web/static/src/js/_deprecated/data.js b/addons/web/static/src/js/_deprecated/data.js index 3c95ababc3a..14b3daeb2c6 100644 --- a/addons/web/static/src/js/_deprecated/data.js +++ b/addons/web/static/src/js/_deprecated/data.js @@ -64,8 +64,7 @@ var Query = Class.extend({ if (!to_set.hasOwnProperty(key)) { continue; } switch(key) { case 'filter': - q._filter = new CompoundDomain( - q._filter, to_set.filter); + q._filter = (q._filter || []).concat(to_set.filter || []); break; case 'context': q._context = new Context( @@ -729,77 +728,12 @@ var DataSetSearch = DataSet.extend({ } }); -var CompoundDomain = Class.extend({ - init: function () { - this.__ref = "compound_domain"; - this.__domains = []; - this.__eval_context = null; - var self = this; - _.each(arguments, function (x) { - self.add(x); - }); - }, - add: function (domain) { - this.__domains.push(domain); - return this; - }, - set_eval_context: function (eval_context) { - this.__eval_context = eval_context; - return this; - }, - get_eval_context: function () { - return this.__eval_context; - }, - eval: function () { - return pyeval.eval('domain', this); - }, -}); - -function rawify (data) { - // replaces the value of relational fields by their raw value (the id, if - // any, for many2ones and an array of ids for x2manys), in turn - return _.mapObject(data, function (value) { - if (value instanceof moment) { - return moment; - } - if (_.isObject(value)) { - return value.type === 'record' ? value.res_id : value.res_ids; - } - return value; - }); -} - -function build_eval_context (record) { - var field_values = rawify(_.extend({}, record.data, { active_model: record.model })); - if ('id' in record.data) { - field_values.active_id = record.data.id; - field_values.active_ids = [record.data.id]; - } - // FIXME: parent? - return new Context(field_values); -} - -function build_context(record, context) { - context = context || {}; - var eval_context = build_eval_context(record); - return new Context(context).set_eval_context(eval_context); -} - -function build_domain(record, domain) { - domain = domain || []; - var eval_context = build_eval_context(record); - return new CompoundDomain(domain).set_eval_context(eval_context); -} - var data = { Query: Query, DataSet: DataSet, DataSetStatic: DataSetStatic, DataSetSearch: DataSetSearch, - CompoundDomain: CompoundDomain, noDisplayContent: "<em class=\"text-warning\">" + _t("Unnamed") + "</em>", - build_context: build_context, - build_domain: build_domain, }; @@ -841,11 +775,11 @@ var Model = Class.extend({ * Fetches the model's domain, combined with the provided domain if any * * @param {Array} [domain] to combine with the model's internal domain - * @returns {instance.web.CompoundDomain} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain + * @returns {Array} The model's internal domain, or the AND-ed union of the model's internal domain and the provided domain */ domain: function (domain) { if (!domain) { return this._domain; } - return new data.CompoundDomain(this._domain, domain); + return this._domain.concat(domain); }, /** * Fetches the combination of the user's context and the domain context, diff --git a/addons/web/static/src/js/chrome/search_inputs.js b/addons/web/static/src/js/chrome/search_inputs.js index df5abe22a93..6855520a39b 100644 --- a/addons/web/static/src/js/chrome/search_inputs.js +++ b/addons/web/static/src/js/chrome/search_inputs.js @@ -3,7 +3,7 @@ odoo.define('web.search_inputs', function (require) { var Context = require('web.Context'); var core = require('web.core'); -var data = require('web.data'); +var Domain = require('web.Domain'); var field_utils = require('web.field_utils'); var pyeval = require('web.pyeval'); var time = require('web.time'); @@ -151,27 +151,27 @@ var Field = Input.extend( /** @lends instance.web.search.Field# */ { var domain = this.attrs.filter_domain; if (domain) { value_to_domain = function (facetValue) { - return new data.CompoundDomain(domain) - .set_eval_context({self: self.value_from(facetValue)}); + return Domain.prototype.stringToArray( + domain, + {self: self.value_from(facetValue)} + ); }; } else { value_to_domain = function (facetValue) { return self.make_domain( self.attrs.name, self.attrs.operator || self.default_operator, - facetValue); + facetValue + ); }; } var domains = facet.values.map(value_to_domain); - - if (domains.length === 1) { return domains[0]; } - for (var i = domains.length; --i;) { - domains.unshift(['|']); + if (domains.length === 1) { + return domains[0]; } - return _.extend(new data.CompoundDomain(), { - __domains: domains - }); + var ors = _.times(domains.length - 1, _.constant("|")); + return ors.concat.apply(ors, domains); } }); @@ -421,7 +421,7 @@ var ManyToOneField = CharField.extend({ var args = this.attrs.domain; if (typeof args === 'string') { try { - args = pyeval.eval('domain', args); + args = Domain.prototype.stringToArray(args); } catch(e) { args = []; } @@ -625,14 +625,15 @@ var FilterGroup = Input.extend(/** @lends instance.web.search.FilterGroup# */{ .reject(_.isEmpty) .value(); - if (!domains.length) { return; } - if (domains.length === 1) { return domains[0]; } - for (var i=domains.length; --i;) { - domains.unshift(['|']); + if (!domains.length) { + return; } - return _.extend(new data.CompoundDomain(), { - __domains: domains - }); + if (domains.length === 1) { + return domains[0]; + } + + var ors = _.times(domains.length - 1, _.constant("|")); + return ors.concat.apply(ors, domains); }, toggle_filter: function (e) { e.preventDefault(); diff --git a/addons/web/static/src/js/core/context.js b/addons/web/static/src/js/core/context.js index 961c67b898a..fe271afd244 100644 --- a/addons/web/static/src/js/core/context.js +++ b/addons/web/static/src/js/core/context.js @@ -14,6 +14,11 @@ var Context = Class.extend({ self.add(x); }); }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + add: function (context) { this.__contexts.push(context); return this; @@ -22,9 +27,6 @@ var Context = Class.extend({ this.__eval_context = eval_context; return this; }, - get_eval_context: function () { - return this.__eval_context; - }, eval: function () { return pyeval.eval('context', this); }, diff --git a/addons/web/static/src/js/core/domain.js b/addons/web/static/src/js/core/domain.js index 11d6a59ec33..2503042d070 100644 --- a/addons/web/static/src/js/core/domain.js +++ b/addons/web/static/src/js/core/domain.js @@ -12,20 +12,22 @@ var Domain = collections.Tree.extend({ /** * @constructor * @param {string|Array|boolean|undefined} domain - * - The given domain can be: + * The given domain can be: * * a string representation of the Python prefix-array * representation of the domain. * * a JS prefix-array representation of the domain. * * a boolean where the "true" domain match all records and the * "false" domain does not match any records. * * undefined, considered as the false boolean. + * @param {Object} [evalContext] - in case the given domain is a string, an + * evaluation context might be needed */ - init: function (domain) { + init: function (domain, evalContext) { this._super.apply(this, arguments); if (domain === true || domain === false || domain === undefined) { this._data = domain || false; } else { - this._parse(this.normalizeArray(_.clone(this.stringToArray(domain)))); + this._parse(this.normalizeArray(_.clone(this.stringToArray(domain, evalContext)))); } }, @@ -107,6 +109,30 @@ var Domain = collections.Tree.extend({ } }, + /** + * @returns {Array} JS prefix-array representation of this domain + */ + toArray: function () { + if (this._data === false) { + throw new Error("'false' domain cannot be converted to array"); + } else if (this._data === true) { + return []; + } else { + var arr = [this._data]; + return arr.concat.apply(arr, _.map(this._children, function (child) { + return child.toArray(); + })); + } + }, + + /** + * @returns {string} representation of the Python prefix-array + * representation of the domain + */ + toString: function () { + return Domain.prototype.arrayToString(this.toArray()); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -179,8 +205,9 @@ var Domain = collections.Tree.extend({ arrayToString: function (domain) { if (_.isString(domain)) return domain; return JSON.stringify(domain || []) - .replace(/false/g, "False") - .replace(/true/g, "True"); + .replace(/null/g, "None") + .replace(/false/g, "False") + .replace(/true/g, "True"); }, /** * Converts a string representation of the Python prefix-array @@ -189,11 +216,12 @@ var Domain = collections.Tree.extend({ * * @static * @param {string|Array} domain + * @param {Object} [evalContext] * @returns {Array} */ - stringToArray: function (domain) { + stringToArray: function (domain, evalContext) { if (!_.isString(domain)) return domain; - return pyeval.eval("domain", domain || "[]"); + return pyeval.eval("domain", domain || "[]", evalContext); }, /** * Makes implicit "&" operators explicit in the given JS prefix-array diff --git a/addons/web/static/src/js/core/pyeval.js b/addons/web/static/src/js/core/pyeval.js index 0227668e1c8..75efc9e813f 100644 --- a/addons/web/static/src/js/core/pyeval.js +++ b/addons/web/static/src/js/core/pyeval.js @@ -867,12 +867,6 @@ function eval_domains(domains, evaluation_context) { evaluation_context.context = evaluation_context; domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context)); break; - case 'compound_domain': - var eval_context = eval_contexts([domain.__eval_context]); - domain_array_to_combine = eval_domains( - domain.__domains, _.extend({}, evaluation_context, eval_context) - ); - break; default: domain_array_to_combine = domain; } @@ -983,7 +977,7 @@ function pyeval(type, object, context) { function eval_arg(arg) { if (typeof arg !== 'object' || !arg.__ref) { return arg; } switch(arg.__ref) { - case 'domain': case 'compound_domain': + case 'domain': return pyeval('domains', [arg]); case 'context': case 'compound_context': return pyeval('contexts', [arg]); diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index 6b914106371..ef67ba9b3c6 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -81,6 +81,9 @@ var AbstractField = Widget.extend({ // 'name' is the field name displayed by this widget this.name = name; + // the datapoint fetched from the model + this.record = record; + // the 'field' property is a description of all the various field properties, // such as the type, the comodel (relation), ... this.field = record.fields[name]; @@ -208,17 +211,6 @@ var AbstractField = Widget.extend({ this._reset(record, event); return this._render(); }, - /** - * @override performModelRPC from ServicesMixin - * Adds the dataPointID to the option parameter so that the BasicController - * (the FieldManagerMixin) knows the context to bind to the rpc call. - */ - performModelRPC: function (model, method, args, kwargs, options) { - return this._super(model, method, args, kwargs, _.extend({ - dataPointID: this.dataPointID, - fieldName: this.name, - }, options || {})); - }, //-------------------------------------------------------------------------- // Private @@ -289,6 +281,7 @@ var AbstractField = Widget.extend({ * @param {OdooEvent} event the event that triggered the change */ _reset: function (record, event) { + this.record = record; this.value = record.data[this.name]; this.recordData = record.data; }, diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 6b2cd4d6754..17e4e33f20e 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1298,9 +1298,9 @@ var FieldDomain = AbstractField.extend({ // ... then query the model to get the number of matched record if valid var self = this; return def.then(function () { - var context = {}; // FIXME ? return self.rpc(self.domainModel, "search_count") - .args([domain,context]) + .args([domain]) + .kwargs({context: self.record.getContext({fieldName: self.name})}) .exec() .then(function (data) { self._isValidForModel = true; diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index c5750c86735..51760a3c3b8 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -16,7 +16,6 @@ odoo.define('web.relational_fields', function (require) { var AbstractField = require('web.AbstractField'); var concurrency = require('web.concurrency'); var config = require('web.config'); -var Context = require('web.Context'); var ControlPanel = require('web.ControlPanel'); var dialogs = require('web.view_dialogs'); var core = require('web.core'); @@ -26,39 +25,10 @@ var field_utils = require('web.field_utils'); var KanbanRenderer = require('web.KanbanRenderer'); var ListRenderer = require('web.ListRenderer'); var Pager = require('web.Pager'); -var pyeval = require('web.pyeval'); var _t = core._t; var qweb = core.qweb; -/** - * Abstract widget to handle relational fields - */ -var AbstractRelationalField = AbstractField.extend({ - init: function (parent, name, record) { - this._super.apply(this, arguments); - // 'domain' and 'context' that should be used by the field widget. It - // can come from the attrs, and is mostly useful for rpcs - this.domain = this.attrs.domain || this.field.domain || []; - this.context = this.attrs.context || this.field.context || {}; - - // todo: remove this - this.record = record; - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * @private - */ - _reset: function (record) { - this.record = record; - this._super.apply(this, arguments); - }, -}); - //------------------------------------------------------------------------------ // Many2one widgets //------------------------------------------------------------------------------ @@ -104,7 +74,7 @@ var M2ODialog = Dialog.extend({ }, }); -var FieldMany2One = AbstractRelationalField.extend({ +var FieldMany2One = AbstractField.extend({ template: 'FieldMany2One', custom_events: { 'quick_create': '_onQuickCreate', @@ -229,6 +199,7 @@ var FieldMany2One = AbstractRelationalField.extend({ if (this.nodeOptions.quick_create) { this.rpc(this.field.relation, "name_create") .args([name]) + .kwargs({context: this.record.getContext({fieldName: this.name})}) .exec() .then(function (result) { if (self.mode === "edit") { @@ -288,18 +259,20 @@ var FieldMany2One = AbstractRelationalField.extend({ var def = $.Deferred(); this.orderer.add(def); - var exclusion_domain = []; + var context = this.record.getContext({fieldName: this.name}); + var domain = this.record.getDomain({fieldName: this.name}); + var blacklisted_ids = this._getSearchBlacklist(); if (blacklisted_ids.length > 0) { - exclusion_domain.push(['id', 'not in', blacklisted_ids]); + domain.push(['id', 'not in', blacklisted_ids]); } - var domain = new data.CompoundDomain(data.build_domain(this.record, this.domain), exclusion_domain); this.performModelRPC(this.field.relation, "name_search", [], { name: search_val, - args: pyeval.eval("domain", domain), + args: domain, operator: "ilike", limit: this.limit + 1, + context: context, }).then(function (result) { // possible selections for the m2o var values = _.map(result, function (x) { @@ -320,9 +293,10 @@ var FieldMany2One = AbstractRelationalField.extend({ action: function () { self.performModelRPC(self.field.relation, "name_search", { name: search_val, - args: pyeval.eval('domain', domain), + args: domain, operator: "ilike", limit: 160, + context: context, }).then(self._searchCreatePopup.bind(self, "search")); }, classname: 'o_m2o_dropdown_option', @@ -370,8 +344,8 @@ var FieldMany2One = AbstractRelationalField.extend({ var self = this; new dialogs.SelectCreateDialog(this, _.extend({}, this.nodeOptions, { res_model: this.field.relation, - domain: data.build_domain(this.record, this.domain), - context: new Context(data.build_context(this.record, this.context), context || {}), + domain: this.record.getDomain({fieldName: this.name}), + context: _.extend({}, this.record.getContext({fieldName: this.name}), context || {}), title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string, initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined, initial_view: view, @@ -405,12 +379,13 @@ var FieldMany2One = AbstractRelationalField.extend({ if (this.mode === 'readonly' && !this.nodeOptions.no_open) { event.preventDefault(); event.stopPropagation(); - var context = data.build_context(this.record, this.context); - var args = [[this.value.res_id], context.eval()]; - this.performModelRPC(this.field.relation, 'get_formview_action', args) - .then(function (action) { - self.trigger_up('do_action', {action: action}); - }); + this.performModelRPC(this.field.relation, 'get_formview_action', [ + [this.value.res_id] + ], { + context: this.record.getContext({fieldName: this.name}), + }).then(function (action) { + self.trigger_up('do_action', {action: action}); + }); } }, /** @@ -422,11 +397,14 @@ var FieldMany2One = AbstractRelationalField.extend({ return; } var self = this; - var context = data.build_context(this.record, this.context); var model = this.field.relation; var method = 'get_formview_id'; - var args = [[this.value.res_id], context.eval()]; - this.performModelRPC(model, method, args).then(function (view_id) { + var context = this.record.getContext({fieldName: this.name}); + this.performModelRPC(model, method, [ + [this.value.res_id] + ], { + context: context, + }).then(function (view_id) { new dialogs.FormViewDialog(self, { res_model: self.field.relation, res_id: self.value.res_id, @@ -492,7 +470,7 @@ var ListFieldMany2One = FieldMany2One.extend({ }, }); -var KanbanFieldMany2One = AbstractRelationalField.extend({ +var KanbanFieldMany2One = AbstractField.extend({ tagName: 'span', init: function () { this._super.apply(this, arguments); @@ -515,9 +493,9 @@ var KanbanFieldMany2One = AbstractRelationalField.extend({ // X2Many widgets //------------------------------------------------------------------------------ -var FieldX2Many = AbstractRelationalField.extend({ +var FieldX2Many = AbstractField.extend({ tagName: 'div', - custom_events: _.extend({}, AbstractRelationalField.prototype.custom_events, { + custom_events: _.extend({}, AbstractField.prototype.custom_events, { field_changed: '_onFieldChanged', kanban_record_delete: '_onDeleteRecord', list_record_delete: '_onDeleteRecord', @@ -716,8 +694,8 @@ var FieldX2Many = AbstractRelationalField.extend({ */ _onOpenRecord: function (event) { _.extend(event.data, { - context: data.build_context(this.record, this.context), - domain: data.build_context(this.record, this.domain), + context: this.record.getContext({fieldName: this.name}), + domain: this.record.getDomain({fieldName: this.name}), form_view: this.field.views && this.field.views.form, readonly: this.mode === 'readonly', string: this.string, @@ -810,8 +788,8 @@ var FieldOne2Many = FieldX2Many.extend({ }); } else { this.trigger_up('add_one2many_record', { - domain: data.build_domain(this.record, this.domain), - context: data.build_context(this.record, this.context), + domain: this.record.getDomain({fieldName: this.name}), + context: this.record.getContext({fieldName: this.name}), field: this.field, on_save: function (record) { this._setValue({ @@ -872,11 +850,13 @@ var FieldMany2Many = FieldX2Many.extend({ */ _onAddRecord: function () { var self = this; - var domain = data.build_domain(this.record, this.domain); + + var domain = this.record.getDomain({fieldName: this.name}); + new dialogs.SelectCreateDialog(this, { res_model: this.field.relation, - domain: new data.CompoundDomain(domain, ["!", ["id", "in", this.value.res_ids]]), - context: data.build_context(this.record, this.context), + domain: domain.concat(["!", ["id", "in", this.value.res_ids]]), + context: this.record.getContext({fieldName: this.name}), title: _t("Add: ") + this.string, no_create: this.nodeOptions.no_create || !this.activeActions.create, on_selected: function (res_ids) { @@ -908,7 +888,7 @@ var FieldMany2Many = FieldX2Many.extend({ }, }); -var FieldMany2ManyTags = AbstractRelationalField.extend({ +var FieldMany2ManyTags = AbstractField.extend({ tag_template: "FieldMany2ManyTag", className: "o_form_field o_form_field_many2manytags", replace_element: true, @@ -1118,9 +1098,9 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({ }, }); -var FieldMany2ManyCheckBoxes = AbstractRelationalField.extend({ +var FieldMany2ManyCheckBoxes = AbstractField.extend({ template: 'FieldMany2ManyCheckBoxes', - events: _.extend({}, AbstractRelationalField.prototype.events, { + events: _.extend({}, AbstractField.prototype.events, { change: '_onChange', }), supportedFieldTypes: ['many2many'], 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 5c0facecef1..2c7ec01f9c1 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -79,7 +79,7 @@ odoo.define('web.BasicModel', function (require) { var AbstractModel = require('web.AbstractModel'); var concurrency = require('web.concurrency'); var Context = require('web.Context'); -var data = require('web.data'); // TODO: remove dependency to data.js +var Domain = require('web.Domain'); var fieldUtils = require('web.field_utils'); var pyeval = require('web.pyeval'); var session = require('web.session'); @@ -365,6 +365,10 @@ var BasicModel = AbstractModel.extend({ delete element.parentID; delete element.relation_field; delete element.rawContext; + + element.getContext = this._getContext.bind(this, record); + element.getDomain = this._getDomain.bind(this, record); + return element; }, /** @@ -929,20 +933,6 @@ var BasicModel = AbstractModel.extend({ }); } }, - /** - * Every RPC done by the model need to add some context, which is a - * combination of the context of the session, of the record/list, and/or of - * the concerned field. This method combines all these contexts and evaluate - * them with the proper evalcontext. - * - * @param {string} recordID - the id of an element from the localData - * @see _getContext for the options parameter - * @returns {Object} the evaluated context - */ - getContext: function (recordID, options) { - var record = this.localData[recordID]; - return this._getContext(record, options); - }, //-------------------------------------------------------------------------- // Private @@ -1626,25 +1616,22 @@ var BasicModel = AbstractModel.extend({ */ _getContext: function (element, options) { var context = new Context(session.user_context, element.context); - var evalContext = this.get(element.id, {raw: true}).data; - if (element.parentID) { - var parent = this.get(element.parentID, {raw: true}); - if (parent.type === 'list' && this.localData[element.parentID].parentID) { - parent = this.get(this.localData[element.parentID].parentID, {raw: true}); - } - _.extend(evalContext, {parent: parent.data}); - } - context.set_eval_context(evalContext); + context.set_eval_context(this._getEvalContext(element)); + if (options && options.fieldName) { var attrs = element.fieldAttrs[options.fieldName]; if (attrs && attrs.context) { context.add(attrs.context); + } else { + var fieldParams = element.fields[options.fieldName]; + if (fieldParams.context) { + context.add(fieldParams.context); + } } } - if (element.rawContext) { var rawContext = new Context(element.rawContext); - var evalContext = this.get(element.parentID, {raw: true}).data; + var evalContext = this._getEvalContext(this.localData[element.parentID]); evalContext.id = evalContext.id || false; rawContext.set_eval_context(evalContext); context.add(rawContext); @@ -1653,6 +1640,63 @@ var BasicModel = AbstractModel.extend({ return context.eval(); }, /** + * Some records are associated to a/some domain(s). This method allows to + * retrieve them, evaluated. + * + * @param {Object} element an element from the localData + * @param {Object} [options] + * @param {string} [options.fieldName] + * the name of the field whose domain needs to be returned + * @returns {Array} the evaluated domain + */ + _getDomain: function (element, options) { + if (options && options.fieldName) { + var attrs = element.fieldAttrs[options.fieldName]; + if (attrs && attrs.domain) { + return Domain.prototype.stringToArray( + attrs.domain, + this._getEvalContext(element) + ); + } + var fieldParams = element.fields[options.fieldName]; + if (fieldParams.domain) { + return Domain.prototype.stringToArray( + fieldParams.domain, + this._getEvalContext(element) + ); + } + return []; + } + + return Domain.prototype.stringToArray( + element.domain, + this._getEvalContext(element) + ); + }, + /** + * Returns the evaluation context that should be used when evaluating the + * context/domain associated to a given element from the localData. + * + * @param {Object} element - an element from the localData + * @returns {Object} + */ + _getEvalContext: function (element) { + var evalContext = this.get(element.id, {raw: true}).data; + evalContext.active_model = element.model; + if (evalContext.id) { + evalContext.active_id = evalContext.id; + evalContext.active_ids = [evalContext.id]; + } + if (element.parentID) { + var parent = this.get(element.parentID, {raw: true}); + if (parent.type === 'list' && this.localData[element.parentID].parentID) { + parent = this.get(this.localData[element.parentID].parentID, {raw: true}); + } + _.extend(evalContext, {parent: parent.data}); + } + return evalContext; + }, + /** * Helper method for the load entry point. * * @see load @@ -1759,8 +1803,7 @@ var BasicModel = AbstractModel.extend({ } return val; }); - var _domain = new data.CompoundDomain(field.domain || {}).set_eval_context(field_values); - var domain = pyeval.eval('domain', _domain); + var domain = Domain.prototype.stringToArray(field.domain || [], field_values); var fold_field = pyeval.py_eval(attrs.options || '{}').fold_field; var fetch_status_information = self.rpc(field.relation, 'search_read') .withFields(['id'].concat(fold_field ? [fold_field] : [])) diff --git a/addons/web/static/src/js/views/field_manager_mixin.js b/addons/web/static/src/js/views/field_manager_mixin.js index 00607157a1f..4e61d25f468 100644 --- a/addons/web/static/src/js/views/field_manager_mixin.js +++ b/addons/web/static/src/js/views/field_manager_mixin.js @@ -18,7 +18,6 @@ var FieldManagerMixin = { custom_events: { field_changed: '_onFieldChanged', load: '_onLoad', - call_service: '_onCallService', }, /** * A FieldManagerMixin can be initialized with an instance of a basicModel. @@ -114,40 +113,6 @@ var FieldManagerMixin = { data.on_success(self.model.get(db_id)); }); }, - /** - * Some widgets perform model RPCs. These are intercepted so that the - * context can be automatically added according to the given dataPointID - * option the AbstractField implementation automatically adds. - * - * @param {OdooEvent} e - * @param {string} e.data.service - the service called, here we are only - * interested in the "ajax" service - * @param {string} e.data.method - the service method called, here we are - * only interested by the "rpc" method - * @param {Array} e.data.args - * the args parameters contains the route, the route arguments and - * the RPC call options. This method purpose is to check the RPC - * call options for the dataPointID (and field name) and merge the - * appropriate context with the route arguments' one. - */ - _onCallService: function (e) { - if (e.data.service !== "ajax" || e.data.method !== "rpc") { - return; - } - - var args = e.data.args[1]; - var options = e.data.args[2]; - if (args.kwargs && options.dataPointID) { - args.kwargs.context = _.extend( - this.model.getContext(options.dataPointID, { - fieldName: options.fieldName || false, - }), - args.kwargs.context || {} - ); - // to prevent doing this work again, in some other widget with FMM. - delete options.dataPointID; - } - }, }; return FieldManagerMixin; diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index 0bbcdefb815..25cecf03822 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -243,7 +243,7 @@ var FormController = BasicController.extend({ var recordID = record.data.id; this.trigger_up('execute_action', { action_data: _.extend({}, attrs, { - context: data.build_context(record, attrs.context) + context: _.extend(record.getContext(), attrs.context), }), model: record.model, record_id: recordID, @@ -532,7 +532,7 @@ var FormController = BasicController.extend({ res_model: model, res_id: res_id, fields_view: form_fields_view, - context: data.build_context(record), + context: record.getContext(), readonly: event.data.readonly, title: _t("Open: ") + event.data.string, on_save: event.data.on_save, @@ -605,7 +605,7 @@ return FormController; // record = record || this.model.get(this.db_id); // var record_id = record.data.id; // this.trigger_up('execute_action', { - // action_data: _.extend({}, attrs, { context: data.build_context(record, attrs.context) }), + // action_data: _.extend({}, attrs, { context: _.extend(record.getContext(), attrs.context) }), // dataset: this.dataset, // record_id: record_id, // on_close: function (reason) { |
