mirror of
https://github.com/inventree/InvenTree.git
synced 2026-01-08 12:17:57 +00:00
Location parameters (#11084)
* Add parameter support for StockLocation model * Serialize parameters for stock location * Frontend support * Bump API version --------- Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 436
|
||||
INVENTREE_API_VERSION = 437
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v437 -> 2026-01-07 : https://github.com/inventree/InvenTree/pull/11084
|
||||
- Add generic parameter support for the StockLocation model
|
||||
|
||||
v436 -> 2026-01-06 : https://github.com/inventree/InvenTree/pull/11035
|
||||
- Removes model-specific metadata endpoints and replaces them with redirects
|
||||
- Adds new generic /api/metadata/<model_name>/ endpoint to retrieve metadata for any model
|
||||
|
||||
@@ -120,6 +120,7 @@ class StockLocationReportContext(report.mixins.BaseReportContext):
|
||||
|
||||
class StockLocation(
|
||||
InvenTree.models.PluginValidationMixin,
|
||||
InvenTree.models.InvenTreeParameterMixin,
|
||||
InvenTree.models.InvenTreeBarcodeMixin,
|
||||
report.mixins.InvenTreeReportMixin,
|
||||
InvenTree.models.PathStringMixin,
|
||||
|
||||
@@ -1159,8 +1159,10 @@ class LocationSerializer(
|
||||
'structural',
|
||||
'external',
|
||||
'location_type',
|
||||
# Optional fields
|
||||
'location_type_detail',
|
||||
'tags',
|
||||
'parameters',
|
||||
]
|
||||
read_only_fields = ['barcode_hash', 'icon', 'level', 'pathstring']
|
||||
|
||||
@@ -1205,6 +1207,8 @@ class LocationSerializer(
|
||||
filter_name='path_detail',
|
||||
)
|
||||
|
||||
parameters = common.filters.enable_parameters_filter()
|
||||
|
||||
# explicitly set this field, so it gets included for AutoSchema
|
||||
icon = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
@@ -8,16 +8,19 @@ import type { PanelType } from './Panel';
|
||||
export default function ParametersPanel({
|
||||
model_type,
|
||||
model_id,
|
||||
hidden,
|
||||
allowEdit = true
|
||||
}: {
|
||||
model_type: ModelType;
|
||||
model_id: number | undefined;
|
||||
hidden?: boolean;
|
||||
allowEdit?: boolean;
|
||||
}): PanelType {
|
||||
return {
|
||||
name: 'parameters',
|
||||
label: t`Parameters`,
|
||||
icon: <IconListDetails />,
|
||||
hidden: hidden ?? false,
|
||||
content:
|
||||
model_type && model_id ? (
|
||||
<ParameterTable
|
||||
|
||||
@@ -6,7 +6,13 @@ import { getDetailUrl } from '@lib/functions/Navigation';
|
||||
import type { StockOperationProps } from '@lib/types/Forms';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Group, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { IconInfoCircle, IconPackages, IconSitemap } from '@tabler/icons-react';
|
||||
import {
|
||||
IconInfoCircle,
|
||||
IconListDetails,
|
||||
IconPackages,
|
||||
IconSitemap,
|
||||
IconTable
|
||||
} from '@tabler/icons-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { api } from '../../App';
|
||||
@@ -30,6 +36,8 @@ import NavigationTree from '../../components/nav/NavigationTree';
|
||||
import { PageDetail } from '../../components/nav/PageDetail';
|
||||
import type { PanelType } from '../../components/panels/Panel';
|
||||
import { PanelGroup } from '../../components/panels/PanelGroup';
|
||||
import ParametersPanel from '../../components/panels/ParametersPanel';
|
||||
import SegmentedControlPanel from '../../components/panels/SegmentedControlPanel';
|
||||
import LocateItemButton from '../../components/plugins/LocateItemButton';
|
||||
import { stockLocationFields } from '../../forms/StockForms';
|
||||
import { InvenTreeIcon } from '../../functions/icons';
|
||||
@@ -42,6 +50,7 @@ import { useStockAdjustActions } from '../../hooks/UseStockAdjustActions';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { PartListTable } from '../../tables/part/PartTable';
|
||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||
import StockLocationParametricTable from '../../tables/stock/StockLocationParametricTable';
|
||||
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
|
||||
|
||||
export default function Stock() {
|
||||
@@ -161,6 +170,8 @@ export default function Stock() {
|
||||
);
|
||||
}, [location, instanceQuery]);
|
||||
|
||||
const [sublocationView, setSublocationView] = useState<string>('table');
|
||||
|
||||
const locationPanels: PanelType[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
@@ -169,12 +180,32 @@ export default function Stock() {
|
||||
icon: <IconInfoCircle />,
|
||||
content: detailsPanel
|
||||
},
|
||||
{
|
||||
SegmentedControlPanel({
|
||||
name: 'sublocations',
|
||||
label: id ? t`Sublocations` : t`Stock Locations`,
|
||||
icon: <IconSitemap />,
|
||||
content: <StockLocationTable parentId={id} />
|
||||
},
|
||||
hidden: !user.hasViewPermission(ModelType.stocklocation),
|
||||
selection: sublocationView,
|
||||
onChange: setSublocationView,
|
||||
options: [
|
||||
{
|
||||
value: 'table',
|
||||
label: t`Table View`,
|
||||
icon: <IconTable />,
|
||||
content: <StockLocationTable parentId={id} />
|
||||
},
|
||||
{
|
||||
value: 'parametric',
|
||||
label: t`Parametric View`,
|
||||
icon: <IconListDetails />,
|
||||
content: (
|
||||
<StockLocationParametricTable
|
||||
queryParams={id ? { parent: id } : {}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
}),
|
||||
{
|
||||
name: 'stock-items',
|
||||
label: t`Stock Items`,
|
||||
@@ -203,9 +234,14 @@ export default function Stock() {
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
ParametersPanel({
|
||||
model_type: ModelType.stocklocation,
|
||||
model_id: location.pk,
|
||||
hidden: !location.pk
|
||||
})
|
||||
];
|
||||
}, [location, id]);
|
||||
}, [sublocationView, location, id]);
|
||||
|
||||
const editLocation = useEditApiFormModal({
|
||||
url: ApiEndpoints.stock_location_list,
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { ApiEndpoints, ModelType } from '@lib/index';
|
||||
import type { TableFilter } from '@lib/types/Filters';
|
||||
import type { TableColumn } from '@lib/types/Tables';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type ReactNode, useMemo } from 'react';
|
||||
import { DescriptionColumn, ReferenceColumn } from '../ColumnRenderers';
|
||||
import {
|
||||
DescriptionColumn,
|
||||
PartColumn,
|
||||
ReferenceColumn
|
||||
} from '../ColumnRenderers';
|
||||
import { OrderStatusFilter, OutstandingFilter } from '../Filter';
|
||||
import ParametricDataTable from '../general/ParametricDataTable';
|
||||
|
||||
@@ -16,6 +21,10 @@ export default function BuildOrderParametricTable({
|
||||
ReferenceColumn({
|
||||
switchable: false
|
||||
}),
|
||||
PartColumn({
|
||||
part: 'part_detail',
|
||||
title: t`Part`
|
||||
}),
|
||||
DescriptionColumn({
|
||||
accessor: 'title'
|
||||
})
|
||||
@@ -33,6 +42,7 @@ export default function BuildOrderParametricTable({
|
||||
customColumns={customColumns}
|
||||
customFilters={customFilters}
|
||||
queryParams={{
|
||||
part_detail: true,
|
||||
...queryParams
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { ModelType } from '@lib/enums/ModelType';
|
||||
import type { TableColumn } from '@lib/types/Tables';
|
||||
import { Group } from '@mantine/core';
|
||||
import { type ReactNode, useMemo } from 'react';
|
||||
import { ApiIcon } from '../../components/items/ApiIcon';
|
||||
import { DescriptionColumn } from '../ColumnRenderers';
|
||||
import ParametricDataTable from '../general/ParametricDataTable';
|
||||
|
||||
export default function StockLocationParametricTable({
|
||||
queryParams
|
||||
}: {
|
||||
queryParams?: Record<string, any>;
|
||||
}): ReactNode {
|
||||
const customColumns: TableColumn[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
accessor: 'name',
|
||||
switchable: false,
|
||||
render: (record: any) => (
|
||||
<Group gap='xs'>
|
||||
{record.icon && <ApiIcon name={record.icon} />}
|
||||
{record.name}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
DescriptionColumn({}),
|
||||
{
|
||||
accessor: 'pathstring',
|
||||
sortable: true
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ParametricDataTable
|
||||
modelType={ModelType.stocklocation}
|
||||
endpoint={ApiEndpoints.stock_location_list}
|
||||
customColumns={customColumns}
|
||||
queryParams={{
|
||||
...queryParams
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user