fix: complete October 22 workflow feedback and request failure recovery
This commit is contained in:
@@ -122,13 +122,12 @@ function setup(confirm, saved = new Map(), scope = 'operator:warehouse', runtime
|
||||
const component = vm.runInContext(script.replace(/^\s*import .*$/mg, '').replace('export default', 'globalThis.component ='), context)
|
||||
vm.runInContext(`restoreWarehouseWork(${JSON.stringify(scope)})`, context)
|
||||
const instance = Object.assign({}, component.methods, {
|
||||
unitType: 'PALLET', palletBoxCount: 1, canConfirm: true, busy: false,
|
||||
receiptResults: [], unitType: 'PALLET', palletBoxCount: 1, canConfirm: true, busy: false,
|
||||
current: { returnNo: 'RT-TEST', expectedItems: [{ skuCode: 'SKU-TEST', expectedQuantity: 10, receivedQuantity: 0 }] },
|
||||
selectedItems: [{ skuCode: 'SKU-TEST', expectedQuantity: 10, receivedQuantity: 0 }], photoFileIds: [], photoNames: [], photoUrls: {}, trackingNo: ' TRACK-TEST ', startedBy: 'SKU',
|
||||
palletCode: ' PLT-TEST ', palletLength: 10, palletWidth: 10, palletHeight: 10, palletWeight: 1,
|
||||
quantity: () => 2, boxCount: () => 1
|
||||
})
|
||||
Object.defineProperty(instance, 'receiptBatch', { get: () => vm.runInContext('warehouseWork.returnReceipt.pendingBatch', context) })
|
||||
Object.defineProperty(instance, 'allBoxRowsConfirmed', { get: () => component.computed.allBoxRowsConfirmed.call(instance) })
|
||||
return instance
|
||||
}
|
||||
@@ -156,7 +155,7 @@ test('PALLET page submits the required palletCode through the actual confirmatio
|
||||
assert.equal(calls.length, 1)
|
||||
assert.equal(calls[0].palletCode, 'PLT-TEST')
|
||||
assert.equal(calls[0].unitCode, undefined)
|
||||
assert.deepEqual(Object.keys(calls[0]).sort(), ['height', 'idempotencyKey', 'items', 'length', 'palletCode', 'returnNo', 'startedBy', 'totalBoxCount', 'trackingNo', 'unitType', 'weight', 'width'].sort())
|
||||
assert.deepEqual(Object.keys(calls[0]).sort(), ['expectedReceivedQuantity', 'height', 'idempotencyKey', 'items', 'length', 'palletCode', 'returnNo', 'startedBy', 'totalBoxCount', 'trackingNo', 'unitType', 'weight', 'width'].sort())
|
||||
assert.equal(calls[0].unitType, 'PALLET')
|
||||
assert.equal(calls[0].totalBoxCount, 1)
|
||||
assert.deepEqual(Object.keys(calls[0].items[0]).sort(), ['boxCount', 'receivedQuantity', 'skuCode'].sort())
|
||||
@@ -232,7 +231,7 @@ test('one submission signs multiple mixed-SKU parcels sharing one waybill and in
|
||||
assert.deepEqual(Array.from(page.result.labels, row => row.cartonMark), ['RP-1', 'RP-2'])
|
||||
})
|
||||
|
||||
test('failed multi-parcel batch resumes immutable requests without re-signing a successful parcel', async () => {
|
||||
test('failed multi-parcel receipt keeps successful results and accepts edits to remaining rows', async () => {
|
||||
const saved = new Map(), calls = []
|
||||
let failSecond = true
|
||||
const confirm = async request => {
|
||||
@@ -243,16 +242,65 @@ test('failed multi-parcel batch resumes immutable requests without re-signing a
|
||||
const page = setup(confirm, saved)
|
||||
page.unitType = 'PARCEL'; page.photoFileIds = ['PHOTO-EXISTING']; page.unitCode = 'WAYBILL-BATCH'; page.boxRows = [physicalRow({ 'SKU-TEST': 1 }), physicalRow({ 'SKU-TEST': 2 })]
|
||||
await page.confirmReceipt()
|
||||
assert.equal(page.receiptBatch.entries[0].result.labels[0].cartonMark, 'RP-1')
|
||||
assert.equal(page.receiptResults[0].labels[0].cartonMark, 'RP-1'); assert.equal(page.boxRows.length, 1); assert.equal(page.busy, false)
|
||||
failSecond = false
|
||||
const restored = setup(confirm, saved)
|
||||
const restored = page; page.boxRows[0].items['SKU-TEST'] = 3
|
||||
await restored.confirmReceipt()
|
||||
assert.equal(calls.length, 3)
|
||||
assert.equal(calls[1].idempotencyKey, calls[2].idempotencyKey)
|
||||
assert.deepEqual(calls[1], calls[2])
|
||||
assert.notEqual(calls[1].idempotencyKey, calls[2].idempotencyKey)
|
||||
assert.equal(calls[2].items[0].receivedQuantity, 3)
|
||||
assert.deepEqual(calls.map(call => call.expectedReceivedQuantity), [0, 1, 1])
|
||||
assert.equal(restored.result.labels.length, 2)
|
||||
})
|
||||
|
||||
test('lost receipt response keeps the old quantity precondition and does not lock editing or refresh', async () => {
|
||||
const calls = []
|
||||
let received = 0
|
||||
const page = setup(async request => {
|
||||
calls.push(JSON.parse(JSON.stringify(request)))
|
||||
if (request.expectedReceivedQuantity !== received) throw new Error('退货单实收数量已变化')
|
||||
received += request.items[0].receivedQuantity
|
||||
if (calls.length === 1) throw new Error('response lost after commit')
|
||||
return { unitType: 'PARCEL', fullyMatched: false }
|
||||
}, new Map(), 'operator:warehouse', {
|
||||
startReturnReceipt: async () => ({ returnNo: 'RT-TEST', expectedItems: [{ skuCode: 'SKU-TEST', expectedQuantity: 10, receivedQuantity: received }], photos: [{ fileId: 'PHOTO' }] }),
|
||||
downloadReturnPalletPhoto: async () => 'photo-url'
|
||||
})
|
||||
page.unitType = 'PARCEL'; page.unitCode = ''; page.photoFileIds = ['PHOTO']
|
||||
page.boxRows = [physicalRow({ 'SKU-TEST': 2 })]
|
||||
await page.confirmReceipt()
|
||||
await page.confirmReceipt()
|
||||
assert.equal(received, 2)
|
||||
assert.equal(page.busy, false)
|
||||
assert.equal(page.boxRows.length, 1)
|
||||
assert.equal(page.error.message, '退货单实收数量已变化')
|
||||
assert.notEqual(calls[0].idempotencyKey, calls[1].idempotencyKey)
|
||||
// 用户刷新后,按当前实收量登记下一份实物。
|
||||
await page.reloadCurrent()
|
||||
assert.equal(page.boxRows[0].confirmed, false)
|
||||
assert.deepEqual(Object.keys(page.boxRows[0].items), [])
|
||||
await page.confirmReceipt()
|
||||
assert.equal(received, 2, '刷新后原包裹不能再次提交')
|
||||
assert.equal(calls.length, 2)
|
||||
page.boxRows[0] = physicalRow({ 'SKU-TEST': 3 })
|
||||
await page.confirmReceipt()
|
||||
assert.equal(received, 5)
|
||||
assert.deepEqual(calls.map(call => call.expectedReceivedQuantity), [0, 0, 2])
|
||||
assert.equal(page.busy, false)
|
||||
})
|
||||
|
||||
test('refresh with unchanged receipt quantities preserves editable input after a rejected request', async () => {
|
||||
const page = setup(async () => { throw new Error('not committed') }, new Map(), 'operator:warehouse', {
|
||||
startReturnReceipt: async () => ({ returnNo: 'RT-TEST', expectedItems: [{ skuCode: 'SKU-TEST', expectedQuantity: 10, receivedQuantity: 0 }], photos: [{ fileId: 'PHOTO' }] }),
|
||||
downloadReturnPalletPhoto: async () => 'photo-url'
|
||||
})
|
||||
page.unitType = 'PARCEL'; page.unitCode = ''; page.photoFileIds = ['PHOTO']
|
||||
page.boxRows = [physicalRow({ 'SKU-TEST': 2 })]
|
||||
await page.confirmReceipt(); await page.reloadCurrent()
|
||||
assert.equal(page.boxRows[0].items['SKU-TEST'], 2)
|
||||
assert.equal(page.busy, false)
|
||||
})
|
||||
|
||||
test('partial receipt continues the same order with fresh pallet measurements', async () => {
|
||||
const page = setup(async () => ({}))
|
||||
page.finishReceipt([{ fullyMatched: false }])
|
||||
@@ -266,7 +314,7 @@ test('partial receipt continues the same order with fresh pallet measurements',
|
||||
assert.equal(page.palletWeight, '')
|
||||
})
|
||||
|
||||
test('PALLET response failure keeps the draft and same idempotency key for retry', async () => {
|
||||
test('PALLET response failure keeps editable input and generates a new key for the next click', async () => {
|
||||
const calls = []
|
||||
const page = setup(async payload => { calls.push(JSON.parse(JSON.stringify(payload))); throw new Error('Response lost') })
|
||||
await page.confirmReceipt()
|
||||
@@ -275,7 +323,7 @@ test('PALLET response failure keeps the draft and same idempotency key for retry
|
||||
assert.equal(page.error.message, 'Response lost')
|
||||
await page.confirmReceipt()
|
||||
assert.equal(calls.length, 2)
|
||||
assert.equal(calls[0].idempotencyKey, calls[1].idempotencyKey)
|
||||
assert.notEqual(calls[0].idempotencyKey, calls[1].idempotencyKey)
|
||||
})
|
||||
|
||||
test('continue signing returns to SKU scanning and accepts a fresh scan', () => {
|
||||
|
||||
Reference in New Issue
Block a user