diff --git a/addons/web/static/src/views/fields/x2many/x2many_field.js b/addons/web/static/src/views/fields/x2many/x2many_field.js index c38a75d28f6..b2f2f274675 100644 --- a/addons/web/static/src/views/fields/x2many/x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/x2many_field.js @@ -17,6 +17,7 @@ import { KanbanRenderer } from "@web/views/kanban/kanban_renderer"; import { ListRenderer } from "@web/views/list/list_renderer"; import { computeViewClassName } from "@web/views/utils"; import { ViewButton } from "@web/views/view_button/view_button"; +import { useService } from "@web/core/utils/hooks"; import { Component } from "@odoo/owl"; @@ -123,6 +124,7 @@ export class X2ManyField extends Component { ]; return selectCreate(p); }; + this.action = useService("action"); } get activeField() { @@ -221,9 +223,25 @@ export class X2ManyField extends Component { !this.props.readonly && ("editable" in params ? params.editable : editable); this.onAdd(params); }; + const openFormView = props.editable ? archInfo.openFormView : false; + props.onOpenFormView = openFormView ? this.switchToForm.bind(this) : undefined; return props; } + switchToForm(record) { + this.action.doAction( + { + type: "ir.actions.act_window", + views: [[false, "form"]], + res_id: record.resId, + res_model: this.list.resModel, + }, + { + props: { resIds: this.list.resIds }, + } + ); + } + async onAdd({ context, editable } = {}) { const domain = typeof this.props.domain === "function" ? this.props.domain() : this.props.domain; 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 f4b3671c0f2..d63d481c4b8 100644 --- a/addons/web/static/src/views/list/list_arch_parser.js +++ b/addons/web/static/src/views/list/list_arch_parser.js @@ -188,6 +188,10 @@ export class ListArchParser extends XMLParser { ? archParseBoolean(node.getAttribute("multi_edit") || "") : false; + treeAttr.openFormView = treeAttr.editable + ? archParseBoolean(xmlDoc.getAttribute("open_form_view") || "") + : false; + const limitAttr = node.getAttribute("limit"); treeAttr.limit = limitAttr && parseInt(limitAttr, 10); diff --git a/addons/web/static/src/views/list/list_controller.js b/addons/web/static/src/views/list/list_controller.js index c0ac3a6a2c9..33c7b509854 100644 --- a/addons/web/static/src/views/list/list_controller.js +++ b/addons/web/static/src/views/list/list_controller.js @@ -47,6 +47,8 @@ export class ListController extends Component { this.rootRef = useRef("root"); this.archInfo = this.props.archInfo; + const openFormView = this.props.editable ? this.archInfo.openFormView : false; + this.onOpenFormView = openFormView ? this.openRecord.bind(this) : undefined; this.activeActions = this.archInfo.activeActions; this.editable = this.activeActions.edit && this.props.editable ? this.archInfo.editable : false; diff --git a/addons/web/static/src/views/list/list_controller.xml b/addons/web/static/src/views/list/list_controller.xml index 68c8e9cc600..25f55725a45 100644 --- a/addons/web/static/src/views/list/list_controller.xml +++ b/addons/web/static/src/views/list/list_controller.xml @@ -75,6 +75,7 @@ archInfo="archInfo" allowSelectors="props.allowSelectors" editable="editable" + onOpenFormView="onOpenFormView" openRecord.bind="openRecord" noContentHelp="props.info.noContentHelp" onAdd.bind="createRecord" diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index 6b9c3bea335..ae3b99e0bd5 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -269,7 +269,11 @@ export class ListRenderer extends Component { return { ...getPropertyFieldInfo(propertyField), id: `${column.id}_${propertyField.name}`, - column_invisible: combineModifiers(propertyField.column_invisible, column.column_invisible, 'OR'), + column_invisible: combineModifiers( + propertyField.column_invisible, + column.column_invisible, + "OR" + ), classNames: column.classNames, optional: "hide", type: "field", @@ -437,6 +441,9 @@ export class ListRenderer extends Component { if (this.activeActions.onDelete || this.displayOptionalFields) { nbCols++; } + if (this.props.onOpenFormView) { + nbCols++; + } return nbCols; } @@ -571,8 +578,9 @@ export class ListRenderer extends Component { get optionalFieldGroups() { const propertyGroups = {}; const optionalFields = []; - const optionalColumns = this.allColumns.filter((col) => - col.optional && !this.evalColumnInvisible(col.column_invisible)); + const optionalColumns = this.allColumns.filter( + (col) => col.optional && !this.evalColumnInvisible(col.column_invisible) + ); for (const col of optionalColumns) { const optionalField = { label: col.label, @@ -597,8 +605,9 @@ export class ListRenderer extends Component { } get hasOptionalFields() { - return this.allColumns.some((col) => - col.optional && !this.evalColumnInvisible(col.column_invisible)); + return this.allColumns.some( + (col) => col.optional && !this.evalColumnInvisible(col.column_invisible) + ); } get displayOptionalFields() { @@ -996,6 +1005,9 @@ export class ListRenderer extends Component { if (this.hasSelectors) { colspan++; } + if (this.props.onOpenFormView) { + colspan++; + } return colspan; } @@ -2122,6 +2134,7 @@ ListRenderer.props = [ "cycleOnTab?", "allowSelectors?", "editable?", + "onOpenFormView?", "noContentHelp?", "nestedKeyOptionalFieldsData?", "onOptionalFieldsChanged?", diff --git a/addons/web/static/src/views/list/list_renderer.scss b/addons/web/static/src/views/list/list_renderer.scss index 72aac79d4c4..d670d69c9bb 100644 --- a/addons/web/static/src/views/list/list_renderer.scss +++ b/addons/web/static/src/views/list/list_renderer.scss @@ -73,7 +73,7 @@ } tbody > tr > td:not(.o_list_record_selector) { - &:not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove) { + &:not(.o_handle_cell):not(.o_list_button):not(.o_list_record_remove):not(.o_list_record_open_form_view) { @include o-text-overflow(table-cell); &.o_list_text { white-space: pre-wrap; @@ -148,7 +148,7 @@ width: 40px !important; // Force to 40px regardless by the font-size } - .o_list_record_remove, .o_handle_cell { + .o_list_record_remove, .o_handle_cell, .o_list_record_open_form_view { width: 1px; // to prevent the column to expand } @@ -159,7 +159,7 @@ } } - .o_list_record_remove button { + .o_list_record_remove button, .o_list_record_open_form_view button { padding: 0px; background: none; border-style: none; @@ -167,6 +167,10 @@ cursor: pointer; } + .o_list_record_remove button, button { + padding-top: 5px; + } + .o_keyboard_navigation { th:focus-within, td:focus-within { --table-accent-bg: none; @@ -259,8 +263,14 @@ cursor: col-resize; } + .o_list_open_form_view { + width: 64px; + min-width: 64px + } + .o_list_actions_header { width: 32px; + min-width: 32px } .o_data_row.o_list_no_open { diff --git a/addons/web/static/src/views/list/list_renderer.xml b/addons/web/static/src/views/list/list_renderer.xml index eeff5f8ac73..b21255dad2c 100644 --- a/addons/web/static/src/views/list/list_renderer.xml +++ b/addons/web/static/src/views/list/list_renderer.xml @@ -41,7 +41,8 @@