From fa1906223fa4f965b8edc74560038df64a2e03ab Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 16 Sep 2022 07:12:06 +0000 Subject: [PATCH] [FIX] web: FormView: add fieldDependencies to fields Since [1], a call to getView no longer returns the all models the list of all their fields, because a lot of them aren't necessary. However, it caused an issue in the following situation: - have a form view with an x2many displayed as a list - in the list, have a field or widget with fieldDependencies (dependency on field A) - in the arch, the x2many form view isn't inline - in readonly mode, the user clicks on a row, which will open the x2many form view - since the form view isn't inline, a call to getView is done - in the returned form view, field A isn't present. Since [1], it crashed at this point because A wasn't part of the known fields in the form view (but it was in the activeFields, since they are shared between the x2many list and form). This commit fixes the issue by adding fieldDependencies not only in activeFields, but also in fields if not already there. [1] 4636620004f0cc0e504cd6694fe8ddb6758ba811 closes odoo/odoo#100495 Signed-off-by: Aaron Bohy (aab) --- .../static/src/views/form/form_arch_parser.js | 12 +++++-- .../src/views/kanban/kanban_arch_parser.js | 12 +++++-- .../static/src/views/list/list_arch_parser.js | 6 +++- addons/web/static/src/views/utils.js | 5 ++- .../tests/views/form/form_view_tests.js | 32 +++++++++++++++++++ 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/views/form/form_arch_parser.js b/addons/web/static/src/views/form/form_arch_parser.js index 0ac621be33a..3dc52ceec9c 100644 --- a/addons/web/static/src/views/form/form_arch_parser.js +++ b/addons/web/static/src/views/form/form_arch_parser.js @@ -29,14 +29,22 @@ export class FormArchParser extends XMLParser { if (archParseBoolean(node.getAttribute("default_focus") || "")) { autofocusFieldId = fieldId; } - addFieldDependencies(activeFields, fieldInfo.FieldComponent.fieldDependencies); + addFieldDependencies( + activeFields, + models[modelName], + fieldInfo.FieldComponent.fieldDependencies + ); return false; } else if (node.tagName === "div" && node.classList.contains("oe_chatter")) { // remove this when chatter fields are declared as attributes on the root node return false; } else if (node.tagName === "widget") { const { WidgetComponent } = Widget.parseWidgetNode(node); - addFieldDependencies(activeFields, WidgetComponent.fieldDependencies); + addFieldDependencies( + activeFields, + models[modelName], + WidgetComponent.fieldDependencies + ); } }); // TODO: generate activeFields for the model based on fieldNodes (merge duplicated fields) diff --git a/addons/web/static/src/views/kanban/kanban_arch_parser.js b/addons/web/static/src/views/kanban/kanban_arch_parser.js index e0328de8922..e97fcc897e6 100644 --- a/addons/web/static/src/views/kanban/kanban_arch_parser.js +++ b/addons/web/static/src/views/kanban/kanban_arch_parser.js @@ -84,11 +84,19 @@ export class KanbanArchParser extends XMLParser { if (fieldInfo.widget === "handle") { handleField = name; } - addFieldDependencies(activeFields, fieldInfo.FieldComponent.fieldDependencies); + addFieldDependencies( + activeFields, + models[modelName], + fieldInfo.FieldComponent.fieldDependencies + ); } if (node.tagName === "widget") { const { WidgetComponent } = Widget.parseWidgetNode(node); - addFieldDependencies(activeFields, WidgetComponent.fieldDependencies); + addFieldDependencies( + activeFields, + models[modelName], + WidgetComponent.fieldDependencies + ); } // Keep track of last update so images can be reloaded when they may have changed. diff --git a/addons/web/static/src/views/list/list_arch_parser.js b/addons/web/static/src/views/list/list_arch_parser.js index fe415739c4e..f945dcdb423 100644 --- a/addons/web/static/src/views/list/list_arch_parser.js +++ b/addons/web/static/src/views/list/list_arch_parser.js @@ -86,7 +86,11 @@ export class ListArchParser extends XMLParser { if (fieldInfo.widget === "handle") { handleField = fieldInfo.name; } - addFieldDependencies(activeFields, fieldInfo.FieldComponent.fieldDependencies); + addFieldDependencies( + activeFields, + models[modelName], + fieldInfo.FieldComponent.fieldDependencies + ); if (fieldInfo.modifiers.column_invisible !== true) { const label = fieldInfo.FieldComponent.label; columns.push({ diff --git a/addons/web/static/src/views/utils.js b/addons/web/static/src/views/utils.js index f4a58ac957e..f4b03dc9fcd 100644 --- a/addons/web/static/src/views/utils.js +++ b/addons/web/static/src/views/utils.js @@ -13,13 +13,16 @@ const NUMERIC_TYPES = ["integer", "float", "monetary"]; * @param {Object} activeFields * @param {Object} [dependencies={}] */ -export function addFieldDependencies(activeFields, dependencies = {}) { +export function addFieldDependencies(activeFields, fields, dependencies = {}) { for (const [name, dependency] of Object.entries(dependencies)) { if (!(name in activeFields)) { activeFields[name] = Object.assign({ name, rawAttrs: {} }, dependency, { modifiers: { invisible: true }, }); } + if (!(name in fields)) { + fields[name] = { ...dependency }; + } } } diff --git a/addons/web/static/tests/views/form/form_view_tests.js b/addons/web/static/tests/views/form/form_view_tests.js index d166ec06d7d..5bec517bc41 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -673,6 +673,38 @@ QUnit.module("Views", (hooks) => { } ); + QUnit.test("form with o2m having a field with fieldDependencies", async function (assert) { + // In this scenario, the x2many form view isn't inline, so when we click on the record, + // it does an independant getView, which doesn't return all fields of the model. In the + // x2many list view, there's a field with fieldDependencies, and the dependency field + // (int_field) in our case, isn't in the form view. This test ensures that we can open + // the form view in this situation. + class MyField extends CharField {} + MyField.fieldDependencies = { + int_field: { type: "integer" }, + }; + fieldRegistry.add("my_widget", MyField); + serverData.models.partner.records[1].p = [1]; + await makeView({ + type: "form", + resModel: "partner", + serverData, + arch: ` +
+ + + + + +
`, + resId: 2, + }); + + assert.containsOnce(target, ".o_field_widget[name=p] .o_data_row"); + await click(target.querySelector(".o_field_widget[name=p] .o_data_row .o_data_cell")); + assert.containsOnce(target, ".modal .o_form_view .o_field_widget[name=p]"); + }); + QUnit.test("decoration-bf works on fields", async function (assert) { await makeView({ type: "form",