Files
pda-overseas-uniapp/tests/receiving-adapter.test.mjs
T

130 lines
7.9 KiB
JavaScript
Raw Normal View History

2026-08-24 19:29:41 +08:00
import test from 'node:test'
import assert from 'node:assert/strict'
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 = []
const api = new Function('overseasRequest', 'normalizeReceivingAssignment', 'normalizeReceivingInbound', source.replace(/^import .*$/gm, '').replace(/^export /gm, '') + '; return {resolveCurrentCarton,resolveCurrentPallet,receiveCurrentBulk,receiveCurrentCarton,receiveCurrentWholePallet};')(async (...args) => {
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, '')
assert.equal((await api.receiveCurrentWholePallet({ sourcePalletNo: 'DP-1' })).urgent, true)
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 }],
['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' }]
])
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-08-24 19:29:41 +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', () => {
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')
})