This commit is contained in:
2026-10-22 11:56:47 +08:00
parent 5d71af1ac9
commit de96e47d74
71 changed files with 1296 additions and 332 deletions
+12 -26
View File
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'
import { createHash } from 'node:crypto'
import { AcceptanceRun, parseArguments, validateExecutionOptions } from './admin-test-env-acceptance.mjs'
import { createPicker, disablePicker, resolvePickerAccess } from './admin-test-env-modules/phase4-overseas-pda.mjs'
import { pickAll, saveReceiptAndComplete, arrangeDispatchDelivery, verifyDepartureReadiness,
import { pickAll, saveReceiptAndComplete, readAutomaticDispatchDelivery, verifyAutomaticDeparture,
dispatchInventorySources, inventorySnapshot, dispatchStockConservation, inventorySourcesPresent,
relabelOutboundMarksMatch } from './admin-test-env-modules/phase5-overseas-fulfillment.mjs'
import { prepareFleetTrip, confirmFleetLoading, finishFleetTrip } from './admin-test-env-modules/fleet-trip-fulfillment.mjs'
@@ -124,7 +124,7 @@ async function ledger(context, scope, dispatchNo) {
return rows.map(row => ({ ...row, id: id(row.id), customerId: id(row.customerId), overseasWarehouseId: id(row.overseasWarehouseId),
beforePackages: Number(row.beforePackages), deltaPackages: Number(row.deltaPackages), afterPackages: Number(row.afterPackages) })).sort((a, b) => a.id.localeCompare(b.id))
}
const NATIVE_DEPS = { prepareFleetTrip, pickAll, confirmFleetLoading, arrangeDispatchDelivery, verifyDepartureReadiness,
const NATIVE_DEPS = { prepareFleetTrip, pickAll, confirmFleetLoading, readAutomaticDispatchDelivery, verifyAutomaticDeparture,
saveReceiptAndComplete, finishFleetTrip, inventorySnapshot, dispatchInventorySources, dispatchStockConservation, ledger }
/** Continue only a proved native dispatch. All mutations remain real public API commands. */
export async function runRetainedNativeDispatch(context, result, scope, pdaToken, overrides = {}) {
@@ -190,15 +190,10 @@ export async function runRetainedNativeDispatch(context, result, scope, pdaToken
detail = await api('GET', path)
}
requireFact(result.inventory?.conservation, 'No original scan quantity/ledger proof for this retained native dispatch')
const recovered = action => result.requests?.find(row => row.status === 'RESPONDED' && String(row.code) === '200' && row.result?.dispatchNo === dispatchNo && row.result?.action === action)?.result
if (!result.transfer) {
const saved = recovered('ARRANGE_DELIVERY')
if (saved) result.transfer = { data: saved, deliveries: saved.result.deliveries, outbounds: saved.result.outbounds }
else {
requireFact(detail.stage === 'AFTER_LOADING', 'Missing original arrangement command; do not borrow existing historical delivery rows')
result.transfer = await deps.arrangeDispatchDelivery(context, dispatchNo)
requireFact(result.transfer, 'Native arrangement failed')
}
requireFact(detail.stage === 'DELIVERING', 'Current dispatch has no verified automatic departure')
result.transfer = await deps.readAutomaticDispatchDelivery(context, dispatchNo)
requireFact(result.transfer, 'Automatic delivery documents failed validation')
await context.checkpoint()
}
const deliveries = result.transfer.deliveries, outbounds = result.transfer.outbounds
@@ -207,24 +202,15 @@ export async function runRetainedNativeDispatch(context, result, scope, pdaToken
&& row.outboundNo === outbounds[0].outboundNo) && deliveries.reduce((sum, row) => sum + Number(row.packageCount), 0) === scope.expectedPackages,
'Native arrangement does not conserve the retained customer and physical quantity')
result.outboundNo = outbounds[0].outboundNo; result.deliveryNos = deliveries.map(row => row.deliveryNo)
if (!result.departure) result.departure = recovered('DEPART')
if (detail.stage === 'AFTER_LOADING') {
const ready = await deps.verifyDepartureReadiness(context, pdaToken, dispatchNo)
requireFact(ready && validVersion(ready.data.recordVersion), 'Native server DEPART gates unavailable')
result.departBody ||= { recordVersion: ready.data.recordVersion, idempotencyKey: `${scope.marker}-DEPART`.slice(0, 128), remark: `${scope.marker}-TEST` }
if (!result.departure) {
const verified = await deps.verifyAutomaticDeparture(context, pdaToken, dispatchNo)
requireFact(verified, 'Automatic departure has not converged on both endpoints')
result.departure = verified.data
await context.checkpoint()
result.departure = await api('POST', `${PDA}/${encodeURIComponent(dispatchNo)}/depart`, result.departBody, pdaToken)
const replay = await api('POST', `${PDA}/${encodeURIComponent(dispatchNo)}/depart`, result.departBody, pdaToken)
requireFact(replay.replayed === true && String(replay.recordVersion) === String(result.departure.recordVersion)
&& same(replay.result, result.departure.result), 'Departure original command replay drift')
result.departureReplay = replay
detail = await api('GET', path)
}
requireFact(result.departure?.stage === 'DELIVERING' && result.departure?.status === 'ACTIVE'
&& Number(result.departure.result?.shippedPackages) === scope.expectedPackages
&& same(sorted(result.departure.result?.deliveries?.map(row => `${id(row.customerId)}:${row.outboundNo}:${row.deliveryNo}`) || []),
sorted(deliveries.map(row => `${id(row.customerId)}:${row.outboundNo}:${row.deliveryNo}`)))
&& ['DELIVERING', 'DELIVERED'].includes(detail.stage), 'Original native departure receipt missing or invalid')
requireFact(result.departure?.dispatchNo === dispatchNo && result.departure.stage === 'DELIVERING'
&& result.departure.status === 'ACTIVE' && Number(result.departure.counts?.shippedPackageCount) === scope.expectedPackages
&& ['DELIVERING', 'DELIVERED'].includes(detail.stage), 'Original automatic departure evidence missing or invalid')
result.inventory.stockAfterDepart = await deps.inventorySnapshot(context, id(scope.customerId), id(scope.warehouseId), scope.inboundCodes, 'retained-after-depart', result.inventory.sourceBalanceIds)
result.inventory.ledgerAfterDepart = await deps.ledger(context, scope, dispatchNo)
requireFact(same(result.inventory.stockAfter, result.inventory.stockAfterDepart)