diff --git a/addons/web/static/src/js/action_manager.js b/addons/web/static/src/js/action_manager.js
index fcce83720ae..359b174a28d 100644
--- a/addons/web/static/src/js/action_manager.js
+++ b/addons/web/static/src/js/action_manager.js
@@ -5,6 +5,7 @@ var ControlPanel = require('web.ControlPanel');
var core = require('web.core');
var crash_manager = require('web.crash_manager');
var data = require('web.data');
+var data_manager = require('web.data_manager');
var Dialog = require('web.Dialog');
var framework = require('web.framework');
var pyeval = require('web.pyeval');
@@ -564,7 +565,7 @@ var ActionManager = Widget.extend({
active_ids : options.additional_context.active_ids,
active_model : options.additional_context.active_model
};
- return self.rpc("/web/action/load", { action_id: action, additional_context : additional_context }).then(function(result) {
+ return data_manager.load_action(action, additional_context).then(function(result) {
return self.do_action(result, options);
});
}
diff --git a/addons/web/static/src/js/framework/data_manager.js b/addons/web/static/src/js/framework/data_manager.js
new file mode 100644
index 00000000000..8065b48b4e1
--- /dev/null
+++ b/addons/web/static/src/js/framework/data_manager.js
@@ -0,0 +1,226 @@
+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._cache = {
+ actions: {},
+ fields_views: {},
+ fields: {},
+ filters: {},
+ views: {},
+ };
+ },
+
+ /**
+ * Loads an action from its id or xmlid.
+ *
+ * @param {int|string} [action_id] the action id or xmlid
+ * @param {Object} [additional_context] used to load the action
+ * @return {Deferred} resolved with the action whose id or xmlid is action_id
+ */
+ load_action: function (action_id, additional_context) {
+ var self = this;
+ var key = this._gen_key(action_id, additional_context || {});
+
+ if (!this._cache.actions[key]) {
+ this._cache.actions[key] = session.rpc("/web/action/load", {
+ action_id: action_id,
+ additional_context : additional_context,
+ }).then(function (action) {
+ self._cache.actions[key] = action.no_cache ? null : self._cache.actions[key];
+ return action;
+ });
+ }
+
+ return this._cache.actions[key];
+ },
+
+ /**
+ * Loads various information concerning views: fields_view for each view,
+ * and optionally filters and fields.
+ *
+ * @param {Object} [dataset] the dataset for which the views are loaded
+ * @param {Array} [views_descr] array of [view_id, view_type]
+ * @param {Object} [options] dictionnary of various options:
+ * - options.load_filters: whether or not to load the filters,
+ * - options.action_id: the action_id (required to load filters),
+ * - options.load_fields: whether or not to load the fields,
+ * - options.toolbar: whether or not a toolbar will be displayed,
+ * @return {Deferred} resolved with the requested views information
+ */
+ load_views: function (dataset, views_descr, options) {
+ var self = this;
+ var model = dataset.model;
+ var context = dataset.get_context();
+ var key = this._gen_key(model, views_descr, options || {}, context);
+
+ if (!this._cache.views[key]) {
+ // Don't load fields or filters if already in cache
+ options.load_fields = options.load_fields && !this._cache.fields[model];
+ var filters_key;
+ if (options.load_filters) {
+ filters_key = this._gen_key(model, options.action_id);
+ options.load_filters = !this._cache.filters[filters_key];
+ }
+
+ this._cache.views[key] = dataset.call('load_views', {
+ views: views_descr,
+ options: options,
+ context: context,
+ }).then(function (result) {
+ // Postprocess fields_views and insert them into the fields_views cache
+ result.fields_views = _.mapObject(result.fields_views, self._postprocess_fvg.bind(self)); // FIXME: lazy postprocess?
+ _.each(views_descr, function (view_descr) {
+ var toolbar = options.toolbar && view_descr[1] !== 'search';
+ var fv_key = self._gen_key(model, view_descr[0], view_descr[1], toolbar, context);
+ self._cache.fields_views[fv_key] = $.when(result.fields_views[view_descr[1]]);
+ });
+
+ // Insert filters, if any, into the filters cache
+ if (result.filters) {
+ self._cache.filters[filters_key] = $.when(result.filters);
+ }
+
+ // Insert fields, if any, into the fields cache
+ if (result.fields) {
+ self._cache.fields[model] = $.when(result.fields);
+ }
+
+ return result.fields_views;
+ });
+ }
+
+ return this._cache.views[key];
+ },
+
+ /**
+ * Loads the fields_view of a given model, view_type and optional view_id.
+ *
+ * @param {Object} [dataset] the dataset for which the fields_view is loaded
+ * @param {int} [view_id] the id of the view (optional)
+ * @param {string} [view_type] the type of the view
+ * @param {Boolean} [toolbar] whether or not a toolbar will be displayed
+ * @return {Deferred} resolved with the requested fields_view
+ */
+ load_fields_view: function (dataset, view_id, view_type, toolbar) {
+ var key = this._gen_key(dataset.model, view_id, view_type, toolbar, dataset.get_context());
+ if (!this._cache.fields_views[key]) {
+ this._cache.fields_views[key] = dataset.call('fields_view_get', {
+ view_id: view_id,
+ view_type: view_type,
+ toolbar: toolbar,
+ context: dataset.get_context(),
+ }).then(this._postprocess_fvg.bind(this));
+ }
+ return this._cache.fields_views[key];
+ },
+
+ /**
+ * Loads the fields of a given model.
+ *
+ * @param {Object} [dataset] the dataset for which the fields are loaded
+ * @return {Deferred} resolved with the requested fields
+ */
+ load_fields: function (dataset) {
+ if (!this._cache.fields[dataset.model]) {
+ this._cache.fields[dataset.model] = dataset.call('fields_get', {
+ context: dataset.get_context(),
+ });
+ }
+ return this._cache.fields[dataset.model];
+ },
+
+ /**
+ * Loads the filters of a given model and optional action id.
+ *
+ * @param {Object} [dataset] the dataset for which the filters are loaded
+ * @param {int} [action_id] the id of the action (optional)
+ * @return {Deferred} resolved with the requested filters
+ */
+ 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(),
+ });
+ }
+ return this._cache.filters[key];
+ },
+
+ /**
+ * Calls 'create_or_replace' on 'ir_filters'.
+ *
+ * @param {Object} [filter] the filter description
+ * @return {Deferred} resolved with the id of the created or replaced filter
+ */
+ create_filter: function (filter) {
+ var self = this;
+ return this.Filters
+ .call('create_or_replace', [filter])
+ .then(function (filter_id) {
+ var key = [
+ filter.model_id,
+ filter.action_id || false,
+ ].join(',');
+ self._cache.filters[key] = null; // invalidate cache
+ return filter_id;
+ });
+ },
+
+ /**
+ * Calls 'unlink' on 'ir_filters'.
+ *
+ * @param {Object} [filter] the description of the filter to remove
+ * @return {Deferred}
+ */
+ delete_filter: function (filter) {
+ var self = this;
+ return this.Filters
+ .call('unlink', [filter.id])
+ .then(function () {
+ self._cache.filters = {}; // invalidate cache
+ });
+ },
+
+ /**
+ * Private function that postprocesses fields_view (mainly parses the arch attribute)
+ */
+ _postprocess_fvg: function (fields_view) {
+ var self = this;
+ var doc = $.parseXML(fields_view.arch).documentElement;
+ fields_view.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
+ if ('id' in fields_view.fields) {
+ // Special case for id's
+ var id_field = fields_view.fields.id;
+ id_field.original_type = id_field.type;
+ id_field.type = 'id';
+ }
+ _.each(fields_view.fields, function(field) {
+ _.each(field.views || {}, function(view) {
+ self._postprocess_fvg(view);
+ });
+ });
+ return fields_view;
+ },
+
+ /**
+ * Private function that generates a cache key from its arguments
+ */
+ _gen_key: function () {
+ return _.map(Array.prototype.slice.call(arguments), function (arg) {
+ if (!arg) {
+ return false;
+ }
+ return _.isObject(arg) ? JSON.stringify(arg) : arg;
+ }).join(',');
+ },
+});
+
+});
diff --git a/addons/web/static/src/js/framework/model.js b/addons/web/static/src/js/framework/model.js
index 8e37f14218e..fc1422d799a 100644
--- a/addons/web/static/src/js/framework/model.js
+++ b/addons/web/static/src/js/framework/model.js
@@ -3,7 +3,6 @@ odoo.define('web.Model', function (require) {
var Class = require('web.Class');
var session = require('web.session');
-var utils = require('web.utils');
var Model = Class.extend({
/**
@@ -70,43 +69,6 @@ var Model = Class.extend({
signal: signal
});
},
- /**
- * Performs a fields_view_get and apply postprocessing.
- * return a {$.Deferred} resolved with the fvg
- *
- * @param {Object} args
- * @param {String|Object} args.model instance.web.Model instance or string repr of the model
- * @param {Object} [args.context] context if args.model is a string
- * @param {Number} [args.view_id] id of the view to be loaded, default view if null
- * @param {String} [args.view_type] type of view to be loaded if view_id is null
- * @param {Boolean} [args.toolbar=false] get the toolbar definition
- */
- fields_view_get: function (options) {
- return this.call('fields_view_get', {
- view_id: options.view_id,
- view_type: options.view_type,
- context: options.context,
- toolbar: options.toolbar || false
- }).then(this.postprocess_fvg.bind(this));
- },
-
- postprocess_fvg: function (fvg) {
- var self = this;
- var doc = $.parseXML(fvg.arch).documentElement;
- fvg.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
- if ('id' in fvg.fields) {
- // Special case for id's
- var id_field = fvg.fields.id;
- id_field.original_type = id_field.type;
- id_field.type = 'id';
- }
- _.each(fvg.fields, function(field) {
- _.each(field.views || {}, function(view) {
- self.postprocess_fvg(view);
- });
- });
- return fvg;
- },
});
return Model;
diff --git a/addons/web/static/src/js/framework/view.js b/addons/web/static/src/js/framework/view.js
index b6153475b0b..fbbede916e5 100644
--- a/addons/web/static/src/js/framework/view.js
+++ b/addons/web/static/src/js/framework/view.js
@@ -3,6 +3,7 @@ odoo.define('web.View', function (require) {
var core = require('web.core');
var data = require('web.data');
+var data_manager = require('web.data_manager');
var pyeval = require('web.pyeval');
var Widget = require('web.Widget');
@@ -122,10 +123,11 @@ var View = Widget.extend({
core.bus.trigger('do_reload_needaction');
});
} else if (action_data.type=="action") {
- return this.rpc('/web/action/load', {
- action_id: action_data.name,
- context: _.extend(pyeval.eval('context', context), {'active_model': dataset.model, 'active_ids': dataset.ids, 'active_id': record_id}),
- }).then(handler);
+ return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), {
+ active_model: dataset.model,
+ active_ids: dataset.ids,
+ active_id: record_id
+ })).then(handler);
} else {
return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js
new file mode 100644
index 00000000000..69fc68908f6
--- /dev/null
+++ b/addons/web/static/src/js/services/data_manager.js
@@ -0,0 +1,10 @@
+odoo.define('web.data_manager', function (require) {
+"use strict";
+
+var DataManager = require('web.DataManager');
+
+var data_manager = new DataManager();
+
+return data_manager;
+
+});
diff --git a/addons/web/static/src/js/view_manager.js b/addons/web/static/src/js/view_manager.js
index 0996f2c080c..0dfab67b4ea 100644
--- a/addons/web/static/src/js/view_manager.js
+++ b/addons/web/static/src/js/view_manager.js
@@ -3,6 +3,7 @@ odoo.define('web.ViewManager', function (require) {
var ControlPanelMixin = require('web.ControlPanelMixin');
var core = require('web.core');
+var data_manager = require('web.data_manager');
var framework = require('web.framework');
var Model = require('web.DataModel');
var pyeval = require('web.pyeval');
@@ -119,33 +120,24 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
views.push([view.view_id, view.type]);
}
});
+ var options = {
+ action_id: this.action.id,
+ load_fields: load_fields,
+ toolbar: this.flags.sidebar,
+ };
if (this.flags.search_view && !this.search_fields_view) {
+ options.load_filters = true;
var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
views.push([searchview_id || false, 'search']);
}
- var options = {
- action_id: this.action.id,
- load_fields: load_fields && !this.fields_get_def,
- toolbar: this.flags.sidebar,
- };
- return this.dataset._model.call('load_views', {
- views: views,
- options: options,
- context: this.dataset.get_context(),
- }).then(function (result) {
- _.each(result.fields_views, function (fields_view, view_type) {
+ return data_manager.load_views(this.dataset, views, options).then(function (fields_views) {
+ _.each(fields_views, function (fields_view, view_type) {
if (view_type === 'search') {
- self.search_fields_view = self.dataset._model.postprocess_fvg(fields_view);
+ self.search_fields_view = fields_view;
} else {
- self.views[view_type].fields_view = self.dataset._model.postprocess_fvg(fields_view);
+ self.views[view_type].fields_view = fields_view;
}
});
- if (result.filters) {
- self.search_filters = result.filters;
- }
- if (result.fields) {
- self.fields_get_def = $.when(result.fields);
- }
});
},
/**
@@ -158,14 +150,6 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
get_default_view: function() {
return this.views[this.flags.default_view || this.view_order[0].type];
},
- get_fields: function() {
- if (!this.fields_get_def) {
- this.fields_get_def = this.dataset._model.call('fields_get', {
- context: this.dataset.get_context()
- });
- }
- return this.fields_get_def;
- },
switch_mode: function(view_type, view_options) {
var self = this;
var view = this.views[view_type];
@@ -383,7 +367,6 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
$buttons: $("
"),
action: this.action,
search_defaults: search_defaults,
- filters: this.search_filters,
};
// Instantiate the SearchView, but do not append it nor its buttons to the DOM as this will
// be done later, simultaneously to all other ControlPanel elements
diff --git a/addons/web/static/src/js/views/form_common.js b/addons/web/static/src/js/views/form_common.js
index 1b29a2819b9..a92cc3cddc4 100644
--- a/addons/web/static/src/js/views/form_common.js
+++ b/addons/web/static/src/js/views/form_common.js
@@ -3,6 +3,7 @@ odoo.define('web.form_common', function (require) {
var core = require('web.core');
var data = require('web.data');
+var data_manager = require('web.data_manager');
var Dialog = require('web.Dialog');
var ListView = require('web.ListView');
var pyeval = require('web.pyeval');
@@ -919,12 +920,7 @@ var FormViewDialog = ViewDialog.extend({
if (this.options.alternative_form_view) {
fields_view_def = $.when(this.options.alternative_form_view);
} else {
- fields_view_def = this.dataset._model.fields_view_get({
- view_id: this.options.view_id || false,
- view_type: 'form',
- context: this.dataset.get_context(),
- toolbar: false,
- });
+ fields_view_def = data_manager.load_fields_view(this.dataset, this.options.view_id, 'form', false);
}
fields_view_def.then(function (fields_view) {
self.view_form = new FormView(self, self.dataset, fields_view, options);
@@ -977,7 +973,6 @@ var SelectCreateDialog = ViewDialog.extend({
return this.create_edit_record();
}
- var self = this;
var _super = this._super.bind(this);
this.init_dataset();
var context = pyeval.sync_eval_domains_and_contexts({
@@ -991,15 +986,12 @@ var SelectCreateDialog = ViewDialog.extend({
search_defaults[match[1]] = value_;
}
});
- this.dataset._model.call('load_views', {
- views: [[false, 'list'], [false, 'search']],
- context: this.dataset.get_context(),
- }).then(function (result) {
- var fields_views = _.mapObject(result.fields_views, self.dataset._model.postprocess_fvg);
- self.setup(search_defaults, fields_views).then(function (fragment) {
+ data_manager
+ .load_views(this.dataset, [[false, 'list'], [false, 'search']], {})
+ .then(this.setup.bind(this, search_defaults))
+ .then(function (fragment) {
_super().$el.append(fragment);
});
- });
return this;
},
diff --git a/addons/web/static/src/js/views/graph_view.js b/addons/web/static/src/js/views/graph_view.js
index 1df6cd14063..193f07c6d2d 100644
--- a/addons/web/static/src/js/views/graph_view.js
+++ b/addons/web/static/src/js/views/graph_view.js
@@ -5,6 +5,7 @@ odoo.define('web.GraphView', function (require) {
*---------------------------------------------------------*/
var core = require('web.core');
+var data_manager = require('web.data_manager');
var GraphWidget = require('web.GraphWidget');
var View = require('web.View');
@@ -28,7 +29,7 @@ var GraphView = View.extend({
},
willStart: function () {
var self = this;
- var load_fields = this.ViewManager.get_fields().then(this.prepare_fields.bind(this));
+ var fields_def = data_manager.load_fields(this.dataset).then(this.prepare_fields.bind(this));
this.fields_view.arch.children.forEach(function (field) {
var name = field.attrs.name;
if (field.attrs.interval) {
@@ -40,7 +41,7 @@ var GraphView = View.extend({
self.initial_groupbys.push(name);
}
});
- return $.when(this._super(), load_fields);
+ return $.when(this._super(), fields_def);
},
/**
* Render the buttons according to the GraphView.buttons and
diff --git a/addons/web/static/src/js/views/pivot_view.js b/addons/web/static/src/js/views/pivot_view.js
index 5559906e905..9dc2c4c2cdd 100644
--- a/addons/web/static/src/js/views/pivot_view.js
+++ b/addons/web/static/src/js/views/pivot_view.js
@@ -6,6 +6,7 @@ odoo.define('web.PivotView', function (require) {
var core = require('web.core');
var crash_manager = require('web.crash_manager');
+var data_manager = require('web.data_manager');
var formats = require('web.formats');
var framework = require('web.framework');
var Model = require('web.DataModel');
@@ -68,8 +69,8 @@ var PivotView = View.extend({
willStart: function () {
var self = this;
- var load_fields = this.ViewManager.get_fields();
- var load_xlwt = session.rpc('/web/pivot/check_xlwt').then(function(result) {
+ var fields_def = data_manager.load_fields(this.dataset);
+ var xlwt_def = session.rpc('/web/pivot/check_xlwt').then(function(result) {
self.xlwt_installed = result;
});
@@ -99,7 +100,7 @@ var PivotView = View.extend({
if ((!this.active_measures.length) || this.fields_view.arch.attrs.display_quantity) {
this.active_measures.push('__count__');
}
- return $.when(load_fields, load_xlwt, this._super()).then(function (fields) {
+ return $.when(fields_def, xlwt_def, this._super()).then(function (fields) {
self.prepare_fields(fields);
// add active measures to the measure list. This is very rarely necessary, but it
// can be useful if one is working with a functional field non stored, but in a
diff --git a/addons/web/static/src/js/views/search_inputs.js b/addons/web/static/src/js/views/search_inputs.js
index 9d08a537e9d..44b79999259 100644
--- a/addons/web/static/src/js/views/search_inputs.js
+++ b/addons/web/static/src/js/views/search_inputs.js
@@ -68,7 +68,9 @@ var Input = Widget.extend( /** @lends instance.web.search.Input# */{
"get_domain not implemented for widget " + this.attrs.type);
},
load_attrs: function (attrs) {
- attrs.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {};
+ if (!_.isObject(attrs.modifiers)) {
+ attrs.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {};
+ }
this.attrs = attrs;
},
/**
diff --git a/addons/web/static/src/js/views/search_menus.js b/addons/web/static/src/js/views/search_menus.js
index c26cdc35ea8..9edb817c567 100644
--- a/addons/web/static/src/js/views/search_menus.js
+++ b/addons/web/static/src/js/views/search_menus.js
@@ -2,7 +2,7 @@ odoo.define('web.FavoriteMenu', function (require) {
"use strict";
var core = require('web.core');
-var Model = require('web.DataModel');
+var data_manager = require('web.data_manager');
var pyeval = require('web.pyeval');
var session = require('web.session');
var Widget = require('web.Widget');
@@ -31,25 +31,9 @@ return Widget.extend({
this.searchview = parent;
this.query = query;
this.target_model = target_model;
- this.model = new Model('ir.filters');
this.action_id = action_id;
- if (filters) {
- this.filters = {};
- _.each(filters, this.add_filter.bind(this));
- }
- },
- willStart: function () {
- var self = this;
- var def;
- if (!this.filters) {
- def = this.model.call('get_filters', [this.target_model, this.action_id], {
- context: this.searchview.dataset.context,
- }).then(function (filters) {
- self.filters = {};
- _.each(filters, self.add_filter.bind(self));
- });
- }
- return $.when(def, this._super());
+ this.filters = {};
+ _.each(filters, this.add_filter.bind(this));
},
start: function () {
var self = this;
@@ -136,7 +120,7 @@ return Widget.extend({
is_default: default_filter,
action_id: this.action_id,
};
- return this.model.call('create_or_replace', [filter]).done(function (id) {
+ return data_manager.create_filter(filter).done(function (id) {
filter.id = id;
self.toggle_save_menu(false);
self.$save_name.find('input').val('').prop('checked', false);
@@ -267,14 +251,16 @@ return Widget.extend({
if (!confirm(filter.user_id ? warning : global_warning)) {
return;
}
- this.model.call('unlink', [filter.id]).done(function () {
- $filter.remove();
- delete self.$filters[key];
- delete self.filters[key];
- if (_.isEmpty(self.filters)) {
- self.$divider.hide();
- }
- });
+ return data_manager
+ .delete_filter(filter)
+ .done(function () {
+ $filter.remove();
+ delete self.$filters[key];
+ delete self.filters[key];
+ if (_.isEmpty(self.filters)) {
+ self.$divider.hide();
+ }
+ });
},
});
@@ -283,6 +269,7 @@ return Widget.extend({
odoo.define('web.FilterMenu', function (require) {
"use strict";
+var data_manager = require('web.data_manager');
var search_filters = require('web.search_filters');
var search_inputs = require('web.search_inputs');
var Widget = require('web.Widget');
@@ -327,7 +314,7 @@ return Widget.extend({
},
get_fields: function () {
if (!this._fields_def) {
- this._fields_def = this.searchview.ViewManager.get_fields().then(function (data) {
+ this._fields_def = data_manager.load_fields(this.searchview.dataset).then(function (data) {
var fields = {
id: { string: 'ID', type: 'id', searchable: true }
};
@@ -399,9 +386,10 @@ return Widget.extend({
odoo.define('web.GroupByMenu', function (require) {
"use strict";
-var Widget = require('web.Widget');
var core = require('web.core');
+var data_manager = require('web.data_manager');
var search_inputs = require('web.search_inputs');
+var Widget = require('web.Widget');
var QWeb = core.qweb;
@@ -436,7 +424,7 @@ return Widget.extend({
get_fields: function () {
var self = this;
if (!this._fields_def) {
- this._fields_def = this.searchview.ViewManager.get_fields().then(function (fields) {
+ this._fields_def = data_manager.load_fields(this.searchview.dataset).then(function (fields) {
var groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
var filter_group_field = _.filter(fields, function(field, name) {
if (field.store && _.contains(groupable_types, field.type)) {
diff --git a/addons/web/static/src/js/views/search_view.js b/addons/web/static/src/js/views/search_view.js
index 4dc6b541a6a..65a4b3a11b5 100644
--- a/addons/web/static/src/js/views/search_view.js
+++ b/addons/web/static/src/js/views/search_view.js
@@ -3,6 +3,7 @@ odoo.define('web.SearchView', function (require) {
var AutoComplete = require('web.AutoComplete');
var core = require('web.core');
+var data_manager = require('web.data_manager');
var FavoriteMenu = require('web.FavoriteMenu');
var FilterMenu = require('web.FilterMenu');
var GroupByMenu = require('web.GroupByMenu');
@@ -390,7 +391,17 @@ var SearchView = View.extend({
this.filter_menu = undefined;
this.groupby_menu = undefined;
this.favorite_menu = undefined;
- },
+ },
+ willStart: function () {
+ var self = this;
+ var def;
+ if (!this.options.disable_favorites) {
+ def = data_manager.load_filters(this.dataset, this.action_id).then(function (filters) {
+ self.favorite_filters = filters;
+ });
+ }
+ return $.when(this._super(), def);
+ },
start: function() {
if (this.headless) {
this.do_hide();
@@ -417,7 +428,7 @@ var SearchView = View.extend({
menu_defs.push(this.groupby_menu.appendTo(this.$buttons));
}
if (!this.options.disable_favorites) {
- this.favorite_menu = new FavoriteMenu(this, this.query, this.dataset.model, this.action_id, this.options.filters);
+ this.favorite_menu = new FavoriteMenu(this, this.query, this.dataset.model, this.action_id, this.favorite_filters);
menu_defs.push(this.favorite_menu.appendTo(this.$buttons));
}
}
diff --git a/addons/web/static/src/js/web_client.js b/addons/web/static/src/js/web_client.js
index 48216f9c0a3..640449d74b3 100644
--- a/addons/web/static/src/js/web_client.js
+++ b/addons/web/static/src/js/web_client.js
@@ -5,6 +5,7 @@ var ActionManager = require('web.ActionManager');
var core = require('web.core');
var crash_manager = require('web.crash_manager');
var data = require('web.data');
+var data_manager = require('web.data_manager');
var framework = require('web.framework');
var Loading = require('web.Loading');
var Menu = require('web.Menu');
@@ -16,7 +17,6 @@ var UserMenu = require('web.UserMenu');
var utils = require('web.utils');
var Widget = require('web.Widget');
-var QWeb = core.qweb;
var _t = core._t;
var WebClient = Widget.extend({
@@ -310,7 +310,7 @@ var WebClient = Widget.extend({
},
on_menu_action: function(options) {
var self = this;
- return this.menu_dm.add(this.rpc("/web/action/load", { action_id: options.action_id }))
+ return this.menu_dm.add(data_manager.load_action(options.action_id))
.then(function (result) {
return self.action_mutex.exec(function() {
if (options.needaction) {
diff --git a/addons/web/static/test/list-editable.js b/addons/web/static/test/list-editable.js
index 1606dabaf85..0b94958020d 100644
--- a/addons/web/static/test/list-editable.js
+++ b/addons/web/static/test/list-editable.js
@@ -171,7 +171,7 @@ odoo.define_section('editor', ['web.ListEditor'], function (test, mock) {
});
});
-odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test, mock) {
+odoo.define_section('list.edition', ['web.data', 'web.ListView', 'web.data_manager'], function (test, mock) {
function setup () {
var records = {};
@@ -198,7 +198,7 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
});
}
- test('newrecord', function (assert, data, ListView) {
+ test('newrecord', function (assert, data, ListView, data_manager) {
setup();
assert.expect(6);
var got_defaults = false;
@@ -213,7 +213,7 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
});
var ds = new data.DataSetStatic(null, 'demo', null, [1]);
- var fields_view = ds._model.postprocess_fvg({
+ var fields_view = data_manager._postprocess_fvg({
type: 'tree',
fields: {
a: {type: 'char', string: "A"},
@@ -247,7 +247,7 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
});
});
-odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], function (test, mock) {
+odoo.define_section('list.edition.events', ['web.data', 'web.ListView', 'web.data_manager'], function (test, mock) {
function fields_view_get () {
return {
type: 'tree',
@@ -265,7 +265,7 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
});
}
- test('edition events',function (assert, data, ListView) {
+ test('edition events',function (assert, data, ListView, data_manager) {
setup();
assert.expect(4);
var ds = new data.DataSetStatic(null, 'demo', null, [1]);
@@ -273,7 +273,7 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
counter: 0,
onEvent: function (e) { this.counter++; }
};
- var fields_view = ds._model.postprocess_fvg(fields_view_get());
+ var fields_view = data_manager._postprocess_fvg(fields_view_get());
var l = new ListView({}, ds, fields_view, {editable: 'top'});
l.on('edit:before edit:after', o, o.onEvent);
@@ -291,11 +291,11 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
});
});
- test('edition events: cancelling', function (assert, data, ListView) {
+ test('edition events: cancelling', function (assert, data, ListView, data_manager) {
setup();
var edit_after = false;
var ds = new data.DataSetStatic(null, 'demo', null, [1]);
- var fields_view = ds._model.postprocess_fvg(fields_view_get());
+ var fields_view = data_manager._postprocess_fvg(fields_view_get());
var l = new ListView({}, ds, fields_view, {editable: 'top'});
l.on('edit:before', {}, function (e) {
e.cancel = true;
@@ -320,9 +320,9 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
});
});
-odoo.define_section('list.edition.onwrite', ['web.data', 'web.ListView'], function (test, mock) {
+odoo.define_section('list.edition.onwrite', ['web.data', 'web.ListView', 'web.data_manager'], function (test, mock) {
- test('record-to-read', function (assert, data, ListView) {
+ test('record-to-read', function (assert, data, ListView, data_manager) {
assert.expect(4);
mock.add('demo:onchange', function () {
@@ -347,7 +347,7 @@ odoo.define_section('list.edition.onwrite', ['web.data', 'web.ListView'], functi
mock.add('demo:on_write', function () { return [42]; });
var ds = new data.DataSetStatic(null, 'demo', null, []);
- var fields_view = ds._model.postprocess_fvg({
+ var fields_view = data_manager._postprocess_fvg({
type: 'tree',
fields: {
a: {type: 'char', string: "A"}
diff --git a/addons/web/static/test/list.js b/addons/web/static/test/list.js
index 1a19e567364..298e65b5972 100644
--- a/addons/web/static/test/list.js
+++ b/addons/web/static/test/list.js
@@ -1,6 +1,6 @@
-odoo.define_section('list.buttons', ['web.ListView', 'web.data'], function (test, mock) {
+odoo.define_section('list.buttons', ['web.ListView', 'web.data', 'web.data_manager'], function (test, mock) {
- test('record-deletion', function (assert, ListView, data) {
+ test('record-deletion', function (assert, ListView, data, data_manager) {
assert.expect(2);
mock.add('demo:read', function (args, kwargs) {
@@ -22,7 +22,7 @@ odoo.define_section('list.buttons', ['web.ListView', 'web.data'], function (test
var ds = new data.DataSetStatic(null, 'demo', null, [1, 2, 3]);
- var fields_view = ds._model.postprocess_fvg({
+ var fields_view = data_manager._postprocess_fvg({
type: 'tree',
fields: {
a: {type: 'char', string: "A"}
diff --git a/addons/web/static/test/search.js b/addons/web/static/test/search.js
index bdf300f7692..dad64c018b4 100644
--- a/addons/web/static/test/search.js
+++ b/addons/web/static/test/search.js
@@ -160,6 +160,7 @@ function makeSearchView (test, dummy_widget_attributes, defaults, options, raw_f
var core = test.deps['web.core'];
var search_inputs = test.deps['web.search_inputs'];
var data = test.deps['web.data'];
+ var data_manager = test.deps['web.data_manager'];
var SearchView = test.deps['web.SearchView'];
var mock = test.mock;
@@ -182,14 +183,11 @@ function makeSearchView (test, dummy_widget_attributes, defaults, options, raw_f
var mock_parent = {
getParent: function () { return null; },
- get_fields: function () {
- return dataset._model.call('fields_get', { context: dataset.get_context() });
- },
};
options = _.defaults(options || {}, {$buttons: $('
')});
options.search_defaults = defaults;
- var fields_view = dataset._model.postprocess_fvg(raw_fields_view || {
+ var fields_view = data_manager._postprocess_fvg(raw_fields_view || {
type: 'search',
fields: {
dummy: {type: 'char', string: "Dummy", searchable: true}
@@ -204,7 +202,7 @@ function makeSearchView (test, dummy_widget_attributes, defaults, options, raw_f
return view;
};
-odoo.define_section('search.defaults', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.defaults', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('calling', function (assert) {
assert.expect(2);
var defaults_called = false;
@@ -430,7 +428,7 @@ odoo.define_section('search.defaults', ['web.search_inputs', 'web.SearchView', '
});
-odoo.define_section('search.completions', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.completions', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('calling', function (assert) {
assert.expect(4);
@@ -850,7 +848,7 @@ odoo.define_section('search.completions', ['web.search_inputs', 'web.SearchView'
});
});
-odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('No facet, no call', function (assert, search_inputs, SearchView, core) {
assert.expect(6);
@@ -1190,7 +1188,7 @@ odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchVie
});
});
-odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('is-drawn', function (assert) {
assert.expect(3);
@@ -1209,7 +1207,7 @@ odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchVie
});
-odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
function fields_view_get () {
// view with a single group of filters
return {
@@ -1317,9 +1315,9 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
});
});
-odoo.define_section('search.groupby', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.groupby', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
- test('basic', ['web.FavoriteMenu', 'web.FilterMenu', 'web.GroupByMenu'], function (assert, search_inputs, SearchView, core, data, FavoriteMenu, FilterMenu, GroupByMenu) {
+ test('basic', ['web.FavoriteMenu', 'web.FilterMenu', 'web.GroupByMenu'], function (assert, search_inputs, SearchView, core, data, data_manager, FavoriteMenu, FilterMenu, GroupByMenu) {
assert.expect(12);
var view = makeSearchView(this, {}, {}, {}, {
type: 'search',
@@ -1401,7 +1399,7 @@ odoo.define_section('search.groupby', ['web.search_inputs', 'web.SearchView', 'w
});
});
-odoo.define_section('search.filters.saved', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.filters.saved', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('checkboxing', function (assert) {
assert.expect(6);
@@ -1517,7 +1515,7 @@ odoo.define_section('search.filters.saved', ['web.search_inputs', 'web.SearchVie
});
});
-odoo.define_section('search.advanced', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
+odoo.define_section('search.advanced', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data', 'web.data_manager'], function (test, mock) {
test('single-advanced', function (assert) {
assert.expect(6);
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 3d077673918..753b22700e7 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -115,6 +115,8 @@
+
+
diff --git a/openerp/models.py b/openerp/models.py
index c54bf4c2ce0..af58afa7627 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -1537,6 +1537,7 @@ class BaseModel(object):
:param views: list of [view_id, view_type]
:param options.toolbar: true to include contextual actions when loading fields_views
+ :param options.load_filters: true to return the model's filters
:param options.action_id: id of the action to get the filters
:param options.load_fields: true to load the model's fields
:return: dictionary with fields_views, filters and fields
@@ -1555,7 +1556,7 @@ class BaseModel(object):
for [v_id, v_type] in views
}
- if 'search' in result['fields_views']:
+ if options.get('load_filters'):
result['filters'] = self.pool['ir.filters'].get_filters(cr, uid, self._name, options.get('action_id'), context=context)
if options.get('load_fields'):