diff --git a/addons/web/static/src/views/kanban/kanban_controller.js b/addons/web/static/src/views/kanban/kanban_controller.js index a770039deb7..9c010366fb8 100644 --- a/addons/web/static/src/views/kanban/kanban_controller.js +++ b/addons/web/static/src/views/kanban/kanban_controller.js @@ -8,7 +8,7 @@ import { standardViewProps } from "@web/views/standard_view_props"; import { MultiRecordViewButton } from "@web/views/view_button/multi_record_view_button"; import { useViewButtons } from "@web/views/view_button/view_button_hook"; import { useSetupView } from "@web/views/view_hook"; -import { KanbanRenderer } from "./kanban_renderer"; +import { canQuickCreate, KanbanRenderer } from "./kanban_renderer"; import { Component, useRef } from "@odoo/owl"; @@ -94,7 +94,7 @@ export class KanbanController extends Component { async createRecord(group) { const { activeActions, onCreate } = this.props.archInfo; const { root } = this.model; - if (activeActions.quickCreate && onCreate === "quick_create" && root.canQuickCreate()) { + if (activeActions.quickCreate && onCreate === "quick_create" && canQuickCreate(root)) { await root.quickCreate(group); } else if (onCreate && onCreate !== "quick_create") { const options = { diff --git a/addons/web/static/src/views/kanban/kanban_record.js b/addons/web/static/src/views/kanban/kanban_record.js index 18b92744cac..b82f5297dd6 100644 --- a/addons/web/static/src/views/kanban/kanban_record.js +++ b/addons/web/static/src/views/kanban/kanban_record.js @@ -213,7 +213,8 @@ export class KanbanRecord extends Component { this.record = getFormattedRecord(this.props.record); // Widget - const deletable = activeActions.delete && (!list.groupedBy || !list.groupedBy("m2m")); + const deletable = + activeActions.delete && (!list.groupByField || list.groupByField.type !== "many2many"); const editable = activeActions.edit; this.widget = { deletable, diff --git a/addons/web/static/src/views/kanban/kanban_renderer.js b/addons/web/static/src/views/kanban/kanban_renderer.js index 3037250ea60..ad59cf0b8be 100644 --- a/addons/web/static/src/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/views/kanban/kanban_renderer.js @@ -10,19 +10,46 @@ import { useBus, useService } from "@web/core/utils/hooks"; import { useSortable } from "@web/core/utils/sortable"; import { sprintf } from "@web/core/utils/strings"; import { session } from "@web/session"; -import { isAllowedDateField } from "@web/views/relational_model"; -import { isNull, isRelational } from "@web/views/utils"; +import { archParseBoolean, isNull, isRelational } from "@web/views/utils"; +import { ColumnProgress } from "@web/views/view_components/column_progress"; import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog"; import { useBounceButton } from "@web/views/view_hook"; -import { ColumnProgress } from "@web/views/view_components/column_progress"; import { KanbanColumnQuickCreate } from "./kanban_column_quick_create"; import { KanbanRecord } from "./kanban_record"; import { KanbanRecordQuickCreate } from "./kanban_record_quick_create"; -import { Component, useState, useRef, onWillDestroy } from "@odoo/owl"; +import { Component, onWillDestroy, useRef, useState } from "@odoo/owl"; const DRAGGABLE_GROUP_TYPES = ["many2one"]; const MOVABLE_RECORD_TYPES = ["char", "boolean", "integer", "selection", "many2one"]; +const QUICK_CREATE_FIELD_TYPES = ["char", "boolean", "many2one", "selection", "many2many"]; + +/** + * @param {Object} groupByField + * @returns {boolean} + */ +export function isAllowedDateField(groupByField) { + return ( + ["date", "datetime"].includes(groupByField.type) && + archParseBoolean(groupByField.attrs.allow_group_range_value) + ); +} + +/** + * @param {DynamicList} list + * @returns {boolean} + */ +export function canQuickCreate(list) { + if (list.groups && !list.groups.length) { + return false; + } + + return ( + list.groupByField && + (isAllowedDateField(list.groupByField) || + QUICK_CREATE_FIELD_TYPES.includes(list.groupByField.type)) + ); +} export class KanbanRenderer extends Component { static template = "web.KanbanRenderer"; @@ -225,7 +252,7 @@ export class KanbanRenderer extends Component { } get showNoContentHelper() { - const { model, isGrouped, groups } = this.props.list; + const { model, isGrouped, groupByField, groups } = this.props.list; if (model.useSampleModel) { return true; } @@ -234,7 +261,7 @@ export class KanbanRenderer extends Component { return false; } if (groups.length === 0) { - return !this.props.list.groupedBy("m2o"); + return groupByField.type !== "many2one"; } } return !model.hasData(); @@ -307,7 +334,7 @@ export class KanbanRenderer extends Component { getGroupUnloadedCount(group) { const progressBar = group.activeProgressBar; - const records = group.getAggregableRecords(); + const records = group.list.records.filter((r) => !r.isInQuickCreation); return (progressBar ? progressBar.count : group.count) - records.length; } @@ -350,12 +377,16 @@ export class KanbanRenderer extends Component { canArchiveGroup(group) { const { activeActions } = this.props.archInfo; const hasActiveField = "active" in group.fields; - return activeActions.archiveGroup && hasActiveField && !this.props.list.groupedBy("m2m"); + return ( + activeActions.archiveGroup && + hasActiveField && + this.props.list.groupByField.type !== "many2many" + ); } canCreateGroup() { const { activeActions } = this.props.archInfo; - return activeActions.createGroup && this.props.list.groupedBy("m2o"); + return activeActions.createGroup && this.props.list.groupByField.type === "many2one"; } canDeleteGroup(group) { @@ -366,10 +397,7 @@ export class KanbanRenderer extends Component { canDeleteRecord() { const { activeActions } = this.props.archInfo; - return ( - activeActions.delete && - (!this.props.list.groupedBy || !this.props.list.groupedBy("m2m")) - ); + return activeActions.delete && this.props.list.groupByField.type !== "many2many"; } canEditGroup(group) { @@ -383,7 +411,7 @@ export class KanbanRenderer extends Component { } canQuickCreate() { - return this.props.archInfo.activeActions.quickCreate && this.props.list.canQuickCreate(); + return this.props.archInfo.activeActions.quickCreate && canQuickCreate(this.props.list); } // ------------------------------------------------------------------------ diff --git a/addons/web/static/src/views/relational_model.js b/addons/web/static/src/views/relational_model.js index e4b56b3683d..16ded49f821 100644 --- a/addons/web/static/src/views/relational_model.js +++ b/addons/web/static/src/views/relational_model.js @@ -20,25 +20,17 @@ import { memoize } from "@web/core/utils/functions"; import { pick } from "@web/core/utils/objects"; import { escape } from "@web/core/utils/strings"; import { session } from "@web/session"; +import { getFieldFromRegistry } from "@web/views/fields/field"; import { FormArchParser } from "@web/views/form/form_arch_parser"; import { ListConfirmationDialog } from "@web/views/list/list_confirmation_dialog"; import { Model } from "@web/views/model"; -import { - archParseBoolean, - evalDomain, - isNumeric, - isRelational, - isX2Many, - orderByToString, -} from "@web/views/utils"; -import { getFieldFromRegistry } from "@web/views/fields/field"; +import { evalDomain, isNumeric, isRelational, isX2Many, orderByToString } from "@web/views/utils"; const { DateTime } = luxon; const preloadedDataRegistry = registry.category("preloadedData"); const { CREATE, UPDATE, DELETE, FORGET, LINK_TO, DELETE_ALL, REPLACE_WITH } = x2ManyCommands; -const QUICK_CREATE_FIELD_TYPES = ["char", "boolean", "many2one", "selection", "many2many"]; const AGGREGATABLE_FIELD_TYPES = ["float", "integer", "monetary"]; // types that can be aggregated in grouped views const DEFAULT_HANDLE_FIELD = "sequence"; const DEFAULT_QUICK_CREATE_FIELDS = { @@ -64,17 +56,6 @@ const DEFAULT_QUICK_CREATE_VIEW = { * }} RawContext */ -/** - * @param {Object} groupByField - * @returns {boolean} - */ -export function isAllowedDateField(groupByField) { - return ( - ["date", "datetime"].includes(groupByField.type) && - archParseBoolean(groupByField.attrs.allow_group_range_value) - ); -} - /** * @param {RawContext} rawContext * @param {Context} defaultContext @@ -1674,14 +1655,6 @@ class DynamicList extends DataPoint { return resIds; } - canQuickCreate() { - return ( - this.groupByField && - (isAllowedDateField(this.groupByField) || - QUICK_CREATE_FIELD_TYPES.includes(this.groupByField.type)) - ); - } - canResequence() { return this.model.handleField || DEFAULT_HANDLE_FIELD in this.fields; } @@ -2326,10 +2299,6 @@ export class DynamicGroupList extends DynamicList { return group; } - canQuickCreate() { - return super.canQuickCreate() && this.groups.length; - } - /** * @see {_createGroup} */ @@ -2381,37 +2350,6 @@ export class DynamicGroupList extends DynamicList { return state; } - /** - * @param {string} shortType - * @returns {boolean} - */ - groupedBy(shortType) { - const { type } = this.groupByField; - switch (shortType) { - case "m2o": - case "many2one": { - return type === "many2one"; - } - case "o2m": - case "one2many": { - return type === "one2many"; - } - case "m2m": - case "many2many": { - return type === "many2many"; - } - case "m2x": - case "many2x": { - return ["many2one", "many2many"].includes(type); - } - case "x2m": - case "x2many": { - return ["one2many", "many2many"].includes(type); - } - } - return false; - } - hasAggregate(fieldName) { const group = this.groups[0]; return group && fieldName in group.aggregates; @@ -2601,7 +2539,7 @@ export class DynamicGroupList extends DynamicList { } else { groupParams.displayName = Array.isArray(value) ? value[1] : value; } - if (this.groupedBy("m2x")) { + if (["many2one", "many2many"].includes(this.groupByField.type)) { groupParams.recordParams = this.groupByInfo[this.firstGroupBy]; } break; @@ -2813,10 +2751,6 @@ export class Group extends DataPoint { }; } - getAggregableRecords() { - return this.list.records.filter((r) => !r.isInQuickCreation); - } - getAggregates(fieldName) { return fieldName ? this.aggregates[fieldName] || 0 : this.count; }