Преглед изворни кода

Merge branch 'master' of http://svn.2fi-solutions.com:8300/derek/FPSMS-frontend

# Conflicts:
#	src/components/JoSave/PickTable.tsx
master
CANCERYS\kw093 пре 2 месеци
родитељ
комит
b319c2f92b
4 измењених фајлова са 159 додато и 102 уклоњено
  1. +71
    -16
      src/components/Jodetail/JobPickExecution.tsx
  2. +83
    -84
      src/components/Jodetail/JobPickExecutionsecondscan.tsx
  3. +3
    -2
      src/i18n/zh/jo.json
  4. +2
    -0
      src/i18n/zh/pickOrder.json

+ 71
- 16
src/components/Jodetail/JobPickExecution.tsx Прегледај датотеку

@@ -526,7 +526,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
stockOutLineId: lot.stockOutLineId,
stockOutLineStatus: lot.stockOutLineStatus,
stockOutLineQty: lot.stockOutLineQty,
suggestedPickLotId: lot.suggestedPickLotId,
// Router info
routerIndex: lot.routerIndex,
secondQrScanStatus: lot.secondQrScanStatus,
@@ -714,9 +714,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
setQrScanSuccess(true);
setQrScanError(false);
setQrScanInput(''); // Clear input after successful processing
setIsManualScanning(false);
stopScan();
resetScan();
} else {
console.error(`❌ QR Code processing failed: ${errorCount} errors`);
setQrScanError(true);
@@ -759,48 +757,87 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
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,
stockOutLineId: selectedLotForQr.stockOutLineId,
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId,
originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId || selectedLotForQr.lotId,
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){
await updateStockOutLineStatus({
id: selectedLotForQr.stockOutLineId,
status: 'checked',
qty: 0
});
console.log("✅ Stock out line status updated to 'checked'");
}
// ✅ Close modal and clean up state BEFORE refreshing
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(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();
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) {
console.error("Error confirming lot substitution:", error);
setQrScanError(true);
setQrScanSuccess(false);
// ✅ Clear refresh flag on error
setIsRefreshingData(false);
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData]);

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;
try {
qrData = JSON.parse(latestQr);
} catch {
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, '');
if (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
console.log(`🔍 Lot mismatch: Expected ${expectedLot.lotNo}, Scanned ${scanned?.lotNo}`);
// ✅ DON'T stop scanning - just pause QR processing by showing modal
setSelectedLotForQr(expectedLot);
handleLotMismatch(
{
@@ -912,7 +951,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
setQrScanSuccess(false);
return;
}
}, [combinedLotData, handleQrCodeSubmit, handleLotMismatch]);
}, [combinedLotData, handleQrCodeSubmit, handleLotMismatch, lotConfirmationOpen]);


const handleManualInputSubmit = useCallback(() => {
if (qrScanInput.trim() !== '') {
@@ -964,7 +1004,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {

useEffect(() => {
if (qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
// ✅ Add isManualScanning check
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData || lotConfirmationOpen) {
return;
}
@@ -982,7 +1023,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
processOutsideQrCode(latestQr);
}
}, [qrValues, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData]);
}, [qrValues, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData, isManualScanning, lotConfirmationOpen]);

const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
@@ -1372,6 +1413,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
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) => {
switch (lot.stockOutLineStatus?.toLowerCase()) {
@@ -1639,6 +1690,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
</Stack>

{/* ✅ QR Code Modal */}
{!lotConfirmationOpen && (
<QrCodeModal
open={qrModalOpen}
onClose={() => {
@@ -1651,6 +1703,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
combinedLotData={combinedLotData}
onQrCodeSubmit={handleQrCodeSubmitFromModal}
/>
)}
{/* ✅ Add Lot Confirmation Modal */}
{lotConfirmationOpen && expectedLotData && scannedLotData && (
<LotConfirmationModal
@@ -1659,6 +1712,8 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
setLotConfirmationOpen(false);
setExpectedLotData(null);
setScannedLotData(null);
setSelectedLotForQr(null);
}}
onConfirm={handleLotConfirmation}
expectedLot={expectedLotData}


+ 83
- 84
src/components/Jodetail/JobPickExecutionsecondscan.tsx Прегледај датотеку

@@ -614,7 +614,16 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
if (successCount > 0) {
setQrScanSuccess(true);
setQrScanError(false);
// ✅ Set refreshing flag briefly to prevent duplicate processing
setIsRefreshingData(true);
await fetchJobOrderData(); // Refresh data
// ✅ Clear refresh flag and success message after a short delay
setTimeout(() => {
setQrScanSuccess(false);
setIsRefreshingData(false);
}, 500);
} else {
setQrScanError(true);
setQrScanSuccess(false);
@@ -626,52 +635,68 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}
}, [combinedLotData, fetchJobOrderData, processedQrCodes]);
useEffect(() => {
if (qrValues.length > 0 && combinedLotData.length > 0) {
const latestQr = qrValues[qrValues.length - 1];
// ✅ Check if this QR was already processed recently
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log("⏭️ QR code already processed, skipping...");
return;
}
// ✅ Mark as processed
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
setLastProcessedQr(latestQr);
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
fetchStockInLineInfo(qrData.stockInLineId)
.then((stockInLineInfo) => {
console.log("Outside QR scan - Stock in line info:", stockInLineInfo);
const extractedLotNo = stockInLineInfo.lotNo;
if (extractedLotNo) {
console.log(`Outside QR scan detected (JSON): ${extractedLotNo}`);
handleQrCodeSubmit(extractedLotNo);
}
})
.catch((error) => {
console.error("Outside QR scan - Error fetching stock in line info:", error);
});
return; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
// ✅ Add isManualScanning and isRefreshingData checks
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
return;
}
const latestQr = qrValues[qrValues.length - 1];
// ✅ Check if this QR was already processed recently
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
console.log("⏭️ QR code already processed, skipping...");
return;
}
// ✅ Mark as processed
setProcessedQrCodes(prev => new Set(prev).add(latestQr));
setLastProcessedQr(latestQr);
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
fetchStockInLineInfo(qrData.stockInLineId)
.then((stockInLineInfo) => {
console.log("Outside QR scan - Stock in line info:", stockInLineInfo);
const extractedLotNo = stockInLineInfo.lotNo;
if (extractedLotNo) {
console.log(`Outside QR scan detected (JSON): ${extractedLotNo}`);
handleQrCodeSubmit(extractedLotNo);
}
})
.catch((error) => {
console.error("Outside QR scan - Error fetching stock in line info:", error);
});
return; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
}, [qrValues, combinedLotData, handleQrCodeSubmit, processedQrCodes, lastProcessedQr]);
}, [qrValues, combinedLotData, handleQrCodeSubmit, processedQrCodes, lastProcessedQr, isManualScanning, isRefreshingData]);

