fix(pda): display physical remaining cartons after partial picks

This commit is contained in:
2026-10-02 13:47:41 +08:00
parent 8d2d80ec11
commit 7aebbd2a70
3 changed files with 62 additions and 3 deletions
+42
View File
@@ -5,6 +5,8 @@ import { readFile } from 'node:fs/promises'
const source = await readFile(new URL('../pages/picking/picking.vue', import.meta.url), 'utf8')
const script = source.match(/<script>([\s\S]*?)<\/script>/)[1]
const stateSource = await readFile(new URL('../utils/picking-state.js', import.meta.url), 'utf8')
const pickingState = await import(`data:text/javascript;base64,${Buffer.from(stateSource).toString('base64')}`)
const noop = () => {}
const deferred = () => { let resolve, reject; const promise = new Promise((yes, no) => { resolve = yes; reject = no }); return { promise, resolve, reject } }
function mount(overrides = {}, state = {}) {
@@ -37,6 +39,46 @@ function mount(overrides = {}, state = {}) {
return { page, ctx }
}
test('real scan handlers display remaining 2→1→1→0, preserve pending semantics and avoid duplicate POST', async () => {
let posts = 0
const messages = []
const { page, ctx } = mount({ ...pickingState, pickContainerItem: async () => {
posts++
return { version: posts + 1, items: [{ id: 'item', plannedPackageCount: 2, pickedPackageCount: posts,
itemStatus: posts === 2 ? 'PICKED' : 'PICKING' }] }
} }, { scannedBoxCodes: {}, requirePickingOperation: () => true, recordScanResult: (...args) => messages.push(args) })
ctx.currentBox.items[0].boxCodes = ['BOX-1', 'BOX-2']
ctx.currentBox.items[0].plannedPackageCount = 2
ctx.currentBox.items[0].pickedPackageCount = 0
const remaining = () => page.computed.remainingCartons.call(ctx)
assert.equal(remaining(), 2)
await ctx.processScannedValue('BOX-1')
assert.equal(remaining(), 1)
assert.equal(page.computed.summary.call(ctx).pending, 0)
await ctx.processScannedValue('BOX-1')
assert.equal(remaining(), 1); assert.equal(posts, 1)
assert.equal(messages.at(-1)[3], '该条码已部分拣货,无需重复处理')
await ctx.processScannedValue('BOX-2')
assert.equal(remaining(), 0); assert.equal(posts, 2)
assert.match(source, /submit-pending-number">\{\{ remainingCartons \}\}/)
})
test('physical remaining distinguishes skipped and partial from business pending and trusts server counts', () => {
const items = [{ id: 'partial', qty: 4, pickedPackageCount: 1 }, { id: 'skip', qty: 3, pickedPackageCount: 0 }]
const states = { partial: pickingState.ITEM_STATUS.PARTIAL, skip: pickingState.ITEM_STATUS.SKIPPED }
const quantities = { partial: 1 }
const before = JSON.stringify({ items, states, quantities })
const summary = pickingState.getPickSummary(items, states, quantities)
assert.equal(summary.pending, 0); assert.equal(summary.partial, 1); assert.equal(summary.skipped, 3)
assert.equal(pickingState.remainingPhysicalCartons(items, states, quantities), 6)
assert.equal(pickingState.getPickSummary(items, states, quantities).canSubmit, summary.canSubmit)
assert.equal(JSON.stringify({ items, states, quantities }), before)
assert.equal(pickingState.remainingPhysicalCartons([{ id: 'partial', qty: 4, pickedPackageCount: 1 }],
{ partial: pickingState.ITEM_STATUS.PICKED }, { partial: 4 }), 3)
assert.equal(pickingState.remainingPhysicalCartons([{ id: 'partial', qty: 4 }], states, quantities), 3)
assert.equal(pickingState.remainingPhysicalCartons([{ id: 'skip', qty: 3 }], states, quantities), 3)
})
test('double confirmation and direct reentry send exactly one snapshot and one batch', async () => {
const snapshot = deferred(), batch = deferred(); let snapshots = 0, batches = 0
const { ctx } = mount({ submitContainerPickingBatch: async () => { batches++; return batch.promise } }, {