test: extend eight-business acceptance flows and offline contracts

This commit is contained in:
2026-10-22 15:41:50 +08:00
parent 86889d7e0e
commit 080ca600e6
33 changed files with 1285 additions and 101 deletions
@@ -47,6 +47,8 @@ export async function fulfillmentEvidenceContext(context) {
await writeFile(`${path}.tmp`, `${JSON.stringify(redact({ ...evidence,
fulfillment: context.createdResources.phase5OverseasFulfillment,
looseboard: context.createdResources.phase4OverseasBusiness?.looseboard,
clientRelabel: context.createdResources.phase4OverseasClientServices?.relabelFulfillment,
pdaRelabel: context.createdResources.phase4OverseasRelabel,
fleetTrips: context.createdResources.fleetTrips }), null, 2)}\n`, 'utf8')
await rename(`${path}.tmp`, path)
}
@@ -844,26 +846,107 @@ export function relabelOutboundMarksMatch(detail, relabel) {
&& Number(detail?.outbound?.packageCount) === expected.length
}
async function verifyRelabelDispatchOutbound(context, relabel, token, warehouseId) {
if (relabel?.marker !== context.marker || !nonBlank(relabel?.dispatchNo)) {
blocker(context, '换标排车箱唛传递前置', '缺少本轮换标排车,不借用历史数据')
return
// 客户原贴标费用只读等待结算回执,不以派送成功推定扣费成功。
export async function verifyClientRelabelSettlement(context, relabel, delay = () => new Promise(resolve => setTimeout(resolve, 1000))) {
if (context.createdResources?.phase4OverseasClientServices?.relabelFulfillment !== relabel
|| relabel?.marker !== context.marker || !nonBlank(relabel.sourceOrderNo)
|| id(relabel.customerId) !== id(context.context?.merchantQualificationFixture?.tenantId)
|| relabel.deliveryExecution?.closure?.status !== 'CARGO_COMPLETED_AND_RESOURCES_RELEASED'
|| !nonBlank(relabel.outboundNo)) throw Error('贴标结算缺少本轮原申请已交付证据')
const evidence = relabel.settlement = { status: 'POLLING', observations: [] }
try {
for (let attempt = 0; attempt < 31; attempt++) {
await context.checkpoint()
const result = await platform(context, 'GET', '客户原贴标申请结算回查',
`${LASTLEG_BASE}/relabel-applications/${encodeURIComponent(relabel.relabelNo)}`)
const application = result.data?.application
const same = success(result.response) && application?.relabelNo === relabel.relabelNo
&& application.customerOrderNo === relabel.sourceOrderNo && id(application.overseasWarehouseId) === id(relabel.warehouseId)
&& Number(application.chargeAmount) === 0.01 && nonBlank(application.chargeCurrency)
&& Boolean(application.customerConfirmedAt)
context.assert(MODULE, '结算回执属于原贴标申请及原费用', result.response, same, `relabelNo=${relabel.relabelNo}`, OPTIONS)
if (!same) throw Error('原贴标申请结算身份或金额不一致')
evidence.observations.push({ walletStatus: application.walletStatus, chargeAmount: application.chargeAmount,
chargeCurrency: application.chargeCurrency, requestId: result.response.requestId, traceId: result.response.traceId })
await context.checkpoint()
if (application.walletStatus === 'SETTLED') { evidence.status = 'SETTLED_HTTP_VERIFIED'; await context.checkpoint(); return evidence }
if (!['FROZEN', 'SETTLE_PENDING'].includes(application.walletStatus)) throw Error(`贴标结算异常状态: ${application.walletStatus}`)
if (attempt < 30) await delay()
}
throw Error('原贴标费用结算回执等待超时')
} catch (error) { evidence.status = 'FAILED'; evidence.failure = error.message; await context.checkpoint(); throw error }
}
// 原换标申请逐车完成,必须等前车释放复用资源,不能只出库不签收。
export async function verifyRelabelDispatchOutbound(context, relabel, token, warehouseId, helpers = {
prepare: prepareFleetTrip, pick: pickAll, load: confirmFleetLoading, read: readAutomaticDispatchDelivery,
sign: saveReceiptAndComplete, finish: finishFleetTrip, settle: verifyClientRelabelSettlement,
}) {
if (relabel?.marker !== context.marker || !nonBlank(relabel?.dispatchNo) || !token || relabel.deliveryExecution
|| ![context.createdResources?.phase4OverseasRelabel, context.createdResources?.phase4OverseasClientServices?.relabelFulfillment].includes(relabel)
|| id(relabel.warehouseId ?? relabel.overseasWarehouseId) !== id(warehouseId)) throw Error('缺少本轮原换标排车、同仓PDA或履约已执行')
const evidence = relabel.deliveryExecution = { status: 'RUNNING', marker: context.marker }
const require = (name, passed) => {
context.assert(MODULE, name, null, Boolean(passed), `relabelNo=${relabel.relabelNo}`, OPTIONS)
if (!passed) throw Error(name)
}
const proof = await prepareFleetTrip(context, MODULE, relabel.dispatchNo, warehouseId)
if (!await pickAll(context, token, relabel.dispatchNo)) return
await confirmFleetLoading(context, MODULE, proof, token, relabel.loadingMeasurements)
const transferred = await readAutomaticDispatchDelivery(context, relabel.dispatchNo)
if (!transferred) return
try {
const proof = await helpers.prepare(context, MODULE, relabel.dispatchNo, warehouseId)
evidence.tripNo = proof.tripNo
require('原换标排车实际扫码完成', await helpers.pick(context, token, relabel.dispatchNo))
await helpers.load(context, MODULE, proof, token, relabel.loadingMeasurements)
const transferred = await helpers.read(context, relabel.dispatchNo)
const customerId = id(relabel.customerId)
const outboundNo = transferred.outbounds.find(row => id(row.customerId) === customerId)?.outboundNo
if (!nonBlank(outboundNo)) { blocker(context, '换标排车客户出库', '全车安排结果缺少本轮换标客户出库单'); return }
require('原换标排车生成唯一客户出库且派送无外来客户', transferred?.outbounds?.length === 1
&& id(transferred.outbounds[0].customerId) === customerId && nonBlank(transferred.outbounds[0].outboundNo)
&& transferred.deliveries?.length > 0 && new Set(transferred.deliveries.map(row => row.deliveryNo)).size === transferred.deliveries.length
&& transferred.deliveries.every(row => id(row.customerId) === customerId && row.outboundNo === transferred.outbounds[0].outboundNo)
&& transferred.deliveries.reduce((sum, row) => sum + Number(row.packageCount), 0) === relabel.relabelCartonMarks.length)
const outboundNo = transferred.outbounds[0].outboundNo
const detail = await platform(context, 'GET', '回查换标排车的真实出库箱唛',
`${LASTLEG_BASE}/outbound-orders/${encodeURIComponent(outboundNo)}`)
context.assert(MODULE, '排车出库快照精确保留全部新箱唛且无原箱唛或重复箱', detail.response,
require('排车出库快照精确保留全部新箱唛且无原箱唛或重复箱',
success(detail.response) && detail.data?.outbound?.outboundNo === outboundNo
&& detail.data?.outbound?.dispatchNo === relabel.dispatchNo
&& relabelOutboundMarksMatch(detail.data, relabel), trace(detail.response), OPTIONS)
&& id(detail.data?.outbound?.customerId) === customerId && detail.data?.outbound?.status === 'OUTBOUNDED'
&& relabelOutboundMarksMatch(detail.data, relabel))
Object.assign(relabel, { outboundNo, deliveryNos: transferred.deliveries.map(row => row.deliveryNo).sort() })
for (const [index, delivery] of transferred.deliveries.entries()) {
const signed = await helpers.sign(context, delivery, customerId, index)
require('原换标货物经正式签收完成派送', signed?.order?.status === 'DELIVERED'
&& signed.order.deliveryNo === delivery.deliveryNo && id(signed.order.customerId) === customerId
&& signed.order.outboundNo === outboundNo)
}
evidence.closure = await helpers.finish(context, MODULE, proof)
require('原换标车签收后车辆司机均已释放', evidence.closure?.status === 'CARGO_COMPLETED_AND_RESOURCES_RELEASED')
if (context.createdResources?.phase4OverseasClientServices?.relabelFulfillment === relabel) {
const settled = await helpers.settle(context, relabel)
require('客户原贴标申请费用已结算', settled?.status === 'SETTLED_HTTP_VERIFIED')
}
evidence.status = 'DELIVERED_HTTP_VERIFIED'
await context.checkpoint()
return evidence
} catch (error) {
evidence.status = 'FAILED'; evidence.failure = error.message
await context.checkpoint(); throw error
}
}
// 主车结案后串行复用同仓PDA及车辆池,客户原申请与旧换标样本分别核验。
async function finishRelabelDeliveries(context, mainTrip, warehouseId) {
if (mainTrip.closure?.status !== 'CARGO_COMPLETED_AND_RESOURCES_RELEASED') throw Error('主车尚未释放,不能启动换标履约')
const access = await resolvePickerAccess(context)
const picker = access ? await createPicker(context, 95, warehouseId, access) : null
try {
if (!picker?.token) throw Error('换标完整履约缺少可复用同仓PDA')
for (const original of [context.createdResources.phase4OverseasRelabel, context.createdResources.phase4OverseasClientServices?.relabelFulfillment]) {
await verifyRelabelDispatchOutbound(context, original, picker.token, warehouseId)
}
} finally {
await disablePicker(context, picker, 95)
if (picker) picker.token = null
await context.checkpoint()
}
}
async function loginMerchant(context, customerId) {
@@ -1144,8 +1227,6 @@ export async function run(context) {
context.assert(MODULE, '发车及重放库存和SHIP零额外扣减', ready.response, unchanged, trace(ready.response), OPTIONS)
if (!unchanged) return
}
// 换标副场景仍核对真实新箱唛快照,执行同一原生装车前置。
await verifyRelabelDispatchOutbound(context, context.createdResources?.phase4OverseasRelabel, picker.token, warehouseId)
} finally {
try { await disablePicker(context, picker, 5) }
finally { if (picker) picker.token = null; await context.checkpoint() }
@@ -1266,6 +1347,7 @@ export async function run(context) {
// 默认验收在正常签收与回访完成后结案。旧异常子场景仅接受调用方明确授权的独立开关。
if (context.context?.phase5TailVerification?.runReturnScenario !== true) {
context.createdResources.phase5OverseasFulfillment.tripClosure = await finishFleetTrip(context, MODULE, tripProof)
await finishRelabelDeliveries(context, tripProof, warehouseId)
await verifyFleetTripFinance(context, MODULE, { tripNo: tripProof.tripNo, dispatchNos: [dispatchNo],
businesses: deliveries.map(row => ({ customerId: id(row.customerId), deliveryNo: row.deliveryNo })), receiptsVisible: true })
context.createdResources.phase5OverseasFulfillment.finalStatus = 'DELIVERED_WITH_COMPLETED_VISIT'
@@ -1315,6 +1397,7 @@ export async function run(context) {
exceptionNo: returnExceptionNo, outboundNo })
context.createdResources.phase5OverseasFulfillment.returnReceipt = returnReceipt
context.createdResources.phase5OverseasFulfillment.tripClosure = await finishFleetTrip(context, MODULE, tripProof)
await finishRelabelDeliveries(context, tripProof, warehouseId)
await verifyFleetTripFinance(context, MODULE, { tripNo: tripProof.tripNo, dispatchNos: [dispatchNo],
businesses: deliveries.map(row => ({ customerId: id(row.customerId), deliveryNo: row.deliveryNo })), receiptsVisible: true })
context.createdResources.phase5OverseasFulfillment.finalStatus = returnReceipt