2
0
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:
Oliver Walters
2025-04-19 12:06:18 +00:00
parent ae10a3a68e
commit 4e41103178
2 changed files with 66 additions and 12 deletions

View File

@ -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)}
/> />
); );

View File

@ -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`
};
});
} }
}); });