[FIX] web: default operators for search fields

During the refactoring of the control panel (https://github.com/odoo/odoo/pull/41268)
a regression concerning the operator used for the fields set as
search_default was introduced: the default operator was fixed to '='
while it would have been necessary to take into consideration the
optional 'operator' attribute or the field types. Moreover, the default
operator associated to many2one fields when using autocompletion was
also mistakenly set to 'ilike' instead of '='. The present commit
restores the previous behavior.

closes odoo/odoo#55099

X-original-commit: 1f8e93fc590a6c7ef6b93953058f2dcd643ac620
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Co-authored-by: Julien Mougenot <jum@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
This commit is contained in:
Mathieu Duckerts-Antoine
2020-07-31 08:04:57 +00:00
committed by Julien Mougenot
co-authored by Julien Mougenot
parent 9dd2c7fdc8
commit dc8973fd6e
3 changed files with 108 additions and 27 deletions
@@ -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;
}
@@ -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 = `
<search>
<field name="bar"/>
<field name="bar_op" operator="child_of"/>
<field name="foo"/>
<field name="foo_op" operator="="/>
<field name="selec"/>
</search>`;
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 = `
<search>
<field name="foo"/>
<filter name="filterDate" string="Date" date="date_field" default_period="last_365_days" invisible="1" groups="base.group_system"/>
<filter name="groupByBar" string="Bar" context="{'group_by': 'bar'}" invisible="1" groups="base.group_system"/>
</search>`;
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 = `<search/>`;
const arch = `<search/>`;
const searchMenuTypes = ['filter'];
const params = {
cpModelConfig: {
@@ -193,7 +252,5 @@ odoo.define('web.control_panel_tests', function (require) {
controlPanel.destroy();
});
});
});
@@ -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"]