| @@ -737,6 +737,12 @@ const [pickOrderSwitching, setPickOrderSwitching] = useState(false); | |||||
| const lastProcessedQrRef = useRef<string>(''); | const lastProcessedQrRef = useRef<string>(''); | ||||
| /** Prevent double-submit while a scan-pick API is in flight (scanner bounce). */ | /** Prevent double-submit while a scan-pick API is in flight (scanner bounce). */ | ||||
| const qrPickInFlightRef = useRef(false); | const qrPickInFlightRef = useRef(false); | ||||
| /** | |||||
| * Only process newly appended qrValues entries. | |||||
| * Prevents refresh/deps re-running the effect from auto-picking the next SOL | |||||
| * with the same stale QR still sitting at the end of qrValues. | |||||
| */ | |||||
| const lastConsumedQrValuesLengthRef = useRef(0); | |||||
| /** Latest lot indexes for QR re-scan allow checks outside processOutsideQrCode. */ | /** Latest lot indexes for QR re-scan allow checks outside processOutsideQrCode. */ | ||||
| const lotDataIndexesRef = useRef<{ | const lotDataIndexesRef = useRef<{ | ||||
| byStockInLineId: Map<number, any[]>; | byStockInLineId: Map<number, any[]>; | ||||
| @@ -2429,6 +2435,21 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) { | if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) { | ||||
| return; | return; | ||||
| } | } | ||||
| // Only react to newly appended QR events — not effect re-runs after refresh / index updates. | |||||
| if (qrValues.length <= lastConsumedQrValuesLengthRef.current) { | |||||
| console.log( | |||||
| ` [QR PROCESS] Skipping — no new QR append (len=${qrValues.length}, consumed=${lastConsumedQrValuesLengthRef.current})`, | |||||
| ); | |||||
| return; | |||||
| } | |||||
| // Don't consume yet if a pick is in flight; retry after refresh / inFlight clears. | |||||
| if (qrPickInFlightRef.current) { | |||||
| console.log( | |||||
| ` [QR PROCESS] Deferring new QR — scan-pick still in flight (len=${qrValues.length})`, | |||||
| ); | |||||
| return; | |||||
| } | |||||
| const qrValuesChangeStartTime = performance.now(); | const qrValuesChangeStartTime = performance.now(); | ||||
| console.log(` [QR VALUES EFFECT] Triggered at: ${new Date().toISOString()}`); | console.log(` [QR VALUES EFFECT] Triggered at: ${new Date().toISOString()}`); | ||||
| @@ -2436,6 +2457,8 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| console.log(` [QR VALUES EFFECT] qrValues:`, qrValues); | console.log(` [QR VALUES EFFECT] qrValues:`, qrValues); | ||||
| const latestQr = qrValues[qrValues.length - 1]; | const latestQr = qrValues[qrValues.length - 1]; | ||||
| // Claim this append immediately so refresh/deps re-entry cannot re-process it. | |||||
| lastConsumedQrValuesLengthRef.current = qrValues.length; | |||||
| console.log(` [QR VALUES EFFECT] Latest QR: ${latestQr}`); | console.log(` [QR VALUES EFFECT] Latest QR: ${latestQr}`); | ||||
| console.log(` [QR VALUES EFFECT] Latest QR detected at: ${new Date().toISOString()}`); | console.log(` [QR VALUES EFFECT] Latest QR detected at: ${new Date().toISOString()}`); | ||||
| @@ -3311,6 +3334,8 @@ const handleStartScan = useCallback(() => { | |||||
| processedQrCodesRef.current = new Set(); | processedQrCodesRef.current = new Set(); | ||||
| lastProcessedQrRef.current = ''; | lastProcessedQrRef.current = ''; | ||||
| qrPickInFlightRef.current = false; | qrPickInFlightRef.current = false; | ||||
| // Ignore any QR values already in the buffer; only new scans after Start count. | |||||
| lastConsumedQrValuesLengthRef.current = qrValues.length; | |||||
| setQrScanError(false); | setQrScanError(false); | ||||
| setQrScanSuccess(false); | setQrScanSuccess(false); | ||||
| @@ -3322,7 +3347,7 @@ const handleStartScan = useCallback(() => { | |||||
| const totalTime = performance.now() - startTime; | const totalTime = performance.now() - startTime; | ||||
| console.log(` [START SCAN] Total start scan time: ${totalTime.toFixed(2)}ms`); | console.log(` [START SCAN] Total start scan time: ${totalTime.toFixed(2)}ms`); | ||||
| console.log(` [START SCAN] Start scan completed at: ${new Date().toISOString()}`); | console.log(` [START SCAN] Start scan completed at: ${new Date().toISOString()}`); | ||||
| }, [startScan]); | |||||
| }, [startScan, qrValues.length]); | |||||
| const handlePickOrderSwitch = useCallback(async (pickOrderId: number) => { | const handlePickOrderSwitch = useCallback(async (pickOrderId: number) => { | ||||
| if (pickOrderSwitching) return; | if (pickOrderSwitching) return; | ||||
| @@ -3347,6 +3372,7 @@ const handleStartScan = useCallback(() => { | |||||
| setQrScanSuccess(false); | setQrScanSuccess(false); | ||||
| stopScan(); | stopScan(); | ||||
| resetScan(); | resetScan(); | ||||
| lastConsumedQrValuesLengthRef.current = 0; | |||||
| }, [stopScan, resetScan]); | }, [stopScan, resetScan]); | ||||
| // ... existing code around line 1469 ... | // ... existing code around line 1469 ... | ||||
| const handlelotnull = useCallback(async (lot: any) => { | const handlelotnull = useCallback(async (lot: any) => { | ||||