2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-12-16 09:18:10 +00:00

Implement more parametric tables:

- ManufacturerPart
- SupplierPart
- Fixes and enhancements
This commit is contained in:
Oliver Walters
2025-11-25 08:54:09 +00:00
parent e3c975e157
commit 59f18f0f8f
4 changed files with 184 additions and 10 deletions

View File

@@ -23,8 +23,10 @@ import SegmentedControlPanel from '../../components/panels/SegmentedControlPanel
import { useUserState } from '../../states/UserState'; import { useUserState } from '../../states/UserState';
import { CompanyTable } from '../../tables/company/CompanyTable'; import { CompanyTable } from '../../tables/company/CompanyTable';
import ParametricCompanyTable from '../../tables/company/ParametricCompanyTable'; import ParametricCompanyTable from '../../tables/company/ParametricCompanyTable';
import ManufacturerPartParametricTable from '../../tables/purchasing/ManufacturerPartParametricTable';
import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable'; import { ManufacturerPartTable } from '../../tables/purchasing/ManufacturerPartTable';
import { PurchaseOrderTable } from '../../tables/purchasing/PurchaseOrderTable'; import { PurchaseOrderTable } from '../../tables/purchasing/PurchaseOrderTable';
import SupplierPartParametricTable from '../../tables/purchasing/SupplierPartParametricTable';
import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable'; import { SupplierPartTable } from '../../tables/purchasing/SupplierPartTable';
export default function PurchasingIndex() { export default function PurchasingIndex() {
@@ -45,6 +47,17 @@ export default function PurchasingIndex() {
defaultValue: 'table' defaultValue: 'table'
}); });
const [manufacturerPartsView, setManufacturerPartsView] =
useLocalStorage<string>({
key: 'manufacturer-parts-view',
defaultValue: 'table'
});
const [supplierPartsView, setSupplierPartsView] = useLocalStorage<string>({
key: 'supplier-parts-view',
defaultValue: 'table'
});
const panels = useMemo(() => { const panels = useMemo(() => {
return [ return [
SegmentedControlPanel({ SegmentedControlPanel({
@@ -103,12 +116,27 @@ export default function PurchasingIndex() {
} }
] ]
}), }),
{ SegmentedControlPanel({
name: 'supplier-parts', name: 'supplier-parts',
label: t`Supplier Parts`, label: t`Supplier Parts`,
icon: <IconPackageExport />, icon: <IconPackageExport />,
content: <SupplierPartTable /> selection: supplierPartsView,
}, onChange: setSupplierPartsView,
options: [
{
value: 'table',
label: t`Table View`,
icon: <IconTable />,
content: <SupplierPartTable />
},
{
value: 'parametric',
label: t`Parametric View`,
icon: <IconListDetails />,
content: <SupplierPartParametricTable />
}
]
}),
SegmentedControlPanel({ SegmentedControlPanel({
name: 'manufacturer', name: 'manufacturer',
label: t`Manufacturers`, label: t`Manufacturers`,
@@ -137,14 +165,36 @@ export default function PurchasingIndex() {
} }
] ]
}), }),
{ SegmentedControlPanel({
name: 'manufacturer-parts', name: 'manufacturer-parts',
label: t`Manufacturer Parts`, label: t`Manufacturer Parts`,
icon: <IconBuildingWarehouse />, icon: <IconBuildingWarehouse />,
content: <ManufacturerPartTable /> selection: manufacturerPartsView,
} onChange: setManufacturerPartsView,
options: [
{
value: 'table',
label: t`Table View`,
icon: <IconTable />,
content: <ManufacturerPartTable />
},
{
value: 'parametric',
label: t`Parametric View`,
icon: <IconListDetails />,
content: <ManufacturerPartParametricTable />
}
]
})
]; ];
}, [user, manufacturerView, purchaseOrderView, supplierView]); }, [
user,
manufacturerPartsView,
manufacturerView,
purchaseOrderView,
supplierPartsView,
supplierView
]);
if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.purchase_order)) { if (!user.isLoggedIn() || !user.hasViewRole(UserRoles.purchase_order)) {
return <PermissionDenied />; return <PermissionDenied />;

View File

@@ -2,7 +2,8 @@ import { cancelEvent } from '@lib/functions/Events';
import { import {
ApiEndpoints, ApiEndpoints,
type ApiFormFieldSet, type ApiFormFieldSet,
ModelType, type ModelType,
RowViewAction,
UserRoles, UserRoles,
YesNoButton, YesNoButton,
apiUrl, apiUrl,
@@ -15,6 +16,7 @@ import type { TableColumn } from '@lib/types/Tables';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { Group } from '@mantine/core'; import { Group } from '@mantine/core';
import { useHover } from '@mantine/hooks'; import { useHover } from '@mantine/hooks';
import { IconCirclePlus } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { type ReactNode, useCallback, useMemo, useState } from 'react'; import { type ReactNode, useCallback, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@@ -247,11 +249,11 @@ export default function ParametricDataTable({
}, [parameterFilters]); }, [parameterFilters]);
const [selectedInstance, setSelectedInstance] = useState<number>(-1); const [selectedInstance, setSelectedInstance] = useState<number>(-1);
const [selectedTemplate, setSelectedTemplate] = useState<number>(-1); const [selectedTemplate, setSelectedTemplate] = useState<number | null>(null);
const [selectedParameter, setSelectedParameter] = useState<number>(-1); const [selectedParameter, setSelectedParameter] = useState<number>(-1);
const parameterFields: ApiFormFieldSet = useParameterFields({ const parameterFields: ApiFormFieldSet = useParameterFields({
modelType: ModelType.part, modelType: modelType,
modelId: selectedInstance modelId: selectedInstance
}); });
@@ -262,6 +264,16 @@ export default function ParametricDataTable({
focus: 'data', focus: 'data',
onFormSuccess: (parameter: any) => { onFormSuccess: (parameter: any) => {
updateParameterRecord(selectedInstance, parameter); updateParameterRecord(selectedInstance, parameter);
// Ensure that the parameter template is included in the table
const template = parameterTemplates.data.find(
(t: any) => t.pk == parameter.template
);
if (!template) {
// Reload the parameter templates
parameterTemplates.refetch();
}
}, },
initialData: { initialData: {
part: selectedInstance, part: selectedInstance,
@@ -374,6 +386,32 @@ export default function ParametricDataTable({
return [...(customColumns || []), ...parameterColumns]; return [...(customColumns || []), ...parameterColumns];
}, [customColumns, parameterColumns]); }, [customColumns, parameterColumns]);
const rowActions = useCallback(
(record: any) => {
return [
{
title: t`Add Parameter`,
icon: <IconCirclePlus />,
color: 'green',
hidden: !user.hasAddPermission(modelType),
onClick: () => {
setSelectedInstance(record.pk);
setSelectedTemplate(null);
addParameter.open();
}
},
RowViewAction({
title: t`View`,
modelType: modelType,
modelId: record.pk,
hidden: !user.hasViewPermission(modelType),
navigate: navigate
})
];
},
[modelType, user]
);
return ( return (
<> <>
{addParameter.modal} {addParameter.modal}
@@ -384,6 +422,7 @@ export default function ParametricDataTable({
columns={tableColumns} columns={tableColumns}
props={{ props={{
enableDownload: true, enableDownload: true,
rowActions: rowActions,
tableFilters: tableFilters, tableFilters: tableFilters,
params: { params: {
...queryParams, ...queryParams,

View File

@@ -0,0 +1,33 @@
import { ApiEndpoints, ModelType } from '@lib/index';
import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables';
import { type ReactNode, useMemo } from 'react';
import ParametricDataTable from '../general/ParametricDataTable';
export default function ManufacturerPartParametricTable({
queryParams
}: {
queryParams?: Record<string, any>;
}): ReactNode {
const customColumns: TableColumn[] = useMemo(() => {
return [];
}, []);
const customFilters: TableFilter[] = useMemo(() => {
return [];
}, []);
return (
<ParametricDataTable
modelType={ModelType.manufacturerpart}
endpoint={ApiEndpoints.manufacturer_part_list}
customColumns={customColumns}
customFilters={customFilters}
queryParams={{
...queryParams,
part_detail: true,
manufacturer_detail: true
}}
/>
);
}

View File

@@ -0,0 +1,52 @@
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 { CompanyColumn, PartColumn } from '../ColumnRenderers';
import ParametricDataTable from '../general/ParametricDataTable';
export default function SupplierPartParametricTable({
queryParams
}: {
queryParams?: Record<string, any>;
}): ReactNode {
const customColumns: TableColumn[] = useMemo(() => {
return [
PartColumn({
switchable: false,
part: 'part_detail'
}),
{
accessor: 'supplier',
sortable: true,
render: (record: any) => (
<CompanyColumn company={record?.supplier_detail} />
)
},
{
accessor: 'SKU',
title: t`Supplier Part`,
sortable: true
}
];
}, []);
const customFilters: TableFilter[] = useMemo(() => {
return [];
}, []);
return (
<ParametricDataTable
modelType={ModelType.supplierpart}
endpoint={ApiEndpoints.supplier_part_list}
customColumns={customColumns}
customFilters={customFilters}
queryParams={{
...queryParams,
part_detail: true,
supplier_detail: true
}}
/>
);
}