From 7ace5a986a042ced675fb821bbf527d2d69ca614 Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Thu, 16 Apr 2020 15:09:47 +0000 Subject: [PATCH] [FIX] web,lunch: separate search panel domain Before this commit, the SearchPanel widget was given a single unified domain coming from the view/controller. The issue was that some modules injected additional leaves in this domain and there was no way to remove them/separate them from the original view/controller domain. Now, another paramater (the `viewDomain`) is given to the SearchPanel, containing the distinct domain related to the view. The Lunch module has been adapted to give the `location` domain through this new key. Note that some tests steps had to be inverted since the method retreiving the domain of the view/controller is asynchronous (needed by Lunch to give time to the model to be loaded). It mainly concerns the RPC order which does not matter in this case. Task 2239706 closes odoo/odoo#49720 X-original-commit: e895e553899933be14c8abfd8c676b44a7f534d8 Signed-off-by: Julien Mougenot (jum) Signed-off-by: Aaron Bohy (aab) --- .../static/src/js/lunch_kanban_controller.js | 7 ++-- .../lunch/static/src/js/lunch_kanban_model.js | 13 ++------ .../lunch/static/src/js/lunch_kanban_view.js | 14 ++------ .../lunch/static/tests/lunch_kanban_tests.js | 32 +++++++++++++++---- .../src/js/views/abstract_controller.js | 17 +++++----- .../web/static/src/js/views/abstract_view.js | 13 ++++++++ .../web/static/src/js/views/search_panel.js | 12 ++++--- 7 files changed, 64 insertions(+), 44 deletions(-) diff --git a/addons/lunch/static/src/js/lunch_kanban_controller.js b/addons/lunch/static/src/js/lunch_kanban_controller.js index 7713c192bb3..6e75d4839b3 100644 --- a/addons/lunch/static/src/js/lunch_kanban_controller.js +++ b/addons/lunch/static/src/js/lunch_kanban_controller.js @@ -81,12 +81,9 @@ var LunchKanbanController = KanbanController.extend({ * to the searchDomain (coming from the controlPanel). * * @override - * @private */ - _getSearchDomain: function () { - var searchDomain = this._super.apply(this, arguments) || []; - var locationId = this.model.getCurrentLocationId(); - return searchDomain.concat([['is_available_at', 'in', [locationId]]]); + _getViewDomain: async function () { + return this.model.getLocationDomain(); }, /** * Renders and appends the lunch banner widget. diff --git a/addons/lunch/static/src/js/lunch_kanban_model.js b/addons/lunch/static/src/js/lunch_kanban_model.js index 7ab7b279e43..07acbcd8b69 100644 --- a/addons/lunch/static/src/js/lunch_kanban_model.js +++ b/addons/lunch/static/src/js/lunch_kanban_model.js @@ -22,12 +22,6 @@ var LunchKanbanModel = KanbanModel.extend({ // Public //-------------------------------------------------------------------------- - /** - * @returns {integer|false} - */ - getCurrentLocationId: function () { - return this.locationId; - }, /** * @return {Promise} resolved with the location domain */ @@ -84,12 +78,11 @@ var LunchKanbanModel = KanbanModel.extend({ * Builds the domain leaf corresponding to the current user's location * * @private - * @return {Array} + * @return {(Array[])|undefined} */ _buildLocationDomainLeaf: function () { - var locationId = this.getCurrentLocationId(); - if (locationId) { - return ['is_available_at', 'in', [locationId]]; + if (this.locationId) { + return ['is_available_at', 'in', [this.locationId]]; } }, _getUserLocation: function () { diff --git a/addons/lunch/static/src/js/lunch_kanban_view.js b/addons/lunch/static/src/js/lunch_kanban_view.js index 8dda159f006..0b54497fc55 100644 --- a/addons/lunch/static/src/js/lunch_kanban_view.js +++ b/addons/lunch/static/src/js/lunch_kanban_view.js @@ -24,19 +24,11 @@ var LunchKanbanView = KanbanView.extend({ //-------------------------------------------------------------------------- /** - * Injects an additional domain in the search panel - * - * @private * @override */ - _createSearchPanel: function (parent, params) { - var self = this; - var model = this.getModel(parent); - var _super = this._super.bind(this); - return model.getLocationDomain().then(function (domain) { - self.loadParams.domain = self.loadParams.domain.concat(domain); - return _super(parent, params); - }); + _getViewDomain: function (parent) { + const model = this.getModel(parent); + return model.getLocationDomain(); }, }); diff --git a/addons/lunch/static/tests/lunch_kanban_tests.js b/addons/lunch/static/tests/lunch_kanban_tests.js index 6dfb524ef9e..bb2b0beb3f7 100644 --- a/addons/lunch/static/tests/lunch_kanban_tests.js +++ b/addons/lunch/static/tests/lunch_kanban_tests.js @@ -212,8 +212,9 @@ QUnit.module('LunchKanbanView', { }); QUnit.test('search panel domain location', async function (assert) { - assert.expect(10); - const locationId = this.data['lunch.location'].records[0].id; + assert.expect(20); + let expectedLocation = 1; + let locationId = this.data['lunch.location'].records[0].id; const regularInfos = _.extend({}, this.regularInfos); const kanban = await createLunchKanbanView({ @@ -233,30 +234,49 @@ QUnit.module('LunchKanbanView', { assert.step(route); if (route.startsWith('/lunch')) { + if (route === '/lunch/user_location_set') { + locationId = args.location_id; + return Promise.resolve(true); + } return mockLunchRPC({ infos: regularInfos, userLocation: locationId, }).apply(this, arguments); } if (args.method === 'search_panel_select_multi_range') { - assert.deepEqual(args.kwargs.search_domain, [["is_available_at", "in", [locationId]]], + assert.deepEqual(args.kwargs.search_domain, [["is_available_at", "in", [expectedLocation]]], 'The initial domain of the search panel must contain the user location'); } if (route === '/web/dataset/search_read') { - assert.deepEqual(args.domain, [["is_available_at", "in", [locationId]]], + assert.deepEqual(args.domain, [["is_available_at", "in", [expectedLocation]]], 'The domain for fetching actual data should be correct'); } return this._super.apply(this, arguments); - } + }, }); + + expectedLocation = 2; + await testUtils.fields.many2one.clickOpenDropdown('locations'); + await testUtils.fields.many2one.clickItem('locations', "Office 2"); + assert.verifySteps([ + // Initial state '/lunch/user_location_get', '/web/dataset/call_kw/product/search_panel_select_multi_range', '/web/dataset/call_kw/product/search_panel_select_multi_range', '/web/dataset/search_read', '/lunch/infos', '/web/dataset/call_kw/ir.model.data/xmlid_to_res_id', - ]) + // Click m2o + '/web/dataset/call_kw/lunch.location/name_search', + // Click new location + '/lunch/user_location_set', + '/web/dataset/call_kw/product/search_panel_select_multi_range', + '/web/dataset/call_kw/product/search_panel_select_multi_range', + '/web/dataset/search_read', + '/lunch/infos', + '/web/dataset/call_kw/ir.model.data/xmlid_to_res_id', + ]); kanban.destroy(); }); diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 09451cdcbfa..866abb03d65 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -228,7 +228,11 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { this._searchPanel.importState(controllerState.spState); this.searchPanelDomain = this._searchPanel.getDomain(); } else { - searchPanelUpdateProm = this._searchPanel.update({searchDomain: this._getSearchDomain()}); + const viewDomain = await this._getViewDomain(); + searchPanelUpdateProm = this._searchPanel.update({ + searchDomain: this.controlPanelDomain, + viewDomain, + }); postponeRendering = !params.noRender; params.noRender = true; // wait for searchpanel to be ready to render } @@ -299,16 +303,13 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { return null; }, /** - * Return the current search domain. This is the searchDomain used to update - * the searchpanel. It returns the domain coming from the controlpanel. This - * function can be overridden to add sub-domains coming from other parts of - * the interface. + * Get the domain defined by the view. It is meant to be overridden. * * @private - * @returns {Array[]} + * @returns {Promise} */ - _getSearchDomain: function () { - return this.controlPanelDomain; + _getViewDomain: async function () { + return []; }, /** * Meant to be overriden to return a proper object. diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index c720a2a515e..4cd503ff480 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -296,9 +296,11 @@ var AbstractView = Factory.extend({ } }); var controlPanelDomain = this.loadParams.domain; + const viewDomain = await this._getViewDomain(parent); var spParams = _.extend({}, this.searchPanelParams, { defaultValues: defaultValues, searchDomain: controlPanelDomain, + viewDomain, classes: params.classes || [], }); var searchPanel = new this.config.SearchPanel(parent, spParams); @@ -351,6 +353,17 @@ var AbstractView = Factory.extend({ withSearchPanel: this.withSearchPanel, }; }, + /** + * Get the domain defined by the view. It is meant to be overridden. The parent + * is provided in case some subcomponents need to be retrieved. + * + * @private + * @param {Object} parent + * @returns {Promise} + */ + _getViewDomain: async function (parent) { + return []; + }, /** * Processes a fieldsView. In particular, parses its arch. * diff --git a/addons/web/static/src/js/views/search_panel.js b/addons/web/static/src/js/views/search_panel.js index 590d52e4941..377eb723621 100644 --- a/addons/web/static/src/js/views/search_panel.js +++ b/addons/web/static/src/js/views/search_panel.js @@ -83,6 +83,7 @@ var SearchPanel = Widget.extend({ * @param {Object} params.fields * @param {string} params.model * @param {Array[]} params.searchDomain domain coming from controlPanel + * @param {Array[]} params.viewDomain domain coming from the view (ill advised) * @param {Object} params.sections * @param {Object} [params.state] state exported by another searchpanel * instance @@ -110,6 +111,7 @@ var SearchPanel = Widget.extend({ this.model = params.model; this.className = params.classes.concat(['o_search_panel']).join(' '); this.searchDomain = params.searchDomain; + this.viewDomain = params.viewDomain; }, /** * @override @@ -229,15 +231,17 @@ var SearchPanel = Widget.extend({ * * @param {Object} params * @param {Array[]} params.searchDomain domain coming from controlPanel + * @param {Array[]} params.viewDomain domain coming from view * @returns {Promise} */ update: function (params) { - var currentSearchDomainStr = JSON.stringify(this.searchDomain); - var newSearchDomainStr = JSON.stringify(params.searchDomain); + const currentDomain = JSON.stringify([...this.searchDomain, ...this.viewDomain]); + const newDomain = JSON.stringify([...params.searchDomain, ...params.viewDomain]); var filtersProm; - if (this.needReload || (currentSearchDomainStr !== newSearchDomainStr)) { + if (this.needReload || (currentDomain !== newDomain)) { this.needReload = false; this.searchDomain = params.searchDomain; + this.viewDomain = params.viewDomain; filtersProm = this._fetchFilters(); } return Promise.resolve(filtersProm).then(this._render.bind(this)); @@ -431,7 +435,7 @@ var SearchPanel = Widget.extend({ disable_counters: filter.disableCounters, filter_domain: filterDomain, group_by: filter.groupBy || false, - search_domain: self.searchDomain, + search_domain: [...self.searchDomain, ...self.viewDomain], }, }).then(function (values) { self._createFilterTree(filterId, values);