mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Category params (#6767)
* Validate default value field for PartCategoryParameterTemplate - Only if unit checks are enforced - Only if default value is not blank * Add basic table for part category parameter templates * Add functions to create / edit / delete via table * Fix unit testing
This commit is contained in:
parent
4eac4902ba
commit
e551e2e753
@ -3833,6 +3833,28 @@ class PartCategoryParameterTemplate(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
return f'{self.category.name} | {self.parameter_template.name} | {self.default_value}'
|
return f'{self.category.name} | {self.parameter_template.name} | {self.default_value}'
|
||||||
return f'{self.category.name} | {self.parameter_template.name}'
|
return f'{self.category.name} | {self.parameter_template.name}'
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""Validate this PartCategoryParameterTemplate instance.
|
||||||
|
|
||||||
|
Checks the provided 'default_value', and (if not blank), ensure it is valid.
|
||||||
|
"""
|
||||||
|
super().clean()
|
||||||
|
|
||||||
|
self.default_value = (
|
||||||
|
'' if self.default_value is None else str(self.default_value.strip())
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.default_value and InvenTreeSetting.get_setting(
|
||||||
|
'PART_PARAMETER_ENFORCE_UNITS', True, cache=False, create=False
|
||||||
|
):
|
||||||
|
if self.parameter_template.units:
|
||||||
|
try:
|
||||||
|
InvenTree.conversion.convert_physical_value(
|
||||||
|
self.default_value, self.parameter_template.units
|
||||||
|
)
|
||||||
|
except ValidationError as e:
|
||||||
|
raise ValidationError({'default_value': e.message})
|
||||||
|
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
PartCategory,
|
PartCategory,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
@ -196,6 +196,11 @@ class PartCategoryAPITest(InvenTreeAPITestCase):
|
|||||||
# Add some more category templates via the API
|
# Add some more category templates via the API
|
||||||
n = PartParameterTemplate.objects.count()
|
n = PartParameterTemplate.objects.count()
|
||||||
|
|
||||||
|
# Ensure validation of parameter values is disabled for these checks
|
||||||
|
InvenTreeSetting.set_setting(
|
||||||
|
'PART_PARAMETER_ENFORCE_UNITS', False, change_user=None
|
||||||
|
)
|
||||||
|
|
||||||
for template in PartParameterTemplate.objects.all():
|
for template in PartParameterTemplate.objects.all():
|
||||||
response = self.post(
|
response = self.post(
|
||||||
url,
|
url,
|
||||||
|
@ -63,6 +63,7 @@ export enum ApiEndpoints {
|
|||||||
part_stocktake_list = 'part/stocktake/',
|
part_stocktake_list = 'part/stocktake/',
|
||||||
category_list = 'part/category/',
|
category_list = 'part/category/',
|
||||||
category_tree = 'part/category/tree/',
|
category_tree = 'part/category/tree/',
|
||||||
|
category_parameter_list = 'part/category/parameters/',
|
||||||
related_part_list = 'part/related/',
|
related_part_list = 'part/related/',
|
||||||
part_attachment_list = 'part/attachment/',
|
part_attachment_list = 'part/attachment/',
|
||||||
part_test_template_list = 'part/test-template/',
|
part_test_template_list = 'part/test-template/',
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
IconListDetails,
|
IconListDetails,
|
||||||
IconPlugConnected,
|
IconPlugConnected,
|
||||||
IconScale,
|
IconScale,
|
||||||
|
IconSitemap,
|
||||||
IconTemplate,
|
IconTemplate,
|
||||||
IconUsersGroup
|
IconUsersGroup
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
@ -52,6 +53,10 @@ const PartParameterTemplateTable = Loadable(
|
|||||||
lazy(() => import('../../../../tables/part/PartParameterTemplateTable'))
|
lazy(() => import('../../../../tables/part/PartParameterTemplateTable'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const PartCategoryTemplateTable = Loadable(
|
||||||
|
lazy(() => import('../../../../tables/part/PartCategoryTemplateTable'))
|
||||||
|
);
|
||||||
|
|
||||||
const CurrencyTable = Loadable(
|
const CurrencyTable = Loadable(
|
||||||
lazy(() => import('../../../../tables/settings/CurrencyTable'))
|
lazy(() => import('../../../../tables/settings/CurrencyTable'))
|
||||||
);
|
);
|
||||||
@ -106,11 +111,17 @@ export default function AdminCenter() {
|
|||||||
content: <CustomUnitsTable />
|
content: <CustomUnitsTable />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'parameters',
|
name: 'part-parameters',
|
||||||
label: t`Part Parameters`,
|
label: t`Part Parameters`,
|
||||||
icon: <IconList />,
|
icon: <IconList />,
|
||||||
content: <PartParameterTemplateTable />
|
content: <PartParameterTemplateTable />
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'category-parameters',
|
||||||
|
label: t`Category Parameters`,
|
||||||
|
icon: <IconSitemap />,
|
||||||
|
content: <PartCategoryTemplateTable />
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'templates',
|
name: 'templates',
|
||||||
label: t`Templates`,
|
label: t`Templates`,
|
||||||
|
@ -378,7 +378,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
return api
|
return api
|
||||||
.get(url, {
|
.get(url, {
|
||||||
params: queryParams,
|
params: queryParams,
|
||||||
timeout: 30 * 1000
|
timeout: 5 * 1000
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
|
@ -184,7 +184,7 @@ export function AddressTable({
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Address`}
|
tooltip={t`Add Address`}
|
||||||
onClick={() => newAddress.open()}
|
onClick={() => newAddress.open()}
|
||||||
disabled={!can_add}
|
hidden={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
@ -130,7 +130,7 @@ export function ContactTable({
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add contact`}
|
tooltip={t`Add contact`}
|
||||||
onClick={() => newContact.open()}
|
onClick={() => newContact.open()}
|
||||||
disabled={!can_add}
|
hidden={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
@ -105,7 +105,7 @@ export function PartCategoryTable({ parentId }: { parentId?: any }) {
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Part Category`}
|
tooltip={t`Add Part Category`}
|
||||||
onClick={() => newCategory.open()}
|
onClick={() => newCategory.open()}
|
||||||
disabled={!can_add}
|
hidden={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
156
src/frontend/src/tables/part/PartCategoryTemplateTable.tsx
Normal file
156
src/frontend/src/tables/part/PartCategoryTemplateTable.tsx
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import { t } from '@lingui/macro';
|
||||||
|
import { Group, Text } from '@mantine/core';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { set } from 'react-hook-form';
|
||||||
|
|
||||||
|
import { AddItemButton } from '../../components/buttons/AddItemButton';
|
||||||
|
import { ApiFormFieldSet } from '../../components/forms/fields/ApiFormField';
|
||||||
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
|
import { UserRoles } from '../../enums/Roles';
|
||||||
|
import {
|
||||||
|
useCreateApiFormModal,
|
||||||
|
useDeleteApiFormModal,
|
||||||
|
useEditApiFormModal
|
||||||
|
} from '../../hooks/UseForm';
|
||||||
|
import { useTable } from '../../hooks/UseTable';
|
||||||
|
import { apiUrl } from '../../states/ApiState';
|
||||||
|
import { useUserState } from '../../states/UserState';
|
||||||
|
import { TableColumn } from '../Column';
|
||||||
|
import { TableFilter } from '../Filter';
|
||||||
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
import { RowDeleteAction, RowEditAction } from '../RowActions';
|
||||||
|
|
||||||
|
export default function PartCategoryTemplateTable({}: {}) {
|
||||||
|
const table = useTable('part-category-parameter-templates');
|
||||||
|
const user = useUserState();
|
||||||
|
|
||||||
|
const formFields: ApiFormFieldSet = useMemo(() => {
|
||||||
|
return {
|
||||||
|
category: {},
|
||||||
|
parameter_template: {},
|
||||||
|
default_value: {}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [selectedTemplate, setSelectedTemplate] = useState<number>(0);
|
||||||
|
|
||||||
|
const newTemplate = useCreateApiFormModal({
|
||||||
|
url: ApiEndpoints.category_parameter_list,
|
||||||
|
title: t`Add Category Parameter`,
|
||||||
|
fields: formFields,
|
||||||
|
onFormSuccess: table.refreshTable
|
||||||
|
});
|
||||||
|
|
||||||
|
const editTemplate = useEditApiFormModal({
|
||||||
|
url: ApiEndpoints.category_parameter_list,
|
||||||
|
pk: selectedTemplate,
|
||||||
|
title: t`Edit Category Parameter`,
|
||||||
|
fields: formFields,
|
||||||
|
onFormSuccess: (record: any) => table.updateRecord(record)
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteTemplate = useDeleteApiFormModal({
|
||||||
|
url: ApiEndpoints.category_parameter_list,
|
||||||
|
pk: selectedTemplate,
|
||||||
|
title: t`Delete Category Parameter`,
|
||||||
|
onFormSuccess: table.refreshTable
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableFilters: TableFilter[] = useMemo(() => {
|
||||||
|
// TODO
|
||||||
|
return [];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const tableColumns: TableColumn[] = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
accessor: 'category_detail.name',
|
||||||
|
title: t`Category`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'category_detail.pathstring'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'parameter_template_detail.name',
|
||||||
|
title: t`Parameter Template`,
|
||||||
|
sortable: true,
|
||||||
|
switchable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessor: 'default_value',
|
||||||
|
sortable: true,
|
||||||
|
switchable: false,
|
||||||
|
render: (record: any) => {
|
||||||
|
if (!record?.default_value) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
let units = '';
|
||||||
|
|
||||||
|
if (record?.parameter_template_detail?.units) {
|
||||||
|
units = t`[${record.parameter_template_detail.units}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group position="apart" grow>
|
||||||
|
<Text>{record.default_value}</Text>
|
||||||
|
{units && <Text size="xs">{units}</Text>}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const rowActions = useCallback(
|
||||||
|
(record: any) => {
|
||||||
|
return [
|
||||||
|
RowEditAction({
|
||||||
|
hidden: !user.hasChangeRole(UserRoles.part),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedTemplate(record.pk);
|
||||||
|
editTemplate.open();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
RowDeleteAction({
|
||||||
|
hidden: !user.hasDeleteRole(UserRoles.part),
|
||||||
|
onClick: () => {
|
||||||
|
setSelectedTemplate(record.pk);
|
||||||
|
deleteTemplate.open();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
];
|
||||||
|
},
|
||||||
|
[user]
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableActions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
<AddItemButton
|
||||||
|
tooltip={t`Add Category Parameter`}
|
||||||
|
onClick={() => newTemplate.open()}
|
||||||
|
hidden={!user.hasAddRole(UserRoles.part)}
|
||||||
|
/>
|
||||||
|
];
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{newTemplate.modal}
|
||||||
|
{editTemplate.modal}
|
||||||
|
{deleteTemplate.modal}
|
||||||
|
<InvenTreeTable
|
||||||
|
url={apiUrl(ApiEndpoints.category_parameter_list)}
|
||||||
|
tableState={table}
|
||||||
|
columns={tableColumns}
|
||||||
|
props={{
|
||||||
|
rowActions: rowActions,
|
||||||
|
tableFilters: tableFilters,
|
||||||
|
tableActions: tableActions
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -134,7 +134,7 @@ export default function PartParameterTemplateTable() {
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add parameter template`}
|
tooltip={t`Add parameter template`}
|
||||||
onClick={() => newTemplate.open()}
|
onClick={() => newTemplate.open()}
|
||||||
disabled={!user.hasAddRole(UserRoles.part)}
|
hidden={!user.hasAddRole(UserRoles.part)}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
@ -190,7 +190,7 @@ export default function PartTestTemplateTable({ partId }: { partId: number }) {
|
|||||||
<AddItemButton
|
<AddItemButton
|
||||||
tooltip={t`Add Test Template`}
|
tooltip={t`Add Test Template`}
|
||||||
onClick={() => newTestTemplate.open()}
|
onClick={() => newTestTemplate.open()}
|
||||||
disabled={!can_add}
|
hidden={!can_add}
|
||||||
/>
|
/>
|
||||||
];
|
];
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
Loading…
Reference in New Issue
Block a user