fix(pda): prevent stale and ambiguous dispatch scan selections

This commit is contained in:
2026-10-09 15:54:21 +08:00
parent 648b0c858d
commit 95078f750e
3 changed files with 43 additions and 8 deletions
+32 -1
View File
@@ -219,7 +219,7 @@ test('pages expose loading empty error offline and permission diagnostics with g
assert.match(page, /正在加载/)
assert.match(page, /暂无/)
assert.match(page, /requestId[\s\S]*traceId/)
assert.match(page, /error\.network[\s\S]*幂等键/)
assert.match(page, /error\.network[\s\S]*保留/)
assert.match(page, /fulfillmentIdempotencyKey/)
assert.match(page, /releaseFulfillmentKey/)
assert.match(page, /if \(!this\.can/)
@@ -297,3 +297,34 @@ test('dispatch review gate preserves the server decision without treating a stri
assert.match(source, /canPick\(\) \{ return this.current\?\.canPick === true/)
assert.match(source, /canComplete\(\) \{ return this.current\?\.canPick === true/)
})
async function dispatchScanHarness(items) {
const source = (await read('../src/pages/dispatch/dispatch.vue')).match(/<script>([\s\S]*?)<\/script>/)[1]
const draft = { palletCode: 'OLD', dispatchItemId: 'old', cartonCode: '' }; let sounds = 0
const deps = { PortalHeader: {}, ScanTargetInput: {}, DispatchHandoverStatus: {},
updateFulfillmentDraft: (_, value) => Object.assign(draft, value), playSuccessSound: () => { sounds++ },
console: { info() {} } }
const definition = new Function(...Object.keys(deps), source.replace(/^\s*import .*$/gm, '').replace('export default', 'return'))(...Object.values(deps))
const state = { current: { dispatchNo: 'DS-TEST', items }, error: { message: 'previous error' }, successMessage: '',
setError(error) { this.error = { message: error.message } } }
state.selectPallet = definition.methods.selectPallet.bind(state)
return { state, draft, scan: definition.methods.handleScan.bind(state), sounds: () => sounds }
}
test('dispatch wrong scan clears prior selection and a valid pallet clears the old error', async () => {
const h = await dispatchScanHarness([{ palletNo: 'PALLET-A', dispatchItemId: 'a', scheduledPackages: 1, pickedPackages: 0, positions: [{ availablePackages: 1 }] }])
h.scan('UNKNOWN'); assert.equal(h.draft.dispatchItemId, ''); assert.ok(h.state.error)
h.scan('PALLET-A'); assert.equal(h.state.error, null); assert.equal(h.draft.dispatchItemId, 'a'); assert.equal(h.sounds(), 1)
})
test('dispatch cannot use a shared destination code to choose a physical pallet', async () => {
const h = await dispatchScanHarness(['A', 'B'].map(x => ({ palletNo: `PALLET-${x}`, fbaWarehouseCode: 'ONT8', dispatchItemId: x, scheduledPackages: 1, pickedPackages: 0 })))
h.scan('ONT8'); assert.equal(h.draft.dispatchItemId, ''); assert.ok(h.state.error); assert.equal(h.sounds(), 0)
})
test('dispatch completed or ambiguous pallet scan never silently selects a row', async () => {
const done = await dispatchScanHarness([{ palletNo: 'PALLET-A', dispatchItemId: 'a', scheduledPackages: 1, pickedPackages: 1 }])
done.scan('PALLET-A'); assert.match(done.state.error.message, /已完成拣货/); assert.equal(done.draft.dispatchItemId, ''); assert.equal(done.sounds(), 0)
const shared = await dispatchScanHarness(['A','B'].map(x => ({ palletNo: 'PALLET-SHARED', dispatchItemId: x, scheduledPackages: 1, pickedPackages: 0 })))
shared.scan('PALLET-SHARED'); assert.match(shared.state.error.message, /多条待拣明细/); assert.equal(shared.draft.dispatchItemId, ''); assert.equal(shared.sounds(), 0)
})