import assert from 'node:assert/strict' import test from 'node:test' import { readFileSync } from 'node:fs' import { fulfillmentHistorySnapshot, validTwoFragmentTree, validSignatureGate, completionCounts, exactCompletionOutboxProof, resolveF40MerchantCredentials, f40TransferBody, f40LocalDateTime, validFragmentCabinetIdentity, validUnloadedRemainder, validRemainderCancellationGate, exactCancelledRemainderOutboxProof, cancelUnloadedRemainder, verifyRootProjections, verifyRemainderTaxIdentity, validFreshReceiptSave, preserveReceivingEvidence, run } from './phase5-root-fulfillment.mjs' import { loadOneFragment } from './phase3-order-splitting.mjs' import { verifyPalletLabels } from './phase3-loading-finalize.mjs' import { awaitProducerConsumption, convertReceivingContainers, runReceiving, resolveSharedWarehouse } from './phase4-overseas-pda.mjs' const source = readFileSync(new URL('./phase5-root-fulfillment.mjs', import.meta.url),'utf8') const history = (...actions) => actions.map((action,index) => ({id:String(index+1),action})) const tree = () => ({parentOrderId:'100',originalPackageCount:2,cumulativeLoadedPackageCount:2, currentRemainderPackageCount:0,children:[ {orderId:'100',sourceOrderId:'100',splitRole:'LOADED',packageCount:1}, {orderId:'101',sourceOrderId:'100',splitRole:'REMAINDER',packageCount:1}, {orderId:'101',sourceOrderId:'101',splitRole:'LOADED',packageCount:1}, ]}) const signed = () => ({root:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED')}, child:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED')}, rootInbound:{processStatus:'DELIVERED',orderStatusSnapshot:'COMPLETED'}}) const outbox = () => ({readOnly:true,customerId:'90',rootOrderId:'100',orders:['100','101'].map(orderId => ({ orderId,customerId:'90',rootOrderId:'100',eventType:'FIRST_LEG_ORDER_FULFILLMENT_COMPLETED', count:1,publishedCount:1,pendingCount:0,failedCount:0, }))}) const fixture = () => ({steps:[],token:'offline',marker:'offline-only',createdResources:{}, addStep(_module,_label,_response,passed){this.steps.push({passed})}, assert(_module,_label,_response,passed){this.steps.push({passed})}, addCoverageGap(){},apiSuccess(_module,_label,response){return response?.body?.data}, }) function remainderTaxFixture(taxMode) { const context=fixture(),calls=[] const evidence={taxMode,customerId:'90',rootOrderId:'100',childOrderId:'101',rootOrderNo:'ORDER-ROOT', rawChild:{id:'101',tenantId:'90',taxMode},rootInbound:{orderId:'100',taxMode}, childInbound:{orderId:'101',tenantId:'90',taxMode}} const rows=[{orderId:'100',tenantId:'90',taxMode,relation:{role:'PARENT',rootOrderId:'100'}}, {orderId:'101',tenantId:'90',taxMode,relation:{role:'CHILD',rootOrderId:'100',parentOrderId:'100'}}] context.request=async(method,path,body,token)=>{ calls.push({method,path,body,token}) const filter=new URL(path,'https://offline.invalid').searchParams.get('taxMode') const records=filter&&filter!==taxMode?[]:structuredClone(rows) return {http:200,body:{code:'200',data:{records,total:records.length}}} } return {context,evidence,calls,rows} } test('raw remainder and actual inbound summary taxMode agree for BUY and TAX and customer filters retain exact parent/child',async()=>{ for(const mode of ['BUY_DECLARATION','TAX_REFUND']){ const f=remainderTaxFixture(mode) await verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer') assert.equal(f.calls.length,3) assert.ok(f.calls.every(c=>c.method==='GET'&&c.token==='own-customer'&&!c.path.includes('/customs'))) assert.deepEqual(f.evidence.matchingTax.records.map(r=>r.orderId),['100','101']) assert.equal(f.evidence.otherTax.total,0) assert.match(f.calls[1].path,new RegExp('taxMode='+mode)) } }) test('missing or wrong raw remainder taxMode is rejected before customer/customs calls despite valid inbound projection',async()=>{ for(const mode of ['BUY_DECLARATION','TAX_REFUND'])for(const actual of [null,undefined,mode==='BUY_DECLARATION'?'TAX_REFUND':'BUY_DECLARATION']){ const f=remainderTaxFixture(mode);f.evidence.rawChild.taxMode=actual await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer'),/原始余量子订单继承/) assert.equal(f.calls.length,0) } }) test('wrong inbound tax, missing filtered child, foreign customer or wrong parent identity cannot pass',async()=>{ for(const mutate of [f=>f.evidence.childInbound.taxMode=null,f=>f.rows.pop(),f=>f.rows[1].tenantId='91', f=>f.rows[1].relation.parentOrderId='999',f=>f.rows[1].taxMode='BUY_DECLARATION']) { const f=remainderTaxFixture('TAX_REFUND');mutate(f) await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer')) } const f=remainderTaxFixture('TAX_REFUND'),original=f.context.request f.context.request=async(...args)=>{const r=await original(...args);if(args[1].includes('taxMode=BUY_DECLARATION'))r.body.data={records:[f.rows[1]],total:1};return r} await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer'),/otherTax/) }) test('F40 unified projections require exact root and customer session with final COMPLETED or unfinished DELIVERED',async()=>{ for (const completed of [true,false]) { const context=fixture(),calls=[],snapshot={root:{status:completed?'COMPLETED':'TRANSPORTING',histories:history('OVERSEAS_DELIVERED')}} context.request=async(method,path,body,token)=>{ calls.push({method,path,token}) return {http:200,body:{code:'200',data:{summary:{orderId:'100',mainStatus:completed?'COMPLETED':'DELIVERED'}}}} } const result=await verifyRootProjections(context,'90','100','customer-session',snapshot) assert.deepEqual(result.platform,result.customer) assert.equal(calls[0].path,'/api/platform/v1/orders/100?tenantId=90') assert.equal(calls[1].path,'/api/merchant/v1/orders/100') assert.equal(calls[1].token,'customer-session') assert.equal(context.steps.every(step=>step.passed),true) } }) test('F40 customer mismatched root or prematurely completed projection fails closed',async()=>{ for (const summary of [{orderId:'101',mainStatus:'COMPLETED'},{orderId:'100',mainStatus:'DELIVERED'}]) { const context=fixture() context.request=async(_method,path)=>({http:200,body:{code:'200',data:{summary: path.startsWith('/api/platform')?{orderId:'100',mainStatus:'COMPLETED'}:summary}}}) await assert.rejects(verifyRootProjections(context,'90','100','customer-session',{root:{status:'COMPLETED',histories:[]}}), /平台客户统一状态/) } }) test('F40 README documents all three independent modes and fail-closed receiving/zero child event evidence',()=>{ const readme=readFileSync(new URL('./README.md',import.meta.url),'utf8') for(const value of ['CHILD_FIRST','ROOT_SIGNED_REMAINDER_CANCELLED','orderStopAtApproved=true', 'PROCESSING','WAIT_SPLIT','A0409','phase4ReceivingEvidenceReader','零事件子行'])assert.ok(readme.includes(value),value) assert.match(source,/proof\.beforeReceiptReplayOutbox = await waitState/) assert.match(source,/outboxCounts\(proof.beforeReceiptReplayOutbox\) === outboxCounts\(proof.outboxProof\)/) assert.match(source,/validFreshReceiptSave\(beforeFresh,afterFresh\)/) assert.match(source,/JSON.stringify\(afterFresh\) === JSON.stringify\(receiptProof.afterSameKey\)/) }) test('fresh receipt save allows exactly one document version/revision/audit while business completion stays unchanged',()=>{ const documents={version:'2',uploadedCategoryCount:1,categories:[{category:'RECEIPT',files:[{fileId:'1'}]}],replayed:false} const before={documents,delivery:{order:{version:'2',status:'DELIVERED',signedAt:'2026-09-30T16:00:00'}, operations:[{id:'1',operationNode:'RECEIPT_AUTO_COMPLETE'}],skus:[{packageCount:1}],fees:[]},revisions:[{version:'2'}]} const after=structuredClone(before);after.documents.version='3';after.delivery.order.version='3' after.delivery.operations.push({id:'2',operationNode:'SAVE_DOCUMENTS',result:'保存派送资料:1/3类', operatedAt:'2026-09-30T16:01:00',operatorName:'platform'}) after.revisions.push({version:'3',savedAt:'2026-09-30T16:01:00',operatorId:'platform',snapshot:after.documents}) assert.equal(validFreshReceiptSave(before,after),true) for(const mutate of [ value=>value.documents.version='4',value=>value.delivery.order.version='2', value=>value.delivery.order.signedAt='2026-09-30T16:01:00',value=>value.delivery.order.status='RETURNED', value=>value.documents.categories[0].files[0].fileId='2',value=>value.delivery.skus[0].packageCount=2, value=>value.delivery.fees.push({amount:1}),value=>value.delivery.operations[0].id='999', value=>value.delivery.operations[1].operationNode='RECEIPT_AUTO_COMPLETE', value=>value.delivery.operations.push({...value.delivery.operations[1],id:'3'}), value=>value.revisions.pop(),value=>value.revisions[0].version='1', ]){const bad=structuredClone(after);mutate(bad);assert.equal(validFreshReceiptSave(before,bad),false)} }) test('F40 retains partial and failed receiving raw snapshots without exporting identity credentials',()=>{ const proof={},scope={createdResources:{phase4OverseasPda:{receivingCompletionEvidence:{}}},token:'secret'} preserveReceivingEvidence(proof,scope) scope.createdResources.phase4OverseasPda.receivingCompletionEvidence['20']={status:'UNPROVEN',stages:{ BEFORE_COMPLETE:{captured:true,evidence:{balances:[]}},AFTER_COMPLETE:{captured:false,evidence:{balances:[{id:'1'}]}}}} assert.deepEqual(proof.receivingCompletionEvidence,scope.createdResources.phase4OverseasPda.receivingCompletionEvidence) assert.equal(JSON.stringify(proof).includes('secret'),false) assert.match(source,/finally \{ preserveReceivingEvidence\(proof,scope\);/) assert.match(source,/proof.receiptReplayProofs \|\|= \[\]/) }) test('F40 transfer uses the current formal Transfer DTO including required positive fee and same LocalDateTime tool',() => { const dto=readFileSync(new URL('../../lastleg-service/src/main/java/com/baiyue/lastleg/delivery/dto/DeliveryRequests.java',import.meta.url),'utf8') const vehicle={carrierName:'offline-fleet',vehiclePlate:'offline-plate',driverName:'offline-driver',driverPhone:'19900000000'} const now=new Date('2026-09-30T01:02:03.456Z'),body=f40TransferBody('offline',1,vehicle,now) for(const field of ['idempotencyKey','plannedDeliveryAt','deliveryMethod','pickupMethod','loadingType','fees']) { assert.match(dto,new RegExp(`\\b${field}\\b`));assert.ok(body[field]) } assert.equal(body.plannedDeliveryAt,'2026-10-02T01:02:03') assert.equal(f40LocalDateTime(now),'2026-09-30T01:02:03') assert.equal(body.deliveryMethod,'TRUCK_PALLET');assert.equal(body.pickupMethod,'WAREHOUSE_PICKUP') assert.equal(body.loadingType,'PALLET');assert.equal(body.vehicleSize,'15托') for(const field of Object.keys(vehicle))assert.equal(body[field],vehicle[field]) assert.equal(body.fees.length,1) assert.deepEqual(Object.fromEntries(['feeType','feeName','direction','currency','amount'].map(field=>[field,body.fees[0][field]])), {feeType:'DELIVERY_FEE',feeName:'派送费',direction:'RECEIVABLE',currency:'USD',amount:10}) assert.match(source,/f40TransferBody\(context.marker,index,vehicle\)/) assert.match(source,/estimatedArrivalAt: f40LocalDateTime\(/) }) test('F40 credentials prefer a complete current fixture pair and never combine fixture with configured customer',() => { const options={username:'offline-admin',password:'offline-admin-secret',merchantUsername:'configured-customer',merchantPassword:'configured-secret'} const current={username:'fixture-customer',password:'fixture-secret',tenantId:'90'} assert.deepEqual(resolveF40MerchantCredentials({options,context:{merchantQualificationFixture:current}}), {username:'fixture-customer',password:'fixture-secret',customerId:'90'}) assert.deepEqual(resolveF40MerchantCredentials({options,context:{}}), {username:'configured-customer',password:'configured-secret',customerId:null}) for(const incomplete of [{username:'fixture-only',tenantId:'90'},{password:'fixture-only',tenantId:'90'}, {username:'fixture-customer',password:'fixture-secret'}]) assert.equal(resolveF40MerchantCredentials({options,context:{merchantQualificationFixture:incomplete}}),null) assert.equal(resolveF40MerchantCredentials({options:{merchantUsername:'incomplete'},context:{}}),null) assert.ok(source.indexOf("'F40 正式订单创建客户与本轮登录客户一致'") < source.indexOf("await stage(scope, 'inbound-measurement')")) }) test('F40 reads formal nested domestic cabinet workbench and compares booking/plan/task identity instead of inventing GET containers/id',() => { const controller=readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java',import.meta.url),'utf8') const dto=readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/terminaldrayage/dto/ContainerWorkbenchDtos.java',import.meta.url),'utf8') assert.match(controller,/@GetMapping\("\/domestic\/\{id\}"\)/) assert.doesNotMatch(controller,/@GetMapping\("\/\{id\}"\)/) assert.match(dto,/record WorkbenchItem\(ContainerBookingEntity container, String workbenchStage/) assert.match(source,/`\/api\/platform\/v1\/containers\/domestic\/\$\{result.containerId\}`/) assert.doesNotMatch(source,/`\/api\/platform\/v1\/containers\/\$\{result.containerId\}`/) const fragment={containerId:'20',planId:'30',taskId:'40',oceanBookingId:'50'},task={id:'40',containerBookingId:'20',loadingPlanId:'30'} const workbench={container:{id:'20',cabinetNo:'D0001',containerNo:'OFFLINE',oceanBookingId:'50'}} assert.equal(validFragmentCabinetIdentity(workbench,fragment,task),true) assert.equal(validFragmentCabinetIdentity(workbench,{...fragment,containerId:'21'},task),false) assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,containerBookingId:'21'}),false) assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,loadingPlanId:'31'}),false) assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,id:'41'}),false) assert.equal(validFragmentCabinetIdentity(workbench,{...fragment,oceanBookingId:'51'},task),false) assert.equal(validFragmentCabinetIdentity(workbench.container,fragment,task),false) }) test('formal tree requires one root physical source plus its exact real remainder child and conserves two boxes',() => { assert.equal(validTwoFragmentTree(tree(),'100','101'),true) for (const change of [ value => value.parentOrderId='999', value => value.currentRemainderPackageCount=1, value => value.children[2].sourceOrderId='999', value => value.children[1].sourceOrderId='999', value => value.children[2].packageCount=2, value => value.children.push({splitRole:'UNKNOWN',orderId:'999'}), ]) {const value=tree();change(value);assert.equal(validTwoFragmentTree(value,'100','101'),false)} }) test('F40 separates immutable physical task cartons from current actual FBA receiving prefix scans',() => { assert.match(source,/cartonMarks: stableLoadedCartonMarks\(task.items\)/) assert.match(source,/receivingCartonMarks:\[`\$\{current.summary.inboundCode\}#PDA-CONTRACT-1`\]/) assert.doesNotMatch(source,/cartonMarks:\s*\[`\$\{current.summary.inboundCode\}#PDA-CONTRACT/) }) test('raw Order and original status history remain in evidence including operator, reason and timestamp',() => { const detail={order:{id:'100',orderRole:'MASTER',version:3,status:'TRANSPORTING'},statusHistory:[ {id:'11',orderId:'100',action:'OVERSEAS_DELIVERED',operatorName:'OVERSEAS_DELIVERY_EVENT', reason:'signed actual delivery',occurredAt:'2026-09-30T01:00:00',fromStatus:'TRANSPORTING',toStatus:'TRANSPORTING'}, ]} const snapshot=fulfillmentHistorySnapshot(detail) assert.deepEqual(snapshot.rawOrder,detail.order) assert.deepEqual(snapshot.histories,detail.statusHistory) assert.deepEqual(completionCounts(snapshot),{own:1,final:0}) }) test('root shipment signed first preserves unfinished root while effective child remains pending',() => { const snapshot=signed();snapshot.root.status='TRANSPORTING' snapshot.root.histories=history('OVERSEAS_DELIVERED');snapshot.child={status:'TRANSPORTING',histories:[]} snapshot.rootInbound.orderStatusSnapshot='TRANSPORTING' assert.equal(validSignatureGate(snapshot,'MASTER'),true) snapshot.root.status='COMPLETED' assert.equal(validSignatureGate(snapshot,'MASTER'),false) }) test('child signed before root remains an independent completed child and cannot finish unsigned root',() => { const snapshot=signed();snapshot.root={status:'TRANSPORTING',histories:[]} snapshot.rootInbound={processStatus:'IN_TRANSIT',orderStatusSnapshot:'TRANSPORTING'} assert.equal(validSignatureGate(snapshot,'CHILD'),true) assert.equal(validSignatureGate(snapshot,'MASTER'),false) snapshot.root.histories=history('OVERSEAS_DELIVERED') assert.equal(validSignatureGate(snapshot,'CHILD'),false) }) test('all signed requires root own fact, exactly one aggregate final history and real Firstleg delivered completion',() => { assert.equal(validSignatureGate(signed(),'MASTER',true),true) for(const change of [ value => value.root.histories=history('FULFILLMENT_COMPLETED'), value => value.root.histories.push({action:'FULFILLMENT_COMPLETED'}), value => value.child.histories.push({action:'OVERSEAS_DELIVERED'}), value => value.child.status='CANCELLED', value => value.rootInbound.orderStatusSnapshot='TRANSPORTING', value => value.rootInbound.processStatus='RECEIVED', ]) {const value=signed();change(value);assert.equal(validSignatureGate(value,'MASTER',true),false)} }) test('Outbox requires exact customer/root/source aggregate membership, unique completion and PUBLISHED',() => { assert.equal(exactCompletionOutboxProof(outbox(),'90','100',['101','100']),true) for(const change of [ value => value.readOnly=false, value => value.customerId='91', value => value.rootOrderId='999', value => value.orders[0].rootOrderId='999', value => value.orders[0].customerId='91', value => value.orders[1].orderId='100', value => value.orders[0].count=2, value => value.orders[1].publishedCount=0, value => value.orders[1].pendingCount=1, value => value.orders[0].failedCount=1, value => value.orders[0].eventType='OVERSEAS_DELIVERED', value => value.orders.pop(), ]) {const value=outbox();change(value);assert.equal(exactCompletionOutboxProof(value,'90','100',['100','101']),false)} }) test('exported fragment producer preserves fail closed supplier precondition and never writes without actual source master',async() => { const context=fixture(),calls=[] context.request=async(method,path) => {calls.push([method,path]);return {http:200,body:{code:200,data:{records:[]}}}} assert.equal(await loadOneFragment(context,{sourceOrderNo:'offline-only'}),null) assert.equal(calls.length,1) assert.equal(calls[0][0],'GET') assert.match(calls[0][1],/category=BOOKING&status=ACTIVE/) }) test('exported pallet label helper preserves FBA eligibility without introducing writes for other destinations',async() => { const context=fixture();context.request=async() => {throw new Error('unexpected HTTP')} assert.equal(await verifyPalletLabels(context,{destinationType:'OVERSEAS'},'1'),null) assert.equal(await verifyPalletLabels(context,{destinationType:'FBA',fulfillmentType:'ONE_CLICK_DROPSHIP'},'1'),null) }) test('exported consumer polling and conversion fail closed when real producer consumption is absent',async() => { const context=fixture(),calls=[] context.request=async(method,path) => {calls.push([method,path]);return {http:200,body:{code:200,data:{records:[]}}}} assert.equal(await awaitProducerConsumption(context,[]),true) assert.equal(await convertReceivingContainers(context,[{containerNo:'OFFLINE'}]),false) assert.equal(calls.every(([method]) => method==='GET'),true) assert.equal(context.steps.at(-1).passed,false) }) test('exported receiving rejects unmatched current producer instead of borrowing any history container',async() => { const context=fixture();context.request=async(_method,path) => ({http:200,body:{code:200,data: path.endsWith('/candidates')?[{containerNo:'ANOTHER',sourceContainerBookingId:'999',totalPackages:1}]:null}}) await runReceiving(context,'offline-pda',null,null,{containerNo:'EXPECTED',containerBookingId:'100'},false,{attempts:1}) assert.equal(context.steps.some(step => step.passed === false),true) }) test('exported receiving warehouse resolver continues to choose only configured active official warehouse',async() => { const context=fixture();context.request=async() => ({http:200,body:{code:200,data:{records:[ {id:'193000000000200003',warehouseType:'OVERSEAS',status:'ACTIVE',warehouseName:'offline'}, ]}}}) assert.deepEqual(await resolveSharedWarehouse(context),{id:'193000000000200003',name:'offline'}) }) test('complete orchestrator calls actual source chain and blocks missing Outbox evidence; no fake callback or SQL',() => { assert.equal(typeof run,'function') for(const name of ['orders','inbound-measurement','phase3-customs','phase3-outbound-dispatch', 'phase3-carrier-schedule-queries'])assert.ok(source.includes(`'${name}'`)) for(const name of ['loadOneFragment','verifyPalletLabels','awaitProducerConsumption', 'convertReceivingContainers','prepareTerminalDrayage','runReceiving','pickAll','withDeliveryEventRecovery']) assert.match(source,new RegExp(`await ${name}\\(`)) assert.match(source,/\(\) => saveReceiptAndComplete\(context, deliveries\[0\], customerId, index\)/) assert.match(source,/rootOrderId,\s*sourceOrderIds:\[rootOrderId,childOrderId\]/) assert.match(source,/HTTP_VERIFIED_OUTBOX_PENDING/) assert.match(source,/finally \{ preserveReceivingEvidence\(proof,scope\); proof.currentStage = 'PDA_IDENTITY_CLEANUP'; await disablePicker/) assert.doesNotMatch(source,/['"`]\/internal\/|UPDATE\s+ord_|INSERT\s+INTO|\/complete-delivery/) }) const oneLoadedTree = () => ({...tree(),cumulativeLoadedPackageCount:1,currentRemainderPackageCount:1, children:tree().children.slice(0,2).map(row=>({...row,loadingPlanId:'30',containerBookingId:'20'}))}) const approvedChild = () => fulfillmentHistorySnapshot({order:{id:'101',tenantId:'90',orderRole:'CHILD', rootOrderId:'100',parentOrderId:'100',status:'APPROVED',version:1,orderNo:'R-1',inboundCode:'I-1',completedAt:null},statusHistory:[]}) const unshippedInbound = () => ({id:'301',tenantId:'90',orderId:'101',orderNo:'R-1',inboundCode:'I-1', processStatus:'WAIT_SPLIT',orderStatusSnapshot:'APPROVED',totalPackageCount:1,containerNo:null,cabinetNo:null}) const cancelled = () => ({root:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED')}, child:{status:'CANCELLED',rawOrder:{completedAt:null},histories:history('CANCELLED')}, rootInbound:{processStatus:'DELIVERED',orderStatusSnapshot:'COMPLETED'}}) const cancellationOutbox = () => {const value=outbox();value.orders[1].count=0;value.orders[1].publishedCount=0;return value} test('unloaded remainder requires real raw APPROVED child, exact customer/root/source and one remaining physical box',() => { const child=approvedChild(),inbound=unshippedInbound(),tree=oneLoadedTree() assert.equal(validUnloadedRemainder(tree,child,inbound,'90','100','101'),true) for(const mutate of [ value => value.child.status='PROCESSING',value => value.child.status='ACCEPTED', value => value.child.rootOrderId='999',value => value.child.parentOrderId='999', value => value.child.rawOrder.tenantId='91',value => value.child.orderId='102', value => value.child.histories=history('OVERSEAS_DELIVERED'), value => value.inbound.orderId='102',value => value.inbound.tenantId='91', value => value.inbound.inboundCode='ANOTHER',value => value.inbound.orderNo='ANOTHER', value => value.inbound.processStatus='LOADED',value => value.inbound.totalPackageCount=2, value => value.inbound.orderStatusSnapshot='PROCESSING',value => value.inbound.containerNo='ACTUAL-CABINET', value => value.inbound.cabinetNo='ACTUAL-CABINET',value => value.tree.children.push(tree.children[0]), value => value.tree.children[0].sourceOrderId='101',value => value.tree.children[1].loadingPlanId='31', value => value.tree.children[1].containerBookingId='21',value => value.tree.currentRemainderPackageCount=0, ]) { const value={tree:oneLoadedTree(),child:approvedChild(),inbound:unshippedInbound()};mutate(value) assert.equal(validUnloadedRemainder(value.tree,value.child,value.inbound,'90','100','101'),false) } }) test('cancelled remainder cannot substitute for child signature in either full-signature mode',() => { assert.equal(validRemainderCancellationGate(cancelled()),true) assert.equal(validSignatureGate(cancelled(),'MASTER',true),false) assert.equal(validSignatureGate(cancelled(),'CHILD',true),false) for(const mutate of [ value=>value.root.histories=history('FULFILLMENT_COMPLETED'), value=>value.root.histories.push({action:'FULFILLMENT_COMPLETED'}), value=>value.child.status='COMPLETED',value=>value.child.histories.push({action:'OVERSEAS_DELIVERED'}), value=>value.child.histories.push({action:'CANCELLED'}),value=>value.child.rawOrder.completedAt='2026-09-30T01:00:00', value=>value.rootInbound.orderStatusSnapshot='APPROVED',value=>value.rootInbound.processStatus='RECEIVED', ]){const value=cancelled();mutate(value);assert.equal(validRemainderCancellationGate(value),false)} }) test('cancellation Outbox requires exactly one published root completion and explicit zero child completion counts',() => { assert.equal(exactCancelledRemainderOutboxProof(cancellationOutbox(),'90','100','101'),true) assert.equal(exactCompletionOutboxProof(cancellationOutbox(),'90','100',['100','101']),false) for(const mutate of [ value=>value.orders.pop(),value=>value.orders[1].count=1,value=>value.orders[1].publishedCount=1, value=>value.orders[1].pendingCount=1,value=>value.orders[1].failedCount=1, value=>value.orders[1].count=null,value=>delete value.orders[1].failedCount, value=>value.orders[1].rootOrderId='999',value=>value.orders[1].customerId='91', value=>value.orders[1].orderId='100',value=>value.orders[1].eventType='ORDER_CANCELLED', value=>value.orders[0].count=2,value=>value.orders[0].publishedCount=0,value=>value.readOnly=false, ]){const value=cancellationOutbox();mutate(value);assert.equal(exactCancelledRemainderOutboxProof(value,'90','100','101'),false)} }) function cancellationFixture(change=null) { const context=fixture(),calls=[],proof={snapshots:[]} const rootDetail={order:{id:'100',tenantId:'90',orderRole:'MASTER',rootOrderId:'100',status:'APPROVED', version:1,orderNo:'R',inboundCode:'I',completedAt:null},statusHistory:history('OVERSEAS_DELIVERED')} const childDetail={order:approvedChild().rawOrder,statusHistory:[]} const rootInbound={summary:{processStatus:'DELIVERED',orderStatusSnapshot:'APPROVED'}} const childInbound={summary:unshippedInbound()},tree=oneLoadedTree() let count=0 context.context={f40BaseMarker:'offline-only',f40ReadOnlyOutboxEvidence:async args=>{ assert.deepEqual(args,{customerId:'90',rootOrderId:'100',sourceOrderIds:['100','101'], eventType:'FIRST_LEG_ORDER_FULFILLMENT_COMPLETED'}) return cancellationOutbox() }} if(change)change({rootDetail,childDetail,childInbound,tree}) let frozenCancel context.request=async(method,path,body,token,headers)=>{ calls.push({method,path,body,headers}) if(method==='POST'){ assert.equal(path,'/api/platform/v1/orders/first-leg/101/cancel?tenantId=90') assert.ok(body.reason.includes('offline-only')) assert.ok(headers?.['Idempotency-Key']) if(count++)return {http:200,body:{code:headers['Idempotency-Key']===frozenCancel.key && JSON.stringify(body)===frozenCancel.payload ? '200' : 'A0409'}} assert.equal(body.expectedVersion,childDetail.order.version) frozenCancel={key:headers['Idempotency-Key'],payload:JSON.stringify(body)} childDetail.order.status='CANCELLED';childDetail.order.version++ childDetail.statusHistory=history('CANCELLED') rootDetail.order.status='COMPLETED';rootDetail.order.version++ rootDetail.order.completedAt='2026-09-30T01:00:00' rootDetail.statusHistory=history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED') rootInbound.summary.orderStatusSnapshot='COMPLETED' return {http:200,body:{code:'200',data:null}} } let data if(path.includes('/fulfillment-tree?'))data=tree else if(path.includes('/orders/first-leg/100?'))data=rootDetail else if(path.includes('/orders/first-leg/101?'))data=childDetail else if(path.includes('/inbounds/300?'))data=rootInbound else if(path.includes('/inbounds/301?'))data=childInbound else throw new Error(`unexpected offline request ${path}`) return {http:200,body:{code:'200',data:structuredClone(data)}} } return {context,proof,calls} } test('formal cancellation helper rereads physical eligibility, uses public cancel and accepts replay rejection only with unchanged facts and Outbox',async()=>{ const {context,proof,calls}=cancellationFixture() const result=await cancelUnloadedRemainder(context,proof,'90','100','101','300','301') assert.equal(validRemainderCancellationGate(result),true) const writes=calls.filter(row=>row.method==='POST') assert.equal(writes.length,4) assert.deepEqual(writes[1].body,writes[0].body) assert.deepEqual(writes[1].headers,writes[0].headers) assert.equal(writes[2].headers['Idempotency-Key'],writes[0].headers['Idempotency-Key']) assert.notEqual(writes[3].headers['Idempotency-Key'],writes[0].headers['Idempotency-Key']) assert.ok(writes[3].body.expectedVersion>writes[0].body.expectedVersion) assert.equal(proof.snapshots.length,3) assert.deepEqual(proof.cancelReplayOutbox.before,proof.cancelReplayOutbox.after) assert.equal(context.steps.every(step=>step.passed),true) }) test('formal cancellation helper stops before any mutation for PROCESSING, unsigned root or already loaded child',async()=>{ for(const change of [value=>value.childDetail.order.status='PROCESSING', value=>value.rootDetail.order.status='PROCESSING',value=>value.rootDetail.statusHistory=[], value=>value.childInbound.summary.processStatus='LOADED',value=>value.tree.children.push(tree().children[2])]) { const {context,proof,calls}=cancellationFixture(change) await assert.rejects(cancelUnloadedRemainder(context,proof,'90','100','101','300','301'),/仅取消真实签收根下未装 APPROVED/) assert.equal(calls.some(row=>row.method!=='GET'),false) } }) test('cancel scenario uses new approved-only order and exactly root shipment; both existing full-signature modes remain independent',()=>{ assert.match(source,/const cancelRemainder = signatureOrder === 'ROOT_SIGNED_REMAINDER_CANCELLED'/) assert.match(source,/orderStopAtApproved:cancelRemainder/) assert.match(source,/approvedRoot.status === 'APPROVED'/) assert.match(source,/if \(cancelRemainder\) \{[\s\S]*?proof.unloadedRemainder[\s\S]*?\} else \{[\s\S]*?second = await fragment/) assert.match(source,/const producers = cancelRemainder \? \[first\] : \[first,second\]/) assert.match(source,/signatureOrder === 'CHILD_FIRST' \? \[second,first\] : producers/) assert.match(source,/if \(cancelRemainder\) await cancelUnloadedRemainder/) const orderSource=readFileSync(new URL('./orders.mjs',import.meta.url),'utf8') assert.match(orderSource,/context.context\?\.orderStopAtApproved === true/) }) test('early F40 failures become FAILED with only a safe stage and preserve the original exception',async() => { const context=fixture();context.context={};context.options={} await assert.rejects(run(context),/F40 本轮客户完整凭据有效/) assert.equal(context.createdResources.phase5RootFulfillment.finalStatus,'FAILED') assert.equal(context.createdResources.phase5RootFulfillment.failedStage,'CUSTOMER_IDENTITY') assert.equal('error' in context.createdResources.phase5RootFulfillment,false) const secretError=new Error('offline-secret-body-not-recorded') const withLogin=fixture();withLogin.context={};withLogin.options={merchantUsername:'offline',merchantPassword:'offline'} withLogin.request=async()=>{throw secretError} await assert.rejects(run(withLogin),error=>error === secretError) assert.equal(withLogin.createdResources.phase5RootFulfillment.finalStatus,'FAILED') assert.equal(JSON.stringify(withLogin.createdResources.phase5RootFulfillment).includes(secretError.message),false) })