diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index ece6600..bc8f79e 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -542,7 +542,37 @@ export const batchQrSubmit = async (data: QrPickBatchSubmitRequest) => { ); return response; }; +export interface BatchScanRequest { + userId: number; + lines: BatchScanLineRequest[]; +} +export interface BatchScanLineRequest { + pickOrderLineId: number; + inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null + pickOrderConsoCode: string; + lotNo: string | null; // 用于日志和验证 + itemId: number; + itemCode: string; + stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用 +} + +export const batchScan = async (data: BatchScanRequest) => { + console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2)); + + const response = await serverFetchJson>( + `${BASE_API_URL}/stockOutLine/batchScan`, + { + method: "POST", + body: JSON.stringify(data), + headers: { + "Content-Type": "application/json", + }, + }, + ); + console.log("📥 batchScan - Response:", response); + return response; +}; export const fetchDoPickOrderDetail = async ( doPickOrderId: number, selectedPickOrderId?: number diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 56a41e4..1f52d6c 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -48,8 +48,10 @@ import { batchQrSubmit, batchSubmitList, // 添加:导入 batchSubmitList batchSubmitListRequest, // 添加:导入类型 - batchSubmitListLineRequest - + batchSubmitListLineRequest, + batchScan, + BatchScanRequest, + BatchScanLineRequest, } from "@/app/api/pickOrder/actions"; import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; @@ -1834,6 +1836,62 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe } try { + // Special case: If submitQty is 0 and all values are 0, mark as completed with qty: 0 + if (submitQty === 0) { + console.log(`=== SUBMITTING ALL ZEROS CASE ===`); + console.log(`Lot: ${lot.lotNo}`); + console.log(`Stock Out Line ID: ${lot.stockOutLineId}`); + console.log(`Setting status to 'completed' with qty: 0`); + + const updateResult = await updateStockOutLineStatus({ + id: lot.stockOutLineId, + status: 'completed', + qty: 0 + }); + + console.log('Update result:', updateResult); + const r: any = updateResult as any; + const updateOk = + r?.code === 'SUCCESS' || + r?.type === 'completed' || + typeof r?.id === 'number' || + typeof r?.entity?.id === 'number' || + (r?.message && r.message.includes('successfully')); + if (!updateResult || !updateOk) { + console.error('Failed to update stock out line status:', updateResult); + throw new Error('Failed to update stock out line status'); + } + + // Check if pick order is completed + if (lot.pickOrderConsoCode) { + console.log(` Lot ${lot.lotNo} completed (all zeros), checking if pick order ${lot.pickOrderConsoCode} is complete...`); + + try { + const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode); + console.log(` Pick order completion check result:`, completionResponse); + + if (completionResponse.code === "SUCCESS") { + console.log(` Pick order ${lot.pickOrderConsoCode} completed successfully!`); + } else if (completionResponse.message === "not completed") { + console.log(`⏳ Pick order not completed yet, more lines remaining`); + } else { + console.error(` Error checking completion: ${completionResponse.message}`); + } + } catch (error) { + console.error("Error checking pick order completion:", error); + } + } + + await fetchAllCombinedLotData(); + console.log("All zeros submission completed successfully!"); + + setTimeout(() => { + checkAndAutoAssignNext(); + }, 1000); + + return; + } + // FIXED: Calculate cumulative quantity correctly const currentActualPickQty = lot.actualPickQty || 0; const cumulativeQty = currentActualPickQty + submitQty; @@ -1882,7 +1940,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe console.log(` Pick order completion check result:`, completionResponse); if (completionResponse.code === "SUCCESS") { - console.log(`�� Pick order ${lot.pickOrderConsoCode} completed successfully!`); + console.log(` Pick order ${lot.pickOrderConsoCode} completed successfully!`); } else if (completionResponse.message === "not completed") { console.log(`⏳ Pick order not completed yet, more lines remaining`); } else { @@ -1905,8 +1963,14 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe } }, [fetchAllCombinedLotData, checkAndAutoAssignNext]); - - // Add these functions after line 395 +const handleSkip = useCallback(async (lot: any) => { + try { + console.log("Skip clicked, submit 0 qty for lot:", lot.lotNo); + await handleSubmitPickQtyWithQty(lot, 0); + } catch (err) { + console.error("Error in Skip:", err); + } +}, [handleSubmitPickQtyWithQty]); const handleStartScan = useCallback(() => { console.log(" Starting manual QR scan..."); setIsManualScanning(true); @@ -2001,7 +2065,86 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe console.error(" Error in handlelotnull:", error); } }, [fetchAllCombinedLotData, session, currentUserId, fgPickOrders]); -// ... existing code ... + const handleBatchScan = useCallback(async () => { + const startTime = performance.now(); + console.log(`⏱️ [BATCH SCAN START]`); + console.log(`⏰ Start time: ${new Date().toISOString()}`); + + // 获取所有活跃批次(未扫描的) + const activeLots = combinedLotData.filter(lot => { + return ( + lot.lotAvailability !== 'rejected' && + lot.stockOutLineStatus !== 'rejected' && + lot.stockOutLineStatus !== 'completed' && + lot.stockOutLineStatus !== 'checked' && // ✅ 只处理未扫描的 + lot.processingStatus !== 'completed' && + lot.noLot !== true && + lot.lotNo // ✅ 必须有 lotNo + ); + }); + + if (activeLots.length === 0) { + console.log("No active lots to scan"); + return; + } + + console.log(`📦 Batch scanning ${activeLots.length} active lots using batch API...`); + + try { + // ✅ 转换为批量扫描 API 所需的格式 + const lines: BatchScanLineRequest[] = activeLots.map((lot) => ({ + pickOrderLineId: Number(lot.pickOrderLineId), + inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null, + pickOrderConsoCode: String(lot.pickOrderConsoCode || ''), + lotNo: lot.lotNo || null, + itemId: Number(lot.itemId), + itemCode: String(lot.itemCode || ''), + stockOutLineId: lot.stockOutLineId ? Number(lot.stockOutLineId) : null, // ✅ 新增 + })); + + const request: BatchScanRequest = { + userId: currentUserId || 0, + lines: lines + }; + + console.log(`📤 Sending batch scan request with ${lines.length} lines`); + console.log(`📋 Request data:`, JSON.stringify(request, null, 2)); + + const scanStartTime = performance.now(); + + // ✅ 使用新的批量扫描 API(一次性处理所有请求) + const result = await batchScan(request); + + const scanTime = performance.now() - scanStartTime; + console.log(`⏱️ Batch scan API call completed in ${scanTime.toFixed(2)}ms (${(scanTime / 1000).toFixed(3)}s)`); + console.log(`📥 Batch scan result:`, result); + + // ✅ 刷新数据以获取最新的状态 + const refreshStartTime = performance.now(); + await fetchAllCombinedLotData(); + const refreshTime = performance.now() - refreshStartTime; + console.log(`⏱️ Data refresh time: ${refreshTime.toFixed(2)}ms (${(refreshTime / 1000).toFixed(3)}s)`); + + const totalTime = performance.now() - startTime; + console.log(`⏱️ [BATCH SCAN END]`); + console.log(`⏱️ Total time: ${totalTime.toFixed(2)}ms (${(totalTime / 1000).toFixed(3)}s)`); + console.log(`⏰ End time: ${new Date().toISOString()}`); + + if (result && result.code === "SUCCESS") { + setQrScanSuccess(true); + setQrScanError(false); + } else { + console.error("❌ Batch scan failed:", result); + setQrScanError(true); + setQrScanSuccess(false); + } + + } catch (error) { + console.error("❌ Error in batch scan:", error); + setQrScanError(true); + setQrScanSuccess(false); + } + }, [combinedLotData, fetchAllCombinedLotData, currentUserId]); const handleSubmitAllScanned = useCallback(async () => { const startTime = performance.now(); console.log(`⏱️ [BATCH SUBMIT START]`); @@ -2174,6 +2317,7 @@ const handleSubmitAllScanned = useCallback(async () => { ( lot.lotAvailability !== 'rejected' && lot.stockOutLineStatus !== 'rejected' && @@ -2542,6 +2686,15 @@ paginatedData.map((lot, index) => { > {t("Issue")} + ); })()} diff --git a/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx index fea833a..485b562 100644 --- a/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx +++ b/src/components/QrCodeScannerProvider/TestQrCodeProvider.tsx @@ -29,6 +29,7 @@ interface TestQrCodeProviderProps { lotData: any[]; // 当前页面的批次数据 onScanLot?: (lotNo: string) => Promise; // 扫描单个批次的回调 filterActive?: (lot: any) => boolean; // 过滤活跃批次的函数 + onBatchScan?: () => Promise; } export const TestQrCodeContext = createContext( @@ -40,6 +41,7 @@ const TestQrCodeProvider: React.FC = ({ lotData, onScanLot, filterActive, + onBatchScan, }) => { const [enableTestMode, setEnableTestMode] = useState(true); const { values: qrValues, resetScan } = useQrCodeScannerContext(); @@ -84,7 +86,6 @@ const TestQrCodeProvider: React.FC = ({ } }, [getActiveLots, onScanLot]); - // 测试扫描所有批次 const testScanAllLots = useCallback(async () => { const activeLots = getActiveLots(); @@ -93,8 +94,27 @@ const TestQrCodeProvider: React.FC = ({ return; } + // ✅ 优化:如果有批量扫描回调,使用它(高效批量处理) + if (onBatchScan) { + console.log( + `%c TEST: Batch scanning ALL ${activeLots.length} lots...`, + "color: orange; font-weight: bold" + ); + try { + await onBatchScan(); + console.log( + `%c TEST: Completed batch scan for all ${activeLots.length} lots`, + "color: green; font-weight: bold" + ); + } catch (error) { + console.error("❌ TEST: Error in batch scan:", error); + } + return; + } + + // 回退到原来的逐个扫描方式(如果没有提供批量回调) console.log( - `%c TEST: Scanning ALL ${activeLots.length} lots...`, + `%c TEST: Scanning ALL ${activeLots.length} lots (one by one)...`, "color: orange; font-weight: bold" ); @@ -116,7 +136,7 @@ const TestQrCodeProvider: React.FC = ({ `%c TEST: Completed scanning all ${activeLots.length} lots`, "color: green; font-weight: bold" ); - }, [getActiveLots, onScanLot]); + }, [getActiveLots, onScanLot, onBatchScan]); // 监听 QR 扫描值,处理测试格式 useEffect(() => { diff --git a/src/i18n/zh/inventory.json b/src/i18n/zh/inventory.json index d2e9663..46fdfb7 100644 --- a/src/i18n/zh/inventory.json +++ b/src/i18n/zh/inventory.json @@ -37,6 +37,7 @@ "miss": "缺貨", "bad": "不良", "expiry": "過期", + "open":"開倉", "Bom Req. Qty": "需求數(BOM單位)", "selected stock take qty": "已選擇盤點數量", "book qty": "帳面庫存", diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json index e273501..127d5cd 100644 --- a/src/i18n/zh/pickOrder.json +++ b/src/i18n/zh/pickOrder.json @@ -12,6 +12,7 @@ "Escalated": "上報狀態", "NotEscalated": "無上報", "Assigned To": "已分配", + "Skip": "跳過", "Do you want to start?": "確定開始嗎?", "Start": "開始", "Pick Order Code(s)": "提料單編號",