fix(lastleg): reconfirm received bookings after releasing all own cargo

This commit is contained in:
2026-10-18 19:53:35 +08:00
parent 2c330bf9df
commit 45fbd6eff8
7 changed files with 139 additions and 13 deletions
@@ -188,8 +188,13 @@ function mixedFixture(fault = '') {
const samples = [1, 2].map(i => ({ displayKind: i === 1 ? 'ORDINARY' : 'EXTERNAL', dispatchNo: i === 1 ? 'DP-N' : 'DP-E',
ordinaryInboundCode: `IN-N${i}`, externalInboundCode: `IN-E${i}`, bookingNo: `BK${i}`, palletId: `30${i}` }))
const ctx = context((method, path) => {
if (path.includes('/inventory/balances') && !path.includes('/positions')) return ok({ total: 4,
records: [1, 2].flatMap(i => ['N', 'E'].map(kind => ({ id: `${kind}${i}`, customerId: 'C', overseasWarehouseId: 'W', inboundCode: `IN-${kind}${i}` }))) })
if (path.includes('/inventory/balances') && !path.includes('/positions')) {
const inboundCode = new URL(path, 'https://test.invalid').searchParams.get('inboundCode')
const all = [1, 2].flatMap(i => ['N', 'E'].map(kind => ({ id: `${kind}${i}`, customerId: 'C', overseasWarehouseId: 'W', inboundCode: `IN-${kind}${i}` })))
// Without source filtering equal-SKU groups expose only the first source's representative balance IDs.
const records = inboundCode ? all.filter(row => row.inboundCode === inboundCode) : all.filter(row => row.id.endsWith('1'))
return ok({ total: records.length, records })
}
if (path.includes('/positions')) {
const balance = path.match(/balances\/([NE][12])\//)[1], kind = balance[0], i = balance[1]
return ok([{ id: `POS-${balance}`, customerId: 'C', overseasWarehouseId: 'W', inboundCode: `IN-${balance}`,
@@ -208,6 +213,7 @@ test('independent mixed display permutations prove per-source packages and comme
const f = mixedFixture(), result = await execute(f.ctx, { mixedSources: f.fixture })
assert.equal(result.cases.mixedSources.status, 'PASS')
assert.deepEqual(result.cases.mixedSources.evidence.map(x => x.expectedSources.map(y => y.packages)), [[3, 2], [3, 2]])
assert.deepEqual(f.ctx.calls.filter(x => x.path.includes('/inventory/balances?')).map(x => new URL(x.path, 'https://test.invalid').searchParams.get('inboundCode')), ['IN-N1', 'IN-E1', 'IN-N2', 'IN-E2'])
})
for (const fault of ['duplicate-sample', 'wrong-display', 'wrong-component', 'wrong-quantity', 'wrong-amount']) test(`mixed source ${fault} cannot pass`, async () => {
const f = mixedFixture(fault)
@@ -309,8 +309,11 @@ export async function verifyDispatchRepair(context, fixtures, { execute = false
const displayed = current.items?.find(row => id(row.warehousePalletId) === id(sample.palletId))
const expectedDisplay = sample.displayKind === 'ORDINARY' ? normal.key : external.key
must(displayed?.inboundCode === expectedDisplay && displayed.palletNo && id(current.overseasWarehouseId) === id(f.warehouseId), 'Displayed source does not match its independently produced ordinary/external origin')
const balances = (await pages(`${BASE}/inventory/balances?${new URLSearchParams({ customerId: f.customerId, overseasWarehouseId: f.warehouseId })}`))
.filter(row => id(row.customerId) === id(f.customerId) && id(row.overseasWarehouseId) === id(f.warehouseId))
// The list groups equal SKUs across inbounds; constrain each real source before taking representative balance IDs.
const sourceBalances = []
for (const inboundCode of [normal.key, external.key]) sourceBalances.push(...await pages(`${BASE}/inventory/balances?${new URLSearchParams({ customerId: f.customerId, overseasWarehouseId: f.warehouseId, inboundCode })}`))
const balances = [...new Map(sourceBalances.filter(row => id(row.customerId) === id(f.customerId)
&& id(row.overseasWarehouseId) === id(f.warehouseId)).map(row => [id(row.id), row])).values()]
const quantities = new Map(), positions = []
for (const balance of balances) {
const rows = await call('GET', `${BASE}/inventory/balances/${encodeURIComponent(id(balance.id))}/positions?${new URLSearchParams({ customerId: f.customerId, palletNo: displayed.palletNo })}`)