mirror of
https://github.com/inventree/InvenTree.git
synced 2026-01-09 04:38:00 +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 information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# 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."""
|
"""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 = """
|
||||||
|
|
||||||
|
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
|
v436 -> 2026-01-06 : https://github.com/inventree/InvenTree/pull/11035
|
||||||
- Removes model-specific metadata endpoints and replaces them with redirects
|
- Removes model-specific metadata endpoints and replaces them with redirects
|
||||||
- Adds new generic /api/metadata/<model_name>/ endpoint to retrieve metadata for any model
|
- 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(
|
class StockLocation(
|
||||||
InvenTree.models.PluginValidationMixin,
|
InvenTree.models.PluginValidationMixin,
|
||||||
|
InvenTree.models.InvenTreeParameterMixin,
|
||||||
InvenTree.models.InvenTreeBarcodeMixin,
|
InvenTree.models.InvenTreeBarcodeMixin,
|
||||||
report.mixins.InvenTreeReportMixin,
|
report.mixins.InvenTreeReportMixin,
|
||||||
InvenTree.models.PathStringMixin,
|
InvenTree.models.PathStringMixin,
|
||||||
|
|||||||
@@ -1159,8 +1159,10 @@ class LocationSerializer(
|
|||||||
'structural',
|
'structural',
|
||||||
'external',
|
'external',
|
||||||
'location_type',
|
'location_type',
|
||||||
|
# Optional fields
|
||||||
'location_type_detail',
|
'location_type_detail',
|
||||||
'tags',
|
'tags',
|
||||||
|
'parameters',
|
||||||
]
|
]
|
||||||
read_only_fields = ['barcode_hash', 'icon', 'level', 'pathstring']
|
read_only_fields = ['barcode_hash', 'icon', 'level', 'pathstring']
|
||||||
|
|
||||||
@@ -1205,6 +1207,8 @@ class LocationSerializer(
|
|||||||
filter_name='path_detail',
|
filter_name='path_detail',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parameters = common.filters.enable_parameters_filter()
|
||||||
|
|
||||||
# explicitly set this field, so it gets included for AutoSchema
|
# explicitly set this field, so it gets included for AutoSchema
|
||||||
icon = serializers.CharField(read_only=True)
|
icon = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,19 @@ import type { PanelType } from './Panel';
|
|||||||
export default function ParametersPanel({
|
export default function ParametersPanel({
|
||||||
model_type,
|
model_type,
|
||||||
model_id,
|
model_id,
|
||||||
|
hidden,
|
||||||
allowEdit = true
|
allowEdit = true
|
||||||
}: {
|
}: {
|
||||||
model_type: ModelType;
|
model_type: ModelType;
|
||||||
model_id: number | undefined;
|
model_id: number | undefined;
|
||||||
|
hidden?: boolean;
|
||||||
allowEdit?: boolean;
|
allowEdit?: boolean;
|
||||||
}): PanelType {
|
}): PanelType {
|
||||||
return {
|
return {
|
||||||
name: 'parameters',
|
name: 'parameters',
|
||||||
label: t`Parameters`,
|
label: t`Parameters`,
|
||||||
icon: <IconListDetails />,
|
icon: <IconListDetails />,
|
||||||
|
hidden: hidden ?? false,
|
||||||
content:
|
content:
|
||||||
model_type && model_id ? (
|
model_type && model_id ? (
|
||||||
<ParameterTable
|
<ParameterTable
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ import { getDetailUrl } from '@lib/functions/Navigation';
|
|||||||
import type { StockOperationProps } from '@lib/types/Forms';
|
import type { StockOperationProps } from '@lib/types/Forms';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Group, Skeleton, Stack, Text } from '@mantine/core';
|
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 { useMemo, useState } from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
@@ -30,6 +36,8 @@ import NavigationTree from '../../components/nav/NavigationTree';
|
|||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
import type { PanelType } from '../../components/panels/Panel';
|
import type { PanelType } from '../../components/panels/Panel';
|
||||||
import { PanelGroup } from '../../components/panels/PanelGroup';
|
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 LocateItemButton from '../../components/plugins/LocateItemButton';
|
||||||
import { stockLocationFields } from '../../forms/StockForms';
|
import { stockLocationFields } from '../../forms/StockForms';
|
||||||
import { InvenTreeIcon } from '../../functions/icons';
|
import { InvenTreeIcon } from '../../functions/icons';
|
||||||
@@ -42,6 +50,7 @@ import { useStockAdjustActions } from '../../hooks/UseStockAdjustActions';
|
|||||||
import { useUserState } from '../../states/UserState';
|
import { useUserState } from '../../states/UserState';
|
||||||
import { PartListTable } from '../../tables/part/PartTable';
|
import { PartListTable } from '../../tables/part/PartTable';
|
||||||
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
import { StockItemTable } from '../../tables/stock/StockItemTable';
|
||||||
|
import StockLocationParametricTable from '../../tables/stock/StockLocationParametricTable';
|
||||||
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
|
import { StockLocationTable } from '../../tables/stock/StockLocationTable';
|
||||||
|
|
||||||
export default function Stock() {
|
export default function Stock() {
|
||||||
@@ -161,6 +170,8 @@ export default function Stock() {
|
|||||||
);
|
);
|
||||||
}, [location, instanceQuery]);
|
}, [location, instanceQuery]);
|
||||||
|
|
||||||
|
const [sublocationView, setSublocationView] = useState<string>('table');
|
||||||
|
|
||||||
const locationPanels: PanelType[] = useMemo(() => {
|
const locationPanels: PanelType[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -169,12 +180,32 @@ export default function Stock() {
|
|||||||
icon: <IconInfoCircle />,
|
icon: <IconInfoCircle />,
|
||||||
content: detailsPanel
|
content: detailsPanel
|
||||||
},
|
},
|
||||||
{
|
SegmentedControlPanel({
|
||||||
name: 'sublocations',
|
name: 'sublocations',
|
||||||
label: id ? t`Sublocations` : t`Stock Locations`,
|
label: id ? t`Sublocations` : t`Stock Locations`,
|
||||||
icon: <IconSitemap />,
|
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',
|
name: 'stock-items',
|
||||||
label: t`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({
|
const editLocation = useEditApiFormModal({
|
||||||
url: ApiEndpoints.stock_location_list,
|
url: ApiEndpoints.stock_location_list,
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import { ApiEndpoints, ModelType } from '@lib/index';
|
import { ApiEndpoints, ModelType } from '@lib/index';
|
||||||
import type { TableFilter } from '@lib/types/Filters';
|
import type { TableFilter } from '@lib/types/Filters';
|
||||||
import type { TableColumn } from '@lib/types/Tables';
|
import type { TableColumn } from '@lib/types/Tables';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
import { type ReactNode, useMemo } from 'react';
|
import { type ReactNode, useMemo } from 'react';
|
||||||
import { DescriptionColumn, ReferenceColumn } from '../ColumnRenderers';
|
import {
|
||||||
|
DescriptionColumn,
|
||||||
|
PartColumn,
|
||||||
|
ReferenceColumn
|
||||||
|
} from '../ColumnRenderers';
|
||||||
import { OrderStatusFilter, OutstandingFilter } from '../Filter';
|
import { OrderStatusFilter, OutstandingFilter } from '../Filter';
|
||||||
import ParametricDataTable from '../general/ParametricDataTable';
|
import ParametricDataTable from '../general/ParametricDataTable';
|
||||||
|
|
||||||
@@ -16,6 +21,10 @@ export default function BuildOrderParametricTable({
|
|||||||
ReferenceColumn({
|
ReferenceColumn({
|
||||||
switchable: false
|
switchable: false
|
||||||
}),
|
}),
|
||||||
|
PartColumn({
|
||||||
|
part: 'part_detail',
|
||||||
|
title: t`Part`
|
||||||
|
}),
|
||||||
DescriptionColumn({
|
DescriptionColumn({
|
||||||
accessor: 'title'
|
accessor: 'title'
|
||||||
})
|
})
|
||||||
@@ -33,6 +42,7 @@ export default function BuildOrderParametricTable({
|
|||||||
customColumns={customColumns}
|
customColumns={customColumns}
|
||||||
customFilters={customFilters}
|
customFilters={customFilters}
|
||||||
queryParams={{
|
queryParams={{
|
||||||
|
part_detail: true,
|
||||||
...queryParams
|
...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