优化
This commit is contained in:
+28
-79
@@ -1,7 +1,7 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { verifyFleetExternalDispatch, nativeArrangementGroups } from './fleet-external-dispatch-acceptance.mjs'
|
||||
import { verifyFleetExternalDispatch } from './fleet-external-dispatch-acceptance.mjs'
|
||||
|
||||
const BASE = '/api/platform/v1/last-leg/dispatches', PDA = '/api/pda/v1/last-leg/dispatches', TRIP = '/api/platform/v1/last-leg/fleet/trips/TR-CURRENT'
|
||||
const OWN = '900719925474099301', EXT = '900719925474099302', clone = value => structuredClone(value)
|
||||
@@ -21,12 +21,13 @@ function fixture(fault = '', nativeMode = false) {
|
||||
const tripProof = { tripNo: 'TR-CURRENT', dispatchNo: 'DS-OWN', warehouseId: '92', vehicleId: '71', driverId: '72', stops: [{ index: 0, destinationKey: 'ORIGIN:92' }, { index: 1, destinationKey: 'FBA:A1' }, { index: 2, destinationKey: 'FBA:A2' }],
|
||||
externalReceipt: { marker: 'CURRENT', status: 'VERIFIED', incomingNo: 'WT-CURRENT', bookingNo: 'BK-CURRENT', warehousePalletId: '33', palletNo: 'PAL-3', createRequest: { customerId: externalCustomer }, finalSegments: [segment], finalBooking: { cargo: bookingCargo, agreedAmount: 30 } } }
|
||||
if (nativeMode) tripProof.stops.pop()
|
||||
let recordVersion = 0, fleetVersion = 0, stage = fault === 'already-loaded' ? 'AFTER_LOADING' : 'LOADING', bound = false, planned = false, review = true, saved
|
||||
let recordVersion = 0, fleetVersion = 0, stage = fault === 'already-loaded' ? 'DELIVERING' : 'LOADING', bound = false, planned = false, review = true, saved
|
||||
const booking = () => ({ bookingNo: 'BK-CURRENT', customerId: externalCustomer, status: bound ? 'BOUND' : 'RECEIVED', incomingNo: 'WT-CURRENT', dispatchNo: bound ? 'DS-OWN' : null, recordVersion: bound ? 1 : 0, cargo: bookingCargo, agreedAmount: 30, receipt: accepted })
|
||||
const counts = rows => ({ itemCount: rows.length, palletCount: rows.length, packageCount: rows.reduce((n, i) => n + i.scheduledPackages, 0), pickedPackageCount: rows.reduce((n, i) => n + i.pickedPackages, 0), shippedPackageCount: 0 })
|
||||
const detail = (full = false) => ({ dispatchNo: full ? 'DS-FULL' : 'DS-OWN', recordVersion: full ? 0 : recordVersion, status: 'ACTIVE', stage: full ? 'AFTER_LOADING' : stage,
|
||||
const detail = (full = false) => ({ dispatchNo: full ? 'DS-FULL' : 'DS-OWN', recordVersion: full ? 0 : recordVersion, status: 'ACTIVE', stage: full ? 'DELIVERING' : stage,
|
||||
reviewState: review ? 'APPROVED' : 'DRAFT', reviews: { dispatchReview: { status: review ? 'APPROVED' : 'NOT_SUBMITTED' }, loadingReview: { status: review ? 'APPROVED' : 'NOT_SUBMITTED' } },
|
||||
counts: counts(full ? fullItems : items), pickingReconciled: !full && items.every(i => i.pickedPackages === i.scheduledPackages), dispatchInfo: { route: 'A1,A2' },
|
||||
documents: stage==='DELIVERING'&&!full?documents():{outbounds:[],deliveries:[]},
|
||||
orderDetails: (full ? fullItems : items).map(i => ({ sourceKey: i.sourceKey, customerId: i.customerId, inboundCode: i.inboundCode, destinationFbaWarehouse: i.destination.warehouseCode })) })
|
||||
const ack = (action, result = {}) => ({ ...detail(), action, result, replayed: false })
|
||||
const groups = () => {
|
||||
@@ -42,6 +43,7 @@ function fixture(fault = '', nativeMode = false) {
|
||||
for (const group of raw) { const previous = merged.get(group.groupKey); if (previous) { previous.cargo.push(...group.cargo); previous.externalQuotes.push(...group.externalQuotes) } else merged.set(group.groupKey, group) }
|
||||
return [...merged.values()].map(g => ({ ...g, requiredServices: g.cargo.some(c => !g.externalQuotes.some(q => q.inboundCode === c.inboundCode)) ? ['DELIVERY_FEE'] : [] }))
|
||||
}
|
||||
const documents=()=>({outbounds:[...new Set(items.map(i=>i.customerId))].map(customerId=>({customerId,outboundNo:customerId===OWN?'OUT-OWN':'OUT-EXT',status:'OUTBOUNDED',packageCount:items.filter(i=>i.customerId===customerId).reduce((n,i)=>n+i.scheduledPackages,0)})),deliveries:groups().map((g,n)=>({groupKey:g.groupKey,customerId:g.customerId,destination:g.destination,outboundNo:g.customerId===EXT?'OUT-EXT':'OUT-OWN',deliveryNo:`DEL-${n}`,status:'DELIVERING',packageCount:g.cargo.reduce((n,c)=>n+c.packages,0)}))})
|
||||
const trip = () => ({ trip: { tripNo: 'TR-CURRENT', warehouseId: '92', vehicleId: '71', driverId: '72', recordVersion: fleetVersion, stops: tripProof.stops }, dispatches: [{ dispatchNo: fault === 'second-dispatch' ? 'DS-OTHER' : 'DS-OWN' }] })
|
||||
const response = (data, message = '') => ({ http: 200, body: { code: message ? 'A0409' : '200', message, data: clone(data) }, requestId: 'OFFLINE-REQ', traceId: 'OFFLINE-TRACE' })
|
||||
const context = { marker: 'CURRENT', createdResources: nativeMode ? { fleetTrips: [tripProof] } : { fleetTrips: [tripProof, { dispatchNo: 'DS-FULL', tripNo: 'TR-FULL', warehouseId: '92' }], fleetFeeCoverageCases: [full, currentCase] }, checkpoint: async () => {},
|
||||
@@ -49,8 +51,8 @@ function fixture(fault = '', nativeMode = false) {
|
||||
async request(method, raw, body, token) {
|
||||
calls.push({ method, path: raw, body: clone(body), token })
|
||||
const path = raw.split('?')[0], params = new URLSearchParams(raw.split('?')[1]), normalized = path.replace(PDA, BASE), key = `${normalized}:${body?.idempotencyKey}`
|
||||
if (method !== 'GET' && body?.idempotencyKey && ledger.has(key)) { const original = ledger.get(key); assert.deepEqual(body, original.body); if (fault === 'replay-row-drift' && path.endsWith('/items')) items[0].dispatchItemId = '999'; return response({ ...clone(original.data), replayed: true }) }
|
||||
const remember = data => { if (body?.idempotencyKey) ledger.set(key, { body: clone(body), data: clone(data) }); return response(data) }
|
||||
if (method !== 'GET' && body?.idempotencyKey && ledger.has(key)) { const original = ledger.get(key); assert.deepEqual(body, original.body); if (fault === 'replay-row-drift' && path.endsWith('/items')) items[0].dispatchItemId = '999'; if(path.endsWith('/loading-complete')&&token!==original.token)return {http:200,body:{code:'A0403',message:'该请求标识不属于当前操作员'},requestId:'R',traceId:'T'}; return response({ ...clone(original.data), replayed: true }) }
|
||||
const remember = data => { if (body?.idempotencyKey) ledger.set(key, { body: clone(body), data: clone(data), token }); return response(data) }
|
||||
if (path === `${TRIP}/capacity-plan`) {
|
||||
if (method === 'POST') { assert.equal(body.recordVersion, fleetVersion); assert.equal(body.pallets.length, 3); assert.equal(body.pallets.find(p => p.warehousePalletId === '33').palletSpaces, 2); planned = true; fleetVersion++ }
|
||||
return response({ recordVersion: fleetVersion, manifestHash: 'c'.repeat(64), planValid: items.length === 2 || planned || fault === 'stale-plan',
|
||||
@@ -74,14 +76,11 @@ function fixture(fault = '', nativeMode = false) {
|
||||
}
|
||||
const delivery = path.match(/\/delivery-orders\/DEL-(\d)$/)
|
||||
if (delivery) {
|
||||
const index = Number(delivery[1]), group = groups()[index], input = saved.groups[index], who = group.customerId
|
||||
assert.equal(params.get('customerId'), who)
|
||||
const feeCoverage = input.feeCoverage.length ? { cargoHash: group.feeCoverageCargoHash, cargo: group.cargo, operatorId: 'OP', confirmedAt: '2026-10-19T01:00:00Z',
|
||||
references: input.feeCoverage.map(s => ({ services: s.services, source: { ...group.feeSources.find(f => f.financeFeeId === s.financeFeeId), sourceType: 'INBOUND_FEE', sourceInboundId: group.cargo[0].sourceInboundId } })) } : null
|
||||
const fees = input.fees.map(f => ({ id: String(981 + index), feeDirection: f.direction, feeType: f.feeType, amount: f.amount, currency: f.currency, sourceStatus: 'ACTIVE', reviewStatus: 'APPROVED' }))
|
||||
if (group.externalQuotes.length) fees.push({ id: '980', feeDirection: 'RECEIVABLE', feeType: 'DELIVERY_FEE', amount: 30, currency: 'CNY', sourceStatus: 'ACTIVE', reviewStatus: 'APPROVED' })
|
||||
if (fault === 'duplicate-fee' && group.externalQuotes.length && calls.filter(c => c.path === raw).length > 1) fees.push(clone(fees[0]))
|
||||
return response({ order: { customerId: who, dispatchNo: 'DS-OWN', deliveryNo: `DEL-${index}`, outboundNo: who === EXT ? 'OUT-EXT' : 'OUT-OWN', destinationWarehouseCode: group.destination.warehouseCode }, fees, feeCoverage })
|
||||
const index=Number(delivery[1]),group=groups()[index],who=group.customerId
|
||||
assert.equal(params.get('customerId'),who)
|
||||
const fees=group.externalQuotes.length?[{id:'980',feeDirection:'RECEIVABLE',feeType:'DELIVERY_FEE',amount:fault==='stale-price'?50:30,currency:'CNY',sourceStatus:'ACTIVE',reviewStatus:'APPROVED'}]:[]
|
||||
if(fault==='duplicate-fee'&&fees.length)fees.push(clone(fees[0]))
|
||||
return response({order:{customerId:who,dispatchNo:'DS-OWN',deliveryNo:`DEL-${index}`,outboundNo:who===EXT?'OUT-EXT':'OUT-OWN',destinationWarehouseCode:group.destination.warehouseCode,status:'DELIVERING',packageCount:group.cargo.reduce((n,c)=>n+c.packages,0)},fees,feeCoverage:null})
|
||||
}
|
||||
const isFull = path.startsWith(`${BASE}/DS-FULL`), data = detail(isFull), cargo = isFull ? fullItems : items
|
||||
if (method === 'GET' && normalized === `${BASE}/DS-OWN` && path.startsWith(PDA)) return response({ ...data, items: items.map((i, n) => ({ ...i, positions: [{ positionId: `6${n}`, availablePackages: i.scheduledPackages }] })) })
|
||||
@@ -89,7 +88,6 @@ function fixture(fault = '', nativeMode = false) {
|
||||
if (method === 'GET' && normalized.endsWith('/route')) return response({ dispatchNo: data.dispatchNo, recordVersion: data.recordVersion, stops: tripProof.stops })
|
||||
if (method === 'GET' && normalized.endsWith('/loading')) return response({ ...data, manifestHash: 'c'.repeat(64), loadingComplete: { allowed: true }, pallets: items.map((i, n) => ({ ...i, ...(fault === 'missing-measurements' ? {} : { palletSpaces: n === 2 ? 2 : 1, weightKg: n === 2 ? 37 : 10, volumeCbm: n === 2 ? 0.7 : 0.2, deliveryIndex: nativeMode || n === 2 ? 1 : n + 1,
|
||||
evidence: [['palletSpaces', n === 2 ? 2 : 1], ['weightKg', n === 2 ? 37 : 10], ['volumeCbm', n === 2 ? 0.7 : 0.2]].map(([field, value]) => ({ field, value, actual: true, sourceType: 'MEASURED', sourceBusinessNo: i.inboundCode, observedAt: '2026-10-19T01:00:00Z' })) }) })) })
|
||||
if (method === 'GET' && normalized.endsWith('/delivery-arrangement')) return response({ ...data, cargoHash: 'a'.repeat(64), loadingCompletionHash: 'c'.repeat(64), groups: groups(), arrange: { allowed: !saved }, arrangement: saved ? { dispatchNo: 'DS-OWN' } : null })
|
||||
if (method === 'GET') { assert.ok(normalized === `${BASE}/DS-OWN` || normalized === `${BASE}/DS-FULL`, raw); return response(data) }
|
||||
if (path.endsWith('/pick') && body.recordVersion !== recordVersion) return response(null, '公共版本已变化')
|
||||
assert.equal(body.recordVersion, recordVersion, raw)
|
||||
@@ -97,32 +95,21 @@ function fixture(fault = '', nativeMode = false) {
|
||||
if (path.endsWith('/items')) { assert.equal(stage, 'BEFORE_LOADING'); assert.equal(body.cargo.length, 1); items.push(external); recordVersion++; fleetVersion++; if (fault === 'unknown-add') return { http: 0, body: null }; return remember(ack('ADD_CARGO')) }
|
||||
if (path.endsWith('/submit-review') || path.endsWith('/dispatch-review') || path.endsWith('/loading-review')) { if (path.endsWith('/loading-review') && fault !== 'review-not-applied') { review = true; stage = 'LOADING' }; recordVersion++; return remember(ack('REVIEW')) }
|
||||
if (path.endsWith('/pick')) { assert.ok(token); const itemId = path.split('/').at(-2); items.find(i => i.dispatchItemId === itemId).pickedPackages += body.packages; recordVersion++; return remember(ack('PICK')) }
|
||||
if (path.endsWith('/loading-complete')) { assert.equal(items.every(i => i.pickedPackages === i.scheduledPackages), true); stage = 'AFTER_LOADING'; recordVersion++; return remember(ack('LOADING_COMPLETE', { confirmedLoad: { manifestHash: body.manifestHash, pallets: body.measurements } })) }
|
||||
assert.ok(path.endsWith('/transfer-delivery'), raw); assert.equal(stage, 'AFTER_LOADING')
|
||||
if (body.cargoHash !== 'a'.repeat(64)) return response(null, '装车或实际货物已经变化')
|
||||
if (body.groups.some(g => g.feeCoverage.some(s => s.sourceVersion !== 1))) return response(null, '原费用版本已变化')
|
||||
if (body.groups.some(g => g.externalFreightHash !== 'b'.repeat(64))) return response(null, '外接实收或约定运费已变化')
|
||||
if (body.groups.some(g => new Set(g.fees.map(f => `${f.direction}:${f.feeType}`)).size !== g.fees.length)) return response(null, '同组同方向同费用不得重复登记')
|
||||
if (body.groups.some(g => g.groupKey === 'G-PLAN:23' && g.fees.length)) return response(null, '本组服务已全部覆盖,不得重复登记应收')
|
||||
if (!body.groups.some(g => g.fees.length)) return response(null, '本组尚有未覆盖的服务费用,请核对')
|
||||
saved = clone(body); recordVersion++
|
||||
if (fault === 'unknown-arrange') return { http: 0, body: null }
|
||||
return remember(ack('ARRANGE_DELIVERY', { dispatchNo: 'DS-OWN', cargoHash: 'a'.repeat(64), outbounds: [...new Set(items.map(i => i.customerId))].map(customerId => ({ customerId, outboundNo: customerId === OWN ? 'OUT-OWN' : 'OUT-EXT', status: 'WAIT_OUTBOUND', packageCount: items.filter(i => i.customerId === customerId).reduce((n, i) => n + i.scheduledPackages, 0) })),
|
||||
deliveries: groups().map((g, n) => ({ groupKey: g.groupKey, customerId: g.customerId, destination: g.destination, outboundNo: g.customerId === EXT ? 'OUT-EXT' : 'OUT-OWN', deliveryNo: `DEL-${n}`, status: 'WAIT_DELIVERY', packageCount: g.cargo.reduce((n, c) => n + c.packages, 0) })) }))
|
||||
if (path.endsWith('/loading-complete')) { assert.equal(items.every(i => i.pickedPackages === i.scheduledPackages), true); stage = 'DELIVERING'; recordVersion++; return remember(ack('LOADING_COMPLETE', { confirmedLoad: { manifestHash: body.manifestHash, pallets: body.measurements } })) }
|
||||
throw Error(`Unexpected ${method} ${raw}`)
|
||||
} }
|
||||
const options = nativeMode ? { pickerToken: 'OFFLINE-ONLY', arrangement: { mode: 'NATIVE', marker: 'CURRENT', dispatchNo: 'DS-OWN', plannedDeliveryAt: '2026-10-22T01:00:00Z', deliveryMethod: 'TRUCK_PALLET', pickupMethod: 'WAREHOUSE_PICKUP', loadingType: 'PALLET', appointmentAccount: 'SELF',
|
||||
groups: [{ customerId: OWN, destinationKey: 'FBA:A1', fees: [{ direction: 'RECEIVABLE', feeType: 'DELIVERY_FEE', feeName: '客户A明确派送费', currency: 'USD', amount: 11 }] }, { customerId: EXT, destinationKey: 'FBA:A1', fees: [{ direction: 'RECEIVABLE', feeType: 'DELIVERY_FEE', feeName: '客户B明确派送费', currency: 'CNY', amount: 17 }] }] } } : { coverageCase: currentCase, pickerToken: 'OFFLINE-ONLY' }
|
||||
const options = nativeMode ? {pickerToken:'OFFLINE-ONLY'} : {coverageCase:currentCase,pickerToken:'OFFLINE-ONLY'}
|
||||
return { context, tripProof, currentCase, calls, assertions, items, options }
|
||||
}
|
||||
|
||||
test('native external flow adds actual received cargo to same whole, re-reviews, scans, loads and arranges all customer groups', async () => {
|
||||
const f = fixture(), proof = await verifyFleetExternalDispatch(f.context, 'external', f.tripProof, f.options)
|
||||
assert.equal(proof.status, 'VERIFIED'); assert.equal(proof.dispatchNo, 'DS-OWN'); assert.equal(proof.sourceFees.length, 1); assert.equal(proof.sourceFees[0].chargeableQuantity, 2)
|
||||
assert.equal(proof.financeAcknowledgement, 'NOT_VERIFIED'); assert.equal(f.currentCase.proof.status, 'VERIFIED'); assert.equal(f.currentCase.proof.newReceivable, 17.5)
|
||||
assert.equal(proof.financeAcknowledgement, 'NOT_VERIFIED'); assert.equal(f.currentCase.proof.status, 'VERIFIED'); assert.equal(f.currentCase.proof.feeCoverageSelection,'NOT_APPLICABLE_AUTOMATIC_DELIVERY')
|
||||
assert.ok(f.assertions.every(a => a.passed)); assert.equal(f.calls.some(c => /pre-arrange|dispatches\/bind|dispatches\/unbind|\/cancel/.test(c.path)), false)
|
||||
assert.equal(f.calls.filter(c => c.method === 'POST' && c.path === BASE).length, 0)
|
||||
assert.ok(f.calls.findIndex(c => c.path.endsWith('/return-to-review')) < f.calls.findIndex(c => c.path.endsWith('/items') && c.method === 'POST'))
|
||||
assert.ok(f.calls.findIndex(c => c.path.endsWith('/loading-complete')) < f.calls.findIndex(c => c.path.endsWith('/transfer-delivery')))
|
||||
assert.equal(f.calls.some(c => c.path.endsWith('/transfer-delivery')||c.path.endsWith('/depart')),false)
|
||||
assert.equal(f.items.length, 3); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external', f.tripProof, f.options), /尚未装入/)
|
||||
})
|
||||
|
||||
@@ -133,8 +120,8 @@ for (const fault of ['foreign-customer', 'stale-plan', 'double-capacity', 'revie
|
||||
})
|
||||
|
||||
test('no external callback injection can replace real registered PARTIAL sources, current PDA or durable journal', async () => {
|
||||
for (const mutate of [f => { delete f.options.coverageCase }, f => { delete f.options.pickerToken }, f => { delete f.context.checkpoint }]) {
|
||||
const f = fixture(); mutate(f); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external', f.tripProof, f.options), /同主单未扫码PARTIAL/); assert.equal(f.calls.length, 0)
|
||||
for (const mutate of [f => { delete f.options.pickerToken }, f => { delete f.context.checkpoint }]) {
|
||||
const f = fixture(); mutate(f); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external', f.tripProof, f.options)); assert.equal(f.calls.length, 0)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -145,52 +132,14 @@ test('server binding requires the same existing whole dispatch and public cargo
|
||||
assert.equal(source.includes('/pre-arrange'), false); assert.equal(source.includes('draftToken'), false); assert.ok(source.includes('/return-to-review')); assert.ok(source.includes("'ADD_CARGO'"))
|
||||
})
|
||||
|
||||
test('ordinary native mode accepts two WAREHOUSE_TRANSFER sources and same-customer external cargo without FULL/PARTIAL fixtures', async () => {
|
||||
const f = fixture('', true), proof = await verifyFleetExternalDispatch(f.context, 'external-native', f.tripProof, f.options)
|
||||
assert.equal(proof.status, 'VERIFIED'); assert.equal(proof.arrangementMode, 'NATIVE'); assert.equal(proof.nativeArrangement.status, 'VERIFIED')
|
||||
assert.equal(Object.hasOwn(f.context.createdResources, 'fleetFeeCoverageCases'), false); assert.equal(f.currentCase.proof, undefined)
|
||||
assert.equal(proof.deliveries.length, 2); assert.equal(proof.deliveries.reduce((n, d) => n + d.packageCount, 0), 3)
|
||||
assert.ok(f.items.some(i => i.inboundCode === 'WT-OWN-1') && f.items.some(i => i.inboundCode === 'WT-OWN-2')); assert.equal(f.items.length, 3)
|
||||
const requests = f.calls.filter(c => c.method === 'POST' && c.path.endsWith('/transfer-delivery')), actual = requests.at(-1)
|
||||
assert.deepEqual(proof.transferRequest.groups.map(g => g.fees.map(f => [f.currency, f.amount])), [[['USD', 11]], [['CNY', 17]]])
|
||||
assert.ok(proof.transferRequest.groups.every(g => g.feeCoverage.length === 0 && !g.groupKey.startsWith('FBA:')))
|
||||
assert.equal(requests.length, 5); assert.equal(actual.body.idempotencyKey, proof.transferRequest.idempotencyKey)
|
||||
assert.ok(f.calls.findIndex(c => c.path.endsWith('/pick')) < f.calls.findIndex(c => c.path.endsWith('/loading-complete')))
|
||||
assert.ok(f.calls.findIndex(c => c.path.endsWith('/loading-complete')) < f.calls.findIndex(c => c.path.endsWith('/transfer-delivery')))
|
||||
assert.ok(f.calls.every(c => !c.path.includes('DS-FULL') && !c.path.includes('pre-arrange'))); assert.ok(f.assertions.every(a => a.passed))
|
||||
const snapshots = proof.nativeArrangement.snapshots
|
||||
assert.deepEqual(snapshots['DEL-0'].fees.map(f => [f.currency, f.amount]), [['USD', 11], ['CNY', 30]])
|
||||
assert.deepEqual(snapshots['DEL-1'].fees.map(f => [f.currency, f.amount]), [['CNY', 17]])
|
||||
assert.equal(proof.sourceFees[0].originalAmount, 30); assert.equal(proof.sourceFees[0].customerId, OWN)
|
||||
test('ordinary current sources automatically deliver without manual fee configuration',async()=>{
|
||||
const f=fixture('',true),proof=await verifyFleetExternalDispatch(f.context,'external-native',f.tripProof,f.options)
|
||||
assert.equal(proof.status,'VERIFIED');assert.equal(proof.fulfillmentMode,'AUTOMATIC_DELIVERY')
|
||||
assert.equal(proof.deliveries.length,2);assert.equal(proof.deliveries.reduce((n,d)=>n+d.packageCount,0),3)
|
||||
assert.ok(proof.deliveries.every(d=>d.status==='DELIVERING'));assert.ok(proof.loadingRequest.idempotencyKey)
|
||||
assert.equal(f.calls.some(c=>c.path.endsWith('/transfer-delivery')||c.path.endsWith('/depart')),false)
|
||||
assert.equal(proof.sourceFees[0].originalAmount,30)
|
||||
})
|
||||
|
||||
test('ordinary native configuration and coverage mode are exclusive and require current explicit group configuration', async () => {
|
||||
for (const mutate of [f => { delete f.options.arrangement }, f => { f.options.coverageCase = f.currentCase }, f => { f.options.arrangement.mode = 'SAME_ROUTE' },
|
||||
f => { f.options.arrangement.marker = 'OLD' }, f => { f.options.arrangement.dispatchNo = 'OTHER' }, f => { f.options.arrangement.groups.push(clone(f.options.arrangement.groups[0])) },
|
||||
f => { f.options.arrangement.groups = async () => ({ success: true }) }, f => { f.options.arrangement.groups[0].fees[0].amount = 0 }, f => { f.options.arrangement.groups[0].fees.push(clone(f.options.arrangement.groups[0].fees[0])) },
|
||||
f => { f.options.arrangement.groups[0].feeCoverage = [] }, f => { delete f.options.pickerToken }, f => { delete f.context.checkpoint }]) {
|
||||
const f = fixture('', true); mutate(f); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external-native', f.tripProof, f.options)); assert.equal(f.calls.length, 0)
|
||||
}
|
||||
for (const mutate of [f => { f.options.arrangement.groups.pop() }, f => { f.options.arrangement.groups[0].customerId = '999' }]) {
|
||||
const f = fixture('', true); mutate(f); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external-native', f.tripProof, f.options), /加货前精确覆盖/); assert.equal(f.calls.some(c => c.method !== 'GET'), false)
|
||||
}
|
||||
})
|
||||
|
||||
for (const fault of ['foreign-customer', 'double-capacity', 'review-not-applied', 'stale-price', 'duplicate-fee', 'foreign-source-fee', 'wrong-quantity', 'replay-row-drift', 'already-loaded', 'missing-measurements', 'unknown-arrange']) test(`ordinary native flow preserves protection against ${fault}`, async () => {
|
||||
const f = fixture(fault, true); await assert.rejects(verifyFleetExternalDispatch(f.context, 'external-native', f.tripProof, f.options)); assert.notEqual(f.tripProof.externalDispatch.status, 'VERIFIED')
|
||||
assert.equal(Object.hasOwn(f.context.createdResources, 'fleetFeeCoverageCases'), false)
|
||||
if (fault === 'missing-measurements') assert.equal(f.calls.some(c => c.path.endsWith('/loading-complete') || c.path.endsWith('/transfer-delivery')), false)
|
||||
if (fault === 'unknown-arrange') { assert.equal(f.tripProof.externalDispatch.status, 'OUTCOME_UNKNOWN'); assert.equal(f.calls.at(-1).path.endsWith('/transfer-delivery'), true); assert.equal(f.context.createdResources.dispatchWriteBoundary.stopped, true); assert.ok(f.tripProof.externalDispatch.nativeArrangement.confirmationRequest.idempotencyKey) }
|
||||
})
|
||||
|
||||
test('ordinary native plan permits explicit zero extra fee only for fully quoted cargo and never expands the quote into each group', () => {
|
||||
const f = fixture('', true), receipt = f.tripProof.externalReceipt, plan = clone(f.options.arrangement)
|
||||
plan.groups = [{ customerId: OWN, destinationKey: 'FBA:A1', fees: [] }]
|
||||
const group = { groupKey: 'REAL-GROUP', customerId: OWN, destination: { key: 'FBA:A1' }, groupCargoHash: 'a'.repeat(64), externalFreightHash: 'b'.repeat(64), feeCoverageCargoHash: 'a'.repeat(64), blockReasons: [], requiredServices: [], feeSources: [],
|
||||
cargo: [{ customerId: OWN, inboundCode: receipt.incomingNo, destination: { key: 'FBA:A1' } }], externalQuotes: [{ groupKey: 'REAL-GROUP', customerId: OWN, bookingNo: receipt.bookingNo, inboundCode: receipt.incomingNo, currency: 'CNY', amount: 30, sourceHash: 'b'.repeat(64) }] }
|
||||
const groups = nativeArrangementGroups({ groups: [group] }, plan, receipt)
|
||||
assert.deepEqual(groups[0].fees, []); assert.deepEqual(groups[0].feeCoverage, []); assert.equal(groups[0].groupKey, 'REAL-GROUP')
|
||||
plan.groups[0].fees = [{ direction: 'RECEIVABLE', feeType: 'DELIVERY_FEE', feeName: 'duplicate', currency: 'CNY', amount: 30 }]
|
||||
assert.throws(() => nativeArrangementGroups({ groups: [group] }, plan, receipt), /不得重复登记/)
|
||||
plan.groups[0].fees = []; group.requiredServices = ['DELIVERY_FEE']; assert.throws(() => nativeArrangementGroups({ groups: [group] }, plan, receipt), /尚未覆盖/)
|
||||
for(const fault of ['foreign-customer','double-capacity','review-not-applied','stale-price','duplicate-fee','foreign-source-fee','wrong-quantity','replay-row-drift','already-loaded','missing-measurements'])test(`automatic external flow rejects ${fault}`,async()=>{
|
||||
const f=fixture(fault,true);await assert.rejects(verifyFleetExternalDispatch(f.context,'external-native',f.tripProof,f.options));assert.notEqual(f.tripProof.externalDispatch.status,'VERIFIED')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user