Merge origin/dev into release/test preserving current workflows

This commit is contained in:
2026-09-14 11:05:49 +08:00
6 changed files with 89 additions and 16 deletions
+4 -1
View File
@@ -63,7 +63,10 @@ test('login has no committed demo credentials and inbound operation implements t
assert.doesNotMatch(operation, /\.split\([^\n]*['"]#['"]/, '前端必须原样提交完整箱码,禁止按 # 猜测入仓码或箱序号')
assert.match(operation, /const payload = isFba \? \{ inboundCode: this\.activeInbound\.inboundCode, palletCode: this\.resolvedPallet\.palletCode, packageCount: Number\(this\.packageCount\), expectedPalletVersion: this\.resolvedPallet\.version \}/)
assert.doesNotMatch(operation, /skuCode/)
assert.match(operation, /clearReceiptContext\(\)[\s\S]*activeScanTarget = 'carton'/)
assert.match(operation, /resolveCurrentCarton\(cartonMark\)[\s\S]*value\.operationMode !== 'DROPSHIP_PALLET'/)
assert.match(operation, /name="carton"[\s\S]*@submit="resolveCarton"/)
assert.match(operation, /sourcePalletNo: value\.sourcePalletNo \|\| cartonMark[\s\S]*receiveCurrentWholePallet/)
assert.match(operation, /clearReceiptContext\(\)[\s\S]*activeScanTarget = 'carton'[\s\S]*drainScanQueue/)
assert.match(operation, /name="pallet"[\s\S]*@activate="activeTarget = 'pallet'"/)
assert.match(operation, /receiving\.activeScanTarget = 'pallet'/, '普通FBA或中转解析后直接进入托盘扫码以减少操作')
assert.match(operation, /\[Overseas PDA scan\] dispatch/)
+57 -1
View File
@@ -1,6 +1,62 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { isNoCurrentReceiving, isReceivingConflict, normalizeReceivingCandidate, normalizeReceivingSession, RECEIVING_PHOTO_ROLES, toReceivingError } from '../src/api/receiving-adapter.js'
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/)
})
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)
})
test('receiving adapter preserves the three signed photo roles in workflow order', () => {
const snowflakeFileId = '2092476334483931138'