test(fba): preserve verified fleet and fulfillment acceptance workflows

This commit is contained in:
2026-10-03 06:26:33 +08:00
parent c55bd7f4b6
commit d858357c5d
52 changed files with 4217 additions and 103 deletions
@@ -1,5 +1,6 @@
import { createPicker, disablePicker, resolvePickerAccess } from './phase4-overseas-pda.mjs'
import { run as createMerchant } from './merchant-qualification-fixture.mjs'
import { prepareFleetTrip, confirmFleetLoading, finishFleetTrip } from './fleet-trip-fulfillment.mjs'
const MODULE = 'phase5-overseas-fulfillment'
const SCENARIO = 'phase5-dispatch-outbound-delivery-exception-visit'
@@ -378,7 +379,7 @@ export async function verifyFulfillmentRootStatus(context, { sourceOrderId, cust
const rootDelivered = root.data.statusHistory?.some(row => row.action === 'OVERSEAS_DELIVERED') === true
const expectedCompleted = rootDelivered && pendingChildren.length === 0
const owned = context.createdResources.phase5OverseasFulfillment
if (owned) owned.orderCompletionProof = { sourceOrderId, rootOrderId,
if (owned) owned.orderCompletionProof = { customerId, sourceOrderId, rootOrderId,
tree: { parentOrderId: rootOrderId, parentOrderNo: tree.parentOrderNo, originalPackageCount: Number(tree.originalPackageCount),
cumulativeLoadedPackageCount: loaded, currentRemainderPackageCount: remaining,
children: rows.map(row => ({ orderId: id(row.orderId), orderNo: row.orderNo, sourceOrderId: id(row.sourceOrderId),
@@ -998,6 +999,7 @@ export async function run(context) {
const access = await resolvePickerAccess(context)
const picker = access ? await createPicker(context, 5, warehouseId, access) : null
const dispatchInboundCodes = [...new Set((updated.data?.items || []).map(item => item.inboundCode).filter(nonBlank))]
let tripProof
if (dispatchInboundCodes.length === 0) {
blocker(context, '排车库存数量守恒', '实际排车详情未提供来源入仓码')
await disablePicker(context, picker, 5)
@@ -1022,6 +1024,7 @@ export async function run(context) {
const sourcesBeforeValid = inventorySourcesPresent(stockBeforePick, sourcePositionBalances)
context.assert(MODULE, '扫码前库存完整覆盖任务冻结的每个原位置关联', null, sourcesBeforeValid, `positions=${sourcePositionIds.length}`, OPTIONS)
if (!sourcesBeforeValid || !ledgerBeforePick) return
tripProof = await prepareFleetTrip(context, MODULE, dispatchNo, warehouseId)
const pickedSuccessfully = await pickAll(context, picker.token, dispatchNo)
const stockAfterPick = await inventorySnapshot(context, customerId, warehouseId, dispatchInboundCodes, '扫码及重放后库存', sourceBalanceIds)
const ledgerAfterPick = await shipmentLedger(context, customerId, dispatchNo, '扫码及重放后SHIP流水', 'SHIP', dispatchNo)
@@ -1031,13 +1034,15 @@ export async function run(context) {
ledgerBefore: ledgerBeforePick, ledgerAfter: ledgerAfterPick })
const shippedRows = conservation.newLedger
if (!pickedSuccessfully) return
const tripVersion = await confirmFleetLoading(context, MODULE, tripProof)
const ready = await verifyDepartureReadiness(context, picker.token, dispatchNo)
if (!ready) return
const departBody = { idempotencyKey: key(context, 'DISPATCH-DEPART'), remark: `${context.marker}-depart` }
const departBody = { recordVersion: tripVersion, idempotencyKey: key(context, 'DISPATCH-DEPART').slice(0, 128), remark: `${context.marker}-depart` }
const departed = await pda(context, picker.token, 'POST', 'PDA确认车辆发车并完成真实出库',
`${PDA_DISPATCH_BASE}/${encodeURIComponent(dispatchNo)}/depart`, departBody)
context.assert(MODULE, '发车推进排车DEPARTED并返回实发数量', departed.response,
departed.data?.status === 'DEPARTED' && Number(departed.data?.shippedPackages) > 0,
departed.data?.status === 'IN_TRANSIT' && departed.data?.tripNo === tripProof.tripNo
&& departed.data?.dispatches?.some(item => item.dispatchNo === dispatchNo) && Number(departed.data?.shippedPackages) > 0,
trace(departed.response), OPTIONS)
if (!success(departed.response)) return
const departReplay = await pda(context, picker.token, 'POST', 'PDA发车同键重放',
@@ -1226,6 +1231,7 @@ export async function run(context) {
const returnReceipt = await completeReturnedException(context, { customerId, warehouseId,
exceptionNo: returnExceptionNo, outboundNo })
context.createdResources.phase5OverseasFulfillment.returnReceipt = returnReceipt
context.createdResources.phase5OverseasFulfillment.tripClosure = await finishFleetTrip(context, MODULE, tripProof)
context.createdResources.phase5OverseasFulfillment.finalStatus = returnReceipt
? 'RETURNED_WITH_COMPLETED_VISIT' : 'RETURN_PENDING_WITH_COMPLETED_VISIT'
}