This commit is contained in:
2026-10-28 17:17:54 +08:00
parent fcba6b6f25
commit 859630fbbe
5 changed files with 99 additions and 10 deletions
+6 -4
View File
@@ -115,7 +115,9 @@ export function isReceivingConflict(error) {
export function toReceivingError(error, fallback = '海外仓作业失败') {
const code = error?.code
const rawMessage = String(error?.message || '').trim()
const palletConflictMessages = {
const receivingMessages = {
'Carton mark must contain a non-empty inbound code prefix': '请扫描箱码或国内DP码',
'Pallet code is not part of the current FBA destination group': '托盘不属于当前目的仓,请重扫',
'Reserved pallet binding is incomplete': '预留托盘资料不完整,请联系管理员检查入仓计划',
'Reserved pallet customer binding is invalid': '预留托盘与当前客户不匹配,请联系管理员检查入仓计划',
'Scanned pallet does not match the reserved inbound plan resource': '扫描托盘与入仓计划预留资源不一致,请扫描已预留的托盘',
@@ -131,13 +133,13 @@ export function toReceivingError(error, fallback = '海外仓作业失败') {
'Pallet FBA destination does not match the inbound code': '该托盘的FBA目的仓与当前入仓码不一致,请更换托盘',
'FBA-designated pallet cannot receive an OVERSEAS destination carton': 'FBA专用托盘不能接收海外仓目的箱,请更换托盘'
}
const conflictMessage = ['409', 'A0409', 'B0409'].includes(String(code || ''))
? palletConflictMessages[rawMessage]
const businessMessage = ['404', 'A0404', 'B0404', '409', 'A0409', 'B0409'].includes(String(code || ''))
? receivingMessages[rawMessage]
: ''
const palletUnavailable = String(code || '') === 'S0503'
&& rawMessage === 'Warehouse pallet service returned no usable response'
const mapped = Object.assign(new Error(
conflictMessage || (palletUnavailable
businessMessage || (palletUnavailable
? '该托盘当前不可用于收货,请更换可用托盘或联系管理员检查托盘状态'
: rawMessage || fallback)
), {
@@ -47,6 +47,8 @@
<style scoped>
.receiving-summary{box-sizing:border-box;margin-bottom:10px;padding:10px 12px;border:1px solid #b8d2f3;border-radius:13px;background:linear-gradient(145deg,#fff,#f2f7ff);box-shadow:0 3px 12px rgba(31,74,125,.08)}
.receiving-summary-head,.receiving-summary-progress>view:first-child{display:flex;align-items:center;justify-content:space-between;gap:10px}
.receiving-summary-head > view:first-child{flex:1;min-width:0}
.receiving-summary-container{white-space:normal;overflow-wrap:anywhere;word-break:break-all}
.receiving-summary-label,.receiving-summary-container,.metric-value,.metric-label{display:block}.receiving-summary-label{color:#718095;font-size:9px;line-height:13px}.receiving-summary-container{margin-top:1px;color:#17324d;font-size:17px;font-weight:850;line-height:22px}
.receiving-summary-status{flex-shrink:0;padding:4px 8px;border-radius:99px;color:#1668dc;font-size:9px;font-weight:700;background:#e3efff}
.receiving-summary-context{display:flex;flex-direction:column;gap:4px;margin-top:7px;color:#617389;font-size:9px;line-height:14px}.receiving-summary-destinations{display:flex;min-width:0;align-items:flex-start}.destination-label{flex-shrink:0}.destination-tags{display:flex;min-width:0;flex-wrap:wrap;gap:3px 4px}.destination-tags text{padding:0 5px;border-radius:99px;color:#42617f;line-height:14px;background:#e8f0fa;white-space:nowrap}
File diff suppressed because one or more lines are too long
+15
View File
@@ -22,6 +22,21 @@ test('reservation mismatch asks for the reserved pallet and retains diagnostics'
assert.equal(result.traceId, 'trace-1')
})
test('cargo and destination-pallet scan errors use concise Chinese and retain diagnostics', () => {
for (const [code, message, expected] of [
['A0409', 'Carton mark must contain a non-empty inbound code prefix', '请扫描箱码或国内DP码'],
['A0404', 'Pallet code is not part of the current FBA destination group', '托盘不属于当前目的仓,请重扫'],
['B0404', 'Pallet code is not part of the current FBA destination group', '托盘不属于当前目的仓,请重扫'],
[404, 'Pallet code is not part of the current FBA destination group', '托盘不属于当前目的仓,请重扫'],
]) {
const result = toReceivingError({ code, message, requestId: 'scan-request', traceId: 'scan-trace' })
assert.equal(result.message, expected)
assert.equal(result.code, code)
assert.equal(result.requestId, 'scan-request')
assert.equal(result.traceId, 'scan-trace')
}
})
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 = []
+55 -1
View File
@@ -2,7 +2,7 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import * as store from '../src/stores/receiving.js'
import { normalizeReceivingSession } from '../src/api/receiving-adapter.js'
import { normalizeReceivingSession, toReceivingError } from '../src/api/receiving-adapter.js'
const source = readFileSync(new URL('../src/pages/operation/operation.vue', import.meta.url), 'utf8').match(/<script>([\s\S]*?)<\/script>/)[1]
const defer = () => { let resolve, reject; const promise = new Promise((a, b) => { resolve = a; reject = b }); return { promise, resolve, reject } }
@@ -37,6 +37,60 @@ test('same current readback retains three signed previews; collapsed unversioned
await vm.applyCurrentReceiving(sessionValue()); assert.equal(calls.length, 18)
})
test('editing and rejecting a pallet cannot submit using the previous successful validation', async t => {
store.resetReceivingSession(); store.setCurrentReceiving(sessionValue())
store.receiving.resolvedCarton = { inboundCode: 'IN-A', cartonMark: 'IN-A#22-1', destinationType: 'FBA' }
store.receiving.resolvedPallet = { palletCode: 'PALLET-OLD', compatible: true, version: 1 }
let writes = 0
const { vm } = page({ toReceivingError,
resolveCurrentPallet: async () => { throw Object.assign(new Error('Pallet code is not part of the current FBA destination group'), { code: 'A0404', requestId: 'scan-request', traceId: 'scan-trace' }) },
receiveCurrentBulk: async () => { writes++ },
})
t.after(() => vm.suspendReceiving())
vm.palletInput = 'PALLET-OLD'; vm.packageCount = 3
assert.equal(vm.canSubmitReceipt, true)
vm.palletInput = 'PALLET-WRONG'
assert.equal(vm.canSubmitReceipt, false)
const toasts = []; vm.showToast = (message, type) => toasts.push({ message, type })
await vm.resolvePallet(vm.palletInput)
assert.equal(vm.resolvedPallet, null)
assert.equal(vm.packageCount, '')
assert.equal(vm.activeTarget, 'pallet')
assert.equal(vm.error.message, '托盘不属于当前目的仓,请重扫')
assert.equal(vm.error.requestId, 'scan-request')
assert.equal(vm.error.traceId, 'scan-trace')
assert.deepEqual(toasts, [{ message: '托盘不属于当前目的仓,请重扫', type: 'error' }])
await vm.submitReceipt()
assert.equal(writes, 0)
})
test('an incompatible pallet never reports success and a corrected scan can be validated normally', async t => {
store.resetReceivingSession(); store.setCurrentReceiving(sessionValue())
store.receiving.resolvedCarton = { inboundCode: 'IN-A', cartonMark: 'IN-A#22-1', destinationType: 'FBA' }
const calls = []; let successes = 0
const { vm } = page({ toReceivingError, playSuccessSound: () => successes++,
resolveCurrentPallet: async (palletCode, inboundCode) => {
calls.push({ palletCode, inboundCode })
return { palletCode, compatible: palletCode === 'PALLET-VALID', version: 2, currentReceivedPackages: 3 }
},
})
t.after(() => vm.suspendReceiving())
const toasts = []; vm.showToast = (message, type) => toasts.push({ message, type })
await vm.resolvePallet('PALLET-WRONG')
assert.equal(vm.resolvedPallet, null)
assert.equal(vm.canSubmitReceipt, false)
assert.equal(successes, 0)
assert.deepEqual(toasts, [{ message: '托盘不匹配,请重扫', type: 'error' }])
await vm.resolvePallet('PALLET-VALID')
assert.deepEqual(calls, [{ palletCode: 'PALLET-WRONG', inboundCode: 'IN-A' }, { palletCode: 'PALLET-VALID', inboundCode: 'IN-A' }])
assert.equal(vm.resolvedPallet.palletCode, 'PALLET-VALID')
assert.equal(vm.error, null)
assert.equal(vm.packageCount, 3)
assert.equal(vm.canSubmitReceipt, true)
assert.equal(vm.activeTarget, 'quantity')
assert.equal(successes, 1)
})
test('signed photo changes, account/customer/cabinet changes and image failure cannot inherit stale previews', async () => {
store.resetReceivingSession(); let calls = 0
const { vm, deps } = page({ downloadCurrentUnloadPhoto: async () => `local:${++calls}`, downloadCurrentDomesticLoadingPhoto: async () => '' })