[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) <jum@odoo.com>
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Julien Mougenot
2020-04-17 14:21:02 +00:00
parent b3149d84a0
commit 7ace5a986a
7 changed files with 64 additions and 44 deletions
@@ -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.
@@ -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 () {
@@ -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();
},
});
@@ -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();
});
@@ -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<Array[]>}
*/
_getSearchDomain: function () {
return this.controlPanelDomain;
_getViewDomain: async function () {
return [];
},
/**
* Meant to be overriden to return a proper object.
@@ -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<Array[]>}
*/
_getViewDomain: async function (parent) {
return [];
},
/**
* Processes a fieldsView. In particular, parses its arch.
*
@@ -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);