From 7ffe0fb889b38aabe2eb9b2c3adb14c515cc4c95 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 3 Nov 2025 18:43:58 +0800 Subject: [PATCH] update --- src/app/api/jo/actions.ts | 129 ++++++++++++++++++ src/app/api/pickOrder/actions.ts | 4 +- .../GoodPickExecutionRecord.tsx | 97 +++++++++---- 3 files changed, 205 insertions(+), 25 deletions(-) diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts index 293d51b..353c799 100644 --- a/src/app/api/jo/actions.ts +++ b/src/app/api/jo/actions.ts @@ -149,6 +149,135 @@ export const recordSecondScanIssue = cache(async ( }, ); }); + + +export interface ProductProcessResponse { + id: number; + productProcessCode: string; + status: string; + startTime?: string; + endTime?: string; + date: string; + bomId?: number; + jobOrderId?: number; +} + +export interface ProductProcessLineResponse { + id: number; + seqNo: number; + name: string; + description?: string; + equipmentType?: string; + startTime?: string; + endTime?: string; + outputFromProcessQty?: number; + outputFromProcessUom?: string; + defectQty?: number; + scrapQty?: number; + byproductName?: string; + byproductQty?: number; + handlerId?: number; +} + +export interface ProductProcessWithLinesResponse { + id: number; + productProcessCode: string; + status: string; + startTime?: string; + endTime?: string; + date: string; + lines: ProductProcessLineResponse[]; +} +export const startProductProcessLine = async (lineId: number, userId: number) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/lines/${lineId}/start?userId=${userId}`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + } + ); +}; +// 查询所有 production processes +export const fetchProductProcesses = cache(async () => { + return serverFetchJson<{ content: ProductProcessResponse[] }>( + `${BASE_API_URL}/product-process`, + { + method: "GET", + next: { tags: ["productProcess"] }, + } + ); +}); + +// 根据 ID 查询 +export const fetchProductProcessById = cache(async (id: number) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/${id}`, + { + method: "GET", + next: { tags: ["productProcess"] }, + } + ); +}); + +// 根据 Job Order ID 查询 +export const fetchProductProcessesByJobOrderId = cache(async (jobOrderId: number) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/by-job-order/${jobOrderId}`, + { + method: "GET", + next: { tags: ["productProcess"] }, + } + ); +}); + +// 获取 process 的所有 lines +export const fetchProductProcessLines = cache(async (processId: number) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/${processId}/lines`, + { + method: "GET", + next: { tags: ["productProcessLines"] }, + } + ); +}); + +// 创建 production process +export const createProductProcess = async (data: { + bomId: number; + jobOrderId?: number; + date?: string; +}) => { + return serverFetchJson<{ id: number; productProcessCode: string; linesCreated: number }>( + `${BASE_API_URL}/product-process`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + } + ); +}; + +// 更新 line 产出数据 +export const updateLineOutput = async (lineId: number, data: { + outputQty?: number; + outputUom?: string; + defectQty?: number; + defectUom?: string; + scrapQty?: number; + scrapUom?: string; + byproductName?: string; + byproductQty?: number; + byproductUom?: string; +}) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/lines/${lineId}/output`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + } + ); +}; export const updateSecondQrScanStatus = cache(async (pickOrderId: number, itemId: number, userId: number, qty: number) => { return serverFetchJson( `${BASE_API_URL}/jo/update-match-status`, diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index ff07541..f8b0272 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -193,8 +193,8 @@ export interface PickExecutionIssueData { itemId: number; itemCode: string; itemDescription: string; - lotId: number; - lotNo: string; + lotId: number|null; + lotNo: string|null; storeLocation: string; requiredQty: number; actualPickQty: number; diff --git a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx index 4bf50ba..f0e4918 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx @@ -411,31 +411,82 @@ const GoodPickExecutionRecord: React.FC = ({ filterArgs }) => { // ✅ 转换为平铺格式 const flatLotData: any[] = []; - - if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) { - hierarchicalData.pickOrders.forEach((po: any) => { - po.pickOrderLines?.forEach((line: any) => { - if (line.lots && line.lots.length > 0) { - line.lots.forEach((lot: any) => { - flatLotData.push({ - pickOrderCode: po.pickOrderCode, - itemCode: line.item.code, - itemName: line.item.name, - lotNo: lot.lotNo, - location: lot.location, - deliveryOrderCode: po.deliveryOrderCode, - requiredQty: lot.requiredQty, - actualPickQty: lot.actualPickQty, - processingStatus: lot.processingStatus, - stockOutLineStatus: lot.stockOutLineStatus - }); + +if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) { + const toProc = (s?: string) => { + if (!s) return 'pending'; + const v = s.toLowerCase(); + if (v === 'completed' || v === 'complete') return 'completed'; + if (v === 'rejected') return 'rejected'; + if (v === 'partially_completed') return 'pending'; + return 'pending'; + }; + + hierarchicalData.pickOrders.forEach((po: any) => { + po.pickOrderLines?.forEach((line: any) => { + const lineStockouts = line.stockouts || []; // ← 用“行级” stockouts + const lots = line.lots || []; + + if (lots.length > 0) { + lots.forEach((lot: any) => { + // 先按该 lot 过滤出匹配的 stockouts(同 lotId) + const sos = lineStockouts.filter((so: any) => (so.lotId ?? null) === (lot.id ?? null)); + if (sos.length > 0) { + sos.forEach((so: any) => { + flatLotData.push({ + pickOrderCode: po.pickOrderCode, + itemCode: line.item?.code, + itemName: line.item?.name, + lotNo: so.lotNo || lot.lotNo, + location: so.location || lot.location, + deliveryOrderCode: po.deliveryOrderCode, + requiredQty: lot.requiredQty, + actualPickQty: (so.qty ?? lot.actualPickQty ?? 0), + processingStatus: toProc(so.status), // ← 用 stockouts.status + stockOutLineStatus: so.status, // 可选保留 + noLot: so.noLot === true }); - } + }); + } else { + // 没有匹配的 stockouts,也要显示 lot + flatLotData.push({ + pickOrderCode: po.pickOrderCode, + itemCode: line.item?.code, + itemName: line.item?.name, + lotNo: lot.lotNo, + location: lot.location, + deliveryOrderCode: po.deliveryOrderCode, + requiredQty: lot.requiredQty, + actualPickQty: lot.actualPickQty ?? 0, + processingStatus: lot.processingStatus || 'pending', + stockOutLineStatus: lot.stockOutLineStatus || 'pending', + noLot: false + }); + } + }); + } else if (lineStockouts.length > 0) { + // ✅ lots 为空但有 stockouts(如「雞絲碗仔翅」),也要显示 + lineStockouts.forEach((so: any) => { + flatLotData.push({ + pickOrderCode: po.pickOrderCode, + itemCode: line.item?.code, + itemName: line.item?.name, + lotNo: so.lotNo || '', + location: so.location || '', + deliveryOrderCode: po.deliveryOrderCode, + requiredQty: line.requiredQty ?? 0, // 行级没有 lot 时,用行的 requiredQty + actualPickQty: (so.qty ?? 0), + processingStatus: toProc(so.status), + stockOutLineStatus: so.status, + noLot: so.noLot === true }); }); } - - setDetailLotData(flatLotData); + }); + }); +} + +setDetailLotData(flatLotData); // ✅ 计算完成状态 const allCompleted = flatLotData.length > 0 && flatLotData.every(lot => @@ -593,8 +644,8 @@ if (showDetailView && selectedDoPickOrder) { {lot.requiredQty || 0} {lot.actualPickQty || 0} -