diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index d63d7b4..ff07541 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -245,6 +245,16 @@ export interface PickOrderCompletionResponse { export interface UpdateSuggestedLotLineIdRequest { newLotLineId: number; } +export interface stockReponse{ + id: number; + status: string; + qty: number; + lotId: number; + lotNo: string; + location: string; + availableQty: number; + noLot: boolean; +} export interface FGPickOrderResponse { // ✅ 新增:支持多个 pick orders doPickOrderId: number; diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 556e6a8..abe81db 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -33,7 +33,7 @@ import { recordPickExecutionIssue, fetchFGPickOrders, // ✅ Add this import FGPickOrderResponse, - + stockReponse, checkPickOrderCompletion, fetchAllPickOrderLotsHierarchical, @@ -369,6 +369,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false); 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); @@ -546,12 +547,17 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos); routerIndex: lot.router?.index, routerRoute: lot.router?.route, routerArea: lot.router?.area, + noLot: false, }); }); } else { - // ✅ 没有 lots 的情况(null stock) + // ✅ 没有 lots 的情况(null stock)- 从 stockouts 数组中获取 id + const firstStockout = line.stockouts && line.stockouts.length > 0 + ? line.stockouts[0] + : null; + flatLotData.push({ - pickOrderConsoCode: mergedPickOrder.consoCode, + pickOrderConsoCode: mergedPickOrder.consoCodes?.[0] || "", // ✅ 修复:consoCodes 是数组 pickOrderTargetDate: mergedPickOrder.targetDate, pickOrderStatus: mergedPickOrder.status, @@ -565,30 +571,31 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos); uomDesc: line.item.uomDesc, uomShortDesc: line.item.uomShortDesc, - // ✅ Null stock 字段 - lotId: null, - lotNo: null, + // ✅ Null stock 字段 - 从 stockouts 数组中获取 + lotId: firstStockout?.lotId || null, + lotNo: firstStockout?.lotNo || null, expiryDate: null, - location: null, + location: firstStockout?.location || null, stockUnit: line.item.uomDesc, - availableQty: 0, + availableQty: firstStockout?.availableQty || 0, requiredQty: line.requiredQty, - actualPickQty: 0, + actualPickQty: firstStockout?.qty || 0, inQty: 0, outQty: 0, holdQty: 0, lotStatus: 'unavailable', lotAvailability: 'insufficient_stock', - processingStatus: 'pending', + processingStatus: firstStockout?.status || 'pending', suggestedPickLotId: null, - stockOutLineId: null, - stockOutLineStatus: null, - stockOutLineQty: 0, + stockOutLineId: firstStockout?.id || null, // ✅ 使用 stockouts 数组中的 id + stockOutLineStatus: firstStockout?.status || null, + stockOutLineQty: firstStockout?.qty || 0, routerId: null, routerIndex: 999999, routerRoute: null, routerArea: null, + noLot: true, }); } }); @@ -684,14 +691,22 @@ console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos); const handleQrCodeSubmit = useCallback(async (lotNo: string) => { console.log(`✅ Processing QR Code for lot: ${lotNo}`); + // ✅ 检查 lotNo 是否为 null 或 undefined(包括字符串 "null") + if (!lotNo || lotNo === 'null' || lotNo.trim() === '') { + console.error("❌ Invalid lotNo: null, undefined, or empty"); + return; + } + // ✅ Use current data without refreshing to avoid infinite loop const currentLotData = combinedLotData; console.log(` Available lots:`, currentLotData.map(lot => lot.lotNo)); - const matchingLots = currentLotData.filter(lot => - lot.lotNo === lotNo || - lot.lotNo?.toLowerCase() === lotNo.toLowerCase() - ); + // ✅ 修复:在比较前确保 lotNo 不为 null + const lotNoLower = lotNo.toLowerCase(); + const matchingLots = currentLotData.filter(lot => { + if (!lot.lotNo) return false; // ✅ 跳过 null lotNo + return lot.lotNo === lotNo || lot.lotNo.toLowerCase() === lotNoLower; + }); if (matchingLots.length === 0) { console.error(`❌ Lot not found: ${lotNo}`); @@ -1451,10 +1466,34 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe stopScan(); resetScan(); }, [stopScan, resetScan]); + const handlelotnull = useCallback(async (lot: any) => { + // ✅ 优先使用 stockouts 中的 id,如果没有则使用 stockOutLineId + const stockOutLineId = lot.stockOutLineId; + + if (!stockOutLineId) { + console.error("❌ No stockOutLineId found for lot:", lot); + return; + } + + await updateStockOutLineStatus({ + id: stockOutLineId, // ✅ 现在这个值应该来自 stockouts 数组的 id + status: 'completed', + qty: 0 + }); + await fetchAllCombinedLotData(); + }, [fetchAllCombinedLotData]); const handleSubmitAllScanned = useCallback(async () => { - const scannedLots = combinedLotData.filter(lot => - lot.stockOutLineStatus === 'checked' // Only submit items that are scanned but not yet submitted - ); + const scannedLots = combinedLotData.filter(lot => { + // ✅ 如果是 noLot 情况,检查状态是否为 pending 或 partially_complete + if (lot.noLot === true) { + return lot.stockOutLineStatus === 'checked' || + lot.stockOutLineStatus === 'pending' || + lot.stockOutLineStatus === 'partially_completed' || + lot.stockOutLineStatus === 'PARTIALLY_COMPLETE'; + } + // ✅ 正常情况:只包含 checked 状态 + return lot.stockOutLineStatus === 'checked'; + }); if (scannedLots.length === 0) { console.log("No scanned items to submit"); @@ -1467,6 +1506,20 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe try { // ✅ Submit all items in parallel using Promise.all const submitPromises = scannedLots.map(async (lot) => { + // ✅ 检查是否是 noLot 情况 + if (lot.noLot === true) { + // ✅ 使用 handlelotnull 处理无 lot 的情况 + console.log(`Submitting no-lot item: ${lot.itemName || lot.itemCode}`); + await updateStockOutLineStatus({ + id: lot.stockOutLineId, + status: 'completed', + qty: 0 + }); + console.log(`✅ No-lot item completed: ${lot.itemName || lot.itemCode}`); + return { success: true, lotNo: lot.lotNo || 'No Lot', isNoLot: true }; + } + + // ✅ 正常情况:有 lot 的处理逻辑 const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty; const currentActualPickQty = lot.actualPickQty || 0; const cumulativeQty = currentActualPickQty + submitQty; @@ -1486,7 +1539,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe }); // Update inventory - if (submitQty > 0) { + if (submitQty > 0 && lot.lotId) { await updateInventoryLotLineQuantities({ inventoryLotLineId: lot.lotId, qty: submitQty, @@ -1526,12 +1579,37 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe } finally { setIsSubmittingAll(false); } - }, [combinedLotData, fetchAllCombinedLotData, checkAndAutoAssignNext]); + }, [combinedLotData, fetchAllCombinedLotData, checkAndAutoAssignNext, handlelotnull]); // ✅ Calculate scanned items count - const scannedItemsCount = useMemo(() => { - return combinedLotData.filter(lot => lot.stockOutLineStatus === 'checked').length; - }, [combinedLotData]); + // ✅ Calculate scanned items count (should match handleSubmitAllScanned filter logic) + const scannedItemsCount = useMemo(() => { + const filtered = combinedLotData.filter(lot => { + // ✅ 如果是 noLot 情况,只要状态不是 completed 或 rejected,就包含 + if (lot.noLot === true) { + const status = lot.stockOutLineStatus?.toLowerCase(); + const include = status !== 'completed' && status !== 'rejected'; + if (include) { + console.log(`📊 Including noLot item: ${lot.itemName || lot.itemCode}, status: ${lot.stockOutLineStatus}`); + } + return include; + } + // ✅ 正常情况:只包含 checked 状态 + return lot.stockOutLineStatus === 'checked'; + }); + + // ✅ 添加调试日志 + const noLotCount = filtered.filter(l => l.noLot === true).length; + const normalCount = filtered.filter(l => l.noLot !== true).length; + console.log(`📊 scannedItemsCount calculation: total=${filtered.length}, noLot=${noLotCount}, normal=${normalCount}`); + console.log(`📊 All items breakdown:`, { + total: combinedLotData.length, + noLot: combinedLotData.filter(l => l.noLot === true).length, + normal: combinedLotData.filter(l => l.noLot !== true).length + }); + + return filtered.length; + }, [combinedLotData]); // ✅ ADD THIS: Auto-stop scan when no data available useEffect(() => { @@ -1782,7 +1860,8 @@ paginatedData.map((lot, index) => { // color: isIssueLot ? 'warning.main' : lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit', }} > - {lot.lotNo || t('⚠️ No Stock Available')} + {lot.lotNo || + t('⚠️ No Stock Available')} @@ -1809,8 +1888,20 @@ paginatedData.map((lot, index) => { }} /> - ) : isIssueLot ? ( - null + ) : isIssueLot&&lot.stockOutLineStatus?.toLowerCase() == 'partially_completed' ? ( + + + ) : null} @@ -1821,8 +1912,7 @@ paginatedData.map((lot, index) => { diff --git a/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx index bab1787..fea833a 100644 --- a/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx +++ b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx @@ -51,7 +51,8 @@ const TestQrCodeProvider: React.FC = ({ lot.stockOutLineStatus !== 'rejected' && lot.stockOutLineStatus !== 'completed' && lot.processingStatus !== 'completed' && - lot.matchStatus !== 'completed' + lot.matchStatus !== 'completed' && + lot.noLot !== true ); }, []);