优化
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { confirmCargoDiscard } from '../src/api/fulfillment.js'
|
||||
import { session } from '../src/stores/session.js'
|
||||
|
||||
const rentPath = '/api/pda/v1/last-leg/storage-rent/preparations'
|
||||
const stockPath = '/api/pda/v1/last-leg/cargo-discards/CD%2F1/pallets/P%2F1/confirm'
|
||||
const body = { lines: [{ itemId: '9007199254740993', actualPackages: 2 }], photoFileIds: ['101'], scannedCartonMarks: ['BOX-1', 'BOX-2'], confirmAllCartons: false, idempotencyKey: 'discard-key' }
|
||||
const ready = { data: { preparationId: '9007199254740995', ready: true, status: 'READY' } }
|
||||
const confirmed = { data: { discardNo: 'CD/1', palletNo: 'P/1', status: 'COMPLETED' } }
|
||||
const pending = { data: { preparationId: '9007199254740995', ready: false, status: 'FREEZE_PENDING' } }
|
||||
const submit = () => confirmCargoDiscard('CD/1', 'P/1', body.lines, body.photoFileIds, body.scannedCartonMarks, false, { idempotencyKey: body.idempotencyKey })
|
||||
|
||||
function transport(responses) {
|
||||
const calls = []; session.apiBase = '/api'; session.token = 'overseas-token'
|
||||
globalThis.uni = { request(options) {
|
||||
calls.push({ method: options.method, url: options.url, data: options.data })
|
||||
const response = responses.shift(); assert.ok(response, 'unexpected additional request')
|
||||
if (response.network) return options.fail({ errMsg: 'network down' })
|
||||
options.success({ statusCode: 200, data: { code: '200', ...response }, header: {} })
|
||||
} }
|
||||
return calls
|
||||
}
|
||||
|
||||
test('出库先预校验仓租,再用相同明细、照片、箱号和幂等键确认一次', async () => {
|
||||
const calls = transport([ready, confirmed]); await submit()
|
||||
assert.deepEqual(calls, [
|
||||
{ method: 'POST', url: rentPath, data: { mode: 'DISCARD', businessNo: 'CD/1', palletNo: 'P/1', ...body } },
|
||||
{ method: 'POST', url: stockPath, data: body }
|
||||
])
|
||||
})
|
||||
|
||||
test('免费或不计仓租的服务端资格可以直接继续出库', async () => {
|
||||
const calls = transport([{ data: { status: 'NOT_REQUIRED', ready: true, amount: 0 } }, confirmed])
|
||||
await submit(); assert.equal(calls.length, 2); assert.equal(calls[1].url, stockPath)
|
||||
})
|
||||
|
||||
test('等待财务回执期间只读状态,就绪后只提交一次出库', async t => {
|
||||
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
|
||||
const calls = transport([pending, ready, confirmed]); await submit()
|
||||
assert.deepEqual(calls.map(call => call.method), ['POST', 'GET', 'POST'])
|
||||
assert.equal(calls[1].url, rentPath + '/9007199254740995')
|
||||
})
|
||||
|
||||
test('余额授信不足时显示财务原因,释放未使用预占且不发出库请求', async () => {
|
||||
const calls = transport([{ data: { ...pending.data, status: 'FAILED', message: '可用余额和授信额度不足' } }, { data: {} }])
|
||||
await assert.rejects(submit(), error => error.code === 'A0409' && /仓租校验未通过.*余额和授信额度不足/.test(error.message))
|
||||
assert.deepEqual(calls.map(call => call.method), ['POST', 'DELETE'])
|
||||
})
|
||||
|
||||
test('缺失、失败或断网的预校验不降级绕过,保留诊断信息', async () => {
|
||||
for (const response of [{ code: 'A0404', message: 'missing', requestId: 'req', traceId: 'trace' }, { code: 'A0409', message: 'conflict' }, { network: true }]) {
|
||||
const calls = transport([response])
|
||||
await assert.rejects(submit(), error => response.network ? error.network : error.code === response.code && error.requestId === response.requestId && error.traceId === response.traceId)
|
||||
assert.equal(calls.length, 1)
|
||||
}
|
||||
})
|
||||
|
||||
test('回执超时释放预占,阻止出库并提示稍后重新提交', async t => {
|
||||
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
|
||||
const calls = transport([pending, ...Array(20).fill(pending), { data: {} }])
|
||||
await assert.rejects(submit(), /仓租资金预校验尚未通过/)
|
||||
assert.equal(calls.filter(call => call.method === 'GET').length, 20)
|
||||
assert.equal(calls.at(-1).method, 'DELETE')
|
||||
assert.ok(calls.every(call => call.url.includes('/storage-rent/')))
|
||||
})
|
||||
|
||||
test('状态查询失败仍释放原预占;释放失败不覆盖原始诊断', async t => {
|
||||
t.mock.method(globalThis, 'setTimeout', callback => { callback(); return 0 })
|
||||
const calls = transport([pending, { code: 'S0503', message: 'read failed', requestId: 'req', traceId: 'trace' }, { code: 'S0503', message: 'release failed' }])
|
||||
await assert.rejects(submit(), error => error.message === 'read failed' && error.requestId === 'req' && error.traceId === 'trace')
|
||||
assert.deepEqual(calls.map(call => call.method), ['POST', 'GET', 'DELETE'])
|
||||
})
|
||||
|
||||
test('就绪标记必须为布尔真,异常响应不能放行出库', async () => {
|
||||
const calls = transport([{ data: { ...ready.data, ready: 'true' } }, { data: {} }])
|
||||
await assert.rejects(submit(), /预校验/); assert.equal(calls.at(-1).method, 'DELETE')
|
||||
})
|
||||
|
||||
test('出库请求已发送后,业务错误或结果不明不重发、不取消原预占', async () => {
|
||||
for (const failure of [{ code: 'A0409', message: 'stock changed' }, { network: true }]) {
|
||||
const calls = transport([ready, failure])
|
||||
await assert.rejects(submit(), error => failure.network ? error.network : error.code === failure.code)
|
||||
assert.equal(calls.length, 2); assert.ok(calls.every(call => call.method === 'POST'))
|
||||
}
|
||||
})
|
||||
@@ -24,7 +24,6 @@ const modes = [
|
||||
{ name: 'dispatch', path: '/dispatches/DSP%2F1/items/9007199254740995/pick', body: dispatch, submit: () => pickDispatchItem('DSP/1', '9007199254740995', dispatch) },
|
||||
{ name: 'outbound', path: '/outbound-tasks/TASK%2F1/pick', body: outbound, submit: () => pickOutboundTask('TASK/1', outbound) },
|
||||
{ name: 'pallet', path: '/outbound-tasks/TASK%2F1/pallets/DP%2F1/pick', body: pallet, submit: () => pickOutboundPallet('TASK/1', 'DP/1', pallet) },
|
||||
{ name: 'discard', path: '/cargo-discards/CD%2F1/pallets/P%2F1/confirm', body: discard, submit: () => confirmCargoDiscard('CD/1', 'P/1', discard.lines, discard.photoFileIds, discard.scannedCartonMarks, false, { idempotencyKey: discard.idempotencyKey }) }
|
||||
]
|
||||
|
||||
for (const mode of modes) {
|
||||
|
||||
@@ -9,7 +9,7 @@ function page(overrides = {}) {
|
||||
const deps = { PortalHeader: {}, ScanTargetInput: {}, session: { currentUser: {} }, fulfillment: { outbound: { packages: 1, currentTaskNo: 'OLD-TASK' } },
|
||||
updateFulfillmentDraft() {}, fulfillmentIdempotencyKey: () => ({ value: 'key' }),
|
||||
playErrorSound() {}, playSuccessSound() {}, pickOutboundPallet: async () => calls.push('pick'), completeOutboundTask: async () => calls.push('complete'),
|
||||
listCargoDiscardPhotos: async () => [], getOutboundTask: async () => ({ taskNo: 'TASK', status: 'COMPLETED', items: [] }), normalizeCargoDiscardTask: value => value,
|
||||
listCargoDiscardPhotos: async () => [], getOutboundTask: async () => ({ taskNo: 'TASK', status: 'COMPLETED', items: [] }), normalizeCargoDiscardTask: value => value, resolveCargoDiscardPallet: async code => vm.discardTasks.find(task => task.palletNo === code || task.cartonMarks?.includes(code)),
|
||||
...overrides }
|
||||
const definition = new Function(...Object.keys(deps), source.replace(/^\s*import .*$/gm, '').replace('export default', 'return'))(...Object.values(deps))
|
||||
const vm = definition.data()
|
||||
@@ -79,10 +79,13 @@ test('同单按箱废弃允许扫描当前托盘及其他托盘所选箱唛,
|
||||
await vm.resolveDiscardPallet('P-1')
|
||||
assert.equal(vm.error, null)
|
||||
await vm.resolveDiscardPallet('BOX-2')
|
||||
assert.equal(vm.discardTask.palletNo, 'P-1')
|
||||
await vm.resolveDiscardPallet('P-2')
|
||||
await vm.resolveDiscardPallet('BOX-2')
|
||||
assert.equal(vm.discardTask.palletNo, 'P-2')
|
||||
assert.deepEqual(vm.discardScannedMarks, ['BOX-2'])
|
||||
await vm.resolveDiscardPallet('BOX-3')
|
||||
assert.deepEqual(requests, ['P-1', 'BOX-2'])
|
||||
assert.deepEqual(requests, ['P-1', 'P-2'])
|
||||
assert.equal(vm.discardTask.palletNo, 'P-2')
|
||||
assert.deepEqual(vm.discardScannedMarks, ['BOX-2'])
|
||||
})
|
||||
@@ -206,6 +209,19 @@ test('无箱唛对应托盘扫码后实际数保持未填,必须由仓库实
|
||||
assert.equal(vm.canConfirmDiscard, false)
|
||||
})
|
||||
|
||||
test('无箱号任务先扫托盘,实际箱数保持未填供现场实点', async () => {
|
||||
const { vm } = page()
|
||||
vm.discardTasks = [{ discardNo: 'CD-NO-BOX', palletNo: 'P-1', status: 'PENDING', scanMode: 'PALLET', cartonMarks: [], skus: [{ itemId: 'sku', plannedPackages: 2, actualQuantityEditable: true }] }]
|
||||
vm.openDiscardOrder('CD-NO-BOX'); await Promise.resolve()
|
||||
assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('P-1')
|
||||
assert.equal(vm.discardTask.palletNo, 'P-1')
|
||||
assert.equal(vm.discardActuals.sku, '')
|
||||
vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
assert.equal(vm.canConfirmDiscard, false)
|
||||
vm.discardActuals.sku = 2; assert.equal(vm.canConfirmDiscard, true)
|
||||
})
|
||||
|
||||
test('discard retry stays in the discard workflow instead of restoring an old outbound task', async () => {
|
||||
const { vm } = page(); const calls = []
|
||||
vm.discardCurrent = { discardNo: 'DISCARD' }; vm.discardPalletCode = 'PALLET'
|
||||
@@ -224,11 +240,12 @@ test('closing or switching discard order ignores the previous resolve response',
|
||||
})
|
||||
|
||||
|
||||
test('loose disposal opens all marks, blocks partial scans and accepts explicit bulk confirmation', async () => {
|
||||
test('loose disposal opens all marks and requires every carton scan', async () => {
|
||||
const { vm } = page()
|
||||
vm.discardTasks = [{ discardNo: 'CD-L', palletNo: '散箱', scanMode: 'CARTON', cartonMarks: ['BOX-1', 'BOX-2'], status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 2 }] }]
|
||||
vm.openDiscardOrder('CD-L')
|
||||
await Promise.resolve()
|
||||
assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('BOX-1')
|
||||
assert.equal(vm.discardTask.scanMode, 'CARTON')
|
||||
assert.deepEqual(vm.discardTask.cartonMarks, ['BOX-1', 'BOX-2'])
|
||||
vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
@@ -240,7 +257,7 @@ test('loose disposal opens all marks, blocks partial scans and accepts explicit
|
||||
assert.equal(vm.canConfirmDiscard, true)
|
||||
vm.discardActuals.sku = 1; assert.equal(vm.canConfirmDiscard, false)
|
||||
vm.discardActuals.sku = 2; vm.discardScannedMarks = []; vm.discardAllCartons = true
|
||||
assert.equal(vm.canConfirmDiscard, true)
|
||||
assert.equal(vm.canConfirmDiscard, false)
|
||||
vm.discardPhotos = []; assert.equal(vm.canConfirmDiscard, false)
|
||||
})
|
||||
|
||||
@@ -250,6 +267,8 @@ test('partial bound pallet confirmation sends only selected marks and preserves
|
||||
const { vm } = page({ confirmCargoDiscard: async (...args) => { payload = args }, listCargoDiscardTasks: async () => [] })
|
||||
vm.discardTasks = [{ discardNo: 'CD-P', outboundNo: 'OB-P', palletNo: 'P-1', scanMode: 'CARTON', cartonMarks: ['BOX-2'], status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 1 }] }]
|
||||
vm.openDiscardOrder('CD-P'); await Promise.resolve()
|
||||
assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('P-1')
|
||||
vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
await vm.resolveDiscardPallet('BOX-2'); assert.equal(vm.canConfirmDiscard, true)
|
||||
await vm.resolveDiscardPallet('BOX-OTHER'); assert.deepEqual(vm.discardScannedMarks, ['BOX-2']); assert.equal(vm.discardTask.palletNo, 'P-1')
|
||||
@@ -267,6 +286,7 @@ test('discard confirmation preserves inputs after stock command failure and star
|
||||
})
|
||||
vm.discardCurrent = { discardNo: 'CD-RETRY' }
|
||||
vm.discardTask = { palletNo: 'P-1', status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
vm.discardTasks = [{ ...vm.discardTask, discardNo: 'CD-RETRY' }]
|
||||
vm.discardActuals = { sku: 1 }; vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
await vm.confirmDiscard()
|
||||
assert.equal(vm.discardBusy, false)
|
||||
@@ -283,3 +303,129 @@ test('discard confirmation preserves inputs after stock command failure and star
|
||||
assert.equal(vm.error, null)
|
||||
assert.match(vm.successMessage, /已完成/)
|
||||
})
|
||||
|
||||
test('dedicated scanner validates carton scope and preserves photos', () => {
|
||||
const { vm } = page()
|
||||
vm.discardTask = { palletNo: 'P-1', scanMode: 'CARTON', cartonMarks: ['BX-1', 'BX-2'], status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 2 }] }
|
||||
vm.discardActuals = { sku: 2 }; vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
assert.deepEqual(vm.discardPendingMarks, ['BX-1', 'BX-2'])
|
||||
assert.deepEqual(vm.discardQuantityLines, [])
|
||||
vm.discardCartonCode = ' BX-1 '; vm.scanDiscardCarton()
|
||||
assert.equal(vm.discardCartonCode, '')
|
||||
vm.scanDiscardCarton('BX-1'); vm.scanDiscardCarton('OTHER')
|
||||
assert.deepEqual(vm.discardScannedMarks, ['BX-1']); assert.equal(vm.canConfirmDiscard, false)
|
||||
assert.deepEqual(vm.discardPendingMarks, ['BX-2'])
|
||||
assert.match(vm.error.message, /箱号不属于/)
|
||||
vm.discardCartonsExpanded = false
|
||||
vm.scanDiscardCarton('BX-2'); assert.equal(vm.canConfirmDiscard, true)
|
||||
assert.deepEqual(vm.discardPendingMarks, [])
|
||||
assert.equal(vm.discardCartonsExpanded, false)
|
||||
assert.deepEqual(vm.discardPhotos, [{ fileId: '101' }]); assert.equal(vm.discardTask.palletNo, 'P-1')
|
||||
vm.discardTask.status = 'COMPLETED'; vm.scanDiscardCarton('OTHER'); assert.equal(vm.error, null)
|
||||
})
|
||||
|
||||
test('bound discard opens only after its pallet scan and summary hides carton numbers', async () => {
|
||||
const { vm } = page()
|
||||
vm.discardTasks = [{ discardNo: 'CD-1', palletNo: 'P-1', status: 'PENDING', scanMode: 'CARTON', cartonMarks: ['BX-1'], skus: [{ itemId: 'sku', plannedPackages: 1 }] }]
|
||||
vm.openDiscardOrder('CD-1'); assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('BX-1'); assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('OTHER'); assert.equal(vm.discardTask, null)
|
||||
await vm.resolveDiscardPallet('P-1'); assert.equal(vm.discardTask.palletNo, 'P-1')
|
||||
assert.deepEqual(vm.discardScannedMarks, [])
|
||||
assert.deepEqual(vm.discardTask.cartonMarks, ['BX-1'])
|
||||
const template = readFileSync(new URL('../src/pages/outbound/outbound.vue', import.meta.url), 'utf8')
|
||||
const summary = template.slice(template.indexOf('本单待废弃托盘'), template.indexOf('<ScanTargetInput name="cargo-discard"'))
|
||||
assert.doesNotMatch(summary, /task.cartonMarks/)
|
||||
})
|
||||
|
||||
test('multi-pallet discard prompts remaining pallet numbers and requires a new pallet scan', async () => {
|
||||
const first = { discardNo: 'CD-M', palletNo: 'P-1', status: 'PENDING', scanMode: 'CARTON', cartonMarks: ['BX-1'], skus: [{ itemId: 'sku1', plannedPackages: 1 }] }
|
||||
const second = { ...first, palletNo: 'P-2', cartonMarks: ['BX-2'], skus: [{ itemId: 'sku2', plannedPackages: 1 }] }
|
||||
const other = { ...first, discardNo: 'CD-OTHER', palletNo: 'P-OTHER' }
|
||||
let completed = 0
|
||||
const { vm } = page({ confirmCargoDiscard: async () => { completed++ }, listCargoDiscardTasks: async () => [{ ...first, status: 'COMPLETED' }, { ...second, status: completed === 2 ? 'COMPLETED' : 'PENDING' }, other] })
|
||||
vm.discardTasks = [first, second, other]; vm.openDiscardOrder('CD-M')
|
||||
await vm.resolveDiscardPallet('P-1'); vm.scanDiscardCarton('BX-1')
|
||||
vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
await vm.confirmDiscard()
|
||||
assert.equal(vm.discardTask, null); assert.equal(vm.discardPalletCode, '')
|
||||
assert.deepEqual(vm.discardPendingTasks.map(task => task.palletNo), ['P-2'])
|
||||
assert.match(vm.successMessage, /P-1.*已完成.*P-2.*继续扫描/)
|
||||
assert.doesNotMatch(vm.successMessage, /P-OTHER|全部/)
|
||||
assert.equal(vm.canConfirmDiscard, false)
|
||||
await vm.resolveDiscardPallet('P-2'); assert.equal(vm.successMessage, '')
|
||||
assert.deepEqual(vm.discardPendingMarks, ['BX-2']); assert.deepEqual(vm.discardPhotos, [])
|
||||
vm.scanDiscardCarton('BX-2'); vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '102' }]
|
||||
await vm.confirmDiscard()
|
||||
assert.deepEqual(vm.discardPendingTasks, []); assert.match(vm.successMessage, /本单全部.*已完成/)
|
||||
})
|
||||
|
||||
test('successful discard followed by refresh failure does not claim the whole order completed', async () => {
|
||||
let confirms = 0
|
||||
const { vm } = page({ confirmCargoDiscard: async () => { confirms++ }, listCargoDiscardTasks: async () => { throw new Error('offline') } })
|
||||
const first = { discardNo: 'CD-M', palletNo: 'P-1', status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
vm.discardTasks = [first, { ...first, palletNo: 'P-2' }]; vm.openDiscardOrder('CD-M')
|
||||
await vm.resolveDiscardPallet('P-1'); vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
await vm.confirmDiscard(); await vm.confirmDiscard()
|
||||
assert.equal(confirms, 1); assert.equal(vm.discardTask, null)
|
||||
assert.match(vm.successMessage, /P-1.*已完成.*刷新失败/); assert.doesNotMatch(vm.successMessage, /全部/)
|
||||
assert.deepEqual(vm.discardPendingTasks.map(task => task.palletNo), ['P-2'])
|
||||
})
|
||||
|
||||
test('仓租余额不足显示原因并保留当前托盘、已扫箱号、实际数与现场照片', async () => {
|
||||
const { vm } = page({ confirmCargoDiscard: async () => { throw Object.assign(new Error('仓租校验未通过:可用余额和授信额度不足'), { code: 'A0409' }) } })
|
||||
vm.discardCurrent = { discardNo: 'CD-1' }
|
||||
vm.discardTask = { palletNo: 'P-1', scanMode: 'CARTON', status: 'PENDING', cartonMarks: ['BX-1'], skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
vm.discardActuals = { sku: 1 }; vm.discardScannedMarks = ['BX-1']
|
||||
vm.discardPhotoReady = true; vm.discardPhotos = [{ fileId: '101' }]
|
||||
await vm.confirmDiscard()
|
||||
assert.match(vm.error.message, /余额和授信额度不足/); assert.equal(vm.error.code, 'A0409')
|
||||
assert.equal(vm.discardTask.palletNo, 'P-1'); assert.deepEqual(vm.discardScannedMarks, ['BX-1'])
|
||||
assert.deepEqual(vm.discardActuals, { sku: 1 }); assert.deepEqual(vm.discardPhotos, [{ fileId: '101' }])
|
||||
assert.equal(vm.successMessage, ''); assert.equal(vm.discardBusy, false)
|
||||
})
|
||||
|
||||
for (const action of ['pallet', 'carton', 'retry']) {
|
||||
test(`出库资金不足后通过 ${action} 重新核对并再次手动提交`, async () => {
|
||||
let submits = 0; let reads = 0; let key = 0; const keys = []
|
||||
const task = { discardNo: 'CD-1', palletNo: 'P-1', status: 'PENDING', scanMode: 'CARTON', cartonMarks: ['BX-1'], skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
const { vm } = page({
|
||||
resolveCargoDiscardPallet: async () => { reads++; return { ...task } },
|
||||
confirmCargoDiscard: async (...args) => { keys.push(args.at(-1).idempotencyKey); if (++submits === 1) throw new Error('可用余额和授信额度不足') },
|
||||
fulfillmentIdempotencyKey: () => ({ value: `key-${++key}` }), listCargoDiscardTasks: async () => []
|
||||
})
|
||||
vm.discardCurrent = { discardNo: 'CD-1' }; vm.discardTask = task; vm.discardTasks = [task]
|
||||
vm.discardActuals = { sku: 1 }; vm.discardScannedMarks = ['BX-1']
|
||||
vm.discardPhotos = [{ fileId: '101' }]; vm.discardPhotoReady = true
|
||||
await vm.confirmDiscard(); assert.equal(vm.discardNeedsRefresh, true)
|
||||
if (action === 'pallet') await vm.resolveDiscardPallet('P-1')
|
||||
else if (action === 'carton') await vm.scanDiscardCarton('BX-1')
|
||||
else await vm.retry()
|
||||
assert.equal(reads, 1); assert.equal(submits, 1); assert.equal(vm.error, null)
|
||||
assert.equal(vm.discardNeedsRefresh, false); assert.deepEqual(vm.discardScannedMarks, ['BX-1'])
|
||||
assert.deepEqual(vm.discardActuals, { sku: 1 }); assert.deepEqual(vm.discardPhotos, [{ fileId: '101' }])
|
||||
await vm.confirmDiscard(); assert.equal(submits, 2); assert.notEqual(keys[0], keys[1])
|
||||
assert.match(vm.successMessage, /全部.*已完成/)
|
||||
})
|
||||
}
|
||||
|
||||
test('结果不明后刷新发现已完成,不再重复出库', async () => {
|
||||
let submits = 0
|
||||
const task = { discardNo: 'CD-1', palletNo: 'P-1', status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
const { vm } = page({ confirmCargoDiscard: async () => { submits++; throw Object.assign(new Error('网络中断'), { network: true }) }, resolveCargoDiscardPallet: async () => ({ ...task, status: 'COMPLETED' }) })
|
||||
vm.discardCurrent = { discardNo: 'CD-1' }; vm.discardTask = task
|
||||
vm.discardActuals = { sku: 1 }; vm.discardPhotos = [{ fileId: '101' }]; vm.discardPhotoReady = true
|
||||
await vm.confirmDiscard(); await vm.confirmDiscard()
|
||||
assert.equal(submits, 1); assert.equal(vm.discardTask.status, 'COMPLETED'); assert.equal(vm.canConfirmDiscard, false)
|
||||
})
|
||||
|
||||
test('失败后刷新仍报错,保留草稿且不再发送出库', async () => {
|
||||
let submits = 0
|
||||
const { vm } = page({ confirmCargoDiscard: async () => { submits++; throw new Error('出库失败') }, resolveCargoDiscardPallet: async () => { throw new Error('任务读取失败') } })
|
||||
vm.discardCurrent = { discardNo: 'CD-1' }; vm.discardTask = { palletNo: 'P-1', status: 'PENDING', skus: [{ itemId: 'sku', plannedPackages: 1 }] }
|
||||
vm.discardActuals = { sku: 1 }; vm.discardPhotos = [{ fileId: '101' }]; vm.discardPhotoReady = true
|
||||
await vm.confirmDiscard(); await vm.confirmDiscard()
|
||||
assert.equal(submits, 1); assert.match(vm.error.message, /任务读取失败/)
|
||||
assert.equal(vm.discardNeedsRefresh, true); assert.equal(vm.discardBusy, false); assert.equal(vm.discardLoading, false)
|
||||
assert.deepEqual(vm.discardPhotos, [{ fileId: '101' }])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user