fix: complete October 22 workflow feedback and request failure recovery

This commit is contained in:
2026-10-22 23:06:06 +08:00
parent 161128ff9e
commit 54e49e2426
28 changed files with 530 additions and 413 deletions
+33 -2
View File
@@ -11,12 +11,15 @@ test('box merge component compiles both its script and template', () => {
compileScript(descriptor, { id: 'box-merge' })
assert.deepEqual(compileTemplate({ source: descriptor.template.content, filename, id: 'box-merge' }).errors, [])
})
function instance() {
function instance(runtime = {}) {
const boxes = [{cartonMark:'A',customerId:'C1',items:[{skuCode:'SKU1',quantity:2},{skuCode:'SKU2',quantity:3}]},{cartonMark:'B',customerId:'C1',items:[{skuCode:'SKU1',quantity:2}]}]
const component = vm.runInNewContext(descriptor.script.content.replace(/^import .*$/gm, '').replace('export default', 'component ='), { ScanTargetInput: {}, ReturnLabelConfirmation: {}, component: null, scanReturnCarton: async mark => boxes.find(box => box.cartonMark === mark) })
const context = vm.createContext({ reactive: value => value, ScanTargetInput: {}, ReturnLabelConfirmation: {}, component: null, scanReturnCarton: async mark => boxes.find(box => box.cartonMark === mark), ...runtime })
vm.runInContext(readFileSync(new URL('../src/stores/warehouse-work.js', import.meta.url), 'utf8').replace(/^import .*$/gm, '').replace(/export /g, ''), context)
const component = vm.runInContext(descriptor.script.content.replace(/^import .*$/gm, '').replace('export default', 'component ='), context)
const work = { ...component.data(), plan: {customerId:'C1',sources:[{cartonMark:'A',items:[{skuCode:'SKU1',quantity:2},{skuCode:'SKU2',quantity:3}]},{cartonMark:'B',items:[{skuCode:'SKU1',quantity:2}]}]}, $emit(){} }
for (const [name, method] of Object.entries(component.methods)) work[name] = method.bind(work)
work.save = () => {}
Object.defineProperty(work, 'canComplete', { get: () => component.computed.canComplete.call(work) })
return {work,component}
}
test('new plan requires scanning each source and preserves locked SKU quantities', async () => {
@@ -31,6 +34,34 @@ test('new plan requires scanning each source and preserves locked SKU quantities
assert.equal(component.computed.canComplete.call(work),true)
})
test('partial merge preserves the scanned snapshot after failure and accepts a fresh scan without locking', async () => {
let available = 10
const calls = [], errors = []
const { work } = instance({
scanReturnCarton: async () => ({ cartonMark: 'A', customerId: 'C1', items: [{ skuCode: 'SKU1', quantity: available }] }),
mergeReturnBoxes: async request => {
calls.push(JSON.parse(JSON.stringify(request)))
if (request.expectedSourceQuantities[0].quantity !== available) throw new Error('来源箱库存已变化')
available -= request.sourceDeductions[0].quantity
if (calls.length === 1) throw new Error('response lost after commit')
return { label: { cartonMark: 'NEW' } }
}
})
work.plan = null; work.choose('NEW'); work.$emit = (name, value) => { if (name === 'error') errors.push(value) }
await work.scanSource('A'); work.setDeduction('A', 'SKU1', 3)
work.dimensions = { length: 10, width: 10, height: 10, weight: 1 }
await work.complete(); await work.complete()
assert.equal(available, 7)
assert.equal(work.busy, false)
assert.equal(errors.at(-1).message, '来源箱库存已变化')
assert.notEqual(calls[0].idempotencyKey, calls[1].idempotencyKey)
work.remove('A'); await work.scanSource('A'); work.setDeduction('A', 'SKU1', 2)
await work.complete()
assert.equal(available, 5)
assert.deepEqual(calls.map(call => call.expectedSourceQuantities[0].quantity), [10, 10, 7])
assert.equal(work.busy, false)
})
test('planned source carton marks are visible before scanning', () => {
const {work, component} = instance()
work.plan = { sourceCartonMarks: ['A', 'B'], sources: [] }