This commit is contained in:
2026-09-30 14:27:45 +08:00
parent 39af04f169
commit 0919a61661
5 changed files with 423 additions and 7 deletions
@@ -1,5 +1,5 @@
import test from 'node:test'
import { relabelOutboundMarksMatch, inventoryQuantitySnapshot, inventorySnapshot, dispatchInventorySources, inventorySourcesPresent, verifyDepartureReadiness, verifyPartialOrderCompletion,
import { relabelOutboundMarksMatch, inventoryQuantitySnapshot, inventorySnapshot, dispatchInventorySources, inventorySourcesPresent, dispatchStockConservation, verifyDepartureReadiness, verifyPartialOrderCompletion,
completeReturnedException, verifyCustomerSignedDocuments, createSignedDocumentIsolationCustomer } from './phase5-overseas-fulfillment.mjs'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
@@ -476,3 +476,77 @@ test('source snapshot never treats unknown or incompletely proven raw 404 as zer
const context = contextFor(() => { throw new Error('Missing fixed IDs cannot issue HTTP reads') })
assert.equal(await inventorySnapshot(context, '100', 'WH-1', ['IN-A'], 'stock'), null)
})
function representativePalletConservationFixture() {
const quantities = [1, 3, 1, 3, 1]
const codes = ['IN-A', 'IN-A-OTHER', 'IN-B', 'IN-B-OTHER', 'IN-C']
const references = quantities.map((_, index) => ({ balanceId: String(index + 1), positionId: String(index + 11) }))
const groups = packages => [{ id: 'GROUP', customerId: '100', overseasWarehouseId: 'WH-1', skuCode: 'MIXED',
totalPackages: packages, availablePackages: packages, holdPackages: 0, allocatedPackages: 0, pickedPackages: 0,
receivingPackages: 0, inTransitPackages: 0, procurementPackages: 0 }]
const snapshot = after => quantities.map((quantity, index) => {
const balanceId = String(index + 1)
const position = { id: String(index + 11), balanceId, customerId: '100', overseasWarehouseId: 'WH-1',
inboundCode: codes[index], palletNo: index < 2 ? 'P-A' : index < 4 ? 'P-B' : 'P-C',
totalPackages: after ? 0 : quantity, availablePackages: after ? 0 : quantity,
holdPackages: 0, allocatedPackages: 0, pickedPackages: 0, receivingPackages: 0 }
const leftover = { ...position, id: '99', palletNo: 'UNSELECTED', totalPackages: 3, availablePackages: 3 }
const positions = index === 4 ? [position, leftover] : [position]
const exact = positions.filter(row => ['IN-A', 'IN-B', 'IN-C'].includes(row.inboundCode))
const total = positions.reduce((sum, row) => sum + row.totalPackages, 0)
return { id: balanceId, totalPackages: exact.reduce((sum, row) => sum + row.totalPackages, 0),
balance: { id: balanceId, totalPackages: total }, positions, globalGroups: groups(after ? 3 : 12) }
})
const ledger = quantities.map((quantity, index) => ({ id: String(index + 101), customerId: '100',
overseasWarehouseId: 'WH-1', businessType: 'SHIP', businessOrderNo: 'DS-1', outboundOrderNo: 'DS-1',
inboundCode: codes[index], palletNo: index < 2 ? 'P-A' : index < 4 ? 'P-B' : 'P-C',
beforePackages: quantity, deltaPackages: -quantity, afterPackages: 0 }))
return { customerId: '100', warehouseId: 'WH-1', dispatchNo: 'DS-1', expectedPackages: 9,
sourcePositionBalances: references, stockBefore: snapshot(false), stockAfter: snapshot(true),
ledgerBefore: [], ledgerAfter: ledger }
}
test('G05 MIN inbound-code display does not filter real same-pallet sources: positions9→0/raw12→3/groups12→3/SHIP-9', () => {
const fixture = representativePalletConservationFixture()
const context = contextFor(() => { throw new Error('Offline arithmetic cannot issue HTTP') })
context.createdResources.phase5OverseasFulfillment = { inventoryProof: {} }
assert.equal(fixture.stockBefore.reduce((sum, row) => sum + row.totalPackages, 0), 6)
assert.equal(fixture.stockAfter.reduce((sum, row) => sum + row.totalPackages, 0), 3)
const result = dispatchStockConservation(context, fixture)
assert.equal(result.passed, true)
assert.deepEqual(result.proof.totals, { positionBefore: 9, positionAfter: 0, rawBefore: 12, rawAfter: 3,
groupBefore: 12, groupAfter: 3, ledgerShipped: 9, expectedPackages: 9 })
assert.equal(result.proof.ledgerRowChecks.length, 5)
assert.deepEqual(context.assertions.filter(row => !row.passed), [])
const saved = context.createdResources.phase5OverseasFulfillment.inventoryProof
assert.deepEqual(saved.snapshots.beforePick, fixture.stockBefore)
assert.deepEqual(saved.ledgers.afterPick, fixture.ledgerAfter)
assert.ok(JSON.stringify(saved).includes('IN-A-OTHER'))
assert.doesNotMatch(JSON.stringify(saved), /password|accessToken|Bearer|base64/)
})
test('G05 failures still independently review and preserve raw, group, each SHIP quantity/source, and unselected positions', () => {
for (const mutate of [
fixture => { fixture.stockAfter[0].positions[0].id = 'WRONG-POSITION' },
fixture => { fixture.stockAfter[0].balance.totalPackages = 1 },
fixture => { for (const row of fixture.stockAfter) row.globalGroups[0].totalPackages = 4 },
fixture => { fixture.ledgerAfter[0].deltaPackages = -2 },
fixture => { fixture.ledgerAfter[0].customerId = 'OTHER' },
fixture => { fixture.ledgerAfter[0].overseasWarehouseId = 'OTHER' },
fixture => { fixture.ledgerAfter[0].outboundOrderNo = 'OTHER' },
fixture => { fixture.ledgerAfter[0].palletNo = 'UNSELECTED' },
fixture => { fixture.stockAfter[4].positions[1].availablePackages = 2 },
]) {
const fixture = representativePalletConservationFixture(); mutate(fixture)
const context = contextFor(() => { throw new Error('Offline only') })
context.createdResources.phase5OverseasFulfillment = { inventoryProof: {} }
const result = dispatchStockConservation(context, fixture)
assert.equal(result.passed, false)
assert.equal(context.assertions.length, 9)
assert.equal(Object.keys(result.proof.checks).length, 8)
assert.equal(result.proof.ledgerRowChecks.length, 5)
assert.deepEqual(context.createdResources.phase5OverseasFulfillment.inventoryProof.snapshots.afterPick, fixture.stockAfter)
assert.deepEqual(context.createdResources.phase5OverseasFulfillment.inventoryProof.ledgers.afterPick, fixture.ledgerAfter)
}
})