From 7cbaeb159ebd6771299f72f9810dfe26a193eec7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 20 Aug 2024 11:21:38 +1000 Subject: [PATCH] [PUI] Sub builds table (#7932) * Allow table filters to be marked "inactive" * Allow build orders to be filtering by 'cascading' parent * Update build order table * Bump API version --- .../InvenTree/InvenTree/api_version.py | 9 +- src/backend/InvenTree/build/api.py | 31 ++++- src/backend/InvenTree/build/serializers.py | 4 + src/frontend/src/tables/Filter.tsx | 1 + src/frontend/src/tables/InvenTreeTable.tsx | 14 +- .../src/tables/build/BuildOrderTable.tsx | 126 ++++++++++-------- 6 files changed, 117 insertions(+), 68 deletions(-) diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 94cf10e91e..fab6ad53f0 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,14 +1,19 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 241 +INVENTREE_API_VERSION = 242 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ -v241 - 2024-09-18 : https://github.com/inventree/InvenTree/pull/7906 +v242 - 2024-08-20 : https://github.com/inventree/InvenTree/pull/7932 + - Adds "level" attribute to BuildOrder serializer + - Allow ordering of BuildOrder API by "level" attribute + - Allow "parent" filter for BuildOrder API to have "cascade=True" option + +v241 - 2024-08-18 : https://github.com/inventree/InvenTree/pull/7906 - Adjusts required fields for the MeUserDetail endpoint v240 - 2024-08-16 : https://github.com/inventree/InvenTree/pull/7900 diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 67a7b0ef17..d456b2d78b 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -34,7 +34,6 @@ class BuildFilter(rest_filters.FilterSet): """Metaclass options.""" model = Build fields = [ - 'parent', 'sales_order', 'part', ] @@ -49,6 +48,35 @@ class BuildFilter(rest_filters.FilterSet): return queryset.filter(status__in=BuildStatusGroups.ACTIVE_CODES) return queryset.exclude(status__in=BuildStatusGroups.ACTIVE_CODES) + cascade = rest_filters.BooleanFilter(label=_('Cascade'), method='filter_cascade') + + def filter_cascade(self, queryset, name, value): + """Filter by whether or not the build is a 'cascade' build. + + Note: this only applies when the 'parent' field filter is specified. + """ + + # No filtering here, see 'filter_parent' + return queryset + + parent = rest_filters.ModelChoiceFilter( + queryset=Build.objects.all(), + label=_('Parent Build'), + field_name='parent', + method='filter_parent' + ) + + def filter_parent(self, queryset, name, parent): + """Filter by 'parent' build order.""" + + cascade = str2bool(self.data.get('cascade', False)) + + if cascade: + builds = parent.get_descendants(include_self=False) + return queryset.filter(pk__in=[b.pk for b in builds]) + + return queryset.filter(parent=parent) + overdue = rest_filters.BooleanFilter(label='Build is overdue', method='filter_overdue') def filter_overdue(self, queryset, name, value): @@ -175,6 +203,7 @@ class BuildList(DataExportViewMixin, BuildMixin, ListCreateAPI): 'responsible', 'project_code', 'priority', + 'level', ] ordering_field_aliases = { diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index 9598daac85..66fd69309d 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -76,6 +76,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre 'responsible', 'responsible_detail', 'priority', + 'level', ] read_only_fields = [ @@ -84,8 +85,11 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre 'completion_data', 'status', 'status_text', + 'level', ] + level = serializers.IntegerField(label=_('Build Level'), read_only=True) + url = serializers.CharField(source='get_absolute_url', read_only=True) status_text = serializers.CharField(source='get_status_display', read_only=True) diff --git a/src/frontend/src/tables/Filter.tsx b/src/frontend/src/tables/Filter.tsx index 948ef95634..4105286703 100644 --- a/src/frontend/src/tables/Filter.tsx +++ b/src/frontend/src/tables/Filter.tsx @@ -28,6 +28,7 @@ export type TableFilter = { defaultValue?: any; value?: any; displayValue?: any; + active?: boolean; }; /** diff --git a/src/frontend/src/tables/InvenTreeTable.tsx b/src/frontend/src/tables/InvenTreeTable.tsx index 233378af0c..1a39276b35 100644 --- a/src/frontend/src/tables/InvenTreeTable.tsx +++ b/src/frontend/src/tables/InvenTreeTable.tsx @@ -164,12 +164,14 @@ export function InvenTreeTable({ // Construct table filters - note that we can introspect filter labels from column names const filters: TableFilter[] = useMemo(() => { return ( - props.tableFilters?.map((filter) => { - return { - ...filter, - label: filter.label ?? fieldNames[filter.name] ?? `${filter.name}` - }; - }) ?? [] + props.tableFilters + ?.filter((f: any) => f.active != false) + ?.map((filter) => { + return { + ...filter, + label: filter.label ?? fieldNames[filter.name] ?? `${filter.name}` + }; + }) ?? [] ); }, [props.tableFilters, fieldNames]); diff --git a/src/frontend/src/tables/build/BuildOrderTable.tsx b/src/frontend/src/tables/build/BuildOrderTable.tsx index ef7a9fec50..737419d551 100644 --- a/src/frontend/src/tables/build/BuildOrderTable.tsx +++ b/src/frontend/src/tables/build/BuildOrderTable.tsx @@ -27,63 +27,6 @@ import { import { StatusFilterOptions, TableFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; -/** - * Construct a list of columns for the build order table - */ -function buildOrderTableColumns(): TableColumn[] { - return [ - ReferenceColumn({}), - { - accessor: 'part', - sortable: true, - switchable: false, - render: (record: any) => PartColumn(record.part_detail) - }, - { - accessor: 'part_detail.IPN', - sortable: true, - switchable: true, - title: t`IPN` - }, - { - accessor: 'title', - sortable: false - }, - { - accessor: 'completed', - sortable: true, - switchable: false, - render: (record: any) => ( - - ) - }, - StatusColumn({ model: ModelType.build }), - ProjectCodeColumn({}), - { - accessor: 'priority', - sortable: true - }, - CreationDateColumn({}), - TargetDateColumn({}), - DateColumn({ - accessor: 'completion_date', - sortable: true - }), - { - accessor: 'issued_by', - sortable: true, - render: (record: any) => ( - - ) - }, - ResponsibleColumn({}) - ]; -} - /* * Construct a table of build orders, according to the provided parameters */ @@ -96,7 +39,65 @@ export function BuildOrderTable({ parentBuildId?: number; salesOrderId?: number; }) { - const tableColumns = useMemo(() => buildOrderTableColumns(), []); + const tableColumns = useMemo(() => { + return [ + ReferenceColumn({}), + { + accessor: 'part', + sortable: true, + switchable: false, + render: (record: any) => PartColumn(record.part_detail) + }, + { + accessor: 'part_detail.IPN', + sortable: true, + switchable: true, + title: t`IPN` + }, + { + accessor: 'title', + sortable: false + }, + { + accessor: 'completed', + sortable: true, + switchable: false, + render: (record: any) => ( + + ) + }, + StatusColumn({ model: ModelType.build }), + ProjectCodeColumn({}), + { + accessor: 'level', + sortable: true, + switchable: true, + hidden: !parentBuildId + }, + { + accessor: 'priority', + sortable: true + }, + CreationDateColumn({}), + TargetDateColumn({}), + DateColumn({ + accessor: 'completion_date', + sortable: true + }), + { + accessor: 'issued_by', + sortable: true, + render: (record: any) => ( + + ) + }, + ResponsibleColumn({}) + ]; + }, [parentBuildId]); const projectCodeFilters = useProjectCodeFilters(); const ownerFilters = useOwnerFilters(); @@ -109,6 +110,13 @@ export function BuildOrderTable({ label: t`Active`, description: t`Show active orders` }, + { + name: 'cascade', + type: 'boolean', + label: t`Cascade`, + description: t`Display recursive child orders`, + active: !!parentBuildId + }, { name: 'status', label: t`Status`, @@ -151,7 +159,7 @@ export function BuildOrderTable({ choices: ownerFilters.choices } ]; - }, [projectCodeFilters.choices, ownerFilters.choices]); + }, [parentBuildId, projectCodeFilters.choices, ownerFilters.choices]); const user = useUserState();