import type { DiscardCandidate } from '../api/warehouseServices' export interface DiscardDocument extends DiscardCandidate { sources: DiscardCandidate[] } /** 下拉按来源单据和操作仓汇总;原始库存身份保留给选箱与提交。 */ export function groupDiscardDocuments(candidates: DiscardCandidate[]): DiscardDocument[] { const groups = new Map() const identities = new Map() for (const candidate of candidates) { const available = Number(candidate.availablePackages) if (candidate.availablePackages == null || !Number.isFinite(available) || available <= 0) continue // 按真实来源身份去重;缺身份的旧接口行仍独立保留,不合并不同库存。 const identity = candidate.sourceType === 'RETURN_CARTON' ? candidate.returnCartonId : candidate.inventoryPositionId || candidate.orderId const sourceKey = identity ? JSON.stringify([candidate.overseasWarehouseId, candidate.sourceType, identity]) : null const existing = sourceKey ? identities.get(sourceKey) : undefined if (existing) { // 同一仓位的分片响应合并 SKU 和箱,重复关联不会丢掉箱内商品。 existing.skus = [...new Map([...existing.skus, ...candidate.skus].map(sku => [sku.skuCode, sku])).values()] const cartons = new Map((existing.cartons || []).map(box => [box.cartonId, box])) for (const box of candidate.cartons || []) { const old = cartons.get(box.cartonId) cartons.set(box.cartonId, old ? { ...old, skus: [...new Map([...old.skus, ...box.skus].map(sku => [sku.skuCode, sku])).values()] } : { ...box }) } existing.cartons = [...cartons.values()] existing.availablePackages = Math.max(Number(existing.availablePackages), available) continue } const source = { ...candidate, skus: [...candidate.skus], cartons: candidate.cartons?.map(box => ({ ...box, skus: [...box.skus] })) } if (sourceKey) identities.set(sourceKey, source) const documentNo = candidate.inboundOrderNo || candidate.inboundCode const key = JSON.stringify([candidate.overseasWarehouseId, candidate.sourceType, documentNo || candidate.orderId]) let document = groups.get(key) if (!document) { document = { ...candidate, orderId: key, sources: [], availablePackages: 0 } groups.set(key, document) } document.sources.push(source) } for (const document of groups.values()) { const boxes = new Set() let untrackedPackages = 0 for (const source of document.sources) { if (source.sourceType === 'RETURN_CARTON' && source.returnCartonId) { // 退货候选的 availablePackages 是箱内 SKU 件数,一条退货箱只计一箱。 boxes.add(`RETURN_CARTON:${source.returnCartonId}`) } else if (source.cartonSelectionSupported && source.cartons?.length) { for (const box of source.cartons) boxes.add(`${source.sourceType}:${box.cartonId}`) } else { // Long 序列化可能返回字符串,必须先转数值再汇总,避免拼成 005010097。 const packages = Number(source.availablePackages) untrackedPackages += Number.isFinite(packages) && packages > 0 ? packages : 0 } } document.availablePackages = boxes.size + untrackedPackages document.cartonSelectionSupported = document.sources.every(source => source.cartonSelectionSupported) document.cartonSelectionBlockReason = document.sources.find(source => !source.cartonSelectionSupported)?.cartonSelectionBlockReason } return [...groups.values()] }