[FIX] web: allow filtering with decimals on price filter + unit test
Issue - Install "Sales" - Switch to "German" language - Go to Sales -> Products -> Products - Try to filter on "Public Price" is equal to "2,3" Not possible to add decimal point at the end (only in middle of number). Cause In case the 'decimal point' in DB params is not a dot '.', the filter input will be considered as 'text' instead of 'number'. In case of the 'number' type; HTML do already a pre and post processing, including managing decimal point (who, for example, is not included in ev.target.value if last char is a '.'). Unfortunalty, the library is not well working with other language and not supported on every browser, therefore, must use own logic. In case of a 'text' type, the value will be send to 'parseFloat' ,then `parseNumber` will replace decimal_point by dot (also one the issues since needed to display decimal_point according user language), and `Number` will remove the decimal_point in case of '123,' -> '123', and therefore we will not be able to write decimals ( apart of adding the decimal point after writing the whole number...) Solution If user input is well parsed, store parsed value in condition.value and set condition.displayedValue to the input value (an so without updating input value). Else, replace input value with previous value (who should be the condition.DisplayedValue). opw-2463441 closes odoo/odoo#67978 X-original-commit: e795ce5bff14b6b748c1f4a2651946aafdc299f4 Signed-off-by: bon-odoo <nboulif@users.noreply.github.com>
This commit is contained in:
committed by
Nasreddin (bon)
parent
387e9a402a
commit
9bf7d15da4
@@ -107,6 +107,7 @@ odoo.define('web.CustomFilterItem', function (require) {
|
||||
const fieldType = this.fields[condition.field].type;
|
||||
const genericType = FIELD_TYPES[fieldType];
|
||||
const operator = FIELD_OPERATORS[genericType][condition.operator];
|
||||
// Logical value
|
||||
switch (genericType) {
|
||||
case 'id':
|
||||
case 'number':
|
||||
@@ -131,6 +132,12 @@ odoo.define('web.CustomFilterItem', function (require) {
|
||||
default:
|
||||
condition.value = "";
|
||||
}
|
||||
// Displayed value
|
||||
if (["float", "monetary"].includes(fieldType)) {
|
||||
condition.displayedValue = `0${this.DECIMAL_POINT}0`;
|
||||
} else {
|
||||
condition.displayedValue = String(condition.value);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
@@ -237,20 +244,25 @@ odoo.define('web.CustomFilterItem', function (require) {
|
||||
* @param {Event} ev
|
||||
*/
|
||||
_onValueInput(condition, ev) {
|
||||
const type = this.fields[condition.field].type;
|
||||
if (['float', 'integer', 'id'].includes(type)) {
|
||||
const previousValue = condition.value;
|
||||
const parser = field_utils.parse[type === 'float' ? 'float' : 'integer'];
|
||||
if (!ev.target.value) {
|
||||
return this._setDefaultValue(condition);
|
||||
}
|
||||
let { type } = this.fields[condition.field];
|
||||
if (type === "id") {
|
||||
type = "integer";
|
||||
}
|
||||
if (FIELD_TYPES[type] === "number") {
|
||||
try {
|
||||
const parsed = parser(ev.target.value || 0);
|
||||
// Force parsed value in the input.
|
||||
ev.target.value = condition.value = (parsed || 0);
|
||||
// Write logical value into the 'value' property
|
||||
condition.value = field_utils.parse[type](ev.target.value);
|
||||
// Write displayed value in the input and 'displayedValue' property
|
||||
condition.displayedValue = ev.target.value;
|
||||
} catch (err) {
|
||||
// Force previous value if non-parseable.
|
||||
ev.target.value = previousValue || 0;
|
||||
// Parsing error: reverts to previous value
|
||||
ev.target.value = condition.displayedValue;
|
||||
}
|
||||
} else {
|
||||
condition.value = ev.target.value || "";
|
||||
condition.value = condition.displayedValue = ev.target.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,20 +390,20 @@
|
||||
t-att-type="DECIMAL_POINT === '.' ? 'number' : 'text'"
|
||||
t-attf-title="Number using {{ DECIMAL_POINT }} as decimal separator."
|
||||
t-attf-pattern="[0-9]+([\\{{ DECIMAL_POINT }}][0-9]+)?"
|
||||
t-att-value="condition.value or (0 + DECIMAL_POINT + 0)"
|
||||
t-att-value="condition.displayedValue"
|
||||
t-on-input="_onValueInput(condition)"
|
||||
/>
|
||||
<input t-elif="['integer', 'id'].includes(fieldType)"
|
||||
class="o_input"
|
||||
step="1"
|
||||
type="number"
|
||||
t-att-value="condition.value or 0"
|
||||
t-att-value="condition.displayedValue"
|
||||
t-on-input="_onValueInput(condition)"
|
||||
/>
|
||||
<input t-else=""
|
||||
type="text"
|
||||
class="o_input"
|
||||
t-att-value="condition.value or ''"
|
||||
t-att-value="condition.displayedValue"
|
||||
t-on-input="_onValueInput(condition)"
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -327,7 +327,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) {
|
||||
});
|
||||
|
||||
QUnit.test('input value parsing', async function (assert) {
|
||||
assert.expect(6);
|
||||
assert.expect(7);
|
||||
|
||||
const cfi = await createComponent(CustomFilterItem, {
|
||||
props: {
|
||||
@@ -352,16 +352,63 @@ odoo.define('web.filter_menu_generator_tests', function (require) {
|
||||
assert.strictEqual(idInput.value, "0");
|
||||
|
||||
// Float parsing
|
||||
await testUtils.fields.editInput(floatInput, 4.2);
|
||||
await testUtils.fields.editInput(floatInput, "4.2");
|
||||
assert.strictEqual(floatInput.value, "4.2");
|
||||
await testUtils.fields.editInput(floatInput, "DefinitelyValidFloat");
|
||||
assert.strictEqual(floatInput.value, "4.2");
|
||||
// String input in a number input gives "", which is parsed as 0
|
||||
assert.strictEqual(floatInput.value, "0.0");
|
||||
|
||||
// Number parsing
|
||||
await testUtils.fields.editInput(idInput, 4);
|
||||
await testUtils.fields.editInput(idInput, "4");
|
||||
assert.strictEqual(idInput.value, "4");
|
||||
await testUtils.fields.editInput(idInput, "4.2");
|
||||
assert.strictEqual(idInput.value, "4");
|
||||
await testUtils.fields.editInput(idInput, "DefinitelyValidID");
|
||||
assert.strictEqual(idInput.value, "4");
|
||||
// String input in a number input gives "", which is parsed as 0
|
||||
assert.strictEqual(idInput.value, "0");
|
||||
|
||||
cfi.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('input value parsing with language', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const cfi = await createComponent(CustomFilterItem, {
|
||||
props: {
|
||||
fields: this.fields,
|
||||
},
|
||||
env: {
|
||||
searchModel: new ActionModel(),
|
||||
_t: Object.assign(s => s, { database: { parameters: { decimal_point: "," } }}),
|
||||
},
|
||||
translateParameters: {
|
||||
decimal_point: ",",
|
||||
thousands_sep: "",
|
||||
grouping: [3, 0],
|
||||
},
|
||||
});
|
||||
|
||||
await cpHelpers.toggleAddCustomFilter(cfi);
|
||||
await testUtils.dom.click('button.o_add_condition');
|
||||
|
||||
const [floatSelect] = cfi.el.querySelectorAll('.o_generator_menu_field');
|
||||
await testUtils.fields.editSelect(floatSelect, 'float_field');
|
||||
|
||||
const [floatInput] = cfi.el.querySelectorAll('.o_generator_menu_value .o_input');
|
||||
|
||||
// Default values
|
||||
assert.strictEqual(floatInput.value, "0,0");
|
||||
|
||||
// Float parsing
|
||||
await testUtils.fields.editInput(floatInput, '4,');
|
||||
assert.strictEqual(floatInput.value, "4,");
|
||||
await testUtils.fields.editInput(floatInput, '4,2');
|
||||
assert.strictEqual(floatInput.value, "4,2");
|
||||
await testUtils.fields.editInput(floatInput, '4,2,');
|
||||
assert.strictEqual(floatInput.value, "4,2");
|
||||
await testUtils.fields.editInput(floatInput, "DefinitelyValidFloat");
|
||||
// The input here is a string, resulting in a parsing error instead of 0
|
||||
assert.strictEqual(floatInput.value, "4,2");
|
||||
|
||||
cfi.destroy();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user