[FIX] web: complete x2m activeField
The goal of this commit is to complete the list of activeFields for an x2many in list mode when a record is opened with the fields present in its form view. This ensures that there is always an activeField for all the data in a record. Before this commit, when the x2many's form view was not inline, when the form view was closed, the fields not present in the list view no longer had an activeField, which could cause crashes. closes odoo/odoo#130453 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { x2ManyCommands } from "@web/core/orm_service";
|
||||
import { intersection } from "@web/core/utils/arrays";
|
||||
import { pick } from "@web/core/utils/objects";
|
||||
import { completeActiveFields } from "@web/model/relational_model/utils";
|
||||
import { DataPoint } from "./datapoint";
|
||||
import { fromUnityToServerValues, getId, patchActiveFields } from "./utils";
|
||||
|
||||
@@ -202,6 +203,7 @@ export class StaticList extends DataPoint {
|
||||
extendRecord(params, record) {
|
||||
return this.model.mutex.exec(async () => {
|
||||
// extend fields and activeFields of the list with those given in params
|
||||
completeActiveFields(this.config.activeFields, params.activeFields);
|
||||
Object.assign(this.fields, params.fields);
|
||||
const activeFields = { ...params.activeFields };
|
||||
for (const fieldName in this.activeFields) {
|
||||
|
||||
@@ -70,6 +70,20 @@ function completeActiveField(activeField, extra) {
|
||||
}
|
||||
}
|
||||
|
||||
export function completeActiveFields(activeFields, extraActiveFields) {
|
||||
for (const fieldName in extraActiveFields) {
|
||||
const extraActiveField = {
|
||||
...extraActiveFields[fieldName],
|
||||
invisible: true,
|
||||
};
|
||||
if (fieldName in activeFields) {
|
||||
completeActiveField(activeFields[fieldName], extraActiveField);
|
||||
} else {
|
||||
activeFields[fieldName] = extraActiveField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createPropertyActiveField(property) {
|
||||
const { type } = property;
|
||||
|
||||
@@ -182,20 +196,10 @@ export function extractFieldsFromArchInfo({ fieldNodes, widgetNodes }, fields) {
|
||||
fieldNode.views.form,
|
||||
fieldNode.views.form.fields
|
||||
);
|
||||
for (const fieldName in formArchInfo.activeFields) {
|
||||
const formActiveField = {
|
||||
...formArchInfo.activeFields[fieldName],
|
||||
invisible: true,
|
||||
};
|
||||
if (fieldName in activeField.related.activeFields) {
|
||||
completeActiveField(
|
||||
activeField.related.activeFields[fieldName],
|
||||
formActiveField
|
||||
);
|
||||
} else {
|
||||
activeField.related.activeFields[fieldName] = formActiveField;
|
||||
}
|
||||
}
|
||||
completeActiveFields(
|
||||
activeField.related.activeFields,
|
||||
formArchInfo.activeFields
|
||||
);
|
||||
Object.assign(activeField.related.fields, formArchInfo.fields);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,7 +546,9 @@ QUnit.module("Fields", (hooks) => {
|
||||
await editInput(target, ".o_field_widget[name=parent_id] input", "ABC");
|
||||
await clickOpenedDropdownItem(target, "parent_id", "Create and edit...");
|
||||
await click(
|
||||
target.querySelector(".o_dialog:not(.o_inactive_modal) .modal-footer .o_form_button_save")
|
||||
target.querySelector(
|
||||
".o_dialog:not(.o_inactive_modal) .modal-footer .o_form_button_save"
|
||||
)
|
||||
);
|
||||
assert.strictEqual(
|
||||
target
|
||||
@@ -558,7 +560,9 @@ QUnit.module("Fields", (hooks) => {
|
||||
await nextTick();
|
||||
// close all dialogs
|
||||
await click(
|
||||
target.querySelector(".o_dialog:not(.o_inactive_modal) .modal-footer .o_form_button_save")
|
||||
target.querySelector(
|
||||
".o_dialog:not(.o_inactive_modal) .modal-footer .o_form_button_save"
|
||||
)
|
||||
);
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_dialog .o_form_view");
|
||||
@@ -3216,6 +3220,111 @@ QUnit.module("Fields", (hooks) => {
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"onchange specification complete after open sub form view not inline",
|
||||
async function (assert) {
|
||||
serverData.models.partner.onchanges.display_name = () => {};
|
||||
serverData.views = {
|
||||
"turtle,false,form": `
|
||||
<form>
|
||||
<field name="display_name"/>
|
||||
<field name="partner_ids">
|
||||
<tree>
|
||||
<field name="display_name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
};
|
||||
|
||||
await makeView({
|
||||
type: "form",
|
||||
resModel: "partner",
|
||||
serverData,
|
||||
arch: `
|
||||
<form>
|
||||
<field name="display_name"/>
|
||||
<field name="turtles">
|
||||
<tree>
|
||||
<field name="turtle_foo"/>
|
||||
</tree>
|
||||
</field>
|
||||
</form>`,
|
||||
resId: 1,
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === "onchange2" && args.model === "partner") {
|
||||
if (args.args[1].display_name === "test") {
|
||||
assert.deepEqual(args.args[3], {
|
||||
display_name: {},
|
||||
turtles: {
|
||||
fields: {
|
||||
turtle_foo: {},
|
||||
},
|
||||
limit: 40,
|
||||
order: "",
|
||||
},
|
||||
});
|
||||
} else if (args.args[1].display_name === "test2") {
|
||||
assert.deepEqual(args.args[3], {
|
||||
display_name: {},
|
||||
turtles: {
|
||||
fields: {
|
||||
display_name: {},
|
||||
partner_ids: {
|
||||
fields: {
|
||||
display_name: {},
|
||||
},
|
||||
limit: 40,
|
||||
order: "",
|
||||
},
|
||||
turtle_foo: {},
|
||||
},
|
||||
limit: 40,
|
||||
order: "",
|
||||
},
|
||||
});
|
||||
return {
|
||||
value: {
|
||||
turtles: [
|
||||
[
|
||||
1,
|
||||
2,
|
||||
{
|
||||
display_name: "yop",
|
||||
partner_ids: [[1, 2, { display_name: "plop" }]],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
await editInput(target, "div[name='display_name'] input", "test");
|
||||
await click(target.querySelector(".o_data_row .o_data_cell"));
|
||||
assert.strictEqual(
|
||||
target.querySelector(".modal [name='display_name'] input").value,
|
||||
"donatello"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".modal .o_data_row")].map((el) => el.textContent),
|
||||
["second record", "aaa"]
|
||||
);
|
||||
|
||||
await click(target.querySelector(".modal .o_form_button_save"));
|
||||
await editInput(target, "div[name='display_name'] input", "test2");
|
||||
await click(target.querySelector(".o_data_row .o_data_cell"));
|
||||
assert.strictEqual(
|
||||
target.querySelector(".modal [name='display_name'] input").value,
|
||||
"yop"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".modal .o_data_row")].map((el) => el.textContent),
|
||||
["plop", "aaa"]
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("sorting one2many fields", async function (assert) {
|
||||
serverData.models.partner.fields.foo.sortable = true;
|
||||
serverData.models.partner.records.push({ id: 23, foo: "abc", int_field: 1 });
|
||||
|
||||
Reference in New Issue
Block a user