[FIX] web: statusbar field: load status with correct domain

Before this commit, there was a race condition with the statusbar
field, and more specifically with useRecordObserver.

The issue could be reproduced in the form view of project.task. In
an existing task, belonging to a project with some stages, change
the project to another project with its own stages. It might happen
that the displayed stages weren't the ones of the newly set project.
If this didn't happen directly, this happened upon saving (i.e. the
correct stages are displayed just after switching the project, but
as soon as the user saves the record, the former stages are back).

This happens because useRecordObserver used an outdated version
of the props to get the domain (i.e. the props of the component
have been updated, but useRecordObserver still used the old version,
in particular the old props.domain).

This commit fixes the issue by ensuring that we always use the last
version of the props.

opw 3693113

closes odoo/odoo#152433

Signed-off-by: Francois Georis (fge) <fge@odoo.com>
This commit is contained in:
Aaron Bohy
2024-02-02 19:49:45 +00:00
parent 6b3e0d8c84
commit b2406f72ad
2 changed files with 85 additions and 5 deletions
@@ -619,7 +619,8 @@ export function isRelational(field) {
export function useRecordObserver(callback) {
const component = useComponent();
let alive = true;
const fct = (props) => {
let props = component.props;
const fct = () => {
const def = new Deferred();
let firstCall = true;
effect(
@@ -652,10 +653,12 @@ export function useRecordObserver(callback) {
onWillDestroy(() => {
alive = false;
});
onWillStart(() => fct(component.props));
onWillUpdateProps((props) => {
if (props.record.id !== component.props.record.id) {
return fct(props);
onWillStart(() => fct());
onWillUpdateProps((nextProps) => {
const currentRecordId = props.record.id;
props = nextProps;
if (props.record.id !== currentRecordId) {
return fct();
}
});
}
@@ -3,10 +3,13 @@
import { makeFakeNotificationService } from "@web/../tests/helpers/mock_services";
import {
click,
clickSave,
editInput,
getFixture,
getNodesTextContent,
nextTick,
patchWithCleanup,
selectDropdownItem,
triggerHotkey,
} from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
@@ -816,4 +819,78 @@ QUnit.module("Fields", (hooks) => {
assert.hasClass(button, "o_first");
}
);
QUnit.test("correctly load statusbar when dynamic domain changes", async function (assert) {
serverData.models = {
stage: {
fields: {
name: { string: "Name", type: "char" },
folded: { string: "Folded", type: "boolean", default: false },
project_ids: { string: "Project", type: "many2many", relation: "project" },
},
records: [
{ id: 1, name: "Stage Project 1", project_ids: [1] },
{ id: 2, name: "Stage Project 2", project_ids: [2] },
],
},
project: {
fields: {
display_name: { string: "Name", type: "char" },
},
records: [
{ id: 1, display_name: "Project 1" },
{ id: 2, display_name: "Project 2" },
],
},
task: {
fields: {
status: { string: "Status", type: "many2one", relation: "stage" },
project_id: { string: "Project", type: "many2one", relation: "project" },
},
records: [{ id: 1, project_id: 1, status: 1 }],
},
};
serverData.models.task.onchanges = {
project_id: (obj) => {
obj.status = obj.project_id === 1 ? 1 : 2;
},
};
await makeView({
type: "form",
resModel: "task",
resId: 1,
serverData,
arch: `
<form>
<header>
<field name="status" widget="statusbar" domain="[('project_ids', 'in', project_id)]" />
</header>
<field name="project_id"/>
</form>`,
mockRPC(route, args) {
if (args.method === "search_read") {
assert.step(JSON.stringify(args.kwargs.domain));
}
},
});
assert.deepEqual(
getNodesTextContent(target.querySelectorAll(".o_statusbar_status button:not(.d-none)")),
["Stage Project 1"]
);
assert.verifySteps(['["|",["id","=",1],["project_ids","in",1]]']);
await selectDropdownItem(target, "project_id", "Project 2");
assert.deepEqual(
getNodesTextContent(target.querySelectorAll(".o_statusbar_status button:not(.d-none)")),
["Stage Project 2"]
);
assert.verifySteps(['["|",["id","=",2],["project_ids","in",2]]']);
await clickSave(target);
assert.deepEqual(
getNodesTextContent(target.querySelectorAll(".o_statusbar_status button:not(.d-none)")),
["Stage Project 2"]
);
assert.verifySteps([]);
});
});