diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 14eb10c663c..6005c9f1a66 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -107,6 +107,7 @@ var FieldMany2One = AbstractField.extend({ 'click': '_onClick', }), AUTOCOMPLETE_DELAY: 200, + SEARCH_MORE_LIMIT: 320, /** * @override @@ -501,18 +502,33 @@ var FieldMany2One = AbstractField.extend({ values.push({ label: _t("Search More..."), action: function () { - self._rpc({ + var prom; + if (search_val !== '') { + prom = self._rpc({ model: self.field.relation, method: 'name_search', kwargs: { name: search_val, args: domain, operator: "ilike", - limit: 160, + limit: self.SEARCH_MORE_LIMIT, context: context, }, - }) - .then(self._searchCreatePopup.bind(self, "search")); + }); + } + $.when(prom).then(function (results) { + var dynamicFilters; + if (results) { + var ids = _.map(results, function (x) { + return x[0]; + }); + dynamicFilters = [{ + description: _.str.sprintf(_t('Quick search: %s'), search_val), + domain: [['id', 'in', ids]], + }]; + } + self._searchCreatePopup("search", false, {}, dynamicFilters); + }); }, classname: 'o_m2o_dropdown_option', }); @@ -555,19 +571,25 @@ var FieldMany2One = AbstractField.extend({ /** * all search/create popup handling * + * TODO: ids argument is no longer used, remove it in master (as well as + * initial_ids param of the dialog) + * * @private * @param {any} view * @param {any} ids * @param {any} context + * @param {Object[]} [dynamicFilters=[]] filters to add to the search view + * in the dialog (each filter has keys 'description' and 'domain') */ - _searchCreatePopup: function (view, ids, context) { + _searchCreatePopup: function (view, ids, context, dynamicFilters) { var self = this; return new dialogs.SelectCreateDialog(this, _.extend({}, this.nodeOptions, { res_model: this.field.relation, domain: this.record.getDomain({fieldName: this.name}), context: _.extend({}, this.record.getContext(this.recordParams), context || {}), + dynamicFilters: dynamicFilters || [], title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string, - initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined, + initial_ids: ids, initial_view: view, disable_multiple_selection: true, no_create: !self.can_create, diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index 5921c1b4bc3..a2e6c5e699c 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -78,6 +78,8 @@ var AbstractView = Factory.extend({ * @param {string} [params.controllerState] * @param {string} [params.displayName] * @param {Array[]} [params.domain=[]] + * @param {Object[]} [params.dynamicFilters] transmitted to the + * ControlPanelView * @param {number[]} [params.ids] * @param {boolean} [params.isEmbedded=false] * @param {Object} [params.searchQuery={}] @@ -165,6 +167,7 @@ var AbstractView = Factory.extend({ this.controlPanelParams = { action: action, activateDefaultFavorite: params.activateDefaultFavorite, + dynamicFilters: params.dynamicFilters, breadcrumbs: params.breadcrumbs, context: this.loadParams.context, domain: this.loadParams.domain, diff --git a/addons/web/static/src/js/views/control_panel/control_panel_view.js b/addons/web/static/src/js/views/control_panel/control_panel_view.js index edda0e3c9ac..c81093f28ff 100644 --- a/addons/web/static/src/js/views/control_panel/control_panel_view.js +++ b/addons/web/static/src/js/views/control_panel/control_panel_view.js @@ -43,8 +43,12 @@ var ControlPanelView = Factory.extend({ * breadcrumbs won't be rendered * @param {boolean} [params.withSearchBar=true] if set to false, no default * search bar will be rendered - * @param {boolean} [activateDefaultFavorite=false] determine if the default - * custom filters can be activated + * @param {boolean} [params.activateDefaultFavorite] determine if the + * default custom filters can be activated + * @param {Object[]} [params.dynamicFilters=[]] filters to add to the + * search (in addition to those described in the arch), each filter being + * an object with keys 'description' (what is displayed in the searchbar) + * and 'domain' */ init: function (params) { var self = this; @@ -103,6 +107,19 @@ var ControlPanelView = Factory.extend({ INTERVAL_OPTIONS = INTERVAL_OPTIONS.map(function (option) { return _.extend(option, {description: option.description.toString()}); }); + + // add a filter group with the dynamic filters, if any + if (params.dynamicFilters && params.dynamicFilters.length) { + var dynamicFiltersGroup = params.dynamicFilters.map(function (filter) { + return { + description: filter.description, + domain: JSON.stringify(filter.domain), + isDefault: true, + type: 'filter', + }; + }); + this.loadParams.groups.unshift(dynamicFiltersGroup); + } }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index fb9cd5afa0c..96f6286cd3e 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -304,6 +304,7 @@ var SelectCreateDialog = ViewDialog.extend({ * - list_view_options: dict of options to pass to the List View * - on_selected: optional callback to execute when records are selected * - disable_multiple_selection: true to allow create/select multiple records + * - dynamicFilters: filters to add to the searchview */ init: function () { this._super.apply(this, arguments); @@ -346,6 +347,7 @@ var SelectCreateDialog = ViewDialog.extend({ controlPanelFieldsView: fieldsViews.search, }, action_buttons: false, + dynamicFilters: this.options.dynamicFilters, context: this.context, domain: domain, hasSelectors: !this.options.disable_multiple_selection, diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index 84fac135ad2..04b58b017ba 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -958,7 +958,7 @@ QUnit.module('fields', {}, function () { return result; }, }); - await testUtils.fields.many2one.searchAndClickItem('trululu', 'b'); + await testUtils.fields.many2one.searchAndClickItem('trululu', {search: 'b'}); testUtils.form.clickSave(form); assert.verifySteps(['name_create'], @@ -2596,6 +2596,107 @@ QUnit.module('fields', {}, function () { form.destroy(); }); + QUnit.test('search more in many2one: no text in input', async function (assert) { + // when the user clicks on 'Search More...' in a many2one dropdown, and there is no text + // in the input (i.e. no value to search on), we bypass the name_search that is meant to + // return a list of preselected ids to filter on in the list view (opened in a dialog) + assert.expect(6); + + for (var i = 0; i < 8; i++) { + this.data.partner.records.push({id: 100 + i, display_name: 'test_' + i}); + } + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
', + archs: { + 'partner,false,list': '