diff --git a/addons/web/static/src/model/relational_model/static_list.js b/addons/web/static/src/model/relational_model/static_list.js index 30788de8ae5..8935dcae513 100644 --- a/addons/web/static/src/model/relational_model/static_list.js +++ b/addons/web/static/src/model/relational_model/static_list.js @@ -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) { diff --git a/addons/web/static/src/model/relational_model/utils.js b/addons/web/static/src/model/relational_model/utils.js index c64ce138c35..d4e9c5f522e 100644 --- a/addons/web/static/src/model/relational_model/utils.js +++ b/addons/web/static/src/model/relational_model/utils.js @@ -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); } } diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js index cb6887a8923..363b5255fea 100644 --- a/addons/web/static/tests/views/fields/one2many_field_tests.js +++ b/addons/web/static/tests/views/fields/one2many_field_tests.js @@ -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": ` +
+ + + + + + + `, + }; + + await makeView({ + type: "form", + resModel: "partner", + serverData, + arch: ` +
+ + + + + + + `, + 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 });