[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:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user