[FIX] web: save an empty many2many_tags required

Before this commit, the required attribute has no effect on the x2many
fields. It will always be valid unless one of its records is invalid.

Problem:
In some cases, we would like to be able to make some x2many fields
required. For example, many2many_tags should be invalid if it contains
no records.

In legacy, if a field is required, then it has the responsibility to
evaluate if its value is valid. To do this, each field contained the
isSet function which returns true if the value is set.

In our new architecture, it is the model that evaluates if the value of
a field is valid based on the field type. It is therefore no longer
possible to customise the evaluation of the validity of a field based
on the FieldComponent.

Solution:
This commit will reintroduce the possibility to customize the evaluation
of the validity of a field based on the FieldComponent.
To do this, we add the possibility to define a static isSet function
on the FieldComponent. If this function is present, it will be used
to evaluate if the field value is set or not.

closes odoo/odoo#98873

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2022-08-31 12:37:35 +02:00
parent ec0b100532
commit 1f43844549
4 changed files with 43 additions and 3 deletions
@@ -355,8 +355,8 @@ export class Record extends DataPoint {
}
for (const fieldName in this.activeFields) {
const fieldType = this.fields[fieldName].type;
const activeField = this.activeFields[fieldName];
if (fieldName in this._requiredFields) {
const activeField = this.activeFields[fieldName];
if (
!evalDomain(this._requiredFields[fieldName], this.evalContext) ||
(activeField && activeField.alwaysInvisible)
@@ -365,6 +365,15 @@ export class Record extends DataPoint {
continue;
}
}
const isSet =
activeField && activeField.FieldComponent && activeField.FieldComponent.isSet;
if (this.isRequired(fieldName) && isSet && !isSet(this.data[fieldName])) {
this.setInvalidField(fieldName);
continue;
}
switch (fieldType) {
case "boolean":
case "float":
@@ -383,7 +392,7 @@ export class Record extends DataPoint {
}
break;
default:
if (this.isRequired(fieldName) && !this.data[fieldName]) {
if (!isSet && this.isRequired(fieldName) && !this.data[fieldName]) {
this.setInvalidField(fieldName);
}
}
@@ -304,6 +304,7 @@ Many2ManyTagsField.supportedTypes = ["many2many"];
Many2ManyTagsField.fieldsToFetch = {
display_name: { name: "display_name", type: "char" },
};
Many2ManyTagsField.isSet = (value) => value.count > 0;
Many2ManyTagsField.extractProps = ({ attrs, field }) => {
return {
@@ -579,6 +579,15 @@ export class Record extends DataPoint {
this._removeInvalidFields([fieldName]);
continue;
}
const isSet =
activeField && activeField.FieldComponent && activeField.FieldComponent.isSet;
if (this.isRequired(fieldName) && isSet && !isSet(this.data[fieldName])) {
this.setInvalidField(fieldName);
continue;
}
switch (fieldType) {
case "boolean":
case "float":
@@ -592,7 +601,7 @@ export class Record extends DataPoint {
}
break;
default:
if (this.isRequired(fieldName) && !this.data[fieldName]) {
if (!isSet && this.isRequired(fieldName) && !this.data[fieldName]) {
this.setInvalidField(fieldName);
}
}
@@ -1559,4 +1559,25 @@ QUnit.module("Fields", (hooks) => {
"goldsilver"
);
});
QUnit.test("save a record with an empty many2many_tags required", async function (assert) {
assert.expect(3);
const form = await makeView({
type: "form",
resModel: "partner",
serverData,
arch: '<form><field name="timmy" widget="many2many_tags" required="1"/></form>',
});
patchWithCleanup(form.env.services.notification, {
add: (message, params) => {
assert.strictEqual(message.toString(), "<ul><li>pokemon</li></ul>");
assert.deepEqual(params, { title: "Invalid fields: ", type: "danger" });
},
});
await clickSave(target);
assert.containsOnce(target, "[name='timmy'].o_field_invalid");
});
});