diff --git a/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx b/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx index 30b2aca690..3457d55534 100644 --- a/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx +++ b/src/frontend/src/components/barcodes/BarcodeScanDialog.tsx @@ -5,6 +5,7 @@ import { apiUrl } from '@lib/functions/Api'; import { getDetailUrl } from '@lib/functions/Navigation'; import { t } from '@lingui/core/macro'; import { Box, Divider, Modal } from '@mantine/core'; +import { hideNotification, showNotification } from '@mantine/notifications'; import { useCallback, useState } from 'react'; import { type NavigateFunction, useNavigate } from 'react-router-dom'; import { api } from '../../App'; @@ -14,8 +15,8 @@ import { StylishText } from '../items/StylishText'; import { BarcodeInput } from './BarcodeInput'; export type BarcodeScanResult = { - success: boolean; - error: string; + success?: string; + error?: string; }; // Callback function for handling a barcode scan @@ -29,10 +30,12 @@ export default function BarcodeScanDialog({ title, opened, callback, + modelType, onClose }: Readonly<{ title?: string; opened: boolean; + modelType?: ModelType; callback?: BarcodeScanCallback; onClose: () => void; }>) { @@ -50,6 +53,7 @@ export default function BarcodeScanDialog({ @@ -59,11 +63,13 @@ export default function BarcodeScanDialog({ export function ScanInputHandler({ callback, + modelType, onClose, navigate }: Readonly<{ callback?: BarcodeScanCallback; onClose: () => void; + modelType?: ModelType; navigate: NavigateFunction; }>) { const [error, setError] = useState(''); @@ -76,6 +82,11 @@ export function ScanInputHandler({ // Find the matching model type 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 (user.hasViewPermission(model_type as ModelType)) { const url = getDetailUrl( @@ -94,7 +105,7 @@ export function ScanInputHandler({ setError(t`No matching item found`); } }, - [navigate, onClose, user] + [navigate, onClose, user, modelType] ); const onScan = useCallback( @@ -114,12 +125,30 @@ export function ScanInputHandler({ const data = response.data ?? {}; 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) .then((result: BarcodeScanResult) => { if (result.success) { + hideNotification('barcode-scan'); + showNotification({ + id: 'barcode-scan', + title: t`Success`, + message: result.success, + color: 'green' + }); onClose(); } else { - setError(result.error); + setError(result.error ?? t`Failed to handle barcode`); } }) .finally(() => { @@ -144,7 +173,7 @@ export function ScanInputHandler({ setProcessing(false); }); }, - [callback, defaultScan] + [callback, defaultScan, modelType, onClose] ); return ; @@ -152,14 +181,16 @@ export function ScanInputHandler({ export function useBarcodeScanDialog({ title, - callback + callback, + modelType }: Readonly<{ title: string; + modelType?: ModelType; callback: BarcodeScanCallback; }>) { const [opened, setOpened] = useState(false); - const open = useCallback((callback?: BarcodeScanCallback) => { + const open = useCallback(() => { setOpened(true); }, []); @@ -168,6 +199,7 @@ export function useBarcodeScanDialog({ title={title} opened={opened} callback={callback} + modelType={modelType} onClose={() => setOpened(false)} /> ); diff --git a/src/frontend/src/pages/stock/LocationDetail.tsx b/src/frontend/src/pages/stock/LocationDetail.tsx index 9c315c2604..dd741a28e4 100644 --- a/src/frontend/src/pages/stock/LocationDetail.tsx +++ b/src/frontend/src/pages/stock/LocationDetail.tsx @@ -2,11 +2,13 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { getDetailUrl } from '@lib/functions/Navigation'; +import { apiUrl } from '@lib/index'; import { t } from '@lingui/core/macro'; import { Group, Skeleton, Stack, Text } from '@mantine/core'; import { IconInfoCircle, IconPackages, IconSitemap } from '@tabler/icons-react'; import { useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; +import { api } from '../../App'; import { useBarcodeScanDialog } from '../../components/barcodes/BarcodeScanDialog'; import AdminButton from '../../components/buttons/AdminButton'; import { PrintingActions } from '../../components/buttons/PrintingActions'; @@ -275,12 +277,32 @@ export default function Stock() { const scanInStockItems = useBarcodeScanDialog({ title: t`Scan Stock Item`, + modelType: ModelType.stockitem, callback: async (barcode, response) => { - console.log('response:', response); - return { - success: true, - error: '' - }; + const item = response.stockitem.instance; + + // Scan the stock item into the current location + 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` + }; + }); } });