mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-15 11:35:41 +00:00
Scan item into location
This commit is contained in:
@ -5,6 +5,7 @@ import { apiUrl } from '@lib/functions/Api';
|
|||||||
import { getDetailUrl } from '@lib/functions/Navigation';
|
import { getDetailUrl } from '@lib/functions/Navigation';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
import { Box, Divider, Modal } from '@mantine/core';
|
import { Box, Divider, Modal } from '@mantine/core';
|
||||||
|
import { hideNotification, showNotification } from '@mantine/notifications';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { type NavigateFunction, useNavigate } from 'react-router-dom';
|
import { type NavigateFunction, useNavigate } from 'react-router-dom';
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
@ -14,8 +15,8 @@ import { StylishText } from '../items/StylishText';
|
|||||||
import { BarcodeInput } from './BarcodeInput';
|
import { BarcodeInput } from './BarcodeInput';
|
||||||
|
|
||||||
export type BarcodeScanResult = {
|
export type BarcodeScanResult = {
|
||||||
success: boolean;
|
success?: string;
|
||||||
error: string;
|
error?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Callback function for handling a barcode scan
|
// Callback function for handling a barcode scan
|
||||||
@ -29,10 +30,12 @@ export default function BarcodeScanDialog({
|
|||||||
title,
|
title,
|
||||||
opened,
|
opened,
|
||||||
callback,
|
callback,
|
||||||
|
modelType,
|
||||||
onClose
|
onClose
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
title?: string;
|
title?: string;
|
||||||
opened: boolean;
|
opened: boolean;
|
||||||
|
modelType?: ModelType;
|
||||||
callback?: BarcodeScanCallback;
|
callback?: BarcodeScanCallback;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}>) {
|
}>) {
|
||||||
@ -50,6 +53,7 @@ export default function BarcodeScanDialog({
|
|||||||
<ScanInputHandler
|
<ScanInputHandler
|
||||||
navigate={navigate}
|
navigate={navigate}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
modelType={modelType}
|
||||||
callback={callback}
|
callback={callback}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@ -59,11 +63,13 @@ export default function BarcodeScanDialog({
|
|||||||
|
|
||||||
export function ScanInputHandler({
|
export function ScanInputHandler({
|
||||||
callback,
|
callback,
|
||||||
|
modelType,
|
||||||
onClose,
|
onClose,
|
||||||
navigate
|
navigate
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
callback?: BarcodeScanCallback;
|
callback?: BarcodeScanCallback;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
modelType?: ModelType;
|
||||||
navigate: NavigateFunction;
|
navigate: NavigateFunction;
|
||||||
}>) {
|
}>) {
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
@ -76,6 +82,11 @@ export function ScanInputHandler({
|
|||||||
|
|
||||||
// Find the matching model type
|
// Find the matching model type
|
||||||
for (const model_type of Object.keys(ModelInformationDict)) {
|
for (const model_type of Object.keys(ModelInformationDict)) {
|
||||||
|
// If a specific model type is provided, check if it matches
|
||||||
|
if (modelType && model_type !== modelType) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (data[model_type]?.['pk']) {
|
if (data[model_type]?.['pk']) {
|
||||||
if (user.hasViewPermission(model_type as ModelType)) {
|
if (user.hasViewPermission(model_type as ModelType)) {
|
||||||
const url = getDetailUrl(
|
const url = getDetailUrl(
|
||||||
@ -94,7 +105,7 @@ export function ScanInputHandler({
|
|||||||
setError(t`No matching item found`);
|
setError(t`No matching item found`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[navigate, onClose, user]
|
[navigate, onClose, user, modelType]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onScan = useCallback(
|
const onScan = useCallback(
|
||||||
@ -114,12 +125,30 @@ export function ScanInputHandler({
|
|||||||
const data = response.data ?? {};
|
const data = response.data ?? {};
|
||||||
|
|
||||||
if (callback && data.success && response.status === 200) {
|
if (callback && data.success && response.status === 200) {
|
||||||
|
const instance = null;
|
||||||
|
|
||||||
|
// If the caller is expecting a specific model type, check if it matches
|
||||||
|
if (modelType) {
|
||||||
|
const pk: number = data[modelType]?.['pk'];
|
||||||
|
if (!pk) {
|
||||||
|
setError(t`Barcode does not match the expected model type`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
callback(barcode, data)
|
callback(barcode, data)
|
||||||
.then((result: BarcodeScanResult) => {
|
.then((result: BarcodeScanResult) => {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
hideNotification('barcode-scan');
|
||||||
|
showNotification({
|
||||||
|
id: 'barcode-scan',
|
||||||
|
title: t`Success`,
|
||||||
|
message: result.success,
|
||||||
|
color: 'green'
|
||||||
|
});
|
||||||
onClose();
|
onClose();
|
||||||
} else {
|
} else {
|
||||||
setError(result.error);
|
setError(result.error ?? t`Failed to handle barcode`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@ -144,7 +173,7 @@ export function ScanInputHandler({
|
|||||||
setProcessing(false);
|
setProcessing(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[callback, defaultScan]
|
[callback, defaultScan, modelType, onClose]
|
||||||
);
|
);
|
||||||
|
|
||||||
return <BarcodeInput onScan={onScan} error={error} processing={processing} />;
|
return <BarcodeInput onScan={onScan} error={error} processing={processing} />;
|
||||||
@ -152,14 +181,16 @@ export function ScanInputHandler({
|
|||||||
|
|
||||||
export function useBarcodeScanDialog({
|
export function useBarcodeScanDialog({
|
||||||
title,
|
title,
|
||||||
callback
|
callback,
|
||||||
|
modelType
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
title: string;
|
title: string;
|
||||||
|
modelType?: ModelType;
|
||||||
callback: BarcodeScanCallback;
|
callback: BarcodeScanCallback;
|
||||||
}>) {
|
}>) {
|
||||||
const [opened, setOpened] = useState(false);
|
const [opened, setOpened] = useState(false);
|
||||||
|
|
||||||
const open = useCallback((callback?: BarcodeScanCallback) => {
|
const open = useCallback(() => {
|
||||||
setOpened(true);
|
setOpened(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -168,6 +199,7 @@ export function useBarcodeScanDialog({
|
|||||||
title={title}
|
title={title}
|
||||||
opened={opened}
|
opened={opened}
|
||||||
callback={callback}
|
callback={callback}
|
||||||
|
modelType={modelType}
|
||||||
onClose={() => setOpened(false)}
|
onClose={() => setOpened(false)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -2,11 +2,13 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
|||||||
import { ModelType } from '@lib/enums/ModelType';
|
import { ModelType } from '@lib/enums/ModelType';
|
||||||
import { UserRoles } from '@lib/enums/Roles';
|
import { UserRoles } from '@lib/enums/Roles';
|
||||||
import { getDetailUrl } from '@lib/functions/Navigation';
|
import { getDetailUrl } from '@lib/functions/Navigation';
|
||||||
|
import { apiUrl } from '@lib/index';
|
||||||
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, IconPackages, IconSitemap } 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 { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialog';
|
import { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialog';
|
||||||
import AdminButton from '../../components/buttons/AdminButton';
|
import AdminButton from '../../components/buttons/AdminButton';
|
||||||
import { PrintingActions } from '../../components/buttons/PrintingActions';
|
import { PrintingActions } from '../../components/buttons/PrintingActions';
|
||||||
@ -275,12 +277,32 @@ export default function Stock() {
|
|||||||
|
|
||||||
const scanInStockItems = useBarcodeScanDialog({
|
const scanInStockItems = useBarcodeScanDialog({
|
||||||
title: t`Scan Stock Item`,
|
title: t`Scan Stock Item`,
|
||||||
|
modelType: ModelType.stockitem,
|
||||||
callback: async (barcode, response) => {
|
callback: async (barcode, response) => {
|
||||||
console.log('response:', response);
|
const item = response.stockitem.instance;
|
||||||
return {
|
|
||||||
success: true,
|
// Scan the stock item into the current location
|
||||||
error: ''
|
return api
|
||||||
};
|
.post(apiUrl(ApiEndpoints.stock_transfer), {
|
||||||
|
location: location.pk,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
pk: item.pk,
|
||||||
|
quantity: item.quantity
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return {
|
||||||
|
success: t`Scanned stock item into location`
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error scanning stock item:', error);
|
||||||
|
return {
|
||||||
|
error: t`Error scanning stock item`
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user