[FIX] web: leave a listView with an empty new record

Before this commit, in a list view, if you create a new record without
modifying it and then exit the action then your new empty record will
be saved.

This causes two problems:
- you don't want to save a new empty record
- you get a "View can't be saved" error if there is a require field

The solution is to abandon new invalid records if they have not yet
been modified.

closes odoo/odoo#97169

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2022-08-02 11:49:00 +02:00
parent ffc525419a
commit f10d5b665d
4 changed files with 72 additions and 19 deletions
@@ -372,11 +372,11 @@ export class Record extends DataPoint {
async switchMode(mode) {
if (this.mode === mode) {
return;
return true;
}
const preventSwitch = await this._onWillSwitchMode(this, mode);
if (preventSwitch === false) {
return;
const canSwitch = await this._onWillSwitchMode(this, mode);
if (canSwitch === false) {
return false;
}
if (mode === "edit") {
@@ -397,6 +397,7 @@ export class Record extends DataPoint {
}
this.mode = mode;
this.model.notify();
return true;
}
/**
@@ -1094,9 +1095,9 @@ export class StaticList extends DataPoint {
}
this.__syncData();
this.editedRecord = null;
await editedRecord.switchMode("readonly");
return await editedRecord.switchMode("readonly");
} else if (await editedRecord.checkValidity()) {
await editedRecord.switchMode("readonly");
return await editedRecord.switchMode("readonly");
} else {
if (!editedRecord.isDirty) {
this.model.__bm__.discardChanges(handle);
@@ -1104,7 +1105,7 @@ export class StaticList extends DataPoint {
this.model.__bm__.removeLine(handle);
}
this.editedRecord = null;
await editedRecord.switchMode("readonly");
return await editedRecord.switchMode("readonly");
}
return false;
}
@@ -86,9 +86,10 @@ export class ListController extends Component {
useSetupView({
rootRef: this.rootRef,
beforeLeave: async () => {
const editedRecord = this.model.root.editedRecord;
const list = this.model.root;
const editedRecord = list.editedRecord;
if (editedRecord) {
if (!(await editedRecord.save())) {
if (!(await list.unselectRecord(true))) {
throw new Error("View can't be saved");
}
}
+11 -10
View File
@@ -853,15 +853,15 @@ export class Record extends DataPoint {
/**
* @param {"edit" | "readonly"} mode
* @returns {Promise<void>}
* @returns {Promise<Boolean>}
*/
async switchMode(mode) {
if (this.mode === mode) {
return;
return true;
}
const preventSwitch = await this._onWillSwitchMode(this, mode);
if (preventSwitch === false) {
return;
const canSwitch = await this._onWillSwitchMode(this, mode);
if (canSwitch === false) {
return false;
}
if (mode === "readonly") {
for (const fieldName in this.activeFields) {
@@ -876,6 +876,7 @@ export class Record extends DataPoint {
}
this.mode = mode;
this.model.notify();
return true;
}
toggleSelection(selected) {
@@ -1412,7 +1413,7 @@ class DynamicList extends DataPoint {
abandonRecord(recordId) {
const record = this.records.find((r) => r.id === recordId);
this.removeRecord(record);
return this.removeRecord(record);
}
/**
@@ -1697,8 +1698,8 @@ class DynamicList extends DataPoint {
return records;
}
async unselectRecord() {
await unselectRecord(this.editedRecord, this.abandonRecord.bind(this));
unselectRecord() {
return unselectRecord(this.editedRecord, this.abandonRecord.bind(this));
}
}
@@ -3182,8 +3183,8 @@ export class StaticList extends DataPoint {
await Promise.all(proms);
}
async unselectRecord() {
await unselectRecord(this.editedRecord);
unselectRecord() {
return unselectRecord(this.editedRecord);
}
}
@@ -13615,6 +13615,56 @@ QUnit.module("Views", (hooks) => {
assert.containsN(target, ".o_data_row", 5);
});
QUnit.test(
"Auto save: create a new record without modifying it and leave action",
async function (assert) {
serverData.models.foo.fields.foo.required = true;
serverData.actions = {
1: {
id: 1,
name: "Action 1",
res_model: "foo",
type: "ir.actions.act_window",
views: [[2, "list"]],
search_view_id: [1, "search"],
},
2: {
id: 2,
name: "Action 2",
res_model: "foo",
type: "ir.actions.act_window",
views: [[3, "list"]],
search_view_id: [1, "search"],
},
};
serverData.views = {
"foo,1,search": "<search></search>",
"foo,2,list": '<tree editable="top"><field name="foo"/></tree>',
"foo,3,list": '<tree editable="top"><field name="foo"/></tree>',
};
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
assert.deepEqual(
[...target.querySelectorAll(".o_data_cell")].map((el) => el.textContent),
["yop", "blip", "gnap", "blip"]
);
assert.containsN(target, ".o_data_row", 4);
await click(target, ".o_list_button_add");
assert.containsN(target, ".o_data_row", 5);
// change action and come back
await doAction(webClient, 2);
await doAction(webClient, 1, { clearBreadcrumbs: true });
assert.deepEqual(
[...target.querySelectorAll(".o_data_cell")].map((el) => el.textContent),
["yop", "blip", "gnap", "blip"]
);
assert.containsN(target, ".o_data_row", 4);
}
);
QUnit.test("Auto save: modify a record and leave action", async function (assert) {
serverData.actions = {
1: {