// ✅ ADD THIS: Cleanup effect
useEffect(() => {
return () => {
// Cleanup when component unmounts (e.g., when switching tabs)
if (isManualScanning) {
console.log("🧹 Second scan component unmounting, stopping QR scanner...");
stopScan();
resetScan();
}
};
}, [isManualScanning, stopScan, resetScan]);
const handleManualInputSubmit = useCallback(() => {
if (qrScanInput.trim() !== '') {
handleQrCodeSubmit(qrScanInput.trim());
@@ -721,42 +746,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs }) => {
}, [selectedLotForQr, fetchJobOrderData]);

// ✅ Outside QR scanning - process QR codes from outside the page automatically
useEffect(() => {
if (qrValues.length > 0 && combinedLotData.length > 0) {
const latestQr = qrValues[qrValues.length - 1];
// Extract lot number from QR code
let lotNo = '';
try {
const qrData = JSON.parse(latestQr);
if (qrData.stockInLineId && qrData.itemId) {
// For JSON QR codes, we need to fetch the lot number
fetchStockInLineInfo(qrData.stockInLineId)
.then((stockInLineInfo) => {
console.log("Outside QR scan - Stock in line info:", stockInLineInfo);
const extractedLotNo = stockInLineInfo.lotNo;
if (extractedLotNo) {
console.log(`Outside QR scan detected (JSON): ${extractedLotNo}`);
handleQrCodeSubmit(extractedLotNo);
}
})
.catch((error) => {
console.error("Outside QR scan - Error fetching stock in line info:", error);
});
return; // Exit early for JSON QR codes
}
} catch (error) {
// Not JSON format, treat as direct lot number
lotNo = latestQr.replace(/[{}]/g, '');
}
// For direct lot number QR codes
if (lotNo) {
console.log(`Outside QR scan detected (direct): ${lotNo}`);
handleQrCodeSubmit(lotNo);
}
}
}, [qrValues, combinedLotData, handleQrCodeSubmit]);

const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
if (value === '' || value === null || value === undefined) {
@@ -993,7 +983,16 @@ const paginatedData = useMemo(() => {
stopScan();
resetScan();
}, [stopScan, resetScan]);

useEffect(() => {
return () => {
// Cleanup when component unmounts (e.g., when switching tabs)
if (isManualScanning) {
console.log("🧹 Second scan component unmounting, stopping QR scanner...");
stopScan();
resetScan();
}
};
}, [isManualScanning, stopScan, resetScan]);
const getStatusMessage = useCallback((lot: any) => {
switch (lot.stockOutLineStatus?.toLowerCase()) {
case 'pending':
@@ -1152,8 +1151,8 @@ const paginatedData = useMemo(() => {
})()}
</TableCell>
<TableCell align="center">
{lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? (
<TableCell align="center">
{lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? (
<Box sx={{
display: 'flex',
justifyContent: 'center',
@@ -1162,12 +1161,12 @@ const paginatedData = useMemo(() => {
height: '100%'
}}>
<Checkbox
checked={lot.secondQrScanStatus?.toLowerCase() !== 'pending'}
checked={lot.secondQrScanStatus?.toLowerCase() !== 'pending'}
disabled={true}
readOnly={true}
size="large"
sx={{
color: lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400',
color: lot.secondQrScanStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400',
'&.Mui-checked': {
color: 'success.main',
},


+ 3
- 2
src/i18n/zh/jo.json Прегледај датотеку

@@ -101,7 +101,8 @@
"Issue Remark": "問題描述",
"Received Qty": "接收數量",
"Create": "創建",
"Confirm Lot Substitution": "確認批號替換"

"Confirm Lot Substitution": "確認批號替換",
"Processing...": "處理中",
"Processing...": "處理中"

}

+ 2
- 0
src/i18n/zh/pickOrder.json Прегледај датотеку

@@ -224,6 +224,8 @@
"Selected items will join above created group": "已選擇的貨品將加入以上建立的分組",
"Issue":"問題",
"issue":"問題",
"Processing...":"處理中",
"Assigning pick order...":"分派提料單中...",
"Pick Execution Issue Form":"提料問題表單",
"This form is for reporting issues only. You must report either missing items or bad items.":"此表單僅用於報告問題。您必須報告缺少的貨品或不良貨品。",
"Bad item Qty":"不良貨品數量",


Loading…
Откажи
Сачувај