From 39cc399a676f0c5cbd3dc1ce1fdb89f8bf32e148 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 11 Jun 2026 15:54:40 +1000 Subject: [PATCH] Improvements for template tables (#12155) * Enable in-column filtering for model type * Enable sorting by label size * Enable backend ordering * Improve filtering for report template table * Update API version --- .../InvenTree/InvenTree/api_version.py | 6 ++++- src/backend/InvenTree/report/api.py | 13 +++++----- src/frontend/lib/types/Tables.tsx | 10 ++++---- .../AdminCenter/LabelTemplatePanel.tsx | 8 +++++-- .../AdminCenter/ReportTemplatePanel.tsx | 20 ++++++++++++++++ .../src/tables/settings/TemplateTable.tsx | 24 ++++++++++++++----- 6 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 40878949ab..7feb9adf48 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 502 +INVENTREE_API_VERSION = 503 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v503 -> 2026-06-11 : https://github.com/inventree/InvenTree/pull/12155 + - Adds additional filtering and sorting options to the LabelTemplate API endpoint + - Adds additional filtering and sorting options to the ReportTemplate API endpoint + v502 -> 2026-06-10 : https://github.com/inventree/InvenTree/pull/12142 - Prevents users from printing reports or labels against models for which they do not have adequate permissions. This change improves the security of the system by ensuring that users cannot access or print reports or labels for models they do not have permission to view. diff --git a/src/backend/InvenTree/report/api.py b/src/backend/InvenTree/report/api.py index 1d5ad521bf..f478a1b1b0 100644 --- a/src/backend/InvenTree/report/api.py +++ b/src/backend/InvenTree/report/api.py @@ -7,7 +7,6 @@ from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache import django_filters.rest_framework.filters as rest_filters -from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework.filterset import FilterSet from rest_framework.exceptions import PermissionDenied from rest_framework.generics import GenericAPIView @@ -21,7 +20,7 @@ import users.permissions from common.models import DataOutput from common.serializers import DataOutputSerializer from InvenTree.api import meta_path -from InvenTree.filters import InvenTreeSearchFilter +from InvenTree.filters import SEARCH_ORDER_FILTER from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI from plugin import PluginMixinEnum from plugin.builtin.labels.inventree_label import InvenTreeLabelPlugin @@ -81,7 +80,7 @@ class ReportFilter(ReportFilterBase): """Filter options.""" model = report.models.ReportTemplate - fields = ['landscape'] + fields = ['landscape', 'merge', 'attach_to_model', 'enabled', 'model_type'] class LabelFilter(ReportFilterBase): @@ -91,7 +90,7 @@ class LabelFilter(ReportFilterBase): """Filter options.""" model = report.models.LabelTemplate - fields = [] + fields = ['enabled'] class LabelPrint(GenericAPIView): @@ -246,9 +245,9 @@ class LabelTemplateList(TemplatePermissionMixin, LabelTemplateMixin, ListCreateA """API endpoint for viewing list of LabelTemplate objects.""" filterset_class = LabelFilter - filter_backends = [DjangoFilterBackend, InvenTreeSearchFilter] + filter_backends = SEARCH_ORDER_FILTER search_fields = ['name', 'description'] - ordering_fields = ['name', 'enabled'] + ordering_fields = ['name', 'enabled', 'width', 'height'] class LabelTemplateDetail( @@ -341,7 +340,7 @@ class ReportTemplateList(TemplatePermissionMixin, ReportTemplateMixin, ListCreat """API endpoint for viewing list of ReportTemplate objects.""" filterset_class = ReportFilter - filter_backends = [DjangoFilterBackend, InvenTreeSearchFilter] + filter_backends = SEARCH_ORDER_FILTER search_fields = ['name', 'description'] ordering_fields = ['name', 'enabled'] diff --git a/src/frontend/lib/types/Tables.tsx b/src/frontend/lib/types/Tables.tsx index d47e3818e9..b3b4e9f0b4 100644 --- a/src/frontend/lib/types/Tables.tsx +++ b/src/frontend/lib/types/Tables.tsx @@ -70,6 +70,11 @@ export type TableState = { idAccessor?: string; }; +export type TableColumnFilterType = + | string + | string[] + | (({ close }: { close: () => void }) => ReactNode); + /** * Table column properties * @@ -109,10 +114,7 @@ export type TableColumnProps = { editable?: boolean; definition?: ApiFormFieldType; render?: (record: T, index?: number) => any; - filter?: - | string - | string[] - | (({ close }: { close: () => void }) => ReactNode); + filter?: TableColumnFilterType; filtering?: boolean; width?: number; minWidth?: string | number; diff --git a/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx b/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx index f065b94068..244adda259 100644 --- a/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx +++ b/src/frontend/src/pages/Index/Settings/AdminCenter/LabelTemplatePanel.tsx @@ -10,8 +10,12 @@ function LabelTemplateTable() { templateEndpoint: ApiEndpoints.label_list, printingEndpoint: ApiEndpoints.label_print, additionalFormFields: { - width: {}, - height: {} + width: { + sortable: true + }, + height: { + sortable: true + } } }} /> diff --git a/src/frontend/src/pages/Index/Settings/AdminCenter/ReportTemplatePanel.tsx b/src/frontend/src/pages/Index/Settings/AdminCenter/ReportTemplatePanel.tsx index 8113064f70..c60343d372 100644 --- a/src/frontend/src/pages/Index/Settings/AdminCenter/ReportTemplatePanel.tsx +++ b/src/frontend/src/pages/Index/Settings/AdminCenter/ReportTemplatePanel.tsx @@ -17,24 +17,44 @@ function ReportTemplateTable() { modelType: ModelType.reporttemplate, templateEndpoint: ApiEndpoints.report_list, printingEndpoint: ApiEndpoints.report_print, + additionalFilters: [ + { + name: 'landscape', + label: t`Landscape`, + type: 'boolean' + }, + { + name: 'merge', + label: t`Merge`, + type: 'boolean' + }, + { + name: 'attach_to_model', + label: t`Attach to Model`, + type: 'boolean' + } + ], additionalFormFields: { page_size: { label: t`Page Size` }, landscape: { label: t`Landscape`, + filter: 'landscape', modelRenderer: (instance: any) => ( ) }, merge: { label: t`Merge`, + filter: 'merge', modelRenderer: (instance: any) => ( ) }, attach_to_model: { label: t`Attach to Model`, + filter: 'attach_to_model', modelRenderer: (instance: any) => ( ) diff --git a/src/frontend/src/tables/settings/TemplateTable.tsx b/src/frontend/src/tables/settings/TemplateTable.tsx index de06ceabd8..69cea1fd55 100644 --- a/src/frontend/src/tables/settings/TemplateTable.tsx +++ b/src/frontend/src/tables/settings/TemplateTable.tsx @@ -18,8 +18,8 @@ import { apiUrl } from '@lib/functions/Api'; import { identifierString } from '@lib/functions/Conversion'; import useTable from '@lib/hooks/UseTable'; import type { TableFilter } from '@lib/types/Filters'; -import type { ApiFormFieldSet } from '@lib/types/Forms'; -import type { TableColumn } from '@lib/types/Tables'; +import type { ApiFormFieldSet, ApiFormFieldType } from '@lib/types/Forms'; +import type { TableColumn, TableColumnFilterType } from '@lib/types/Tables'; import { CodeEditor, PdfPreview, @@ -68,11 +68,21 @@ export type TemplateI = { template: string; }; +// Additional field props to control the column behaviour in the template table +interface TemplateFormFieldType extends ApiFormFieldType { + sortable?: boolean; + switchable?: boolean; + filter?: TableColumnFilterType; +} + +type TemplateFormFieldSet = Record; + export interface TemplateProps { modelType: ModelType.labeltemplate | ModelType.reporttemplate; templateEndpoint: ApiEndpoints; printingEndpoint: ApiEndpoints; - additionalFormFields?: ApiFormFieldSet; + additionalFilters?: TableFilter[]; + additionalFormFields?: TemplateFormFieldSet; } export function TemplateDrawer({ @@ -233,6 +243,7 @@ export function TemplateTable({ }, { accessor: 'model_type', + filter: 'model_type', sortable: true, switchable: false }, @@ -276,10 +287,10 @@ export function TemplateTable({ }, ...Object.entries(additionalFormFields || {}).map(([key, field]) => ({ accessor: key, - ...field, - title: field.label, sortable: false, switchable: true, + title: field.label, + ...field, render: field.modelRenderer })), BooleanColumn({ accessor: 'enabled', title: t`Enabled` }) @@ -391,6 +402,7 @@ export function TemplateTable({ const tableFilters: TableFilter[] = useMemo(() => { return [ + ...(templateProps.additionalFilters || []), { name: 'enabled', label: t`Enabled`, @@ -404,7 +416,7 @@ export function TemplateTable({ choices: modelTypeFilters.choices } ]; - }, [modelTypeFilters.choices]); + }, [templateProps.additionalFilters, modelTypeFilters.choices]); return ( <>