| @@ -692,6 +692,8 @@ const [pickOrderSwitching, setPickOrderSwitching] = useState(false); | |||
| // Use refs for processed QR tracking to avoid useEffect dependency issues and delays | |||
| const processedQrCodesRef = useRef<Set<string>>(new Set()); | |||
| const lastProcessedQrRef = useRef<string>(''); | |||
| /** Last qrValues.length handled; new pick on same QR requires a new scanner event (length increase). */ | |||
| const lastProcessedQrScanCountRef = useRef(0); | |||
| const qrPickInFlightRef = useRef(false); | |||
| // Store callbacks in refs to avoid useEffect dependency issues | |||
| @@ -1518,14 +1520,9 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| const indexAccessTime = performance.now() - indexAccessStart; | |||
| console.log(` [PERF] Index access time: ${indexAccessTime.toFixed(2)}ms`); | |||
| const maybeReleaseQrForNextSol = ( | |||
| itemId: number, | |||
| stockInLineId: number, | |||
| processedAfterMark: ProcessedStockOutLinesByItemId, | |||
| ) => { | |||
| if (hasPendingActiveRowForStockInLine(indexes, itemId, stockInLineId, processedAfterMark)) { | |||
| lastProcessedQrRef.current = ""; | |||
| processedQrCodesRef.current.delete(latestQr); | |||
| const recordHandledQrScanCount = (scanCount: number | undefined) => { | |||
| if (scanCount != null && scanCount > lastProcessedQrScanCountRef.current) { | |||
| lastProcessedQrScanCountRef.current = scanCount; | |||
| } | |||
| }; | |||
| @@ -1812,7 +1809,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| expectedLot.stockOutLineId, | |||
| ); | |||
| setProcessedQrCombinations(nextProcessedNoActive); | |||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedNoActive); | |||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||
| if (workbenchMode) { | |||
| await refreshWorkbenchAfterScanPick(); | |||
| @@ -1978,7 +1975,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| expectedLot.stockOutLineId, | |||
| ); | |||
| setProcessedQrCombinations(nextProcessedAuto); | |||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedAuto); | |||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||
| if (workbenchMode) { | |||
| await refreshWorkbenchAfterScanPick(); | |||
| } | |||
| @@ -2077,7 +2074,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| exactMatch.stockOutLineId, | |||
| ); | |||
| setProcessedQrCombinations(nextProcessedExact); | |||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedExact); | |||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||
| const markProcessedTime = performance.now() - markProcessedStartTime; | |||
| console.log(` [PERF] Mark processed time: ${markProcessedTime.toFixed(2)}ms`); | |||
| @@ -2279,7 +2276,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| expectedLot.stockOutLineId, | |||
| ); | |||
| setProcessedQrCombinations(nextProcessedMismatch); | |||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedMismatch); | |||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||
| if (workbenchMode) { | |||
| await refreshWorkbenchAfterScanPick(); | |||
| @@ -2435,9 +2432,12 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| console.log(` [QR DETECTION] Detection time: ${new Date().toISOString()}`); | |||
| console.log(` [QR DETECTION] Time since QR scanner set value: ${(qrDetectionStartTime - qrValuesChangeStartTime).toFixed(2)}ms`); | |||
| const scanCount = qrValues.length; | |||
| const qrPayload = parseWorkbenchQrPayload(latestQr); | |||
| const isNewScanEvent = scanCount > lastProcessedQrScanCountRef.current; | |||
| const canRetrySamePhysicalLot = | |||
| qrPayload != null && | |||
| isNewScanEvent && | |||
| hasPendingActiveRowForStockInLine( | |||
| lotDataIndexes, | |||
| qrPayload.itemId, | |||
| @@ -2445,7 +2445,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| processedQrCombinations, | |||
| ); | |||
| // Skip if already processed (allow same QR when another pending SOL needs this lot) | |||
| // Skip if already processed; same QR only retries on a new scanner event (qrValues.length increase) | |||
| const checkProcessedStartTime = performance.now(); | |||
| if ( | |||
| (processedQrCodesRef.current.has(latestQr) || lastProcessedQrRef.current === latestQr) && | |||
| @@ -2455,12 +2455,6 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| console.log(` [QR PROCESS] Already processed check time: ${checkTime.toFixed(2)}ms`); | |||
| return; | |||
| } | |||
| if (canRetrySamePhysicalLot) { | |||
| processedQrCodesRef.current.delete(latestQr); | |||
| if (lastProcessedQrRef.current === latestQr) { | |||
| lastProcessedQrRef.current = ""; | |||
| } | |||
| } | |||
| const checkTime = performance.now() - checkProcessedStartTime; | |||
| console.log(` [QR PROCESS] Not processed check time: ${checkTime.toFixed(2)}ms`); | |||
| @@ -2495,7 +2489,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| } | |||
| // Process new QR code immediately (background mode - no modal) | |||
| if (latestQr && (latestQr !== lastProcessedQrRef.current || canRetrySamePhysicalLot)) { | |||
| if (latestQr && (latestQr !== lastProcessedQrRef.current || isNewScanEvent)) { | |||
| const processingStartTime = performance.now(); | |||
| console.log(` [QR PROCESS] Starting processing at: ${new Date().toISOString()}`); | |||
| console.log(` [QR PROCESS] Time since detection: ${(processingStartTime - qrDetectionStartTime).toFixed(2)}ms`); | |||