diff --git a/addons/web/static/src/views/basic_relational_model.js b/addons/web/static/src/views/basic_relational_model.js index 88043ce1f97..68b3037e79c 100644 --- a/addons/web/static/src/views/basic_relational_model.js +++ b/addons/web/static/src/views/basic_relational_model.js @@ -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; } } diff --git a/addons/web/static/tests/views/fields/many2one_field_tests.js b/addons/web/static/tests/views/fields/many2one_field_tests.js index 31f841f068e..2bf3046c82d 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -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]", diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js index 0910a211c25..c75366ceda2 100644 --- a/addons/web/static/tests/views/fields/one2many_field_tests.js +++ b/addons/web/static/tests/views/fields/one2many_field_tests.js @@ -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: ` +
`, + 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" + ); + } + ); });