[FIX] web: wait for static list operation before saving
Have a required many2manytags field and a slow network. The field is initially empty. Add a tag to the relation and directly save. An invalid field notification is displayed, because the field is still considered empty. This commit ensures that we wait for the operations done in the static list before saving. Part of task~3179751 Part-of: odoo/odoo#114024
This commit is contained in:
@@ -168,9 +168,11 @@ export class StaticList extends DataPoint {
|
||||
});
|
||||
}
|
||||
|
||||
async delete(record) {
|
||||
await this._applyCommands([[x2ManyCommands.DELETE, record.resId || record._virtualId]]);
|
||||
this._onUpdate();
|
||||
delete(record) {
|
||||
return this.model.mutex.exec(async () => {
|
||||
await this._applyCommands([[x2ManyCommands.DELETE, record.resId || record._virtualId]]);
|
||||
await this._onUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
async enterEditMode(record) {
|
||||
@@ -279,9 +281,11 @@ export class StaticList extends DataPoint {
|
||||
});
|
||||
}
|
||||
|
||||
async forget(record) {
|
||||
await this._applyCommands([[x2ManyCommands.FORGET, record.resId]]);
|
||||
this._onUpdate();
|
||||
forget(record) {
|
||||
return this.model.mutex.exec(async () => {
|
||||
await this._applyCommands([[x2ManyCommands.FORGET, record.resId]]);
|
||||
await this._onUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
leaveEditMode({ discard, canAbandon, validate } = {}) {
|
||||
@@ -312,9 +316,11 @@ export class StaticList extends DataPoint {
|
||||
});
|
||||
}
|
||||
|
||||
async linkTo(resId, serverData) {
|
||||
await this._applyCommands([[x2ManyCommands.LINK_TO, resId, serverData]]);
|
||||
this._onUpdate();
|
||||
linkTo(resId, serverData) {
|
||||
return this.model.mutex.exec(async () => {
|
||||
await this._applyCommands([[x2ManyCommands.LINK_TO, resId, serverData]]);
|
||||
await this._onUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
load({ limit, offset, orderBy } = {}) {
|
||||
@@ -338,27 +344,29 @@ export class StaticList extends DataPoint {
|
||||
}
|
||||
|
||||
async replaceWith(ids, { reload = false, silent = false } = {}) {
|
||||
const resIds = reload ? ids : ids.filter((id) => !this._cache[id]);
|
||||
if (resIds.length) {
|
||||
const records = await this.model._loadRecords({
|
||||
...this.config,
|
||||
resIds,
|
||||
context: this.context,
|
||||
});
|
||||
for (const record of records) {
|
||||
this._createRecordDatapoint(record);
|
||||
return this.model.mutex.exec(async () => {
|
||||
const resIds = reload ? ids : ids.filter((id) => !this._cache[id]);
|
||||
if (resIds.length) {
|
||||
const records = await this.model._loadRecords({
|
||||
...this.config,
|
||||
resIds,
|
||||
context: this.context,
|
||||
});
|
||||
for (const record of records) {
|
||||
this._createRecordDatapoint(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.records = ids.map((id) => this._cache[id]);
|
||||
const updateCommandsToKeep = this._commands.filter(
|
||||
(c) => c[0] === x2ManyCommands.UPDATE && ids.includes(c[1])
|
||||
);
|
||||
this._commands = [x2ManyCommands.replaceWith(ids)].concat(updateCommandsToKeep);
|
||||
this._currentIds = [...ids];
|
||||
this.count = this._currentIds.length;
|
||||
if (!silent) {
|
||||
this._onUpdate();
|
||||
}
|
||||
this.records = ids.map((id) => this._cache[id]);
|
||||
const updateCommandsToKeep = this._commands.filter(
|
||||
(c) => c[0] === x2ManyCommands.UPDATE && ids.includes(c[1])
|
||||
);
|
||||
this._commands = [x2ManyCommands.replaceWith(ids)].concat(updateCommandsToKeep);
|
||||
this._currentIds = [...ids];
|
||||
this.count = this._currentIds.length;
|
||||
if (!silent) {
|
||||
await this._onUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async resequence(movedId, targetId) {
|
||||
|
||||
@@ -1758,6 +1758,55 @@ QUnit.module("Fields", (hooks) => {
|
||||
assert.containsOnce(target, "[name='timmy'].o_field_invalid");
|
||||
});
|
||||
|
||||
QUnit.test("set a required many2many_tags and save directly", async function (assert) {
|
||||
let def;
|
||||
const form = await makeView({
|
||||
type: "form",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: '<form><field name="timmy" widget="many2many_tags" required="1"/></form>',
|
||||
async mockRPC(route, args) {
|
||||
assert.step(args.method);
|
||||
if (args.method === "web_read") {
|
||||
await def;
|
||||
}
|
||||
}
|
||||
});
|
||||
patchWithCleanup(form.env.services.notification, {
|
||||
add: () => assert.step("notification"),
|
||||
});
|
||||
|
||||
assert.verifySteps([
|
||||
"get_views",
|
||||
"onchange2",
|
||||
]);
|
||||
|
||||
assert.containsNone(target, ".o_tag");
|
||||
|
||||
def = makeDeferred();
|
||||
await clickDropdown(target, "timmy");
|
||||
await clickOpenedDropdownItem(target, "timmy", "gold");
|
||||
assert.containsNone(target, ".o_tag");
|
||||
|
||||
assert.verifySteps([
|
||||
"name_search",
|
||||
"web_read",
|
||||
]);
|
||||
|
||||
await clickSave(target);
|
||||
assert.doesNotHaveClass(target, "[name='timmy']", "o_field_invalid");
|
||||
|
||||
assert.verifySteps([]);
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
|
||||
assert.verifySteps([
|
||||
"create",
|
||||
"web_read",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("Many2ManyTagsField with option 'no_quick_create' set to true", async (assert) => {
|
||||
serverData.views = {
|
||||
"partner_type,false,form": `<form><field name="name"/><field name="color"/></form>`,
|
||||
|
||||
Reference in New Issue
Block a user