import test from 'node:test' import assert from 'node:assert/strict' import { findReusableTransferReceiptTarget, receiveWarehouseTransfer } from './warehouse-transfer-receipt.mjs' // 收货替身使用独立目标仓资源,覆盖资源不足、串单、重复入库和结算未完成。 function fixture(fault = '') { const allocation = { inventoryPositionId: '301', palletId: '401', packageCount: 1, skuCode: 'NEW', inboundCode: 'IN-1' } const proof = { marker: 'BATCH', customerId: '101', warehouseId: '201', transferNo: 'WT-1', relabelNo: 'RL-1', relabelApplicationId: '501', inboundCode: 'IN-1', status: 'OUTBOUNDED', outboundAllocations: [allocation], outbound: { status: 'OUTBOUNDED_HTTP_VERIFIED' } } const pallet = { id: '601', warehouseId: '202', customerId: '0', locationId: '701', palletCode: 'TARGET', status: 'IDLE', fbaWarehouseCode: 'FBA-1' } const calls = [], saved = [] let received = false, writes = 0 const context = { marker: 'BATCH', context: { merchantQualificationFixture: { tenantId: '101' } }, createdResources: { phase4OverseasClientServices: { transferFulfillment: proof } }, assert() {}, async request(method, path, body) { calls.push({ method, path, body }) let data if (method === 'POST') { writes++ if (fault === 'network') throw Error('connection lost') assert.equal(body.destinationOverseasWarehouseId, '202') assert.equal(body.destinationPalletId, '601') received = true } if (path.includes('/pallets?')) data = { total: fault === 'no resource' ? 0 : 1, records: fault === 'no resource' ? [] : [{ ...pallet }] } else if (path.endsWith('/pallets/601')) data = { ...pallet, status: fault === 'occupied' ? 'OCCUPIED' : 'IDLE' } else if (path.endsWith('/locations/701')) data = { id: '701', warehouseId: '202', customerId: '0', locationCode: 'A0', locationType: 'VIRTUAL', status: fault === 'location blocked' ? 'OCCUPIED' : 'ACTIVE' } else if (path.includes('/warehouse-admin/')) data = { warehouse: { id: '202', tenantId: '0', warehouseType: 'OVERSEAS', status: 'ACTIVE', warehouseCode: 'WH2', warehouseName: 'Target' } } else if (path.includes('/ledger?')) { const rows = received ? [{ id: '801', customerId: fault === 'foreign customer' ? '999' : '101', overseasWarehouseId: '202', businessOrderNo: 'WT-1', businessType: 'STOCK_IN', inboundCode: 'IN-1', skuCode: 'NEW', palletNo: 'TARGET', warehouseLocationCode: 'A0', deltaPackages: 1 }] : [] if (fault === 'double receipt' && received) rows.push({ ...rows[0], id: '802' }) if (fault === 'replay drift' && writes === 2) rows[0].id = '899' data = { total: rows.length, records: rows } } else if (path.includes('/relabel-applications/')) data = { application: { relabelNo: 'RL-1', customerOrderNo: 'WT-1', status: 'COMPLETED', fulfillmentStatus: 'COMPLETED', walletStatus: fault === 'unsettled' ? 'SETTLE_PENDING' : 'SETTLED' } } else data = { transferNo: 'WT-1', status: received ? 'RECEIVED' : 'OUTBOUNDED', relabelNo: 'RL-1', relabelApplicationId: '501', fbaWarehouseCode: 'FBA-1', receiptCommands: received ? [{ status: 'COMPLETED', sourceInventoryPositionId: '301', request: { idempotencyKey: 'BATCH-TRANSFER-RECEIVE', batchNo: 'WT-1' }, target: { warehouseId: '202', palletId: fault === 'target drift' ? '999' : '601', palletNo: 'TARGET', locationId: '701', locationCode: 'A0', fbaCode: 'FBA-1' } }] : [], allocations: [{ ...allocation, status: received ? 'RECEIVED' : 'SHIPPED', destinationInventoryPositionId: received ? '901' : null }] } return { http: 200, body: { code: '200', data } } } } const options = { proof, merchantToken: 'secret', delay: async () => {}, checkpoint: async () => { saved.push(structuredClone(proof)); if (fault === 'disk') throw Error('disk full') } } return { context, options, proof, calls, saved } } test('原转运复用异仓托盘收货并核对唯一入库和结算终态', async () => { const f = fixture() await receiveWarehouseTransfer(f.context, f.options) assert.equal(f.proof.status, 'RECEIVED') assert.equal(f.proof.receipt.status, 'RECEIVED_AND_SETTLED_HTTP_VERIFIED') assert.equal(f.calls.filter(c => c.method === 'POST').length, 2) assert.ok(f.calls.filter(c => c.method === 'POST').every(c => c.path.includes('/receipts?'))) assert.equal(JSON.stringify(f.saved).includes('secret'), false) const count = f.calls.length await assert.rejects(receiveWarehouseTransfer(f.context, f.options)) assert.equal(f.calls.length, count) }) test('造单前只读选取异仓目的仓,翻过同仓和未绑定目的仓的托盘且不改主档', async () => { const f = fixture() const request = async (method, path) => { assert.equal(method, 'GET') if (path.includes('/pallets?') && path.includes('page=1&')) return { total: '201', records: Array.from({ length: 200 }, (_, i) => ({ id: String(1000 + i), customerId: '0', warehouseId: i % 2 ? '201' : '202', status: 'IDLE', locationId: '701', fbaWarehouseCode: i % 2 ? 'FBA-OTHER' : null })) } if (path.includes('/pallets?')) return { total: '201', records: [{ id: '601', customerId: '0', warehouseId: '202', status: 'IDLE', locationId: '701', fbaWarehouseCode: 'FBA-1' }] } return (await f.context.request(method, path)).body.data } const target = await findReusableTransferReceiptTarget({ sourceWarehouseId: '201', request, check: (name, ok) => assert.ok(ok, name) }) assert.equal(target.pallet.fbaWarehouseCode, 'FBA-1') assert.equal(target.warehouse.id, '202') assert.equal(f.calls.some(c => c.method !== 'GET'), false) }) for (const fault of ['no resource', 'occupied', 'location blocked', 'network', 'target drift', 'foreign customer', 'double receipt', 'replay drift', 'unsettled', 'disk']) { test(`转运收货遇到 ${fault} 明确失败且不补造资源`, async () => { const f = fixture(fault) await assert.rejects(receiveWarehouseTransfer(f.context, f.options)) assert.equal(f.proof.receipt.status, 'FAILED') const writes = f.calls.filter(c => c.method === 'POST').length assert.equal(writes, ['no resource', 'occupied', 'location blocked', 'disk'].includes(fault) ? 0 : ['replay drift', 'unsettled'].includes(fault) ? 2 : 1) if (fault === 'network') assert.equal(f.proof.receipt.requests.at(-1).status, 'UNKNOWN_RESULT') }) }