diff --git a/src/modules/overseas-warehouse/utils/discardDocuments.test.mjs b/src/modules/overseas-warehouse/utils/discardDocuments.test.mjs new file mode 100644 index 0000000..042799f --- /dev/null +++ b/src/modules/overseas-warehouse/utils/discardDocuments.test.mjs @@ -0,0 +1,38 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' +import ts from 'typescript' + +const compiled = ts.transpileModule(readFileSync(new URL('./discardDocuments.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 } }).outputText +const { groupDiscardDocuments } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`) + +const returned = (id, warehouse = 'WH', document = 'TT-BYWL-261019152551') => ({ + orderId: `return:${id}`, returnCartonId: id, sourceType: 'RETURN_CARTON', overseasWarehouseId: warehouse, + inboundCode: document, inboundOrderNo: document, availablePackages: 5, cartonSelectionSupported: true, + cartons: [{ cartonId: id, cartonMark: `BOX-${id}`, skus: [{ skuCode: 'A', availablePackages: 5 }] }], skus: [{ skuCode: 'A', availablePackages: 5 }], +}) + +test('同单两箱合并成一个选项,可用量为两箱而非十件,保留原始提交身份', () => { + const sources = [returned('1'), returned('2')] + const result = groupDiscardDocuments(sources) + assert.equal(result.length, 1) + assert.equal(result[0].availablePackages, 2) + assert.deepEqual(result[0].sources, sources) + assert.equal(result[0].cartonSelectionSupported, true) +}) + +test('不同单据或操作仓独立展示,混装SKU不重复计算物理箱', () => { + assert.equal(groupDiscardDocuments([returned('1'), returned('2', 'OTHER'), returned('3', 'WH', 'TT-OTHER')]).length, 3) + const source = { ...returned('1'), sourceType: 'INVENTORY', orderId: 'inventory:1', inventoryPositionId: '1', availablePackages: 10 } + const second = { ...source, orderId: 'inventory:2', inventoryPositionId: '2', cartons: source.cartons } + assert.equal(groupDiscardDocuments([source, second])[0].availablePackages, 1) +}) + +test('缺少箱级记录时合并仓位可用箱数并保持全部废弃明细', () => { + const first = { ...returned('1'), sourceType: 'INVENTORY', orderId: 'inventory:1', cartonSelectionSupported: false, availablePackages: 3 } + const second = { ...first, orderId: 'inventory:2', availablePackages: 4 } + const result = groupDiscardDocuments([first, second])[0] + assert.equal(result.availablePackages, 7) + assert.equal(result.cartonSelectionSupported, false) + assert.equal(result.sources.length, 2) +}) diff --git a/src/modules/overseas-warehouse/utils/discardDocuments.ts b/src/modules/overseas-warehouse/utils/discardDocuments.ts new file mode 100644 index 0000000..8e876b8 --- /dev/null +++ b/src/modules/overseas-warehouse/utils/discardDocuments.ts @@ -0,0 +1,38 @@ +import type { DiscardCandidate } from '../api/warehouseServices' + +export interface DiscardDocument extends DiscardCandidate { + sources: DiscardCandidate[] +} + +/** 下拉按来源单据和操作仓汇总;原始库存身份保留给选箱与提交。 */ +export function groupDiscardDocuments(candidates: DiscardCandidate[]): DiscardDocument[] { + const groups = new Map() + for (const candidate of candidates) { + 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(candidate) + } + 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 { + untrackedPackages += source.availablePackages + } + } + 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()] +} diff --git a/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs b/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs index cdce72c..39b6251 100644 --- a/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs +++ b/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs @@ -62,7 +62,6 @@ test('新建退货预报选择海外仓后带出联系人和电话', () => { assert.match(view, /label="海外仓联系电话"/) assert.match(view, /:readonly="Boolean\(selectedOperationWarehouse\?\.contactName\)"/) assert.match(view, /:readonly="Boolean\(selectedOperationWarehouse\?\.contactPhone\)"/) - assert.match(view, /该海外仓未维护,请手动填写/) assert.match(view, /warehouseContactName: form\.warehouseContactName\.trim\(\)/) assert.match(view, /warehouseContactPhone: form\.warehouseContactPhone\.trim\(\)/) }) diff --git a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue index ecf1e38..586915e 100644 --- a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue +++ b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue @@ -298,25 +298,23 @@ onBeforeUnmount(clearPhotoUrls)