| @@ -768,180 +768,196 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||
| // 设置 FG info 到 fgPickOrders(用于显示 FG 信息卡片) | |||
| // 修改第 478-509 行的 fgOrder 构建逻辑: | |||
| const fgOrder: FGPickOrderResponse = { | |||
| doPickOrderId: hierarchicalData.fgInfo.doPickOrderId, | |||
| ticketNo: hierarchicalData.fgInfo.ticketNo, | |||
| storeId: hierarchicalData.fgInfo.storeId, | |||
| shopCode: hierarchicalData.fgInfo.shopCode, | |||
| shopName: hierarchicalData.fgInfo.shopName, | |||
| truckLanceCode: hierarchicalData.fgInfo.truckLanceCode, | |||
| DepartureTime: hierarchicalData.fgInfo.departureTime, | |||
| shopAddress: "", | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| // 兼容字段 | |||
| pickOrderId: mergedPickOrder.pickOrderIds?.[0] || 0, | |||
| pickOrderConsoCode: mergedPickOrder.consoCode || "", | |||
| pickOrderTargetDate: mergedPickOrder.targetDate || "", | |||
| pickOrderStatus: mergedPickOrder.status || "", | |||
| deliveryOrderId: mergedPickOrder.doOrderIds?.[0] || 0, | |||
| deliveryNo: mergedPickOrder.deliveryOrderCodes?.[0] || "", | |||
| deliveryDate: "", | |||
| shopId: 0, | |||
| shopPoNo: "", | |||
| numberOfCartons: mergedPickOrder.pickOrderLines?.length || 0, | |||
| qrCodeData: hierarchicalData.fgInfo.doPickOrderId, | |||
| // 新增:多个 pick orders 信息 - 保持数组格式,不要 join | |||
| numberOfPickOrders: mergedPickOrder.pickOrderIds?.length || 0, | |||
| pickOrderIds: mergedPickOrder.pickOrderIds || [], | |||
| pickOrderCodes: Array.isArray(mergedPickOrder.pickOrderCodes) | |||
| ? mergedPickOrder.pickOrderCodes | |||
| : [], // 改:保持数组 | |||
| deliveryOrderIds: mergedPickOrder.doOrderIds || [], | |||
| deliveryNos: Array.isArray(mergedPickOrder.deliveryOrderCodes) | |||
| ? mergedPickOrder.deliveryOrderCodes | |||
| : [], // 改:保持数组 | |||
| lineCountsPerPickOrder: Array.isArray(mergedPickOrder.lineCountsPerPickOrder) | |||
| ? mergedPickOrder.lineCountsPerPickOrder | |||
| : [] | |||
| }; | |||
| setFgPickOrders([fgOrder]); | |||
| console.log(" DEBUG fgOrder.lineCountsPerPickOrder:", fgOrder.lineCountsPerPickOrder); | |||
| console.log(" DEBUG fgOrder.pickOrderCodes:", fgOrder.pickOrderCodes); | |||
| console.log(" DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos); | |||
| // 移除:不需要 doPickOrderDetail 和 switcher 逻辑 | |||
| // if (hierarchicalData.pickOrders.length > 1) { ... } | |||
| // 直接使用合并后的 pickOrderLines | |||
| console.log("🎯 Displaying merged pick order lines"); | |||
| // 将层级数据转换为平铺格式(用于表格显示) | |||
| const flatLotData: any[] = []; | |||
| mergedPickOrder.pickOrderLines.forEach((line: any) => { | |||
| // ✅ FIXED: 处理 lots(如果有) | |||
| if (line.lots && line.lots.length > 0) { | |||
| // 修复:先对 lots 按 lotId 去重并合并 requiredQty | |||
| const lotMap = new Map<number, any>(); | |||
| line.lots.forEach((lot: any) => { | |||
| const lotId = lot.id; | |||
| if (lotMap.has(lotId)) { | |||
| // 如果已存在,合并 requiredQty | |||
| const existingLot = lotMap.get(lotId); | |||
| existingLot.requiredQty = (existingLot.requiredQty || 0) + (lot.requiredQty || 0); | |||
| // 保留其他字段(使用第一个遇到的 lot 的字段) | |||
| } else { | |||
| // 首次遇到,添加到 map | |||
| lotMap.set(lotId, { ...lot }); | |||
| } | |||
| }); | |||
| // 遍历去重后的 lots | |||
| lotMap.forEach((lot: any) => { | |||
| flatLotData.push({ | |||
| // 使用合并后的数据 | |||
| pickOrderConsoCode: mergedPickOrder.consoCode, | |||
| pickOrderTargetDate: mergedPickOrder.targetDate, | |||
| pickOrderStatus: mergedPickOrder.status, | |||
| pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, // 使用第一个 pickOrderId | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| pickOrderLineId: line.id, | |||
| pickOrderLineRequiredQty: line.requiredQty, | |||
| pickOrderLineStatus: line.status, | |||
| itemId: line.item.id, | |||
| itemCode: line.item.code, | |||
| itemName: line.item.name, | |||
| uomDesc: line.item.uomDesc, | |||
| uomShortDesc: line.item.uomShortDesc, | |||
| lotId: lot.id, | |||
| lotNo: lot.lotNo, | |||
| expiryDate: lot.expiryDate, | |||
| location: lot.location, | |||
| stockUnit: lot.stockUnit, | |||
| availableQty: lot.availableQty, | |||
| requiredQty: lot.requiredQty, // 使用合并后的 requiredQty | |||
| actualPickQty: lot.actualPickQty, | |||
| inQty: lot.inQty, | |||
| outQty: lot.outQty, | |||
| holdQty: lot.holdQty, | |||
| lotStatus: lot.lotStatus, | |||
| lotAvailability: lot.lotAvailability, | |||
| processingStatus: lot.processingStatus, | |||
| suggestedPickLotId: lot.suggestedPickLotId, | |||
| stockOutLineId: lot.stockOutLineId, | |||
| stockOutLineStatus: lot.stockOutLineStatus, | |||
| stockOutLineQty: lot.stockOutLineQty, | |||
| stockInLineId: lot.stockInLineId, | |||
| routerId: lot.router?.id, | |||
| routerIndex: lot.router?.index, | |||
| routerRoute: lot.router?.route, | |||
| routerArea: lot.router?.area, | |||
| noLot: false, | |||
| }); | |||
| const fgOrder: FGPickOrderResponse = { | |||
| doPickOrderId: hierarchicalData.fgInfo.doPickOrderId, | |||
| ticketNo: hierarchicalData.fgInfo.ticketNo, | |||
| storeId: hierarchicalData.fgInfo.storeId, | |||
| shopCode: hierarchicalData.fgInfo.shopCode, | |||
| shopName: hierarchicalData.fgInfo.shopName, | |||
| truckLanceCode: hierarchicalData.fgInfo.truckLanceCode, | |||
| DepartureTime: hierarchicalData.fgInfo.departureTime, | |||
| shopAddress: "", | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| // 兼容字段(注意 consoCodes 是数组) | |||
| pickOrderId: mergedPickOrder.pickOrderIds?.[0] || 0, | |||
| pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes) | |||
| ? mergedPickOrder.consoCodes[0] || "" | |||
| : "", | |||
| pickOrderTargetDate: mergedPickOrder.targetDate || "", | |||
| pickOrderStatus: mergedPickOrder.status || "", | |||
| deliveryOrderId: mergedPickOrder.doOrderIds?.[0] || 0, | |||
| deliveryNo: mergedPickOrder.deliveryOrderCodes?.[0] || "", | |||
| deliveryDate: "", | |||
| shopId: 0, | |||
| shopPoNo: "", | |||
| numberOfCartons: mergedPickOrder.pickOrderLines?.length || 0, | |||
| qrCodeData: hierarchicalData.fgInfo.doPickOrderId, | |||
| // 多个 pick orders 信息:全部保留为数组 | |||
| numberOfPickOrders: mergedPickOrder.pickOrderIds?.length || 0, | |||
| pickOrderIds: mergedPickOrder.pickOrderIds || [], | |||
| pickOrderCodes: Array.isArray(mergedPickOrder.pickOrderCodes) | |||
| ? mergedPickOrder.pickOrderCodes | |||
| : [], | |||
| deliveryOrderIds: mergedPickOrder.doOrderIds || [], | |||
| deliveryNos: Array.isArray(mergedPickOrder.deliveryOrderCodes) | |||
| ? mergedPickOrder.deliveryOrderCodes | |||
| : [], | |||
| lineCountsPerPickOrder: Array.isArray(mergedPickOrder.lineCountsPerPickOrder) | |||
| ? mergedPickOrder.lineCountsPerPickOrder | |||
| : [], | |||
| }; | |||
| setFgPickOrders([fgOrder]); | |||
| console.log(" DEBUG fgOrder.lineCountsPerPickOrder:", fgOrder.lineCountsPerPickOrder); | |||
| console.log(" DEBUG fgOrder.pickOrderCodes:", fgOrder.pickOrderCodes); | |||
| console.log(" DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos); | |||
| // 直接使用合并后的 pickOrderLines | |||
| console.log("🎯 Displaying merged pick order lines"); | |||
| // 将层级数据转换为平铺格式(用于表格显示) | |||
| const flatLotData: any[] = []; | |||
| mergedPickOrder.pickOrderLines.forEach((line: any) => { | |||
| // 用来记录这一行已经通过 lots 出现过的 lotId | |||
| const lotIdSet = new Set<number>(); | |||
| // ✅ lots:按 lotId 去重并合并 requiredQty | |||
| if (line.lots && line.lots.length > 0) { | |||
| const lotMap = new Map<number, any>(); | |||
| line.lots.forEach((lot: any) => { | |||
| const lotId = lot.id; | |||
| if (lotMap.has(lotId)) { | |||
| const existingLot = lotMap.get(lotId); | |||
| existingLot.requiredQty = | |||
| (existingLot.requiredQty || 0) + (lot.requiredQty || 0); | |||
| } else { | |||
| lotMap.set(lotId, { ...lot }); | |||
| } | |||
| }); | |||
| lotMap.forEach((lot: any) => { | |||
| if (lot.id != null) { | |||
| lotIdSet.add(lot.id); | |||
| } | |||
| flatLotData.push({ | |||
| pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes) | |||
| ? mergedPickOrder.consoCodes[0] || "" | |||
| : "", | |||
| pickOrderTargetDate: mergedPickOrder.targetDate, | |||
| pickOrderStatus: mergedPickOrder.status, | |||
| pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| pickOrderLineId: line.id, | |||
| pickOrderLineRequiredQty: line.requiredQty, | |||
| pickOrderLineStatus: line.status, | |||
| itemId: line.item.id, | |||
| itemCode: line.item.code, | |||
| itemName: line.item.name, | |||
| uomDesc: line.item.uomDesc, | |||
| uomShortDesc: line.item.uomShortDesc, | |||
| lotId: lot.id, | |||
| lotNo: lot.lotNo, | |||
| expiryDate: lot.expiryDate, | |||
| location: lot.location, | |||
| stockUnit: lot.stockUnit, | |||
| availableQty: lot.availableQty, | |||
| requiredQty: lot.requiredQty, | |||
| actualPickQty: lot.actualPickQty, | |||
| inQty: lot.inQty, | |||
| outQty: lot.outQty, | |||
| holdQty: lot.holdQty, | |||
| lotStatus: lot.lotStatus, | |||
| lotAvailability: lot.lotAvailability, | |||
| processingStatus: lot.processingStatus, | |||
| suggestedPickLotId: lot.suggestedPickLotId, | |||
| stockOutLineId: lot.stockOutLineId, | |||
| stockOutLineStatus: lot.stockOutLineStatus, | |||
| stockOutLineQty: lot.stockOutLineQty, | |||
| stockInLineId: lot.stockInLineId, | |||
| routerId: lot.router?.id, | |||
| routerIndex: lot.router?.index, | |||
| routerRoute: lot.router?.route, | |||
| routerArea: lot.router?.area, | |||
| noLot: false, | |||
| }); | |||
| } | |||
| // ✅ FIXED: 同时处理 stockouts(无论是否有 lots) | |||
| if (line.stockouts && line.stockouts.length > 0) { | |||
| // ✅ FIXED: 处理所有 stockouts,而不仅仅是第一个 | |||
| line.stockouts.forEach((stockout: any) => { | |||
| flatLotData.push({ | |||
| pickOrderConsoCode: mergedPickOrder.consoCodes?.[0] || "", | |||
| pickOrderTargetDate: mergedPickOrder.targetDate, | |||
| pickOrderStatus: mergedPickOrder.status, | |||
| pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| pickOrderLineId: line.id, | |||
| pickOrderLineRequiredQty: line.requiredQty, | |||
| pickOrderLineStatus: line.status, | |||
| itemId: line.item.id, | |||
| itemCode: line.item.code, | |||
| itemName: line.item.name, | |||
| uomDesc: line.item.uomDesc, | |||
| uomShortDesc: line.item.uomShortDesc, | |||
| // Null stock 字段 - 从 stockouts 数组中获取 | |||
| lotId: stockout.lotId || null, | |||
| lotNo: stockout.lotNo || null, | |||
| expiryDate: null, | |||
| location: stockout.location || null, | |||
| stockUnit: line.item.uomDesc, | |||
| availableQty: stockout.availableQty || 0, | |||
| requiredQty: line.requiredQty, | |||
| actualPickQty: stockout.qty || 0, | |||
| inQty: 0, | |||
| outQty: 0, | |||
| holdQty: 0, | |||
| lotStatus: 'unavailable', | |||
| lotAvailability: 'insufficient_stock', | |||
| processingStatus: stockout.status || 'pending', | |||
| suggestedPickLotId: null, | |||
| stockOutLineId: stockout.id || null, // 使用 stockouts 数组中的 id | |||
| stockOutLineStatus: stockout.status || null, | |||
| stockOutLineQty: stockout.qty || 0, | |||
| routerId: null, | |||
| routerIndex: 999999, | |||
| routerRoute: null, | |||
| routerArea: null, | |||
| noLot: true, | |||
| }); | |||
| }); | |||
| } | |||
| // ✅ stockouts:只保留“真正无批次 / 未在 lots 出现过”的行 | |||
| if (line.stockouts && line.stockouts.length > 0) { | |||
| line.stockouts.forEach((stockout: any) => { | |||
| const hasLot = stockout.lotId != null; | |||
| const lotAlreadyInLots = | |||
| hasLot && lotIdSet.has(stockout.lotId as number); | |||
| // 有批次 & 已经通过 lots 渲染过 → 跳过,避免一条变两行 | |||
| if (!stockout.noLot && lotAlreadyInLots) { | |||
| return; | |||
| } | |||
| // 只渲染: | |||
| // - noLot === true 的 Null stock 行 | |||
| // - 或者 lotId 在 lots 中不存在的特殊情况 | |||
| flatLotData.push({ | |||
| pickOrderConsoCode: Array.isArray(mergedPickOrder.consoCodes) | |||
| ? mergedPickOrder.consoCodes[0] || "" | |||
| : "", | |||
| pickOrderTargetDate: mergedPickOrder.targetDate, | |||
| pickOrderStatus: mergedPickOrder.status, | |||
| pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, | |||
| pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "", | |||
| pickOrderLineId: line.id, | |||
| pickOrderLineRequiredQty: line.requiredQty, | |||
| pickOrderLineStatus: line.status, | |||
| itemId: line.item.id, | |||
| itemCode: line.item.code, | |||
| itemName: line.item.name, | |||
| uomDesc: line.item.uomDesc, | |||
| uomShortDesc: line.item.uomShortDesc, | |||
| lotId: stockout.lotId || null, | |||
| lotNo: stockout.lotNo || null, | |||
| expiryDate: null, | |||
| location: stockout.location || null, | |||
| stockUnit: line.item.uomDesc, | |||
| availableQty: stockout.availableQty || 0, | |||
| requiredQty: line.requiredQty, | |||
| actualPickQty: stockout.qty || 0, | |||
| inQty: 0, | |||
| outQty: 0, | |||
| holdQty: 0, | |||
| lotStatus: stockout.noLot ? "unavailable" : "available", | |||
| lotAvailability: stockout.noLot ? "insufficient_stock" : "available", | |||
| processingStatus: stockout.status || "pending", | |||
| suggestedPickLotId: null, | |||
| stockOutLineId: stockout.id || null, | |||
| stockOutLineStatus: stockout.status || null, | |||
| stockOutLineQty: stockout.qty || 0, | |||
| routerId: null, | |||
| routerIndex: stockout.noLot ? 999999 : null, | |||
| routerRoute: null, | |||
| routerArea: null, | |||
| noLot: !!stockout.noLot, | |||
| }); | |||
| } | |||
| }); | |||
| console.log(" Transformed flat lot data:", flatLotData); | |||
| console.log(" Total items (including null stock):", flatLotData.length); | |||
| setCombinedLotData(flatLotData); | |||
| setOriginalCombinedData(flatLotData); | |||
| checkAllLotsCompleted(flatLotData); | |||
| }); | |||
| } | |||
| }); | |||
| console.log(" Transformed flat lot data:", flatLotData); | |||
| console.log(" Total items (including null stock):", flatLotData.length); | |||
| setCombinedLotData(flatLotData); | |||
| setOriginalCombinedData(flatLotData); | |||
| checkAllLotsCompleted(flatLotData); | |||
| } catch (error) { | |||
| console.error(" Error fetching combined lot data:", error); | |||
| setCombinedLotData([]); | |||