2026-08-24 19:29:41 +08:00
import test from 'node:test'
import assert from 'node:assert/strict'
2026-09-09 12:25:13 +08:00
import { isNoCurrentReceiving , isReceivingConflict , normalizeReceivingAssignment , normalizeReceivingInbound , normalizeReceivingCandidate , normalizeReceivingSession , RECEIVING_PHOTO_ROLES , toReceivingError } from '../src/api/receiving-adapter.js'
import { readFile } from 'node:fs/promises'
test ( 'assignment hints are server booleans only and merge is disabled by default' , () => {
for ( const value of [ undefined , null , false , 1 , 'true' , 'false' ]) {
const result = normalizeReceivingAssignment ({ assigned : value , mergeAllowed : value , urgent : value , mergeHint : 'do not expose' })
assert . deepEqual ( result , { assigned : false , mergeAllowed : false , urgent : false , mergeHint : '' })
}
const flags = { assigned : true , mergeAllowed : true , urgent : true , mergeHint : ' 按平台安排合托 ' }
const session = normalizeReceivingSession ({ inbounds : [ flags ] })
assert . equal ( session . inbounds [ 0 ]. mergeHint , '按平台安排合托' )
assert . equal ( session . inbounds [ 0 ]. assigned , true )
assert . equal ( session . inbounds [ 0 ]. urgent , true )
})
test ( 'reservation mismatch asks for the reserved pallet and retains diagnostics' , () => {
const result = toReceivingError ({ code : 'A0409' , message : 'Scanned pallet does not match the reserved inbound plan resource' , requestId : 'request-1' , traceId : 'trace-1' })
assert . equal ( result . message , '扫描托盘与入仓计划预留资源不一致,请扫描已预留的托盘' )
assert . equal ( result . requestId , 'request-1' )
assert . equal ( result . traceId , 'trace-1' )
})
test ( 'first scan and receipt requests confirm server reservations without resource assignment commands' , async () => {
const source = await readFile ( new URL ( '../src/api/inbound.js' , import . meta . url ), 'utf8' )
const calls = []
2026-09-09 18:33:17 +08:00
const api = new Function ( 'overseasRequest' , 'normalizeReceivingAssignment' , 'normalizeReceivingInbound' , source . replace ( /^import .*$/gm , '' ). replace ( /^export /gm , '' ) + '; return {resolveCurrentCarton,resolveCurrentPallet,receiveCurrentBulk,receiveCurrentCarton,receiveCurrentWholePallet};' )( async (... args ) => {
2026-09-09 12:25:13 +08:00
calls . push ( args )
return { compatible : true , assigned : true , mergeAllowed : 'true' , urgent : true , mergeHint : 'unsafe hint' }
}, normalizeReceivingAssignment , normalizeReceivingInbound )
assert . equal (( await api . resolveCurrentCarton ( 'BOX-1' )). mergeAllowed , false )
assert . equal (( await api . resolveCurrentPallet ( 'P-1' , 'IN-1' )). compatible , true )
assert . equal (( await api . receiveCurrentBulk ({ inboundCode : 'IN-1' , palletCode : 'P-1' , packageCount : 2 })). urgent , true )
assert . equal (( await api . receiveCurrentCarton ({ cartonMark : 'BOX-1' , palletCode : 'P-1' })). mergeHint , '' )
2026-09-09 18:33:17 +08:00
assert . equal (( await api . receiveCurrentWholePallet ({ sourcePalletNo : 'DP-1' })). urgent , true )
2026-09-09 12:25:13 +08:00
assert . deepEqual ( calls , [
[ 'POST' , '/pda/v1/last-leg/receiving-containers/current/cartons/resolve' , { cartonMark : 'BOX-1' }],
[ 'POST' , '/pda/v1/last-leg/receiving-containers/current/pallets/resolve' , { palletCode : 'P-1' , inboundCode : 'IN-1' }],
[ 'POST' , '/pda/v1/last-leg/receiving-containers/current/receipts/bulk' , { inboundCode : 'IN-1' , palletCode : 'P-1' , packageCount : 2 }],
2026-09-09 18:33:17 +08:00
[ 'POST' , '/pda/v1/last-leg/receiving-containers/current/receipts/carton' , { cartonMark : 'BOX-1' , palletCode : 'P-1' }],
[ 'POST' , '/pda/v1/last-leg/receiving-containers/current/receipts/pallet' , { sourcePalletNo : 'DP-1' }]
2026-09-09 12:25:13 +08:00
])
const page = await readFile ( new URL ( '../src/pages/operation/operation.vue' , import . meta . url ), 'utf8' )
assert . match ( page , /activeInbound\.assigned/ )
assert . match ( page , /activeInbound\.urgent/ )
assert . match ( page , /activeInbound\.mergeAllowed && activeInbound\.mergeHint/ )
const gate = page . match ( /canSubmitReceipt\(\) \{.*?\},/ ) ? .[ 0 ]
assert . ok ( gate )
assert . doesNotMatch ( gate , /mergeAllowed|assigned|urgent/ )
assert . match ( gate , /resolvedPallet\?\.compatible/ )
2026-09-18 16:48:44 +08:00
assert . match ( page , /const MAX_PACKAGES_PER_PALLET = 66/ )
assert . match ( page , /Number\(this\.packageCount\) > MAX_PACKAGES_PER_PALLET/ )
assert . match ( page , /一个托盘最多只能收66箱,请拆分到其他托盘/ )
assert . match ( page , /this\.showToast\(PALLET_CAPACITY_MESSAGE, 'error'\)/ )
2026-09-09 12:25:13 +08:00
})
2026-08-24 19:29:41 +08:00
2026-09-09 18:33:17 +08:00
test ( 'one-click dropship inbound is marked only by an explicit server boolean' , () => {
for ( const value of [ undefined , null , false , 1 , 'true' , 'false' ]) {
assert . equal ( normalizeReceivingInbound ({ wholePalletInbound : value }). wholePalletInbound , false )
}
assert . equal ( normalizeReceivingInbound ({ wholePalletInbound : true }). wholePalletInbound , true )
})
2026-08-24 19:29:41 +08:00
test ( 'receiving adapter preserves the three signed photo roles in workflow order' , () => {
2026-08-26 15:24:51 +08:00
const snowflakeFileId = '2092476334483931138'
2026-08-24 19:29:41 +08:00
const session = normalizeReceivingSession ({
status : 'WAIT_SIGN' , totalPackages : 8 , receivedPackages : 2 , pendingPackages : 6 ,
2026-08-26 15:59:08 +08:00
photos : [{ role : 'DOOR_OPEN' , fileId : '3' }, { role : 'LICENSE_PLATE' , fileId : snowflakeFileId }, { role : 'CONTAINER_SEAL' , fileId : '2' }, { role : 'DOOR_OPEN' , fileId : '4' }],
2026-08-24 19:29:41 +08:00
inbounds : [{ inboundCode : 'IN-1' , plannedPalletCount : 2 , totalPackages : 8 , receivedPackages : 2 , pendingPackages : 6 }]
})
assert . deepEqual ( session . photos . map (({ role }) => role ), RECEIVING_PHOTO_ROLES )
2026-08-26 15:24:51 +08:00
assert . equal ( session . photos [ 0 ]. fileId , snowflakeFileId )
2026-08-26 15:59:08 +08:00
assert . equal ( session . photos . length , 3 )
2026-08-24 19:29:41 +08:00
assert . equal ( session . inbounds [ 0 ]. plannedPalletCount , 2 )
})
2026-09-15 13:37:44 +08:00
test ( 'domestic loading photos remain cabinet-level read-only references without file IDs' , () => {
const session = normalizeReceivingSession ({ domesticLoadingPhotos : [
{ evidenceType : 'PHOTO_STEP_1' , label : '空仓' , downloadPath : '/current/domestic-loading-photos/PHOTO_STEP_1/content' },
{ evidenceType : 'SEAL' , label : '带锁码' , downloadPath : '/current/domestic-loading-photos/SEAL/content' },
{ evidenceType : '' , label : 'invalid' , downloadPath : '/invalid' }
] })
assert . equal ( session . domesticLoadingPhotos . length , 2 )
assert . deepEqual ( session . domesticLoadingPhotos . map (({ evidenceType , status , localPath }) => ({ evidenceType , status , localPath })), [
{ evidenceType : 'PHOTO_STEP_1' , status : 'idle' , localPath : '' },
{ evidenceType : 'SEAL' , status : 'idle' , localPath : '' }
])
assert . equal ( 'fileId' in session . domesticLoadingPhotos [ 0 ], false )
})
2026-08-24 19:29:41 +08:00
test ( 'receiving adapter exposes calculable candidate facts and workflow errors' , () => {
2026-08-27 15:56:13 +08:00
const candidate = normalizeReceivingCandidate ({ containerNo : ' C10000000 ' , status : 'WAIT_SIGN' , planCount : 15 , inboundCodeCount : 15 , destinationCodes : [ 'ONT8' , 'LAX' ], plannedPalletCount : 4 , currentAccount : true })
assert . equal ( candidate . containerNo , 'C10000000' )
assert . equal ( candidate . status , 'WAIT_SIGN' )
assert . equal ( candidate . planCount , 15 )
assert . equal ( candidate . inboundCodeCount , 15 )
2026-08-24 19:29:41 +08:00
assert . deepEqual ( candidate . destinationCodes , [ 'ONT8' , 'LAX' ])
assert . equal ( candidate . plannedPalletCount , 4 )
assert . equal ( candidate . currentAccount , true )
assert . equal ( isNoCurrentReceiving ({ code : 404 }), true )
assert . equal ( isReceivingConflict ({ code : 'B0409' }), true )
})
2026-09-08 17:56:56 +08:00
test ( 'unusable warehouse pallet error is actionable Chinese and retains diagnostics' , () => {
const mapped = toReceivingError ({
code : 'S0503' ,
message : 'Warehouse pallet service returned no usable response' ,
requestId : '4dd3028c-9112-429d-a9d2-4f71233c5c64' ,
traceId : 'c01eeddb77afc5c018828c3cb8ccc1ec'
}, '校验托盘失败' )
assert . equal ( mapped . message , '该托盘当前不可用于收货,请更换可用托盘或联系管理员检查托盘状态' )
assert . equal ( mapped . code , 'S0503' )
assert . equal ( mapped . requestId , '4dd3028c-9112-429d-a9d2-4f71233c5c64' )
assert . equal ( mapped . traceId , 'c01eeddb77afc5c018828c3cb8ccc1ec' )
const occupied = toReceivingError ({
code : 'A0409' ,
message : 'Pallet is not idle' ,
requestId : 'occupied-request' ,
traceId : 'occupied-trace'
})
assert . equal ( occupied . message , '该托盘已被占用,当前不可用于收货,请更换可用托盘' )
assert . equal ( occupied . requestId , 'occupied-request' )
assert . equal ( occupied . traceId , 'occupied-trace' )
const unknown = toReceivingError ({ code : 'S0503' , message : 'Another warehouse failure' }, '校验托盘失败' )
assert . equal ( unknown . message , 'Another warehouse failure' )
})