fix(fleet): preserve unknown commercial facts for pooled freight

This commit is contained in:
2026-10-07 19:38:56 +08:00
parent cfdedd8861
commit c216f393a7
14 changed files with 123 additions and 13 deletions
+13 -1
View File
@@ -34,6 +34,7 @@ export const POOL_COVERAGE = Object.freeze([
'receipt-overcapacity-rejected-with-source-unchanged', 'open-offer-bind-replan-requires-explicit-resume',
'bound-cargo-reservation-removed-without-double-count', 'other-sales-write-and-sensitive-trip-denied',
'other-customer-commercial-read-denied-with-source-unchanged',
'pool-unspecified-container-and-palletizing-remain-unknown',
])
export const POOL_REMAINING = Object.freeze([
'out-of-band-assigned-vehicle-drift-not-constructible-through-supported-api',
@@ -62,6 +63,14 @@ export function assertSingleCommercial(before, after) {
&& before.customerId === after.customerId, 'COMMERCIAL_REPLAY_IDENTITY_CHANGED')
}
export function assertPoolUnknownCommercialFacts(businessType, summary) {
requireThat(['LOCAL_LOOSEBOARD', 'US_TRUCK'].includes(businessType) && summary && typeof summary === 'object',
'POOL_COMMERCIAL_SUMMARY_REQUIRED')
requireThat(summary.containerType == null, 'POOL_CONTAINER_TYPE_MUST_REMAIN_UNKNOWN')
if (businessType === 'LOCAL_LOOSEBOARD') requireThat(summary.palletRequired == null, 'POOL_PALLETIZING_SERVICE_MUST_REMAIN_UNKNOWN')
return true
}
export function poolInquiryWrite(inquiry, idempotencyKey) {
return { tripRecordVersion: inquiry.tripRecordVersion, offerRecordVersion: inquiry.offerRecordVersion,
recordVersion: inquiry.recordVersion, customerId: inquiry.customerId, businessType: inquiry.businessType,
@@ -397,6 +406,7 @@ async function poolScenario(context, fixture, { execute = false, readInventory,
const poolMembers = []
for (const [index, order] of proof.orders.entries()) {
const source = await request('GET', businessPath(order))
check('New pool commercial order retains unknown container and palletizing facts', assertPoolUnknownCommercialFacts(order.businessType, source.summary))
check('Pool commercial order has source arrangement and waits for real inbound', source.summary.status === 'WAITING_INBOUND'
&& source.summary.fleetArrangement?.bookingNo === order.bookingNo && !source.summary.fleetArrangement.incomingNo)
const oldStatusPath = businessPath(order).replace('?', '/status?')
@@ -459,6 +469,7 @@ async function poolScenario(context, fixture, { execute = false, readInventory,
proof.covered.push('shared-real-departure-and-signature')
for (const [index, order] of proof.orders.entries()) {
const commercial = await request('GET', businessPath(order))
check('Completed commercial order retains unknown container and palletizing facts', assertPoolUnknownCommercialFacts(order.businessType, commercial.summary))
check('Each business type completes from actual signature', commercial.summary.status === 'COMPLETED'
&& commercial.summary.fleetArrangement?.fulfillmentStage === 'COMPLETED' && Number(commercial.summary.fleetArrangement.signedPackages) === 1)
const member = poolMembers[index], actual = await request('GET', `${API}/delivery-orders/${member.deliveryNo}?customerId=${order.customerId}`)
@@ -467,9 +478,10 @@ async function poolScenario(context, fixture, { execute = false, readInventory,
const merchant = merchantSessions.find(session => session.customerId === order.customerId)
const merchantPath = `/api/merchant/v1/last-leg/${order.businessType === 'US_TRUCK' ? 'us-truck-orders' : 'local-looseboard-orders'}/${encode(order.businessOrderNo)}`
const own = await request('GET', merchantPath, undefined, merchant.accessToken)
check('Client completed order retains unknown container and palletizing facts', assertPoolUnknownCommercialFacts(order.businessType, own.summary))
check('Client sees only own complete commercial arrangement', own.summary.orderNo === order.businessOrderNo && own.summary.fleetArrangement?.businessOrderNo === order.businessOrderNo)
}
proof.covered.push('business-completion-and-fee-uniqueness')
proof.covered.push('business-completion-and-fee-uniqueness', 'pool-unspecified-container-and-palletizing-remain-unknown')
proof.poolMembers = poolMembers; proof.status = 'VERIFIED'
return proof
} catch (error) { proof.status = 'FAILED'; throw error }