[FIX] web: save a x2m with an invalid new record
The purpose of this commit is to prevent saving to a form view if you have created and edited a new invalid record in an x2many. How to reproduce - go to a form view with an x2m - create a new record with at least one required field empty in the x2m - edit another field than the required one - click on the save button Before this commit: - The view is switched to readonly mode and the new record is deleted. After this commit: - The view stays in edit mode, the new record is not deleted and a notification indicating invalid fields is displayed. closes odoo/odoo#99288 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -382,18 +382,18 @@ export class Record extends DataPoint {
|
||||
continue;
|
||||
case "properties":
|
||||
if (!this.checkPropertiesValidity(fieldName)) {
|
||||
this.setInvalidField(fieldName);
|
||||
this._setInvalidField(fieldName);
|
||||
}
|
||||
break;
|
||||
case "one2many":
|
||||
case "many2many":
|
||||
if (!(await this.checkX2ManyValidity(fieldName))) {
|
||||
this.setInvalidField(fieldName);
|
||||
this._setInvalidField(fieldName);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!isSet && this.isRequired(fieldName) && !this.data[fieldName]) {
|
||||
this.setInvalidField(fieldName);
|
||||
this._setInvalidField(fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,7 +454,7 @@ export class Record extends DataPoint {
|
||||
const list = this.data[fieldName];
|
||||
const record = list.editedRecord;
|
||||
if (record && record.isNew && !(await record.checkValidity())) {
|
||||
if (record.canBeAbandoned) {
|
||||
if (record.canBeAbandoned && !record.isDirty) {
|
||||
list.abandonRecord(record.id);
|
||||
} else {
|
||||
return false;
|
||||
@@ -481,11 +481,15 @@ export class Record extends DataPoint {
|
||||
);
|
||||
}
|
||||
|
||||
setInvalidField(fieldName) {
|
||||
_setInvalidField(fieldName) {
|
||||
this._invalidFields.add(fieldName);
|
||||
this.model.notify();
|
||||
}
|
||||
|
||||
setInvalidField(fieldName) {
|
||||
const bm = this.model.__bm__;
|
||||
bm.setDirty(this.__bm_handle__);
|
||||
this.model.notify();
|
||||
this._setInvalidField(fieldName);
|
||||
}
|
||||
|
||||
isInvalid(fieldName) {
|
||||
@@ -1134,31 +1138,24 @@ export class StaticList extends DataPoint {
|
||||
async unselectRecord(canDiscard = false) {
|
||||
// something seems wrong with switchMode --> review system?
|
||||
const editedRecord = this.editedRecord;
|
||||
if (editedRecord) {
|
||||
const handle = editedRecord.__bm_handle__;
|
||||
if (canDiscard && editedRecord.isNew && !editedRecord.isDirty) {
|
||||
this.model.__bm__.discardChanges(handle);
|
||||
if (editedRecord.canBeAbandoned) {
|
||||
this.model.__bm__.removeLine(handle);
|
||||
}
|
||||
this.__syncData();
|
||||
this.editedRecord = null;
|
||||
return await editedRecord.switchMode("readonly");
|
||||
} else if (await editedRecord.checkValidity()) {
|
||||
return await editedRecord.switchMode("readonly");
|
||||
} else {
|
||||
if (!editedRecord.isDirty) {
|
||||
this.model.__bm__.discardChanges(handle);
|
||||
if (editedRecord.canBeAbandoned) {
|
||||
this.model.__bm__.removeLine(handle);
|
||||
}
|
||||
this.editedRecord = null;
|
||||
return await editedRecord.switchMode("readonly");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!editedRecord) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
const isValid = await editedRecord.checkValidity();
|
||||
const handle = editedRecord.__bm_handle__;
|
||||
if (!editedRecord.isDirty && ((canDiscard && editedRecord.isNew) || !isValid)) {
|
||||
this.model.__bm__.discardChanges(handle);
|
||||
if (editedRecord.canBeAbandoned) {
|
||||
this.model.__bm__.removeLine(handle);
|
||||
}
|
||||
this.__syncData();
|
||||
this.editedRecord = null;
|
||||
return await editedRecord.switchMode("readonly");
|
||||
} else if (isValid) {
|
||||
return await editedRecord.switchMode("readonly");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { session } from "@web/session";
|
||||
import { Field } from "@web/views/fields/field";
|
||||
import { Record } from "@web/views/record";
|
||||
import {
|
||||
addRow,
|
||||
click,
|
||||
clickDiscard,
|
||||
clickDropdown,
|
||||
@@ -2120,24 +2121,7 @@ QUnit.module("Fields", (hooks) => {
|
||||
});
|
||||
|
||||
// Click on "Add an item"
|
||||
await click(target, ".o_field_x2many_list_row_add a");
|
||||
target.querySelector(".o_field_widget.o_field_char[name=display_name] input").value =
|
||||
"some text";
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_field_widget.o_field_char[name=display_name]",
|
||||
"should have a char field 'display_name' on this record"
|
||||
);
|
||||
assert.doesNotHaveClass(
|
||||
target.querySelector(".o_field_widget.o_field_char[name=display_name]"),
|
||||
"o_required_modifier",
|
||||
"the char field should not be required on this record"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_field_widget.o_field_char[name=display_name] input").value,
|
||||
"some text",
|
||||
"should have entered text in the char field on this record"
|
||||
);
|
||||
await addRow(target);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_field_widget.o_required_modifier[name=trululu]",
|
||||
|
||||
@@ -12562,4 +12562,39 @@ QUnit.module("Fields", (hooks) => {
|
||||
await triggerEvents(target, ".o_data_row", ["touchstart", "touchend"]);
|
||||
assert.containsNone(target, ".o_data_row_selected");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"save a record after creating and editing a new invalid record in a one2many",
|
||||
async function (assert) {
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="p">
|
||||
<tree editable="bottom">
|
||||
<field name="display_name" required="1"/>
|
||||
<field name="int_field"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
resId: 1,
|
||||
});
|
||||
|
||||
await clickEdit(target);
|
||||
await addRow(target);
|
||||
await editInput(target, ".o_field_widget[name=int_field] input", "3");
|
||||
await clickSave(target);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_data_row.o_selected_row",
|
||||
"line should not have been removed and should still be in edition"
|
||||
);
|
||||
assert.hasClass(
|
||||
target.querySelector(".o_field_widget[name=display_name]"),
|
||||
"o_field_invalid"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user