mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[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
This commit is contained in:
parent
6591286e27
commit
7cbaeb159e
@ -1,14 +1,19 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# 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."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
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
|
- Adjusts required fields for the MeUserDetail endpoint
|
||||||
|
|
||||||
v240 - 2024-08-16 : https://github.com/inventree/InvenTree/pull/7900
|
v240 - 2024-08-16 : https://github.com/inventree/InvenTree/pull/7900
|
||||||
|
@ -34,7 +34,6 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
model = Build
|
model = Build
|
||||||
fields = [
|
fields = [
|
||||||
'parent',
|
|
||||||
'sales_order',
|
'sales_order',
|
||||||
'part',
|
'part',
|
||||||
]
|
]
|
||||||
@ -49,6 +48,35 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
return queryset.filter(status__in=BuildStatusGroups.ACTIVE_CODES)
|
return queryset.filter(status__in=BuildStatusGroups.ACTIVE_CODES)
|
||||||
return queryset.exclude(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')
|
overdue = rest_filters.BooleanFilter(label='Build is overdue', method='filter_overdue')
|
||||||
|
|
||||||
def filter_overdue(self, queryset, name, value):
|
def filter_overdue(self, queryset, name, value):
|
||||||
@ -175,6 +203,7 @@ class BuildList(DataExportViewMixin, BuildMixin, ListCreateAPI):
|
|||||||
'responsible',
|
'responsible',
|
||||||
'project_code',
|
'project_code',
|
||||||
'priority',
|
'priority',
|
||||||
|
'level',
|
||||||
]
|
]
|
||||||
|
|
||||||
ordering_field_aliases = {
|
ordering_field_aliases = {
|
||||||
|
@ -76,6 +76,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
|||||||
'responsible',
|
'responsible',
|
||||||
'responsible_detail',
|
'responsible_detail',
|
||||||
'priority',
|
'priority',
|
||||||
|
'level',
|
||||||
]
|
]
|
||||||
|
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
@ -84,8 +85,11 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
|||||||
'completion_data',
|
'completion_data',
|
||||||
'status',
|
'status',
|
||||||
'status_text',
|
'status_text',
|
||||||
|
'level',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
level = serializers.IntegerField(label=_('Build Level'), read_only=True)
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||||
|
@ -28,6 +28,7 @@ export type TableFilter = {
|
|||||||
defaultValue?: any;
|
defaultValue?: any;
|
||||||
value?: any;
|
value?: any;
|
||||||
displayValue?: any;
|
displayValue?: any;
|
||||||
|
active?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,12 +164,14 @@ export function InvenTreeTable<T = any>({
|
|||||||
// Construct table filters - note that we can introspect filter labels from column names
|
// Construct table filters - note that we can introspect filter labels from column names
|
||||||
const filters: TableFilter[] = useMemo(() => {
|
const filters: TableFilter[] = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
props.tableFilters?.map((filter) => {
|
props.tableFilters
|
||||||
return {
|
?.filter((f: any) => f.active != false)
|
||||||
...filter,
|
?.map((filter) => {
|
||||||
label: filter.label ?? fieldNames[filter.name] ?? `${filter.name}`
|
return {
|
||||||
};
|
...filter,
|
||||||
}) ?? []
|
label: filter.label ?? fieldNames[filter.name] ?? `${filter.name}`
|
||||||
|
};
|
||||||
|
}) ?? []
|
||||||
);
|
);
|
||||||
}, [props.tableFilters, fieldNames]);
|
}, [props.tableFilters, fieldNames]);
|
||||||
|
|
||||||
|
@ -27,63 +27,6 @@ import {
|
|||||||
import { StatusFilterOptions, TableFilter } from '../Filter';
|
import { StatusFilterOptions, TableFilter } from '../Filter';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
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) => (
|
|
||||||
<ProgressBar
|
|
||||||
progressLabel={true}
|
|
||||||
value={record.completed}
|
|
||||||
maximum={record.quantity}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
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) => (
|
|
||||||
<RenderUser instance={record?.issued_by_detail} />
|
|
||||||
)
|
|
||||||
},
|
|
||||||
ResponsibleColumn({})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Construct a table of build orders, according to the provided parameters
|
* Construct a table of build orders, according to the provided parameters
|
||||||
*/
|
*/
|
||||||
@ -96,7 +39,65 @@ export function BuildOrderTable({
|
|||||||
parentBuildId?: number;
|
parentBuildId?: number;
|
||||||
salesOrderId?: 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) => (
|
||||||
|
<ProgressBar
|
||||||
|
progressLabel={true}
|
||||||
|
value={record.completed}
|
||||||
|
maximum={record.quantity}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
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) => (
|
||||||
|
<RenderUser instance={record?.issued_by_detail} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ResponsibleColumn({})
|
||||||
|
];
|
||||||
|
}, [parentBuildId]);
|
||||||
|
|
||||||
const projectCodeFilters = useProjectCodeFilters();
|
const projectCodeFilters = useProjectCodeFilters();
|
||||||
const ownerFilters = useOwnerFilters();
|
const ownerFilters = useOwnerFilters();
|
||||||
@ -109,6 +110,13 @@ export function BuildOrderTable({
|
|||||||
label: t`Active`,
|
label: t`Active`,
|
||||||
description: t`Show active orders`
|
description: t`Show active orders`
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'cascade',
|
||||||
|
type: 'boolean',
|
||||||
|
label: t`Cascade`,
|
||||||
|
description: t`Display recursive child orders`,
|
||||||
|
active: !!parentBuildId
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
label: t`Status`,
|
label: t`Status`,
|
||||||
@ -151,7 +159,7 @@ export function BuildOrderTable({
|
|||||||
choices: ownerFilters.choices
|
choices: ownerFilters.choices
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}, [projectCodeFilters.choices, ownerFilters.choices]);
|
}, [parentBuildId, projectCodeFilters.choices, ownerFilters.choices]);
|
||||||
|
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user