|
|
@@ -526,7 +526,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
stockOutLineId: lot.stockOutLineId, |
|
|
stockOutLineId: lot.stockOutLineId, |
|
|
stockOutLineStatus: lot.stockOutLineStatus, |
|
|
stockOutLineStatus: lot.stockOutLineStatus, |
|
|
stockOutLineQty: lot.stockOutLineQty, |
|
|
stockOutLineQty: lot.stockOutLineQty, |
|
|
|
|
|
|
|
|
|
|
|
suggestedPickLotId: lot.suggestedPickLotId, |
|
|
// Router info |
|
|
// Router info |
|
|
routerIndex: lot.routerIndex, |
|
|
routerIndex: lot.routerIndex, |
|
|
secondQrScanStatus: lot.secondQrScanStatus, |
|
|
secondQrScanStatus: lot.secondQrScanStatus, |
|
|
@@ -714,9 +714,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
setQrScanSuccess(true); |
|
|
setQrScanSuccess(true); |
|
|
setQrScanError(false); |
|
|
setQrScanError(false); |
|
|
setQrScanInput(''); // Clear input after successful processing |
|
|
setQrScanInput(''); // Clear input after successful processing |
|
|
setIsManualScanning(false); |
|
|
|
|
|
stopScan(); |
|
|
|
|
|
resetScan(); |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
console.error(`❌ QR Code processing failed: ${errorCount} errors`); |
|
|
console.error(`❌ QR Code processing failed: ${errorCount} errors`); |
|
|
setQrScanError(true); |
|
|
setQrScanError(true); |
|
|
@@ -759,48 +757,87 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
await confirmLotSubstitution({ |
|
|
|
|
|
|
|
|
console.log("=== Lot Confirmation Debug ==="); |
|
|
|
|
|
console.log("Selected Lot:", selectedLotForQr); |
|
|
|
|
|
console.log("Pick Order Line ID:", selectedLotForQr.pickOrderLineId); |
|
|
|
|
|
console.log("Stock Out Line ID:", selectedLotForQr.stockOutLineId); |
|
|
|
|
|
console.log("Suggested Pick Lot ID:", selectedLotForQr.suggestedPickLotId); |
|
|
|
|
|
console.log("Lot ID (fallback):", selectedLotForQr.lotId); |
|
|
|
|
|
console.log("New Inventory Lot Line ID:", newLotLineId); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Call confirmLotSubstitution to update the suggested lot |
|
|
|
|
|
const substitutionResult = await confirmLotSubstitution({ |
|
|
pickOrderLineId: selectedLotForQr.pickOrderLineId, |
|
|
pickOrderLineId: selectedLotForQr.pickOrderLineId, |
|
|
stockOutLineId: selectedLotForQr.stockOutLineId, |
|
|
stockOutLineId: selectedLotForQr.stockOutLineId, |
|
|
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId, |
|
|
|
|
|
|
|
|
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId || selectedLotForQr.lotId, |
|
|
newInventoryLotLineId: newLotLineId |
|
|
newInventoryLotLineId: newLotLineId |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
setQrScanError(false); |
|
|
|
|
|
setQrScanSuccess(false); |
|
|
|
|
|
setQrScanInput(''); |
|
|
|
|
|
setProcessedQrCodes(new Set()); |
|
|
|
|
|
setLastProcessedQr(''); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log("✅ Lot substitution result:", substitutionResult); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Update stock out line status to 'checked' after substitution |
|
|
if(selectedLotForQr?.stockOutLineId){ |
|
|
if(selectedLotForQr?.stockOutLineId){ |
|
|
await updateStockOutLineStatus({ |
|
|
await updateStockOutLineStatus({ |
|
|
id: selectedLotForQr.stockOutLineId, |
|
|
id: selectedLotForQr.stockOutLineId, |
|
|
status: 'checked', |
|
|
status: 'checked', |
|
|
qty: 0 |
|
|
qty: 0 |
|
|
}); |
|
|
}); |
|
|
|
|
|
console.log("✅ Stock out line status updated to 'checked'"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Close modal and clean up state BEFORE refreshing |
|
|
setLotConfirmationOpen(false); |
|
|
setLotConfirmationOpen(false); |
|
|
setExpectedLotData(null); |
|
|
setExpectedLotData(null); |
|
|
setScannedLotData(null); |
|
|
setScannedLotData(null); |
|
|
setSelectedLotForQr(null); |
|
|
setSelectedLotForQr(null); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Clear QR processing state but DON'T clear processedQrCodes yet |
|
|
|
|
|
setQrScanError(false); |
|
|
|
|
|
setQrScanSuccess(true); |
|
|
|
|
|
setQrScanInput(''); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Set refreshing flag to prevent QR processing during refresh |
|
|
|
|
|
setIsRefreshingData(true); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Refresh data to show updated lot |
|
|
|
|
|
console.log("🔄 Refreshing job order data..."); |
|
|
await fetchJobOrderData(); |
|
|
await fetchJobOrderData(); |
|
|
console.log("✅ Lot substitution confirmed and data refreshed"); |
|
|
console.log("✅ Lot substitution confirmed and data refreshed"); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ Clear processed QR codes and flags immediately after refresh |
|
|
|
|
|
// This allows new QR codes to be processed right away |
|
|
|
|
|
setTimeout(() => { |
|
|
|
|
|
console.log("✅ Clearing processed QR codes and resuming scan"); |
|
|
|
|
|
setProcessedQrCodes(new Set()); |
|
|
|
|
|
setLastProcessedQr(''); |
|
|
|
|
|
setQrScanSuccess(false); |
|
|
|
|
|
setIsRefreshingData(false); |
|
|
|
|
|
}, 500); // ✅ Reduced from 3000ms to 500ms - just enough for UI update |
|
|
|
|
|
|
|
|
} catch (error) { |
|
|
} catch (error) { |
|
|
console.error("Error confirming lot substitution:", error); |
|
|
console.error("Error confirming lot substitution:", error); |
|
|
|
|
|
setQrScanError(true); |
|
|
|
|
|
setQrScanSuccess(false); |
|
|
|
|
|
// ✅ Clear refresh flag on error |
|
|
|
|
|
setIsRefreshingData(false); |
|
|
} finally { |
|
|
} finally { |
|
|
setIsConfirmingLot(false); |
|
|
setIsConfirmingLot(false); |
|
|
} |
|
|
} |
|
|
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData]); |
|
|
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData]); |
|
|
|
|
|
|
|
|
const processOutsideQrCode = useCallback(async (latestQr: string) => { |
|
|
const processOutsideQrCode = useCallback(async (latestQr: string) => { |
|
|
|
|
|
// ✅ Don't process if confirmation modal is open |
|
|
|
|
|
if (lotConfirmationOpen) { |
|
|
|
|
|
console.log("⏸️ Confirmation modal is open, skipping QR processing"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
let qrData: any = null; |
|
|
let qrData: any = null; |
|
|
try { |
|
|
try { |
|
|
qrData = JSON.parse(latestQr); |
|
|
qrData = JSON.parse(latestQr); |
|
|
} catch { |
|
|
} catch { |
|
|
console.log("QR is not JSON format"); |
|
|
console.log("QR is not JSON format"); |
|
|
// ✅ Handle non-JSON QR codes as direct lot numbers |
|
|
|
|
|
|
|
|
// Handle non-JSON QR codes as direct lot numbers |
|
|
const directLotNo = latestQr.replace(/[{}]/g, ''); |
|
|
const directLotNo = latestQr.replace(/[{}]/g, ''); |
|
|
if (directLotNo) { |
|
|
if (directLotNo) { |
|
|
console.log(`Processing direct lot number: ${directLotNo}`); |
|
|
console.log(`Processing direct lot number: ${directLotNo}`); |
|
|
@@ -891,6 +928,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
|
|
|
|
|
// Case 2: Same item, different lot - show confirmation modal |
|
|
// Case 2: Same item, different lot - show confirmation modal |
|
|
console.log(`🔍 Lot mismatch: Expected ${expectedLot.lotNo}, Scanned ${scanned?.lotNo}`); |
|
|
console.log(`🔍 Lot mismatch: Expected ${expectedLot.lotNo}, Scanned ${scanned?.lotNo}`); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ DON'T stop scanning - just pause QR processing by showing modal |
|
|
setSelectedLotForQr(expectedLot); |
|
|
setSelectedLotForQr(expectedLot); |
|
|
handleLotMismatch( |
|
|
handleLotMismatch( |
|
|
{ |
|
|
{ |
|
|
@@ -912,7 +951,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
setQrScanSuccess(false); |
|
|
setQrScanSuccess(false); |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
}, [combinedLotData, handleQrCodeSubmit, handleLotMismatch]); |
|
|
|
|
|
|
|
|
}, [combinedLotData, handleQrCodeSubmit, handleLotMismatch, lotConfirmationOpen]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleManualInputSubmit = useCallback(() => { |
|
|
const handleManualInputSubmit = useCallback(() => { |
|
|
if (qrScanInput.trim() !== '') { |
|
|
if (qrScanInput.trim() !== '') { |
|
|
@@ -964,7 +1004,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
if (qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) { |
|
|
|
|
|
|
|
|
// ✅ Add isManualScanning check |
|
|
|
|
|
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData || lotConfirmationOpen) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@@ -982,7 +1023,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
|
|
|
|
|
processOutsideQrCode(latestQr); |
|
|
processOutsideQrCode(latestQr); |
|
|
} |
|
|
} |
|
|
}, [qrValues, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData]); |
|
|
|
|
|
|
|
|
}, [qrValues, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData, isManualScanning, lotConfirmationOpen]); |
|
|
|
|
|
|
|
|
const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => { |
|
|
const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => { |
|
|
if (value === '' || value === null || value === undefined) { |
|
|
if (value === '' || value === null || value === undefined) { |
|
|
@@ -1372,6 +1413,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
stopScan(); |
|
|
stopScan(); |
|
|
resetScan(); |
|
|
resetScan(); |
|
|
}, [stopScan, resetScan]); |
|
|
}, [stopScan, resetScan]); |
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
return () => { |
|
|
|
|
|
// Cleanup when component unmounts (e.g., when switching tabs) |
|
|
|
|
|
if (isManualScanning) { |
|
|
|
|
|
console.log("🧹 Component unmounting, stopping QR scanner..."); |
|
|
|
|
|
stopScan(); |
|
|
|
|
|
resetScan(); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
}, [isManualScanning, stopScan, resetScan]); |
|
|
|
|
|
|
|
|
const getStatusMessage = useCallback((lot: any) => { |
|
|
const getStatusMessage = useCallback((lot: any) => { |
|
|
switch (lot.stockOutLineStatus?.toLowerCase()) { |
|
|
switch (lot.stockOutLineStatus?.toLowerCase()) { |
|
|
@@ -1639,6 +1690,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
</Stack> |
|
|
</Stack> |
|
|
|
|
|
|
|
|
{/* ✅ QR Code Modal */} |
|
|
{/* ✅ QR Code Modal */} |
|
|
|
|
|
{!lotConfirmationOpen && ( |
|
|
<QrCodeModal |
|
|
<QrCodeModal |
|
|
open={qrModalOpen} |
|
|
open={qrModalOpen} |
|
|
onClose={() => { |
|
|
onClose={() => { |
|
|
@@ -1651,6 +1703,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
combinedLotData={combinedLotData} |
|
|
combinedLotData={combinedLotData} |
|
|
onQrCodeSubmit={handleQrCodeSubmitFromModal} |
|
|
onQrCodeSubmit={handleQrCodeSubmitFromModal} |
|
|
/> |
|
|
/> |
|
|
|
|
|
)} |
|
|
{/* ✅ Add Lot Confirmation Modal */} |
|
|
{/* ✅ Add Lot Confirmation Modal */} |
|
|
{lotConfirmationOpen && expectedLotData && scannedLotData && ( |
|
|
{lotConfirmationOpen && expectedLotData && scannedLotData && ( |
|
|
<LotConfirmationModal |
|
|
<LotConfirmationModal |
|
|
@@ -1659,6 +1712,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
setLotConfirmationOpen(false); |
|
|
setLotConfirmationOpen(false); |
|
|
setExpectedLotData(null); |
|
|
setExpectedLotData(null); |
|
|
setScannedLotData(null); |
|
|
setScannedLotData(null); |
|
|
|
|
|
setSelectedLotForQr(null); |
|
|
|
|
|
|
|
|
}} |
|
|
}} |
|
|
onConfirm={handleLotConfirmation} |
|
|
onConfirm={handleLotConfirmation} |
|
|
expectedLot={expectedLotData} |
|
|
expectedLot={expectedLotData} |
|
|
|