summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/_deprecated/data.js100
-rw-r--r--addons/web/static/src/js/_deprecated/data_model.js81
-rw-r--r--addons/web/static/src/js/_deprecated/model.js60
-rw-r--r--addons/web/static/src/js/apps.js44
-rw-r--r--addons/web/static/src/js/chrome/search_inputs.js44
-rw-r--r--addons/web/static/src/js/chrome/web_client.js66
-rw-r--r--addons/web/static/src/js/core/mixins.js57
-rw-r--r--addons/web/static/src/js/core/widget.js40
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js16
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js10
-rw-r--r--addons/web/static/src/js/services/data_manager.js25
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js983
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_controller.js2
-rw-r--r--addons/web/static/src/js/widgets/debug_manager.js24
-rw-r--r--addons/web/static/src/js/widgets/switch_company_menu.js3
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));