diff options
| author | Géry Debongnie <ged@odoo.com> | 2017-03-05 09:34:56 +0100 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2017-04-11 19:44:38 +0200 |
| commit | e5740270568404d71a3bc093074a085c9c2eded8 (patch) | |
| tree | 7b84740d92c550236de25a37cf37d244760bdbf3 /addons/web/static/src/js | |
| parent | 0df9968433389e0d504276cd711473c2eef937ae (diff) | |
[REF] web,*: remove 'web.model' and 'web.dataModel
Use this.rpc instead of Model object, with web.ajax if necessary.
nb: EventDispatcherMixin include now the custom_events managment instead
of widget
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/_deprecated/data.js | 100 | ||||
| -rw-r--r-- | addons/web/static/src/js/_deprecated/data_model.js | 81 | ||||
| -rw-r--r-- | addons/web/static/src/js/_deprecated/model.js | 60 | ||||
| -rw-r--r-- | addons/web/static/src/js/apps.js | 44 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/search_inputs.js | 44 | ||||
| -rw-r--r-- | addons/web/static/src/js/chrome/web_client.js | 66 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/mixins.js | 57 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/widget.js | 40 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 16 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/relational_fields.js | 10 | ||||
| -rw-r--r-- | addons/web/static/src/js/services/data_manager.js | 25 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 983 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/calendar/calendar_controller.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/debug_manager.js | 24 | ||||
| -rw-r--r-- | addons/web/static/src/js/widgets/switch_company_menu.js | 3 |
15 files changed, 775 insertions, 780 deletions
diff --git a/addons/web/static/src/js/_deprecated/data.js b/addons/web/static/src/js/_deprecated/data.js index f6a58367903..3c95ababc3a 100644 --- a/addons/web/static/src/js/_deprecated/data.js +++ b/addons/web/static/src/js/_deprecated/data.js @@ -5,7 +5,6 @@ var Class = require('web.Class'); var Context = require('web.Context'); var concurrency = require('web.concurrency'); var mixins = require('web.mixins'); -var Model = require('web.Model'); var session = require('web.session'); var translation = require('web.translation'); var pyeval = require('web.pyeval'); @@ -792,7 +791,7 @@ function build_domain(record, domain) { return new CompoundDomain(domain).set_eval_context(eval_context); } -return { +var data = { Query: Query, DataSet: DataSet, DataSetStatic: DataSetStatic, @@ -803,4 +802,101 @@ return { build_domain: build_domain, }; + +var Model = Class.extend({ + /** + new openerp.web.Model(name[, context[, domain]]) + + @constructs instance.web.Model + @extends instance.web.Class + + @param {String} name name of the OpenERP model this object is bound to + @param {Object} [context] + @param {Array} [domain] + */ + init: function(name, context, domain) { + this.name = name; + this._context = context || {}; + this._domain = domain || []; + }, + /** + * @deprecated does not allow to specify kwargs, directly use call() instead + */ + get_func: function (method_name) { + var self = this; + return function () { + return self.call(method_name, _.toArray(arguments)); + }; + }, + /** + * Fetches a Query instance bound to this model, for searching + * + * @param {Array<String>} [fields] fields to ultimately fetch during the search + * @returns {instance.web.Query} + */ + query: function (fields) { + return new data.Query(this, fields); + }, + /** + * 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 + */ + domain: function (domain) { + if (!domain) { return this._domain; } + return new data.CompoundDomain(this._domain, domain); + }, + /** + * Fetches the combination of the user's context and the domain context, + * combined with the provided context if any + * + * @param {Object} [context] to combine with the model's internal context + * @returns {web.Context} The union of the user's context and the model's internal context, as well as the provided context if any. In that order. + */ + context: function (context) { + return new Context(session.user_context, this._context, context || {}); + }, + /** + * Call a method (over RPC) on the bound OpenERP model. + * + * @param {String} method name of the method to call + * @param {Array} [args] positional arguments + * @param {Object} [kwargs] keyword arguments + * @param {Object} [options] additional options for the rpc() method + * @returns {jQuery.Deferred<>} call result + */ + call: function (method, args, kwargs, options) { + args = args || []; + kwargs = kwargs || {}; + if (!_.isArray(args)) { + // call(method, kwargs) + kwargs = args; + args = []; + } + pyeval.ensure_evaluated(args, kwargs); + var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method; + return session.rpc(call_kw, { + model: this.name, + method: method, + args: args, + kwargs: kwargs + }, options); + }, + call_button: function (method, args) { + pyeval.ensure_evaluated(args, {}); + return session.rpc('/web/dataset/call_button', { + model: this.name, + method: method, + // Should not be necessary anymore. Integrate remote in this? + domain_id: null, + context_id: args.length - 1, + args: args || [] + }); + }, +}); + + +return data; + }); diff --git a/addons/web/static/src/js/_deprecated/data_model.js b/addons/web/static/src/js/_deprecated/data_model.js deleted file mode 100644 index 3bae06a3f4c..00000000000 --- a/addons/web/static/src/js/_deprecated/data_model.js +++ /dev/null @@ -1,81 +0,0 @@ -odoo.define('web.DataModel', function (require) { -"use strict"; - -var Context = require('web.Context'); -var data = require('web.data'); -var Model = require('web.Model'); -var pyeval = require('web.pyeval'); -var session = require('web.session'); - -Model.include({ - /** - * Fetches a Query instance bound to this model, for searching - * - * @param {Array<String>} [fields] fields to ultimately fetch during the search - * @returns {instance.web.Query} - */ - query: function (fields) { - return new data.Query(this, fields); - }, - /** - * 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 - */ - domain: function (domain) { - if (!domain) { return this._domain; } - return new data.CompoundDomain(this._domain, domain); - }, - /** - * Fetches the combination of the user's context and the domain context, - * combined with the provided context if any - * - * @param {Object} [context] to combine with the model's internal context - * @returns {web.Context} The union of the user's context and the model's internal context, as well as the provided context if any. In that order. - */ - context: function (context) { - return new Context(session.user_context, this._context, context || {}); - }, - /** - * Call a method (over RPC) on the bound OpenERP model. - * - * @param {String} method name of the method to call - * @param {Array} [args] positional arguments - * @param {Object} [kwargs] keyword arguments - * @param {Object} [options] additional options for the rpc() method - * @returns {jQuery.Deferred<>} call result - */ - call: function (method, args, kwargs, options) { - args = args || []; - kwargs = kwargs || {}; - if (!_.isArray(args)) { - // call(method, kwargs) - kwargs = args; - args = []; - } - pyeval.ensure_evaluated(args, kwargs); - var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method; - return session.rpc(call_kw, { - model: this.name, - method: method, - args: args, - kwargs: kwargs - }, options); - }, - call_button: function (method, args) { - pyeval.ensure_evaluated(args, {}); - return session.rpc('/web/dataset/call_button', { - model: this.name, - method: method, - // Should not be necessary anymore. Integrate remote in this? - domain_id: null, - context_id: args.length - 1, - args: args || [] - }); - }, - -}); - -return Model; -}); diff --git a/addons/web/static/src/js/_deprecated/model.js b/addons/web/static/src/js/_deprecated/model.js deleted file mode 100644 index 377ff1a1757..00000000000 --- a/addons/web/static/src/js/_deprecated/model.js +++ /dev/null @@ -1,60 +0,0 @@ -odoo.define('web.Model', function (require) { -"use strict"; - -var Class = require('web.Class'); -var session = require('web.session'); - -var Model = Class.extend({ - /** - new openerp.web.Model(name[, context[, domain]]) - - @constructs instance.web.Model - @extends instance.web.Class - - @param {String} name name of the OpenERP model this object is bound to - @param {Object} [context] - @param {Array} [domain] - */ - init: function(name, context, domain) { - this.name = name; - this._context = context || {}; - this._domain = domain || []; - }, - /** - * @deprecated does not allow to specify kwargs, directly use call() instead - */ - get_func: function (method_name) { - var self = this; - return function () { - return self.call(method_name, _.toArray(arguments)); - }; - }, - /** - * Call a method (over RPC) on the bound OpenERP model. - * - * @param {String} method name of the method to call - * @param {Array} [args] positional arguments - * @param {Object} [kwargs] keyword arguments - * @param {Object} [options] additional options for the rpc() method - * @returns {jQuery.Deferred<>} call result - */ - call: function (method, args, kwargs, options) { - args = args || []; - kwargs = kwargs || {}; - if (!_.isArray(args)) { - // call(method, kwargs) - kwargs = args; - args = []; - } - var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method; - return session.rpc(call_kw, { - model: this.name, - method: method, - args: args, - kwargs: kwargs - }, options); - }, -}); - -return Model; -}); diff --git a/addons/web/static/src/js/apps.js b/addons/web/static/src/js/apps.js index 8c5135f3b7b..a1f85d6ae31 100644 --- a/addons/web/static/src/js/apps.js +++ b/addons/web/static/src/js/apps.js @@ -3,7 +3,6 @@ odoo.define('web.Apps', function (require) { var core = require('web.core'); var framework = require('web.framework'); -var Model = require('web.DataModel'); var session = require('web.session'); var Widget = require('web.Widget'); @@ -41,21 +40,22 @@ var Apps = Widget.extend({ if (apps_client) { return check_client_available(apps_client); } else { - var Mod = new Model('ir.module.module'); - return Mod.call('get_apps_server').then(function(u) { - var link = $(_.str.sprintf('<a href="%s"></a>', u))[0]; - var host = _.str.sprintf('%s//%s', link.protocol, link.host); - var dbname = link.pathname; - if (dbname[0] === '/') { - dbname = dbname.substr(1); - } - var client = { - origin: host, - dbname: dbname - }; - apps_client = client; - return check_client_available(client); - }); + return this.rpc('ir.module.module', 'get_apps_server') + .exec() + .then(function(u) { + var link = $(_.str.sprintf('<a href="%s"></a>', u))[0]; + var host = _.str.sprintf('%s//%s', link.protocol, link.host); + var dbname = link.pathname; + if (dbname[0] === '/') { + dbname = dbname.substr(1); + } + var client = { + origin: host, + dbname: dbname + }; + apps_client = client; + return check_client_available(client); + }); } }, @@ -90,11 +90,13 @@ var Apps = Widget.extend({ }); }, 'Model': function(m) { - var M = new Model(m.model); - M[m.method].apply(M, m.args).then(function(r) { - var w = self.$ifr[0].contentWindow; - w.postMessage({id: m.id, result: r}, client.origin); - }); + return this.rpc(m.model, m.method) + .args(m.args) + .exec() + .then(function(r) { + var w = self.$ifr[0].contentWindow; + w.postMessage({id: m.id, result: r}, client.origin); + }); }, }; // console.log(e.data); diff --git a/addons/web/static/src/js/chrome/search_inputs.js b/addons/web/static/src/js/chrome/search_inputs.js index a5ff4401006..df5abe22a93 100644 --- a/addons/web/static/src/js/chrome/search_inputs.js +++ b/addons/web/static/src/js/chrome/search_inputs.js @@ -5,7 +5,6 @@ var Context = require('web.Context'); var core = require('web.core'); var data = require('web.data'); var field_utils = require('web.field_utils'); -var Model = require('web.DataModel'); var pyeval = require('web.pyeval'); var time = require('web.time'); var utils = require('web.utils'); @@ -394,7 +393,6 @@ var ManyToOneField = CharField.extend({ default_operator: {}, init: function (view_section, field, parent) { this._super(view_section, field, parent); - this.model = new Model(this.attrs.relation); this.searchview = parent; }, @@ -428,20 +426,23 @@ var ManyToOneField = CharField.extend({ args = []; } } - return this.model.call('name_search', [], { - name: needle, - args: args, - limit: 8, - context: context - }).then(function (results) { - if (_.isEmpty(results)) { return null; } - return _(results).map(function (result) { - return { - label: _.escape(result[1]), - facet: facet_from(self, result) - }; + return this.rpc(this.attrs.relation, 'name_search') + .kwargs({ + name: needle, + args: args, + limit: 8, + context: context + }) + .exec() + .then(function (results) { + if (_.isEmpty(results)) { return null; } + return _(results).map(function (result) { + return { + label: _.escape(result[1]), + facet: facet_from(self, result) + }; + }); }); - }); }, facet_for: function (value) { var self = this; @@ -456,10 +457,15 @@ var ManyToOneField = CharField.extend({ value = value[0]; } var context = pyeval.eval('contexts', [this.searchview.dataset.get_context()]); - return this.model.call('name_get', [value], {context: context}).then(function (names) { - if (_(names).isEmpty()) { return null; } - return facet_from(self, names[0]); - }); + + return this.rpc(this.attrs.relation, 'name_get') + .args([value]) + .withContext(context) + .exec() + .then(function (names) { + if (_(names).isEmpty()) { return null; } + return facet_from(self, names[0]); + }); }, value_from: function (facetValue) { return facetValue.get('label'); diff --git a/addons/web/static/src/js/chrome/web_client.js b/addons/web/static/src/js/chrome/web_client.js index 17993c73638..f668676673c 100644 --- a/addons/web/static/src/js/chrome/web_client.js +++ b/addons/web/static/src/js/chrome/web_client.js @@ -6,7 +6,6 @@ var core = require('web.core'); var data_manager = require('web.data_manager'); var framework = require('web.framework'); var Menu = require('web.Menu'); -var Model = require('web.DataModel'); var session = require('web.session'); var SystrayMenu = require('web.SystrayMenu'); var UserMenu = require('web.UserMenu'); @@ -66,25 +65,31 @@ return AbstractWebClient.extend({ logo_edit: function(ev) { var self = this; ev.preventDefault(); - self.alive(new Model("res.users").call("read", [[session.uid], ["company_id"]])).then(function(data) { - self.rpc("/web/action/load", { action_id: "base.action_res_company_form" }).done(function(result) { - result.res_id = data[0].company_id[0]; - result.target = "new"; - result.views = [[false, 'form']]; - result.flags = { - action_buttons: true, - headless: true, - }; - self.action_manager.do_action(result).then(function () { - var form = self.action_manager.dialog_widget.views.form.controller; - form.on("on_button_cancel", self.action_manager, self.action_manager.dialog_stop); - form.on('record_saved', self, function() { - self.action_manager.dialog_stop(); - self.update_logo(); + this.rpc("res.users", "read") + .args([[session.uid], ["company_id"]]) + .exec() + .then(function(data) { + self.rpc("/web/action/load") + .kwargs({ action_id: "base.action_res_company_form" }) + .exec() + .done(function(result) { + result.res_id = data[0].company_id[0]; + result.target = "new"; + result.views = [[false, 'form']]; + result.flags = { + action_buttons: true, + headless: true, + }; + self.action_manager.do_action(result).then(function () { + var form = self.action_manager.dialog_widget.views.form.controller; + form.on("on_button_cancel", self.action_manager, self.action_manager.dialog_stop); + form.on('record_saved', self, function() { + self.action_manager.dialog_stop(); + self.update_logo(); + }); + }); }); - }); }); - }); return false; }, bind_hashchange: function() { @@ -94,18 +99,21 @@ return AbstractWebClient.extend({ var state = $.bbq.getState(true); if (_.isEmpty(state) || state.action === "login") { self.menu.is_bound.done(function() { - new Model("res.users").call("read", [[session.uid], ["action_id"]]).done(function(result) { - var data = result[0]; - if(data.action_id) { - self.action_manager.do_action(data.action_id[0]); - self.menu.open_action(data.action_id[0]); - } else { - var first_menu_id = self.menu.$el.find("a:first").data("menu"); - if(first_menu_id) { - self.menu.menu_click(first_menu_id); + self.rpc("res.users", "read") + .args([[session.uid], ["action_id"]]) + .exec() + .done(function(result) { + var data = result[0]; + if(data.action_id) { + self.action_manager.do_action(data.action_id[0]); + self.menu.open_action(data.action_id[0]); + } else { + var first_menu_id = self.menu.$el.find("a:first").data("menu"); + if(first_menu_id) { + self.menu.menu_click(first_menu_id); + } } - } - }); + }); }); } else { $(window).trigger('hashchange'); diff --git a/addons/web/static/src/js/core/mixins.js b/addons/web/static/src/js/core/mixins.js index 6f640cb3300..1a72beefafd 100644 --- a/addons/web/static/src/js/core/mixins.js +++ b/addons/web/static/src/js/core/mixins.js @@ -2,6 +2,7 @@ odoo.define('web.mixins', function (require) { "use strict"; var Class = require('web.Class'); +var rpc = require('web.rpc'); var utils = require('web.utils'); var AbstractService = require('web.AbstractService'); @@ -237,10 +238,50 @@ var Events = Class.extend({ */ var EventDispatcherMixin = _.extend({}, ParentedMixin, { __eventDispatcherMixin: true, + custom_events: {}, init: function () { ParentedMixin.init.call(this); this.__edispatcherEvents = new Events(); this.__edispatcherRegisteredEvents = []; + this._delegateCustomEvents(); + }, + /** + * Proxies a method of the object, in order to keep the right ``this`` on + * method invocations. + * + * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and + * even more so to ``jQuery.proxy`` with a fundamental difference: its + * resolution of the method being called is lazy, meaning it will use the + * method as it is when the proxy is called, not when the proxy is created. + * + * Other methods will fix the bound method to what it is when creating the + * binding/proxy, which is fine in most javascript code but problematic in + * OpenERP Web where developers may want to replace existing callbacks with + * theirs. + * + * The semantics of this precisely replace closing over the method call. + * + * @param {String|Function} method function or name of the method to invoke + * @returns {Function} proxied method + */ + proxy: function (method) { + var self = this; + return function () { + var fn = (typeof method === 'string') ? self[method] : method; + if (fn === void 0) { + throw new Error("Couldn't find method '" + method + "' in widget " + self); + } + return fn.apply(self, arguments); + }; + }, + _delegateCustomEvents: function () { + if (_.isEmpty(this.custom_events)) { return; } + for (var key in this.custom_events) { + if (!this.custom_events.hasOwnProperty(key)) { continue; } + + var method = this.proxy(this.custom_events[key]); + this.on(key, this, method); + } }, on: function (events, dest, func) { var self = this; @@ -373,6 +414,21 @@ var ServicesMixin = { }); return result; }, + /** + * Build a RPC query + * + * @param {string} arg1 either a route or a model + * @param {string} method if a model is given, this argument is a method + * @returns {RPCBuilder} + */ + rpc: function (arg1, method) { + return rpc.query({ + method: method, + model: method && arg1, + parent: this, + route: !method && arg1, + }); + }, // AJAX calls performRPC: function (route, args, options) { return this.call('ajax', 'rpc', route, args || {}, options || {}); @@ -452,6 +508,7 @@ var ServiceProvider = { var service = new Service(); self.services[service.name] = service; }); + this.custom_events = _.clone(this.custom_events); this.custom_events.call_service = this._call_service.bind(this); }, _call_service: function (event) { diff --git a/addons/web/static/src/js/core/widget.js b/addons/web/static/src/js/core/widget.js index 221a127c9dd..84a7c48c20e 100644 --- a/addons/web/static/src/js/core/widget.js +++ b/addons/web/static/src/js/core/widget.js @@ -62,7 +62,6 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, mixins.ServicesMixin, { className: null, attributes: {}, events: {}, - custom_events: {}, /** * The name of the QWeb template that will be used for rendering. Must be * redefined in subclasses or the default render() method can not be used. @@ -93,7 +92,6 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, mixins.ServicesMixin, { } // FIXME: this should not be this.setElement(this._make_descriptive()); - this.delegateCustomEvents(); }, /** * Method called between init and start. Performs asynchronous calls required by start. @@ -315,15 +313,6 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, mixins.ServicesMixin, { } } }, - delegateCustomEvents: function () { - if (_.isEmpty(this.custom_events)) { return; } - for (var key in this.custom_events) { - if (!this.custom_events.hasOwnProperty(key)) { continue; } - - var method = this.proxy(this.custom_events[key]); - this.on(key, this, method); - } - }, undelegateEvents: function () { this.$el.off('.widget_events'); }, @@ -361,35 +350,6 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, mixins.ServicesMixin, { this.$el.hasClass('o_hidden') ? this.do_show() : this.do_hide(); } }, - /** - * Proxies a method of the object, in order to keep the right ``this`` on - * method invocations. - * - * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and - * even more so to ``jQuery.proxy`` with a fundamental difference: its - * resolution of the method being called is lazy, meaning it will use the - * method as it is when the proxy is called, not when the proxy is created. - * - * Other methods will fix the bound method to what it is when creating the - * binding/proxy, which is fine in most javascript code but problematic in - * OpenERP Web where developers may want to replace existing callbacks with - * theirs. - * - * The semantics of this precisely replace closing over the method call. - * - * @param {String|Function} method function or name of the method to invoke - * @returns {Function} proxied method - */ - proxy: function (method) { - var self = this; - return function () { - var fn = (typeof method === 'string') ? self[method] : method; - if (fn === void 0) { - throw new Error("Couldn't find method '" + method + "' in widget " + self); - } - return fn.apply(self, arguments); - }; - }, }); return Widget; diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 76275a11d3e..6b2cd4d6754 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1299,19 +1299,17 @@ var FieldDomain = AbstractField.extend({ var self = this; return def.then(function () { var context = {}; // FIXME ? - return self.performModelRPC(self.domainModel, "search_count", [ - domain, - context - ]).then( - function (data) { + return self.rpc(self.domainModel, "search_count") + .args([domain,context]) + .exec() + .then(function (data) { self._isValidForModel = true; return data; - }, - function (error, e) { + }, function (error, e) { e.preventDefault(); self._isValidForModel = false; - } - ).always(self._replaceContent.bind(self)); + }) + .always(self._replaceContent.bind(self)); }); }, /** diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index b3b3a26c437..c5750c86735 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -227,14 +227,14 @@ var FieldMany2One = AbstractRelationalField.extend({ var self = this; var slowCreate = this._searchCreatePopup.bind(this, "form", false, this._createContext(name)); if (this.nodeOptions.quick_create) { - this.performModelRPC(this.field.relation, "name_create", [name]).then( - function (result) { + this.rpc(this.field.relation, "name_create") + .args([name]) + .exec() + .then(function (result) { if (self.mode === "edit") { self.reinitialize({id: result[0], display_name: result[1]}); } - }, - slowCreate - ); + }, slowCreate); } else { slowCreate(); } diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js index 7188f7d9126..e86f1b9bb39 100644 --- a/addons/web/static/src/js/services/data_manager.js +++ b/addons/web/static/src/js/services/data_manager.js @@ -2,13 +2,11 @@ odoo.define('web.DataManager', function (require) { "use strict"; var core = require('web.core'); -var Model = require('web.DataModel'); var session = require('web.session'); var utils = require('web.utils'); return core.Class.extend({ init: function () { - this.Filters = new Model('ir.filters'); this._init_cache(); }, @@ -128,8 +126,13 @@ return core.Class.extend({ load_filters: function (dataset, action_id) { var key = this._gen_key(dataset.model, action_id); if (!this._cache.filters[key]) { - this._cache.filters[key] = this.Filters.call('get_filters', [dataset.model, action_id], { - context: dataset.get_context(), + this._cache.filters[key] = session.rpc('/web/dataset/call_kw/ir.filters/get_filters', { + args: [dataset.model, action_id], + kwargs: { + context: dataset.get_context(), + }, + model: 'ir.filters', + method: 'get_filters', }).fail(this._invalidate.bind(this, this._cache.filters, key)); } return this._cache.filters[key]; @@ -143,8 +146,11 @@ return core.Class.extend({ */ create_filter: function (filter) { var self = this; - return this.Filters - .call('create_or_replace', [filter]) + return session.rpc('/web/dataset/call_kw/ir.filters/create_or_replace', { + args: [filter], + model: 'ir.filters', + method: 'create_or_replace', + }) .then(function (filter_id) { var key = [ filter.model_id, @@ -163,8 +169,11 @@ return core.Class.extend({ */ delete_filter: function (filter) { var self = this; - return this.Filters - .call('unlink', [filter.id]) + return session.rpc('/web/dataset/call_kw/ir.filters/unlink', { + args: [filter.id], + model: 'ir.filters', + method: 'unlink', + }) .then(function () { self._cache.filters = {}; // invalidate cache }); 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 a66e3a9d3ab..5c0facecef1 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -182,7 +182,7 @@ var BasicModel = AbstractModel.extend({ }); }, /** - * Delete a list of records, then, if a parent_id is given, reload the + * Delete a list of records, then, if a parentID is given, reload the * parent. * * @todo we should remove the deleted records from the localData @@ -193,24 +193,26 @@ var BasicModel = AbstractModel.extend({ * @param {string[]} recordIds list of local resources ids. They should all * be of type 'record', and of the same model * @param {string} modelName - * @param {string} parent_id + * @param {string} parentID * @returns {Deferred} */ - deleteRecords: function (recordIds, modelName, parent_id) { + deleteRecords: function (recordIds, modelName, parentID) { var self = this; var records = _.map(recordIds, function (id) { return self.localData[id]; }); - return this.performModelRPC(modelName, 'unlink', [_.pluck(records, 'res_id')], { - context: session.user_context, // todo: combine with view context - }).then(function () { - _.each(records, function (record) { - record.res_ids.splice(record.offset, 1)[0]; - record.res_id = record.res_ids[record.offset]; - record.count--; + return this.rpc(modelName, 'unlink') + .args([_.pluck(records, 'res_id')]) + .withContext(session.user_context) // todo: combine with view context + .exec() + .then(function () { + _.each(records, function (record) { + record.res_ids.splice(record.offset, 1)[0]; + record.res_id = record.res_ids[record.offset]; + record.count--; + }); + if (parentID) { + return self.reload(parentID); + } }); - if (parent_id) { - return self.reload(parent_id); - } - }); }, /** * Discard all changes in a local resource. Basically, it removes @@ -233,20 +235,22 @@ var BasicModel = AbstractModel.extend({ duplicateRecord: function (recordID) { var self = this; var record = this.localData[recordID]; - return this.performModelRPC(record.model, 'copy', [record.data.id], { - context: this._getContext(record), - }).then(function (res_id) { - var index = record.res_ids.indexOf(record.res_id); - record.res_ids.splice(index + 1, 0, res_id); - return self.load({ - fieldAttrs: record.fieldAttrs, - fieldNames: record.fieldNames, - fields: record.fields, - modelName: record.model, - res_id: res_id, - res_ids: record.res_ids.slice(0), + return this.rpc(record.model, 'copy') + .args([record.data.id]) + .withContext(this._getContext(record)) + .exec() + .then(function (res_id) { + var index = record.res_ids.indexOf(record.res_id); + record.res_ids.splice(index + 1, 0, res_id); + return self.load({ + fieldAttrs: record.fieldAttrs, + fieldNames: record.fieldNames, + fields: record.fields, + modelName: record.model, + res_id: res_id, + res_ids: record.res_ids.slice(0), + }); }); - }); }, /** * The get method first argument is the handle returned by the load method. @@ -442,156 +446,158 @@ var BasicModel = AbstractModel.extend({ var self = this; var fields_key = _.without(params.fieldNames, '__last_update'); - return this.performModelRPC(modelName, 'default_get', [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; + return this.rpc(modelName, 'default_get') + .args([fields_key]) + .withContext(params.context) + .exec() + .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, - fieldAttrs: params.fieldAttrs, - context: params.context, - parentID: params.parentID, - relationField: params.relationField, - res_ids: params.res_ids, - }); + var data = {}; + var record = self._makeDataPoint({ + modelName: modelName, + data: data, + fields: params.fields, + fieldNames: params.fieldNames, + fieldAttrs: params.fieldAttrs, + 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 attrs = record.fieldAttrs[name]; - var x2manyList = self._makeDataPoint({ - context: record.context, - fieldAttrs: field.fieldAttrs, - fields: field.relatedFields, - limit: field.limit, - modelName: field.relation, - parentID: record.id, - rawContext: attrs && attrs.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 + 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 attrs = record.fieldAttrs[name]; + var x2manyList = self._makeDataPoint({ + context: record.context, + fieldAttrs: field.fieldAttrs, + fields: field.relatedFields, + limit: field.limit, + modelName: field.relation, + parentID: record.id, + rawContext: attrs && attrs.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, + fields: field.relatedFields, + fieldAttrs: field.fieldAttrs, + }); + 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, fields: field.relatedFields, fieldAttrs: field.fieldAttrs, + res_id: value, }); - 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 (!x2manyList._changes) { + x2manyList._changes = []; } + x2manyList._changes.push(r.id); } - if (value[0] === 6) { - // REPLACE_WITH - x2manyList._changes = []; + }); + + // 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 { - // value is an id - r = self._makeDataPoint({ - modelName: x2manyList.model, - context: x2manyList.context, - fields: field.relatedFields, - fieldAttrs: field.fieldAttrs, - res_id: value, - }); - if (!x2manyList._changes) { - x2manyList._changes = []; - } - x2manyList._changes.push(r.id); + return $.when(); } + }) + .then(function () { + return self._fetchRelationalData(record); + }) + .then(function () { + return self._postprocess(record); + }) + .then(function () { + return record.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 @@ -808,21 +814,23 @@ var BasicModel = AbstractModel.extend({ // in the case of a write, only perform the RPC if there are changes to save if (method === 'create' || Object.keys(changes).length) { var args = method === 'write' ? [[record.data.id], changes] : [changes]; - return self.performModelRPC(record.model, method, args, { - context: session.user_context, // todo: combine with view context - }).then(function (id) { - if (method === 'create') { - record.res_id = id; // create returns an id, write returns a boolean - record.data.id = id; - record.offset = record.res_ids.length; - record.res_ids.push(id); - record.count++; - } - // erase changes as they have been applied - record._changes = {}; + return self.rpc(record.model, method) + .args(args) + .withContext(session.user_context) // todo: combine with view context + .exec() + .then(function (id) { + if (method === 'create') { + record.res_id = id; // create returns an id, write returns a boolean + record.data.id = id; + record.offset = record.res_ids.length; + record.res_ids.push(id); + record.count++; + } + // erase changes as they have been applied + record._changes = {}; - return shouldReload ? self._fetchRecord(record) : false; - }); + return shouldReload ? self._fetchRecord(record) : false; + }); } else { return $.when(record_id); } @@ -881,7 +889,7 @@ var BasicModel = AbstractModel.extend({ * @param {Array} recordIDs local ids of the records to (un)archive * @param {boolean} value false to archive, true to unarchive (value of the active field) * @param {string} parentID id of the parent resource to reload - * @returns {Deferred -> string} resolves to the parent id + * @returns {Deferred<string>} resolves to the parent id */ toggleActive: function (recordIDs, value, parentID) { var self = this; @@ -889,8 +897,9 @@ var BasicModel = AbstractModel.extend({ var resIDs = _.map(recordIDs, function (recordID) { return self.localData[recordID].res_id; }); - return this - .performModelRPC(parent.model, 'write', [resIDs, { active: value }]) + return this.rpc(parent.model, 'write') + .args([resIDs, { active: value }]) + .exec() .then(this.reload.bind(this, parentID)); }, @@ -960,11 +969,13 @@ var BasicModel = AbstractModel.extend({ var def; if (!('display_name' in rel_data)) { var field = record.fields[fieldName]; - def = this.performModelRPC(field.relation, 'name_get', [data.id], { - context: record.context, - }).then(function (result) { - rel_data.display_name = result[0][1]; - }); + def = this.rpc(field.relation, 'name_get') + .args([data.id]) + .withContext(record.context) + .exec() + .then(function (result) { + rel_data.display_name = result[0][1]; + }); } return $.when(def).then(function () { var rec = self._makeDataPoint({ @@ -1010,96 +1021,98 @@ var BasicModel = AbstractModel.extend({ fields = fields[0]; } return this.mutex.exec(function () { - var args = [idList, currentData, fields, onchange_spec, context]; - return self.performModelRPC(record.model, 'onchange', args, {}).then(function (result) { - if (result.warning) { - self.trigger_up('warning', { - message: result.warning.message, - title: result.warning.title, - type: 'dialog', - }); - } - var defs = []; - _.each(result.value, function (val, name) { - var field = record.fields[name]; - if (!field) { return; } // ignore changes of unknown fields - - var rec; - if (field.type === 'many2one' ) { - // in some case, the value returned by the onchange can - // be false (no value), so we need to avoid creating a - // local record for that. - // FIXME: shouldn't we erase the value in that case? - if (val) { - // when the value isn't false, it can be either - // an array [id, display_name] or just an id. - var data = _.isArray(val) ? - {id: val[0], display_name: val[1]} : - {id: val}; - rec = self._makeDataPoint({ - context: context, - data: data, - modelName: field.relation, - }); - record._changes[name] = rec.id; - } - } else if (field.type === 'one2many' || field.type === 'many2many') { - record._changes = record._changes || {}; - var listId = record._changes[name] || record.data[name]; - var list = self.localData[listId]; - _.each(val, function (command) { - if (command[0] === 0 || command[0] === 1) { - // CREATE or UPDATE - var params = { + return self.rpc(record.model, 'onchange') + .args([idList, currentData, fields, onchange_spec, context]) + .exec() + .then(function (result) { + if (result.warning) { + self.trigger_up('warning', { + message: result.warning.message, + title: result.warning.title, + type: 'dialog', + }); + } + var defs = []; + _.each(result.value, function (val, name) { + var field = record.fields[name]; + if (!field) { return; } // ignore changes of unknown fields + + var rec; + if (field.type === 'many2one' ) { + // in some case, the value returned by the onchange can + // be false (no value), so we need to avoid creating a + // local record for that. + // FIXME: shouldn't we erase the value in that case? + if (val) { + // when the value isn't false, it can be either + // an array [id, display_name] or just an id. + var data = _.isArray(val) ? + {id: val[0], display_name: val[1]} : + {id: val}; + rec = self._makeDataPoint({ context: context, - fields: list.fields, - fieldAttrs: list.fieldAttrs, - modelName: list.model, - fieldNames: _.keys(command[2]), - }; - if (command[0] === 1) { - params.res_id = command[1]; - } - rec = self._makeDataPoint(params); - var data = command[2]; - _.each(rec.fieldNames, function (name) { - var field = rec.fields[name]; - if (field.type === 'many2one') { - var r = self._makeDataPoint({ - modelName: field.relation, - data: { - id: data[name][0], - display_name: data[name][1], - }, - }); - data[name] = r.id; - } + data: data, + modelName: field.relation, }); - rec._changes = data; - for (var f in rec._changes) { - rec.data[f] = null; - } - list._changes.push(rec.id); - } - if (command[0] === 5) { - // DELETE ALL - list._changes = []; + record._changes[name] = rec.id; } - }); - } else if (field.type === 'date') { - // process data: convert into a moment instance - record._changes[name] = fieldUtils.parse.date(val); - } else if (field.type === 'datetime') { - // process datetime: convert into a moment instance - record._changes[name] = fieldUtils.parse.datetime(val); - } else { - record._changes[name] = val; - } - }); - return $.when.apply($, defs).then(function () { - return result; + } else if (field.type === 'one2many' || field.type === 'many2many') { + record._changes = record._changes || {}; + var listId = record._changes[name] || record.data[name]; + var list = self.localData[listId]; + _.each(val, function (command) { + if (command[0] === 0 || command[0] === 1) { + // CREATE or UPDATE + var params = { + context: context, + fields: list.fields, + fieldAttrs: list.fieldAttrs, + modelName: list.model, + fieldNames: _.keys(command[2]), + }; + if (command[0] === 1) { + params.res_id = command[1]; + } + rec = self._makeDataPoint(params); + var data = command[2]; + _.each(rec.fieldNames, function (name) { + var field = rec.fields[name]; + if (field.type === 'many2one') { + var r = self._makeDataPoint({ + modelName: field.relation, + data: { + id: data[name][0], + display_name: data[name][1], + }, + }); + data[name] = r.id; + } + }); + rec._changes = data; + for (var f in rec._changes) { + rec.data[f] = null; + } + list._changes.push(rec.id); + } + if (command[0] === 5) { + // DELETE ALL + list._changes = []; + } + }); + } else if (field.type === 'date') { + // process data: convert into a moment instance + record._changes[name] = fieldUtils.parse.date(val); + } else if (field.type === 'datetime') { + // process datetime: convert into a moment instance + record._changes[name] = fieldUtils.parse.datetime(val); + } else { + record._changes[name] = val; + } + }); + return $.when.apply($, defs).then(function () { + return result; + }); }); - }); }); }, /** @@ -1158,8 +1171,9 @@ 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 def = this - .performModelRPC(list.model, 'read', [_.pluck(data, 'id'), list.fieldNames], {}) + var def = this.rpc(list.model, 'read') + .args([_.pluck(data, 'id'), list.fieldNames]) + .exec() .then(function (records) { _.each(records, function (record) { list_records[record.id].data = record; @@ -1241,14 +1255,16 @@ var BasicModel = AbstractModel.extend({ _fetchMany2OneGroup: function (group) { var ids = _.uniq(_.pluck(group, 'res_id')); - return this.performModelRPC(group[0].model, 'name_get', [ids], { - context: group[0].context, - }).then(function (name_gets) { - _.each(group, function (record) { - var nameGet = _.find(name_gets, function (n) { return n[0] === record.res_id;}); - record.data.display_name = nameGet[1]; + return this.rpc(group[0].model, 'name_get') + .args([ids]) + .withContext(group[0].context) + .exec() + .then(function (name_gets) { + _.each(group, function (record) { + var nameGet = _.find(name_gets, function (n) { return n[0] === record.res_id;}); + record.data.display_name = nameGet[1]; + }); }); - }); }, _fetchNameGets: function (list, fieldName) { var self = this; @@ -1263,13 +1279,15 @@ var BasicModel = AbstractModel.extend({ model = many2oneRecord.model; return many2oneRecord.res_id; }); - return this.performModelRPC(model, 'name_get', [ids], { - context: list.context, - }).then(function (name_gets) { - for (var i = 0; i < name_gets.length; i++) { - records[i].data.display_name = name_gets[i][1]; - } - }); + return this.rpc(model, 'name_get') + .args([ids]) + .withContext(list.context) + .exec() + .then(function (name_gets) { + for (var i = 0; i < name_gets.length; i++) { + records[i].data.display_name = name_gets[i][1]; + } + }); }, /** * For a given resource of type 'record', fetch all data. @@ -1283,45 +1301,47 @@ var BasicModel = AbstractModel.extend({ var self = this; fieldNames = fieldNames || _.uniq(record.fieldNames.concat(['display_name'])); var oldData = record.data; - return this.performModelRPC(record.model, 'read', [record.res_id, fieldNames], { - // context: { 'bin_size': true }, // FIXME: when editing a subrecord in the partner form view, it tries to write the bin_size on the image field - }).then(function (result) { - result = result[0]; - record.data = _.extend({}, record.data, result); - }).then(function () { - return self._fetchX2Manys(record, oldData, fieldNames).then(function () { - return self._postprocess(record); - }); - }).then(function (record) { - _.each(fieldNames, function (name) { - var field = record.fields[name]; - var val = record.data[name]; - if (field.type === 'many2one') { - // process many2one: split [id, nameget] and create corresponding record - if (val !== false) { - // the many2one value is of the form [id, display_name] - var r = self._makeDataPoint({ - modelName: field.relation, - data: { - display_name: val[1], - id: val[0], - }, - }); - record.data[name] = r.id; - } else { - // no value for the many2one - record.data[name] = false; + return this.rpc(record.model, 'read') + .args([[record.res_id], fieldNames]) + // .withContext({ bin_size: true }) // FIXME: when editing a subrecord in the partner form view, it tries to write the bin_size on the image field + .exec() + .then(function (result) { + result = result[0]; + record.data = _.extend({}, record.data, result); + }).then(function () { + return self._fetchX2Manys(record, oldData, fieldNames).then(function () { + return self._postprocess(record); + }); + }).then(function (record) { + _.each(fieldNames, function (name) { + var field = record.fields[name]; + var val = record.data[name]; + if (field.type === 'many2one') { + // process many2one: split [id, nameget] and create corresponding record + if (val !== false) { + // the many2one value is of the form [id, display_name] + var r = self._makeDataPoint({ + modelName: field.relation, + data: { + display_name: val[1], + id: val[0], + }, + }); + record.data[name] = r.id; + } else { + // no value for the many2one + record.data[name] = false; + } + } else if (field.type === 'date') { + // process data: convert into a moment instance + record.data[name] = fieldUtils.parse.date(val); + } else if (field.type === 'datetime') { + // process datetime: convert into a moment instance + record.data[name] = fieldUtils.parse.datetime(val); } - } else if (field.type === 'date') { - // process data: convert into a moment instance - record.data[name] = fieldUtils.parse.date(val); - } else if (field.type === 'datetime') { - // process datetime: convert into a moment instance - record.data[name] = fieldUtils.parse.datetime(val); - } + }); + return record; }); - return record; - }); }, /** * This method is incorrectly named. It should be named something like @@ -1473,33 +1493,35 @@ var BasicModel = AbstractModel.extend({ } // step 2: fetch data from server - return this.performModelRPC(field.relation, 'read', [ids, _.keys(field.relatedFields)], { - context: {}, // FIXME - }).then(function (results) { - // step 3: assign values to correct datapoints - var dataPoints = _.map(results, function (result) { - return self._makeDataPoint({ - modelName: field.relation, - data: result, - fields: field.relatedFields, - fieldAttrs: field.fieldAttrs, + return this.rpc(field.relation, 'read') + .args([ids, _.keys(field.relatedFields)]) + .withContext({}) // FIXME + .exec() + .then(function (results) { + // step 3: assign values to correct datapoints + var dataPoints = _.map(results, function (result) { + return self._makeDataPoint({ + modelName: field.relation, + data: result, + fields: field.relatedFields, + fieldAttrs: field.fieldAttrs, + }); }); - }); - _.each(list.data, function (dataPoint) { - var record = self.localData[dataPoint]; - var m2mList = self.localData[record.data[fieldName]]; + _.each(list.data, function (dataPoint) { + var record = self.localData[dataPoint]; + var m2mList = self.localData[record.data[fieldName]]; - m2mList.data = []; - _.each(m2mList.res_ids, function (res_id) { - var dataPoint = _.find(dataPoints, function (d) { - return d.res_id === res_id; + m2mList.data = []; + _.each(m2mList.res_ids, function (res_id) { + var dataPoint = _.find(dataPoints, function (d) { + return d.res_id === res_id; + }); + m2mList.data.push(dataPoint.id); + m2mList.count++; }); - m2mList.data.push(dataPoint.id); - m2mList.count++; }); }); - }); }, /** * batch request for x2ms for datapoint of type list @@ -1740,46 +1762,50 @@ var BasicModel = AbstractModel.extend({ var _domain = new data.CompoundDomain(field.domain || {}).set_eval_context(field_values); var domain = pyeval.eval('domain', _domain); var fold_field = pyeval.py_eval(attrs.options || '{}').fold_field; - var fetch_status_information = self.performRPC('/web/dataset/search_read', { - model: field.relation, - fields: ['id'].concat(fold_field ? [fold_field] : []), - domain: domain, - }).then(function (result) { - var ids = _.pluck(result.records, 'id'); - return self.performModelRPC(field.relation, 'name_get', [ids], { - context: self._getContext(record, {fieldName: name}), - }).then(function (name_gets) { - _.each(result.records, function (record) { - var name_get = _.find(name_gets, function (n) { - return n[0] === record.id; + var fetch_status_information = self.rpc(field.relation, 'search_read') + .withFields(['id'].concat(fold_field ? [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; }); - record.display_name = name_get[1]; - }); - field.__status_information = result.records; }); - }); defs.push(fetch_status_information); } if (field.__always_reload) { if (record.data[name] instanceof Array) { - defs.push(self.performModelRPC(field.relation, 'name_get', [record.data[name][0]], { - context: self._getContext(record, {fieldName: name}), - }).then(function (result) { - record.data[name] = result[0]; - })); + defs.push(self.rpc(field.relation, 'name_get') + .args([record.data[name][0]]) + .withContext(self._getContext(record, {fieldName: name})) + .exec() + .then(function (result) { + record.data[name] = result[0]; + })); } } if (field.__fetch_selection && !self.many2ones[field.relation]) { - var fetchSelection = self - .performModelRPC(field.relation, 'name_search', [], {}) + var fetchSelection = self.rpc(field.relation, 'name_search') + .exec() .then(function (result) { self.many2ones[field.relation] = result; }); defs.push(fetchSelection); } if (field.__fetch_many2manys) { - var fetchMany2Manys = self - .performModelRPC(field.relation, 'name_search', [], {}) + var fetchMany2Manys = self.rpc(field.relation, 'name_search') + .exec() .then(function (result) { self.many2manys[field.relation] = result; }); @@ -1802,69 +1828,68 @@ var BasicModel = AbstractModel.extend({ _readGroup: function (list) { var self = this; var fields = _.uniq(list.fieldNames.concat(list.groupedBy)); - return this.performModelRPC(list.model, 'read_group', [], { - fields: fields, - context: list.context, - groupby: list.groupedBy, - domain: list.domain, - orderby: false, - lazy: true, - }).then(function (groups) { - var rawGroupBy = list.groupedBy[0].split(':')[0]; - var previousGroups = _.map(list.data, function (groupID) { - return self.localData[groupID]; - }); - list.data = []; - list.count = groups.length; - var defs = []; - - _.each(groups, function (group) { - var aggregateValues = {}; - _.each(group, function (value, key) { - if (_.contains(fields, key) && key !== list.groupedBy[0]) { - aggregateValues[key] = value; - } + return this.rpc(list.model, 'read_group') + .withFields(fields) + .withContext(list.context) + .groupBy(list.groupedBy) + .lazy(true) + .exec() + .then(function (groups) { + var rawGroupBy = list.groupedBy[0].split(':')[0]; + var previousGroups = _.map(list.data, function (groupID) { + return self.localData[groupID]; }); - var newGroup = self._makeDataPoint({ - modelName: list.model, - count: group[rawGroupBy + '_count'], - domain: group.__domain, - context: list.context, - fields: list.fields, - fieldNames: list.fieldNames, - fieldAttrs: list.fieldAttrs, - value: group[rawGroupBy], - aggregateValues: aggregateValues, - groupedBy: list.groupedBy.slice(1), - orderedBy: list.orderedBy, - limit: list.limit, - openGroupByDefault: list.openGroupByDefault, - type: 'list', // nested groupedBys not handled yet + list.data = []; + list.count = groups.length; + var defs = []; + + _.each(groups, function (group) { + var aggregateValues = {}; + _.each(group, function (value, key) { + if (_.contains(fields, key) && key !== list.groupedBy[0]) { + aggregateValues[key] = value; + } + }); + var newGroup = self._makeDataPoint({ + modelName: list.model, + count: group[rawGroupBy + '_count'], + domain: group.__domain, + context: list.context, + fields: list.fields, + fieldNames: list.fieldNames, + fieldAttrs: list.fieldAttrs, + value: group[rawGroupBy], + aggregateValues: aggregateValues, + groupedBy: list.groupedBy.slice(1), + orderedBy: list.orderedBy, + limit: list.limit, + openGroupByDefault: list.openGroupByDefault, + type: 'list', // nested groupedBys not handled yet + }); + list.data.push(newGroup.id); + var old_group = _.find(previousGroups, function (g) { + return g.res_id === newGroup.res_id && g.value === newGroup.value; + }); + if (old_group) { + newGroup.isOpen = old_group.isOpen; + } else if (!newGroup.openGroupByDefault) { + newGroup.isOpen = false; + } else { + newGroup.isOpen = '__fold' in group ? !group.__fold : true; + } + if (newGroup.isOpen && newGroup.count > 0) { + defs.push(self._fetchUngroupedList(newGroup)); + } }); - list.data.push(newGroup.id); - var old_group = _.find(previousGroups, function (g) { - return g.res_id === newGroup.res_id && g.value === newGroup.value; + return $.when.apply($, defs).then(function () { + // generate the res_ids of the main list, being the concatenation + // of the fetched res_ids in each group + list.res_ids = _.flatten(_.map(arguments, function (group) { + return group ? group.res_ids : []; + })); + return list; }); - if (old_group) { - newGroup.isOpen = old_group.isOpen; - } else if (!newGroup.openGroupByDefault) { - newGroup.isOpen = false; - } else { - newGroup.isOpen = '__fold' in group ? !group.__fold : true; - } - if (newGroup.isOpen && newGroup.count > 0) { - defs.push(self._fetchUngroupedList(newGroup)); - } }); - return $.when.apply($, defs).then(function () { - // generate the res_ids of the main list, being the concatenation - // of the fetched res_ids in each group - list.res_ids = _.flatten(_.map(arguments, function (group) { - return group ? group.res_ids : []; - })); - return list; - }); - }); }, /** * For 'static' list, such as one2manys in a form view, we can do a /read @@ -1887,9 +1912,10 @@ var BasicModel = AbstractModel.extend({ } } if (missingIds.length) { - def = this.performModelRPC(list.model, 'read', [missingIds, list.fieldNames], { - context: {}, // FIXME - }); + def = this.rpc(list.model, 'read') + .args([missingIds, list.fieldNames]) + .withContext({}) // FIXME + .exec(); } else { def = $.when(); } @@ -1946,67 +1972,52 @@ var BasicModel = AbstractModel.extend({ */ _searchReadUngroupedList: function (list) { var self = this; - return this.performRPC('/web/dataset/search_read', { - model: list.model, - fields: list.fieldNames, - domain: list.domain || [], - offset: list.offset, - limit: list.limit, - sort: this._serializeSort(list.orderedBy), - }).then(function (result) { - list.count = result.length; - var data = _.map(result.records, function (record) { - var dataPoint = self._makeDataPoint({ - data: record, - fields: list.fields, - fieldNames: list.fieldNames, - fieldAttrs: list.fieldAttrs, - modelName: list.model, - parentID: list.id, - }); + return this.rpc(list.model, 'search_read') + .withFields(list.fieldNames) + .withDomain(list.domain || []) + .withLimit(list.limit) + .withOffset(list.offset) + .orderBy(list.orderedBy) + .exec() + .then(function (result) { + list.count = result.length; + var data = _.map(result.records, function (record) { + var dataPoint = self._makeDataPoint({ + data: record, + fields: list.fields, + fieldNames: list.fieldNames, + fieldAttrs: list.fieldAttrs, + modelName: list.model, + parentID: list.id, + }); - // add many2one records - _.each(dataPoint.fieldNames, function (name) { - var field = dataPoint.fields[name]; - if (field.type === 'many2one') { - var r = self._makeDataPoint({ - modelName: field.relation, - data: { - id: dataPoint.data[name][0], - display_name: dataPoint.data[name][1], - } - }); + // add many2one records + _.each(dataPoint.fieldNames, function (name) { + var field = dataPoint.fields[name]; + if (field.type === 'many2one') { + var r = self._makeDataPoint({ + modelName: field.relation, + data: { + id: dataPoint.data[name][0], + display_name: dataPoint.data[name][1], + } + }); - dataPoint.data[name] = r.id; - } - if (field.type === 'date') { - dataPoint.data[name] = fieldUtils.parse.date(dataPoint.data[name]); - } - if (field.type === 'datetime') { - dataPoint.data[name] = fieldUtils.parse.datetime(dataPoint.data[name]); - } + dataPoint.data[name] = r.id; + } + if (field.type === 'date') { + dataPoint.data[name] = fieldUtils.parse.date(dataPoint.data[name]); + } + if (field.type === 'datetime') { + dataPoint.data[name] = fieldUtils.parse.datetime(dataPoint.data[name]); + } + }); + return dataPoint.id; }); - return dataPoint.id; + list.data = data; + list.res_ids = _.pluck(result.records, 'id'); + return list; }); - list.data = data; - list.res_ids = _.pluck(result.records, 'id'); - return list; - }); - }, - /** - * Helper method, generates a string to describe a ordered by sequence for - * SQL. - * - * For example, [{name: 'foo', asc: true}, {name: 'bar', asc: false}] will - * be converted into 'foo ASC, bar DESC' - * - * @param {Object[]} orderBy list of objects {name:..., asc: ...} - * @returns {string} - */ - _serializeSort: function (orderBy) { - return _.map(orderBy, function (order) { - return order.asc ? order.name + ' ASC' : order.name + ' DESC'; - }).join(', '); }, /** * Change the offset of a record. Note that this does not reload the data. diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js index dbb574176f9..5f1b37a4a32 100644 --- a/addons/web/static/src/js/views/calendar/calendar_controller.js +++ b/addons/web/static/src/js/views/calendar/calendar_controller.js @@ -103,7 +103,7 @@ var CalendarController = AbstractController.extend({ _updateRecord: function (record) { // Cannot modify actual name yet var data = _.omit(this.model.calendarEventToRecord(record), 'name'); - this.performModelRPC(this.model, 'write', [record.id, data]) + this.performModelRPC(this.model.modelName, 'write', [record.id, data]) .then(this.reload.bind(this)); }, diff --git a/addons/web/static/src/js/widgets/debug_manager.js b/addons/web/static/src/js/widgets/debug_manager.js index b09fa2d498b..59becd8faea 100644 --- a/addons/web/static/src/js/widgets/debug_manager.js +++ b/addons/web/static/src/js/widgets/debug_manager.js @@ -7,7 +7,6 @@ var core = require('web.core'); var Dialog = require('web.Dialog'); var field_utils = require('web.field_utils'); var framework = require('web.framework'); -var Model = require('web.Model'); var session = require('web.session'); var SystrayMenu = require('web.SystrayMenu'); var utils = require('web.utils'); @@ -18,8 +17,6 @@ var Widget = require('web.Widget'); var QWeb = core.qweb; var _t = core._t; -var ADD = function (id) { return [4, id, false]; }, - REMOVE = function (id) { return [3, id, false]; }; /** * DebugManager base + general features (applicable to any context) */ @@ -66,9 +63,9 @@ var DebugManager = Widget.extend({ // whether the current user is an administrator this._is_admin = session.is_system; return $.when( - new Model('res.users').call('check_access_rights', {operation: 'write', raise_exception: false}), + this.performModelRPC('res.users', 'check_access_rights', {operation: 'write', raise_exception: false}), session.user_has_group('base.group_no_one'), - new Model('ir.model.data').call('xmlid_to_res_id', {xmlid: 'base.group_no_one'}), + this.performModelRPC('ir.model.data', 'xmlid_to_res_id', {xmlid: 'base.group_no_one'}), this._super() ).then(function (can_write_user, has_group_no_one, group_no_one_id) { this._features_group = can_write_user && group_no_one_id; @@ -164,11 +161,7 @@ var DebugManager = Widget.extend({ title: _t('Select a view'), disable_multiple_selection: true, on_selected: function (element_ids) { - new Model('ir.ui.view') - .query(['name', 'model', 'type']) - .filter([['id', '=', element_ids[0]]]) - .first() - .then(function (view) { + self.performModelRPC('ir.ui.view', [[['id', '=', element_ids[0]]], ['name', 'model', 'type']], {limit: 1}).then(function (view) { self.do_action({ type: 'ir.actions.act_window', name: view.name, @@ -226,7 +219,7 @@ DebugManager.include({ get_view_fields: function () { var self = this; var model = this._action.res_model; - new Model(model).call('fields_get', { + this.performModelRPC(model, 'fields_get', [], { attributes: ['string', 'searchable', 'required', 'readonly', 'type', 'store', 'sortable', 'relation', 'help'] }).done(function (fields) { new Dialog(self, { @@ -262,9 +255,7 @@ DebugManager.include({ }, translate: function() { var model = this._action.res_model; - new Model("ir.translation") - .call('get_technical_translations', [model]) - .then(this.do_action); + this.performModelRPC("ir.translation", 'get_technical_translations', [model]).then(this.do_action); } }); @@ -278,9 +269,8 @@ DebugManager.include({ this._can_edit_views = false; return $.when( this._super(), - new Model('ir.ui.view').call( - 'check_access_rights', {operation: 'write', raise_exception: false} - ).then(function (ar) { + this.performModelRPC('ir.ui.view', 'check_access_rights', {operation: 'write', raise_exception: false}) + .then(function (ar) { this._can_edit_views = ar; }.bind(this)) ); diff --git a/addons/web/static/src/js/widgets/switch_company_menu.js b/addons/web/static/src/js/widgets/switch_company_menu.js index 89dc6ae29b3..b953791eec2 100644 --- a/addons/web/static/src/js/widgets/switch_company_menu.js +++ b/addons/web/static/src/js/widgets/switch_company_menu.js @@ -1,7 +1,6 @@ odoo.define('web.SwitchCompanyMenu', function(require) { "use strict"; -var Model = require('web.Model'); var session = require('web.session'); var SystrayMenu = require('web.SystrayMenu'); var Widget = require('web.Widget'); @@ -19,7 +18,7 @@ var SwitchCompanyMenu = Widget.extend({ this.$el.on('click', '.dropdown-menu li a[data-menu]', _.debounce(function(ev) { ev.preventDefault(); var company_id = $(ev.currentTarget).data('company-id'); - new Model('res.users').call('write', [[session.uid], {'company_id': company_id}]).then(function() { + self.performModelRPC('res.users', 'write', [[session.uid], {'company_id': company_id}]).then(function() { location.reload(); }); }, 1500, true)); |
