2026-09-08 14:07:31 +08:00
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { readFile } from 'node:fs/promises'
|
|
|
|
|
import test from 'node:test'
|
|
|
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
|
import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs'
|
2026-09-20 19:57:47 +08:00
|
|
|
import { verifyReceivedOrderHold } from './phase4-tail-order-controls.mjs'
|
2026-09-08 14:07:31 +08:00
|
|
|
|
|
|
|
|
const source = await readFile(fileURLToPath(new URL('./phase4-tail-order-controls.mjs', import.meta.url)), 'utf8')
|
|
|
|
|
|
2026-09-20 19:57:47 +08:00
|
|
|
test('hold positive lifecycle stays after receiving; pre-receipt verifies refusal without mutation', async () => {
|
|
|
|
|
assert.match(source, /入库前暂扣必须拒绝/)
|
|
|
|
|
assert.match(source, /拒绝暂扣后订单事实零漂移/)
|
|
|
|
|
assert.match(source, /representative\?\.currentStatus !== 'INBOUND_COMPLETED'/)
|
|
|
|
|
const operations = await readFile(new URL('./phase4-overseas-pda-operations.mjs', import.meta.url), 'utf8')
|
|
|
|
|
assert.ok(operations.indexOf('await verifyReceivedOrderHold(context)') > operations.indexOf('handoff.completedFixtures'))
|
|
|
|
|
assert.ok(operations.indexOf('await verifyReceivedOrderHold(context)') < operations.indexOf('await verifyStocktake(context'))
|
|
|
|
|
await assert.rejects(verifyReceivedOrderHold({ marker: 'run', createdResources: {} }), /缺少本轮/)
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-08 14:07:31 +08:00
|
|
|
test('tail order controls execute after current producer and before overseas PDA', () => {
|
|
|
|
|
const modules = normalizeModuleOrder(['phase4-overseas-pda'])
|
|
|
|
|
const controls = modules.indexOf('phase4-tail-order-controls')
|
|
|
|
|
assert.ok(controls > modules.indexOf('phase4-carrier-pickup-ready'))
|
|
|
|
|
assert.ok(controls < modules.indexOf('phase4-overseas-pda'))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('module covers the public tail-order write surface before PDA receiving', () => {
|
|
|
|
|
for (const path of ['/receiving-warehouse', '/destination', '/pallet-merge-required', '/fees', '/export',
|
|
|
|
|
'/hold', '/manual-nodes', '/api/platform/v1/last-leg/inbound-plans', '/manual']) {
|
|
|
|
|
assert.ok(source.includes(path), `missing ${path}`)
|
|
|
|
|
}
|
|
|
|
|
for (const evidence of [
|
|
|
|
|
'接柜仓变更同键同载荷重放', '接柜仓变更同键异载荷返回A0409', '接柜仓变更旧版本返回A0409',
|
|
|
|
|
'整柜恢复原公共接柜仓', '单票变更FBA目的仓', '批量变更FBA目的仓',
|
|
|
|
|
'单票开启PDA合托提示', '批量开启PDA合托分组', '批量取消PDA合托分组',
|
|
|
|
|
'尾程费用详情可追溯', '导出本轮尾程订单CSV',
|
|
|
|
|
'暂扣命令同键同载荷重放', '暂扣同键异载荷返回A0409', '暂扣尾程订单不能生成人工计划',
|
|
|
|
|
'解除本轮单票尾程暂扣', '保存尾程八个人工节点快照', '人工节点同键同载荷重放',
|
|
|
|
|
'人工节点同键异载荷返回A0409', '人工节点读取固定为MANUAL且不回退柜级节点',
|
2026-09-09 02:54:09 +08:00
|
|
|
'预览整柜待生成入仓计划清单', '按柜确认并自动生成入仓计划与实体托盘',
|
|
|
|
|
'按柜确认同键同快照幂等重放', '确认后预览待生成票数和托数归零',
|
2026-09-08 14:07:31 +08:00
|
|
|
]) assert.ok(source.includes(evidence), `missing ${evidence}`)
|
|
|
|
|
assert.match(source, /nodes\.data\?\.nodes\?\.length === 8/)
|
2026-09-09 02:54:09 +08:00
|
|
|
assert.match(source, /creationMode === 'AUTO'/)
|
2026-09-29 19:48:34 +08:00
|
|
|
assert.match(source, /requestBinary\('GET'/)
|
2026-09-08 14:07:31 +08:00
|
|
|
assert.match(source, /palletMergeBatchId/)
|
2026-09-09 19:11:37 +08:00
|
|
|
assert.match(source, /feeItemCode/)
|
|
|
|
|
assert.match(source, /feeItemName/)
|
|
|
|
|
assert.match(source, /direction: 'RECEIVABLE'/)
|
2026-09-09 21:13:10 +08:00
|
|
|
assert.match(source, /allIds\.length === 1 \|\| nonBlank\(groupRead\.data\?\.order\?\.palletMergeBatchId\)/)
|
2026-09-09 19:11:37 +08:00
|
|
|
assert.match(source, /businessDate:/)
|
2026-09-09 19:25:13 +08:00
|
|
|
assert.match(source, /originalCurrency === 'USD'/)
|
|
|
|
|
assert.match(source, /currency === 'CNY'/)
|
|
|
|
|
assert.match(source, /reviewStatus === 'PENDING'/)
|
|
|
|
|
assert.match(source, /'Idempotency-Key': `\$\{context\.marker\}-tail-fee`/)
|
2026-09-09 19:41:29 +08:00
|
|
|
assert.match(source, /ensureCurrentUsdRate/)
|
|
|
|
|
assert.match(source, /monthFrom=\$\{month\}&monthTo=\$\{month\}/)
|
|
|
|
|
assert.match(source, /existing\?\.status === 'ACTIVE'/)
|
|
|
|
|
assert.match(source, /businessDate: `\$\{feeMonth\}-01`/)
|
2026-09-09 19:11:37 +08:00
|
|
|
assert.doesNotMatch(source, /supplierDescription/)
|
2026-09-08 14:07:31 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('receiving warehouse verification reuses public master data and restores the original assignment', () => {
|
|
|
|
|
assert.match(source, /tenantId=0&page=1&size=200&capabilityCode=RECEIVING&warehouseType=OVERSEAS&status=ACTIVE/)
|
|
|
|
|
assert.match(source, /id\(row\.id\) !== originalWarehouseId/)
|
|
|
|
|
assert.match(source, /overseasWarehouseId: originalWarehouseId, expectedVersion: Number\(changed\.data\.assignmentVersion\)/)
|
|
|
|
|
assert.doesNotMatch(source, /request\('POST', '\/api\/platform\/v1\/warehouse-admin'/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('module is marker-owned and never borrows historical tail orders', () => {
|
|
|
|
|
assert.match(source, /matrix\?\.marker !== context\.marker/)
|
|
|
|
|
assert.match(source, /containerNo: producer\.containerNo/)
|
|
|
|
|
assert.match(source, /customerId\) === id\(producer\.tenantId\)/)
|
|
|
|
|
assert.match(source, /禁止借历史尾程订单/)
|
|
|
|
|
assert.doesNotMatch(source, /records\([^)]*\)\[0\]/)
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-09 02:54:09 +08:00
|
|
|
test('container conversion preserves long identifiers and generated pallets for PDA consumption', () => {
|
2026-09-08 14:07:31 +08:00
|
|
|
assert.match(source, /orderId: id\(order\.id\)/)
|
|
|
|
|
assert.doesNotMatch(source, /plannedWarehouse(?:Id|Code|Name):/)
|
|
|
|
|
assert.match(source, /!order\.overseasWarehouseId/)
|
2026-09-09 02:54:09 +08:00
|
|
|
assert.match(source, /container-preview/)
|
|
|
|
|
assert.match(source, /snapshotVersion/)
|
|
|
|
|
assert.match(source, /snapshotHash/)
|
|
|
|
|
assert.match(source, /convert-container/)
|
|
|
|
|
assert.match(source, /自动托盘进入托盘列表并保留FBA目的仓/)
|
|
|
|
|
assert.match(source, /planNos: generated\.map/)
|
|
|
|
|
assert.match(source, /generatedPlans: generated/)
|
|
|
|
|
assert.match(source, /AUTO_CONTAINER_PLANS_READY/)
|
|
|
|
|
assert.match(source, /remain as marker-owned prerequisites consumed by overseas PDA receiving/)
|
2026-09-08 14:07:31 +08:00
|
|
|
})
|