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'):