diff --git a/addons/web/static/src/js/control_panel/control_panel_model_extension.js b/addons/web/static/src/js/control_panel/control_panel_model_extension.js index 2b12ca90802..c04205ecccb 100644 --- a/addons/web/static/src/js/control_panel/control_panel_model_extension.js +++ b/addons/web/static/src/js/control_panel/control_panel_model_extension.js @@ -163,7 +163,7 @@ odoo.define("web/static/src/js/control_panel/control_panel_model_extension.js", this.searchMenuTypes = this.config.searchMenuTypes || []; this.favoriteFilters = this.config.favoriteFilters || []; this.fields = this.config.fields || {}; - this.searchDefaults = []; + this.searchDefaults = {}; for (const key in this.actionContext) { const match = /^search_default_(.*)$/.exec(key); if (match) { @@ -747,6 +747,21 @@ odoo.define("web/static/src/js/control_panel/control_panel_model_extension.js", // and others are passive (require input(s) to become determined) // What is the right place to process the attrs? }; + if (preFilter.attrs && JSON.parse(preFilter.attrs.modifiers || '{}').invisible) { + filter.invisible = true; + let preFilterFieldName = null; + if (preFilter.tag === 'filter' && preFilter.attrs.date) { + preFilterFieldName = preFilter.attrs.date; + } else if (preFilter.tag === 'groupBy') { + preFilterFieldName = preFilter.attrs.fieldName; + } + if (preFilterFieldName && !this.fields[preFilterFieldName]) { + // In some case when a field is limited to specific groups + // on the model, we need to ensure to discard related filter + // as it may still be present in the view (in 'invisible' state) + return; + } + } if (filter.type === 'filter' || filter.type === 'groupBy') { filter.groupNumber = groupNumber; } @@ -838,14 +853,11 @@ odoo.define("web/static/src/js/control_panel/control_panel_model_extension.js", } } catch (e) { } } - if (this.searchDefaults.hasOwnProperty(child.attrs.name)) { + if (child.attrs.name in this.searchDefaults) { child.attrs.isDefault = true; let value = this.searchDefaults[child.attrs.name]; if (child.tag === 'field') { - if (value instanceof Array) { - value = value[0]; - } - child.attrs.defaultAutocompleteValue = { value, operator: '=' }; + child.attrs.defaultValue = Array.isArray(value) ? value[0] : value; } else if (child.tag === 'groupBy') { child.attrs.defaultRank = typeof value === 'number' ? value : 100; } @@ -865,9 +877,6 @@ odoo.define("web/static/src/js/control_panel/control_panel_model_extension.js", filter.isDefault = attrs.isDefault; } filter.description = attrs.string || attrs.help || attrs.name || attrs.domain || 'Ω'; - if (JSON.parse(attrs.modifiers || '{}').invisible) { - filter.invisible = true; - } switch (filter.type) { case 'filter': if (attrs.context) { @@ -913,8 +922,23 @@ odoo.define("web/static/src/js/control_panel/control_panel_model_extension.js", filter.context = attrs.context; } if (filter.isDefault) { + let operator = filter.operator; + if (!operator) { + const type = attrs.widget || filter.fieldType; + // Note: many2one as a default filter will have a + // numeric value instead of a string => we want "=" + // instead of "ilike". + if (["char", "html", "many2many", "one2many", "text"].includes(type)) { + operator = "ilike"; + } else { + operator = "="; + } + } filter.defaultRank = -10; - filter.defaultAutocompleteValue = attrs.defaultAutocompleteValue; + filter.defaultAutocompleteValue = { + operator, + value: attrs.defaultValue, + }; } break; } diff --git a/addons/web/static/tests/control_panel/control_panel_tests.js b/addons/web/static/tests/control_panel/control_panel_tests.js index 24744291cc9..c9e89387505 100644 --- a/addons/web/static/tests/control_panel/control_panel_tests.js +++ b/addons/web/static/tests/control_panel/control_panel_tests.js @@ -18,6 +18,75 @@ odoo.define('web.control_panel_tests', function (require) { } }, function () { + QUnit.test('default field operator', async function (assert) { + assert.expect(2); + + const fields = { + foo_op: { string: "Foo Op", type: "char", store: true, sortable: true, searchable: true }, + foo: { string: "Foo", type: "char", store: true, sortable: true, searchable: true }, + bar_op: { string: "Bar Op", type: "many2one", relation: 'partner', searchable: true }, + bar: { string: "Bar", type: "many2one", relation: 'partner', searchable: true }, + selec: { string: "Selec", type: "selection", selection: [['red', "Red"], ['black', "Black"]] }, + }; + const arch = ` + + + + + + + `; + const searchMenuTypes = []; + const params = { + cpModelConfig: { + arch, + fields, + context: { + show_filterC: true, + search_default_bar: 10, + search_default_bar_op: 10, + search_default_foo: "foo", + search_default_foo_op: "foo_op", + search_default_selec: 'red', + }, + searchMenuTypes, + }, + cpProps: { fields, searchMenuTypes }, + env: { + session: { + async rpc() { + return [[10, "Deco Addict"]]; + }, + }, + }, + }; + const controlPanel = await createControlPanel(params); + + assert.deepEqual( + cpHelpers.getFacetTexts(controlPanel).map(t => t.replace(/\s/g, "")), + [ + "BarDecoAddict", + "BarOpDecoAddict", + "Foofoo", + "FooOpfoo_op", + "SelecRed" + ] + ); + assert.deepEqual( + controlPanel.getQuery().domain, + [ + "&", "&", "&", "&", + ["bar", "=", 10], + ["bar_op", "child_of", 10], + ["foo", "ilike", "foo"], + ["foo_op", "=", "foo_op"], + ["selec", "=", "red"], + ] + ); + + controlPanel.destroy(); + }); + QUnit.module('Keyboard navigation'); QUnit.test('remove a facet with backspace', async function (assert) { @@ -146,21 +215,11 @@ odoo.define('web.control_panel_tests', function (require) { // view to emulate the behavior of fields_view_get() // [see ir.ui.view `_apply_group()`] - delete this.fields['bar']; - delete this.fields['date_field']; + delete this.fields.bar; + delete this.fields.date_field; - const arch = ` - - - - - `; const searchMenuTypes = []; const params = { - cpStoreConfig: { - viewInfo: { arch, fields: this.fields }, - searchMenuTypes - }, cpProps: { fields: this.fields, searchMenuTypes }, }; const controlPanel = await createControlPanel(params); @@ -176,7 +235,7 @@ odoo.define('web.control_panel_tests', function (require) { QUnit.test('groupby menu is not rendered if searchMenuTypes does not have groupBy', async function (assert) { assert.expect(2); - const arch = ``; + const arch = ``; const searchMenuTypes = ['filter']; const params = { cpModelConfig: { @@ -193,7 +252,5 @@ odoo.define('web.control_panel_tests', function (require) { controlPanel.destroy(); }); - - }); }); diff --git a/addons/web/static/tests/control_panel/favorite_menu_tests.js b/addons/web/static/tests/control_panel/favorite_menu_tests.js index d01afdfb063..bf2ec76d564 100644 --- a/addons/web/static/tests/control_panel/favorite_menu_tests.js +++ b/addons/web/static/tests/control_panel/favorite_menu_tests.js @@ -371,7 +371,7 @@ odoo.define('web.favorite_menu_tests', function (require) { search: function (searchQuery) { const { domain, groupBy, timeRanges } = searchQuery; if (firstSearch) { - assert.deepEqual(domain, [['foo', '=', 'a']]); + assert.deepEqual(domain, [['foo', 'ilike', 'a']]); assert.deepEqual(groupBy, ['date_field:month']); assert.deepEqual(timeRanges, { comparisonId: "previous_period", @@ -399,7 +399,7 @@ odoo.define('web.favorite_menu_tests', function (require) { const { domain, groupBy, timeRanges } = controlPanel.getQuery(); assert.deepEqual(domain, [ "&", - ["foo", "=", "a"], + ["foo", "ilike", "a"], "&", ["date_field", ">=", "2019-01-01"], ["date_field", "<=", "2019-12-31"]