| @@ -692,6 +692,8 @@ const [pickOrderSwitching, setPickOrderSwitching] = useState(false); | |||||
| // Use refs for processed QR tracking to avoid useEffect dependency issues and delays | // Use refs for processed QR tracking to avoid useEffect dependency issues and delays | ||||
| const processedQrCodesRef = useRef<Set<string>>(new Set()); | const processedQrCodesRef = useRef<Set<string>>(new Set()); | ||||
| const lastProcessedQrRef = useRef<string>(''); | 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); | const qrPickInFlightRef = useRef(false); | ||||
| // Store callbacks in refs to avoid useEffect dependency issues | // 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; | const indexAccessTime = performance.now() - indexAccessStart; | ||||
| console.log(` [PERF] Index access time: ${indexAccessTime.toFixed(2)}ms`); | 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, | expectedLot.stockOutLineId, | ||||
| ); | ); | ||||
| setProcessedQrCombinations(nextProcessedNoActive); | setProcessedQrCombinations(nextProcessedNoActive); | ||||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedNoActive); | |||||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||||
| if (workbenchMode) { | if (workbenchMode) { | ||||
| await refreshWorkbenchAfterScanPick(); | await refreshWorkbenchAfterScanPick(); | ||||
| @@ -1978,7 +1975,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| expectedLot.stockOutLineId, | expectedLot.stockOutLineId, | ||||
| ); | ); | ||||
| setProcessedQrCombinations(nextProcessedAuto); | setProcessedQrCombinations(nextProcessedAuto); | ||||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedAuto); | |||||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||||
| if (workbenchMode) { | if (workbenchMode) { | ||||
| await refreshWorkbenchAfterScanPick(); | await refreshWorkbenchAfterScanPick(); | ||||
| } | } | ||||
| @@ -2077,7 +2074,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| exactMatch.stockOutLineId, | exactMatch.stockOutLineId, | ||||
| ); | ); | ||||
| setProcessedQrCombinations(nextProcessedExact); | setProcessedQrCombinations(nextProcessedExact); | ||||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedExact); | |||||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||||
| const markProcessedTime = performance.now() - markProcessedStartTime; | const markProcessedTime = performance.now() - markProcessedStartTime; | ||||
| console.log(` [PERF] Mark processed time: ${markProcessedTime.toFixed(2)}ms`); | console.log(` [PERF] Mark processed time: ${markProcessedTime.toFixed(2)}ms`); | ||||
| @@ -2279,7 +2276,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| expectedLot.stockOutLineId, | expectedLot.stockOutLineId, | ||||
| ); | ); | ||||
| setProcessedQrCombinations(nextProcessedMismatch); | setProcessedQrCombinations(nextProcessedMismatch); | ||||
| maybeReleaseQrForNextSol(scannedItemId, scannedStockInLineId, nextProcessedMismatch); | |||||
| recordHandledQrScanCount(qrScanCountAtInvoke); | |||||
| if (workbenchMode) { | if (workbenchMode) { | ||||
| await refreshWorkbenchAfterScanPick(); | 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] Detection time: ${new Date().toISOString()}`); | ||||
| console.log(` [QR DETECTION] Time since QR scanner set value: ${(qrDetectionStartTime - qrValuesChangeStartTime).toFixed(2)}ms`); | console.log(` [QR DETECTION] Time since QR scanner set value: ${(qrDetectionStartTime - qrValuesChangeStartTime).toFixed(2)}ms`); | ||||
| const scanCount = qrValues.length; | |||||
| const qrPayload = parseWorkbenchQrPayload(latestQr); | const qrPayload = parseWorkbenchQrPayload(latestQr); | ||||
| const isNewScanEvent = scanCount > lastProcessedQrScanCountRef.current; | |||||
| const canRetrySamePhysicalLot = | const canRetrySamePhysicalLot = | ||||
| qrPayload != null && | qrPayload != null && | ||||
| isNewScanEvent && | |||||
| hasPendingActiveRowForStockInLine( | hasPendingActiveRowForStockInLine( | ||||
| lotDataIndexes, | lotDataIndexes, | ||||
| qrPayload.itemId, | qrPayload.itemId, | ||||
| @@ -2445,7 +2445,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| processedQrCombinations, | 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(); | const checkProcessedStartTime = performance.now(); | ||||
| if ( | if ( | ||||
| (processedQrCodesRef.current.has(latestQr) || lastProcessedQrRef.current === latestQr) && | (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`); | console.log(` [QR PROCESS] Already processed check time: ${checkTime.toFixed(2)}ms`); | ||||
| return; | return; | ||||
| } | } | ||||
| if (canRetrySamePhysicalLot) { | |||||
| processedQrCodesRef.current.delete(latestQr); | |||||
| if (lastProcessedQrRef.current === latestQr) { | |||||
| lastProcessedQrRef.current = ""; | |||||
| } | |||||
| } | |||||
| const checkTime = performance.now() - checkProcessedStartTime; | const checkTime = performance.now() - checkProcessedStartTime; | ||||
| console.log(` [QR PROCESS] Not processed check time: ${checkTime.toFixed(2)}ms`); | 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) | // 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(); | const processingStartTime = performance.now(); | ||||
| console.log(` [QR PROCESS] Starting processing at: ${new Date().toISOString()}`); | console.log(` [QR PROCESS] Starting processing at: ${new Date().toISOString()}`); | ||||
| console.log(` [QR PROCESS] Time since detection: ${(processingStartTime - qrDetectionStartTime).toFixed(2)}ms`); | console.log(` [QR PROCESS] Time since detection: ${(processingStartTime - qrDetectionStartTime).toFixed(2)}ms`); | ||||