From 0154dec661117cdedf2d8ce875608ba7c3fa9e3a Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 22 Sep 2025 12:02:35 +0800 Subject: [PATCH] update --- src/app/api/pickOrder/actions.ts | 1 + src/components/DoDetail/DoInfoCard.tsx | 9 +- .../FinishedGoodSearch/FGPickOrderCard.tsx | 8 + .../FinishedGoodSearch/FinishedGoodSearch.tsx | 32 +- .../GoodPickExecutiondetail.tsx | 420 +++++++++--------- src/i18n/zh/pickOrder.json | 6 +- 6 files changed, 253 insertions(+), 223 deletions(-) diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index 9425394..5fa527b 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -255,6 +255,7 @@ export interface FGPickOrderResponse { shopCode: string; shopName: string; shopAddress: string; + ticketNo: string; shopPoNo: string; numberOfCartons: number; DepartureTime: string; diff --git a/src/components/DoDetail/DoInfoCard.tsx b/src/components/DoDetail/DoInfoCard.tsx index 316b22a..44c5850 100644 --- a/src/components/DoDetail/DoInfoCard.tsx +++ b/src/components/DoDetail/DoInfoCard.tsx @@ -78,14 +78,7 @@ const DoInfoCard: React.FC = ({ disabled={true} /> - - - + diff --git a/src/components/FinishedGoodSearch/FGPickOrderCard.tsx b/src/components/FinishedGoodSearch/FGPickOrderCard.tsx index f1bfd05..befb6f2 100644 --- a/src/components/FinishedGoodSearch/FGPickOrderCard.tsx +++ b/src/components/FinishedGoodSearch/FGPickOrderCard.tsx @@ -93,6 +93,14 @@ const FGPickOrderCard: React.FC = ({ fgOrder, onQrCodeClick }) => { value={fgOrder.truckNo} /> + + + - - + + + + + diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index aa7142e..158a537 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -350,6 +350,11 @@ const PickExecution: React.FC = ({ filterArgs }) => { const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState(null); const [fgPickOrders, setFgPickOrders] = useState([]); const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false); + // ✅ Add these missing state variables after line 352 + const [isManualScanning, setIsManualScanning] = useState(false); + const [processedQrCodes, setProcessedQrCodes] = useState>(new Set()); + const [lastProcessedQr, setLastProcessedQr] = useState(''); + const [isRefreshingData, setIsRefreshingData] = useState(false); const fetchFgPickOrdersData = useCallback(async () => { if (!currentUserId) return; @@ -394,13 +399,6 @@ const PickExecution: React.FC = ({ filterArgs }) => { // TODO: Implement QR code functionality }; - useEffect(() => { - startScan(); - return () => { - stopScan(); - resetScan(); - }; - }, [startScan, stopScan, resetScan]); const fetchAllCombinedLotData = useCallback(async (userId?: number) => { setCombinedDataLoading(true); @@ -455,14 +453,12 @@ const PickExecution: React.FC = ({ filterArgs }) => { }; }, [fetchAllCombinedLotData]); - // ✅ Handle QR code submission for matched lot (external scanning) - // ✅ Handle QR code submission for matched lot (external scanning) - const handleQrCodeSubmit = useCallback(async (lotNo: string) => { + const handleQrCodeSubmit = useCallback(async (lotNo: string) => { console.log(`✅ Processing QR Code for lot: ${lotNo}`); // ✅ Use current data without refreshing to avoid infinite loop const currentLotData = combinedLotData; - console.log(`🔍 Available lots:`, currentLotData.map(lot => lot.lotNo)); + console.log(`�� Available lots:`, currentLotData.map(lot => lot.lotNo)); const matchingLots = currentLotData.filter(lot => lot.lotNo === lotNo || @@ -488,35 +484,50 @@ const PickExecution: React.FC = ({ filterArgs }) => { console.log(`🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`); if (matchingLot.stockOutLineId) { - console.log(`✅ Stock out line already exists for line ${matchingLot.pickOrderLineId}`); - existsCount++; - } else { + // ✅ FIXED: Use matchingLot.stockOutLineId instead of selectedLotForQr.stockOutLineId const stockOutLineUpdate = await updateStockOutLineStatus({ - id: selectedLotForQr.stockOutLineId, + id: matchingLot.stockOutLineId, // ✅ Use the correct ID status: 'checked', - qty: selectedLotForQr.stockOutLineQty || 0 + qty: matchingLot.stockOutLineQty || matchingLot.requiredQty || 0 }); - console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`,stockOutLineUpdate); + console.log(`Update stock out line result for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); - if (stockOutLineUpdate && stockOutLineUpdate.code === "EXISTS") { - console.log(`✅ Stock out line already exists for line ${matchingLot.pickOrderLineId}`); - existsCount++; - } else if (stockOutLineUpdate && stockOutLineUpdate.code === "SUCCESS") { + if (stockOutLineUpdate && stockOutLineUpdate.code === "SUCCESS") { + console.log(`✅ Stock out line updated successfully for line ${matchingLot.pickOrderLineId}`); + successCount++; + } else { + console.error(`❌ Failed to update stock out line for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); + errorCount++; + } + } else { + // ✅ If no stock out line exists, create one + const createStockOutLineData = { + consoCode: matchingLot.pickOrderConsoCode, + pickOrderLineId: matchingLot.pickOrderLineId, + inventoryLotLineId: matchingLot.lotId, + qty: matchingLot.requiredQty || 0 + }; + + const createResult = await createStockOutLine(createStockOutLineData); + console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`, createResult); + + if (createResult && createResult.code === "SUCCESS") { console.log(`✅ Stock out line created successfully for line ${matchingLot.pickOrderLineId}`); successCount++; } else { - console.error(`❌ Failed to create stock out line for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); + console.error(`❌ Failed to create stock out line for line ${matchingLot.pickOrderLineId}:`, createResult); errorCount++; } } } - // ✅ Always refresh data after processing (success or failure) + // ✅ FIXED: Set refresh flag before refreshing data + setIsRefreshingData(true); console.log("🔄 Refreshing data after QR code processing..."); await fetchAllCombinedLotData(); - if (successCount > 0 || existsCount > 0) { - console.log(`✅ QR Code processing completed: ${successCount} created, ${existsCount} already existed`); + if (successCount > 0) { + console.log(`✅ QR Code processing completed: ${successCount} updated/created`); setQrScanSuccess(true); setQrScanInput(''); // Clear input after successful processing @@ -540,12 +551,18 @@ const PickExecution: React.FC = ({ filterArgs }) => { setQrScanSuccess(false); // ✅ Still refresh data even on error + setIsRefreshingData(true); await fetchAllCombinedLotData(); // ✅ Clear error state after a delay setTimeout(() => { setQrScanError(false); }, 3000); + } finally { + // ✅ Clear refresh flag after a short delay + setTimeout(() => { + setIsRefreshingData(false); + }, 1000); } }, [combinedLotData, fetchAllCombinedLotData]); @@ -598,15 +615,28 @@ const PickExecution: React.FC = ({ filterArgs }) => { // ✅ 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]; + // ✅ Don't process QR codes when refreshing data or if not manually scanning + if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) { + return; + } + + const latestQr = qrValues[qrValues.length - 1]; + + // ✅ Prevent processing the same QR code multiple times + if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) { + console.log(" QR code already processed, skipping..."); + return; + } + + if (latestQr && latestQr !== lastProcessedQr) { + console.log(` Processing new QR code: ${latestQr}`); + setLastProcessedQr(latestQr); + setProcessedQrCodes(prev => new Set(prev).add(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); @@ -619,20 +649,18 @@ const PickExecution: React.FC = ({ filterArgs }) => { .catch((error) => { console.error("Outside QR scan - Error fetching stock in line info:", error); }); - return; // Exit early for JSON QR codes + return; } } 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]); + }, [qrValues, isManualScanning, processedQrCodes, lastProcessedQr, isRefreshingData, handleQrCodeSubmit]); const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => { @@ -910,39 +938,45 @@ const paginatedData = useMemo(() => { return combinedLotData.slice(startIndex, endIndex); // ✅ No sorting needed }, [combinedLotData, paginationController]); + + + // ✅ Add these functions after line 395 + const handleStartScan = useCallback(() => { + console.log(" Starting manual QR scan..."); + setIsManualScanning(true); + setProcessedQrCodes(new Set()); + setLastProcessedQr(''); + startScan(); + }, [startScan]); + + const handleStopScan = useCallback(() => { + console.log("⏹️ Stopping manual QR scan..."); + setIsManualScanning(false); + stopScan(); + resetScan(); + }, [stopScan, resetScan]); + const getStatusMessage = useCallback((lot: any) => { + switch (lot.stockOutLineStatus?.toLowerCase()) { + case 'pending': + return t("Please finish QR code scan and pick order."); + case 'checked': + return t("Please submit the pick order."); + case 'partially_completed': + return t("Partial quantity submitted. Please submit more or complete the order."); + case 'completed': + return t("Pick order completed successfully!"); + case 'rejected': + return t("Lot has been rejected and marked as unavailable."); + case 'unavailable': + return t("This order is insufficient, please pick another lot."); + default: + return t("Please finish QR code scan and pick order."); + } + }, [t]); return ( - {/* Search Box */} - {/* - - - {fgPickOrdersLoading ? ( - - - - ) : ( - - {fgPickOrders.length === 0 ? ( - - - {t("No FG pick orders found")} - - - ) : ( - fgPickOrders.map((fgOrder) => ( - - )) - )} - - )} - -*/} - + {/* DO Header */} @@ -960,9 +994,13 @@ const paginatedData = useMemo(() => { {t("Pick Order Code")}:{fgPickOrders[0].pickOrderCode || '-'} + + {t("Ticket No.")}: {fgPickOrders[0].ticketNo || '-'} + {t("Departure Time")}: {fgPickOrders[0].DepartureTime || '-'} + ) @@ -975,8 +1013,39 @@ const paginatedData = useMemo(() => { {t("All Pick Order Lots")} - - + + + {!isManualScanning ? ( + + ) : ( + + )} + + {isManualScanning && ( + + + + {t("Scanning...")} + + + )} + @@ -991,10 +1060,11 @@ const paginatedData = useMemo(() => { {/* {t("Lot Location")} */} {t("Lot Required Pick Qty")} {/* {t("Original Available Qty")} */} - {t("Lot Actual Pick Qty")} + {t("Scan Result")} + {t("Submit Required Pick Qty")} {/* {t("Remaining Available Qty")} */} - {t("Finish Scan?")} - {t("Action")} + + {/* {t("Action")} */} @@ -1028,7 +1098,7 @@ const paginatedData = useMemo(() => { {lot.routerRoute || '-'} - {lot.itemName} + {lot.itemName+'('+lot.stockUnit+')'} { const inQty = lot.inQty || 0; const outQty = lot.outQty || 0; const result = inQty - outQty; - return result.toLocaleString()+'('+lot.stockUnit+')'; - })()} - - - {/* ✅ QR Scan Button if not scanned, otherwise show TextField + Issue button */} - {lot.stockOutLineStatus?.toLowerCase() === 'pending' ? ( - - ) : ( - - - { - const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`; - handlePickQtyChange(lotKey, parseFloat(e.target.value) || 0); - }} - disabled={ - (lot.lotAvailability === 'expired' || - lot.lotAvailability === 'status_unavailable' || - lot.lotAvailability === 'rejected') || - lot.stockOutLineStatus === 'completed' - } - inputProps={{ - min: 0, - max: calculateRemainingRequiredQty(lot), - step: 0.01 - }} - sx={{ - width: '60px', - height: '28px', - '& .MuiInputBase-input': { - fontSize: '0.7rem', - textAlign: 'center', - padding: '6px 8px' - } - }} - placeholder="0" - /> - - - - )} - - {/* - {(() => { - const inQty = lot.inQty || 0; - const outQty = lot.outQty || 0; - const result = inQty - outQty; - return result.toLocaleString(); + return result.toLocaleString()+'('+lot.uomShortDesc+')'; })()} - */} - - - - - - - + {lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? ( + + ) : null} + + + + + + + + + + + + )) )} - + {/* ✅ Status Messages Display - Move here, outside the table */} +{paginatedData.length > 0 && ( + + {paginatedData.map((lot, index) => ( + + + {t("Lot")} {lot.lotNo}: {getStatusMessage(lot)} + + + ))} + +)}