2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 04:25:42 +00:00

[PUI] Serialize Stock (#8192)

* Add PUI form to serialize existing stock item

* Remove debug statement

* Ensure that stock item trees are rebuilt correctly after serialization

- No idea how this has not been detected previously

* Add unit test to ensure child_items annotation works as expected

* Add link to parent item in stock detail page

* Refactor to use new placeholder hook
This commit is contained in:
Oliver
2024-09-27 08:29:40 +10:00
committed by GitHub
parent 23de1e038d
commit a5f2273e14
6 changed files with 143 additions and 13 deletions

View File

@ -116,12 +116,14 @@ export enum ApiEndpoints {
manufacturer_part_list = 'company/part/manufacturer/',
manufacturer_part_parameter_list = 'company/part/manufacturer/parameter/',
// Stock API endpoints
stock_item_list = 'stock/',
stock_tracking_list = 'stock/track/',
// Stock location endpoints
stock_location_list = 'stock/location/',
stock_location_type_list = 'stock/location-type/',
stock_location_tree = 'stock/location/tree/',
// Stock item API endpoints
stock_item_list = 'stock/',
stock_tracking_list = 'stock/track/',
stock_test_result_list = 'stock/test/',
stock_transfer = 'stock/transfer/',
stock_remove = 'stock/remove/',
@ -131,9 +133,10 @@ export enum ApiEndpoints {
stock_merge = 'stock/merge/',
stock_assign = 'stock/assign/',
stock_status = 'stock/status/',
stock_install = 'stock/:id/install',
build_test_statistics = 'test-statistics/by-build/:id',
part_test_statistics = 'test-statistics/by-part/:id',
stock_install = 'stock/:id/install/',
stock_serialize = 'stock/:id/serialize/',
build_test_statistics = 'test-statistics/by-build/:id/',
part_test_statistics = 'test-statistics/by-part/:id/',
// Generator API endpoints
generate_batch_code = 'generate/batch-code/',

View File

@ -40,6 +40,7 @@ import {
useBatchCodeGenerator,
useSerialNumberGenerator
} from '../hooks/UseGenerator';
import { useSerialNumberPlaceholder } from '../hooks/UsePlaceholder';
import { apiUrl } from '../states/ApiState';
import { useGlobalSettingsState } from '../states/SettingsState';
@ -214,6 +215,30 @@ export function useCreateStockItem() {
});
}
export function useStockItemSerializeFields({
partId,
trackable
}: {
partId: number;
trackable: boolean;
}) {
const snPlaceholder = useSerialNumberPlaceholder({
partId: partId,
key: 'stock-item-serialize',
enabled: trackable
});
return useMemo(() => {
return {
quantity: {},
serial_numbers: {
placeholder: snPlaceholder
},
destination: {}
};
}, [snPlaceholder]);
}
function StockItemDefaultMove({
stockItem,
value

View File

@ -90,7 +90,7 @@ export function useInstance<T = any>({
});
const refreshInstance = useCallback(function () {
instanceQuery.refetch();
return instanceQuery.refetch();
}, []);
return {

View File

@ -39,12 +39,14 @@ import { StatusRenderer } from '../../components/render/StatusRenderer';
import { ApiEndpoints } from '../../enums/ApiEndpoints';
import { ModelType } from '../../enums/ModelType';
import { UserRoles } from '../../enums/Roles';
import { partCategoryFields } from '../../forms/PartForms';
import {
StockOperationProps,
useAddStockItem,
useCountStockItem,
useRemoveStockItem,
useStockFields,
useStockItemSerializeFields,
useTransferStockItem
} from '../../forms/StockForms';
import { InvenTreeIcon } from '../../functions/icons';
@ -203,6 +205,16 @@ export default function StockDetail() {
model: ModelType.stockitem,
hidden: !stockitem.belongs_to
},
{
type: 'link',
name: 'parent',
label: t`Parent Item`,
model: ModelType.stockitem,
hidden: !stockitem.parent,
model_formatter: (model: any) => {
return t`Parent stock item`;
}
},
{
type: 'link',
name: 'consumed_by',
@ -481,8 +493,39 @@ export default function StockDetail() {
const removeStockItem = useRemoveStockItem(stockActionProps);
const transferStockItem = useTransferStockItem(stockActionProps);
const stockActions = useMemo(
() => [
const serializeStockFields = useStockItemSerializeFields({
partId: stockitem.part,
trackable: stockitem.part_detail?.trackable
});
const serializeStockItem = useCreateApiFormModal({
url: ApiEndpoints.stock_serialize,
pk: stockitem.pk,
title: t`Serialize Stock Item`,
fields: serializeStockFields,
initialData: {
quantity: stockitem.quantity,
destination: stockitem.location ?? stockitem.part_detail?.default_location
},
onFormSuccess: () => {
const partId = stockitem.part;
refreshInstance().catch(() => {
// Part may have been deleted - redirect to the part detail page
navigate(getDetailUrl(ModelType.part, partId));
});
},
successMessage: t`Stock item serialized`
});
const stockActions = useMemo(() => {
const serial = stockitem.serial;
const serialized =
serial != null &&
serial != undefined &&
serial != '' &&
stockitem.quantity == 1;
return [
<AdminButton model={ModelType.stockitem} pk={stockitem.pk} />,
<BarcodeActionDropdown
model={ModelType.stockitem}
@ -503,6 +546,7 @@ export default function StockDetail() {
{
name: t`Count`,
tooltip: t`Count stock`,
hidden: serialized,
icon: (
<InvenTreeIcon icon="stocktake" iconProps={{ color: 'blue' }} />
),
@ -513,6 +557,7 @@ export default function StockDetail() {
{
name: t`Add`,
tooltip: t`Add stock`,
hidden: serialized,
icon: <InvenTreeIcon icon="add" iconProps={{ color: 'green' }} />,
onClick: () => {
stockitem.pk && addStockItem.open();
@ -521,11 +566,21 @@ export default function StockDetail() {
{
name: t`Remove`,
tooltip: t`Remove stock`,
hidden: serialized,
icon: <InvenTreeIcon icon="remove" iconProps={{ color: 'red' }} />,
onClick: () => {
stockitem.pk && removeStockItem.open();
}
},
{
name: t`Serialize`,
tooltip: t`Serialize stock`,
hidden: serialized || stockitem?.part_detail?.trackable != true,
icon: <InvenTreeIcon icon="serial" iconProps={{ color: 'blue' }} />,
onClick: () => {
serializeStockItem.open();
}
},
{
name: t`Transfer`,
tooltip: t`Transfer stock`,
@ -555,9 +610,8 @@ export default function StockDetail() {
})
]}
/>
],
[id, stockitem, user]
);
];
}, [id, stockitem, user]);
const stockBadges: ReactNode[] = useMemo(() => {
let available = (stockitem?.quantity ?? 0) - (stockitem?.allocated ?? 0);
@ -642,6 +696,7 @@ export default function StockDetail() {
{addStockItem.modal}
{removeStockItem.modal}
{transferStockItem.modal}
{serializeStockItem.modal}
</Stack>
</InstanceDetail>
);