Files
baiyue-firstleg/scripts/admin-test-env-modules/phase5-root-tax-customs.mjs
T

98 lines
7.5 KiB
JavaScript
Raw Normal View History

import { createHash } from 'node:crypto'
const MODULE='phase5-root-fulfillment',OPTIONS={scenario:'f40-tax-customs-after-completion',deploymentBlocker:true}
const PLATFORM='/api/platform/v1/customs/loading-plans',MERCHANT='/api/merchant/v1/customs'
const id=value=>String(value??'')
const sha=bytes=>createHash('sha256').update(bytes).digest('hex')
function check(context,name,passed,response=null){
context.assert(MODULE,name,response,Boolean(passed),'exact-current-tax-source',OPTIONS)
if(!passed)throw Error(name)
}
async function api(context,method,name,path,body,token=context.token,key){
const response=await context.request(method,path,body,token,key?{'Idempotency-Key':key}:{})
const data=context.apiSuccess(MODULE,name,response,'',OPTIONS)
check(context,name,response?.http===200&&String(response.body?.code)==='200',response)
return data
}
export function taxDocumentMatches(view,proof){
const documents=view?.documents||[]
const document=documents.find(row=>id(row.documentId)===proof.documentId)
return view?.status==='COMPLETED'&&id(view.loadingPlanId)===proof.loadingPlanId
&&id(view.containerBookingId)===proof.containerBookingId&&document?.frozen===true
&&id(document.tenantId)===proof.customerId&&document.inboundIds?.map(id).includes(proof.inboundId)
&&document.source?.fileHash===proof.files.source.sha256&&document.receiptStatus==='CONFIRMED'
&&document.receipt?.fileHash===proof.files.receipt.sha256&&document.finalDocument?.fileHash===proof.files.final.sha256
}
export async function prepareTaxCustoms(context,loading){
const proof={customerId:id(loading.tenantId),orderId:id(loading.orderId),inboundId:id(loading.inboundId),
inboundCode:loading.inboundCode,containerBookingId:id(loading.containerBookingId),loadingPlanId:id(loading.loadingPlanId),
containerNo:loading.containerNo,files:{},stages:[],status:'PREPARING'}
context.context.f40TaxEvidence.push(proof)
const token=context.context.f40TaxMerchantToken
check(context,'F40 退税报关使用本轮客户会话',Boolean(token))
const base=`${PLATFORM}/${proof.loadingPlanId}`
const key=action=>`${context.marker}-F40-TAX-${action}`.slice(0,128)
const detail=()=>api(context,'GET','F40 回查真实退税报关聚合',base)
const upload=async(kind,path,token)=>{
const content=new TextEncoder().encode(`${context.marker} TAX_REFUND ${proof.inboundCode} ${kind}`)
proof.files[kind]={sha256:sha(content),byteCount:content.length}
const response=await context.requestMultipart('POST',path,{name:`${context.marker}-${kind}.txt`,type:'text/plain',content},token,{'Idempotency-Key':key(kind)})
const data=context.apiSuccess(MODULE,`F40 真实上传退税${kind}文件`,response,'',OPTIONS)
check(context,`F40 退税${kind}上传成功`,response.http===200&&String(response.body?.code)==='200',response)
return data
}
let current=await detail()
check(context,'F40 实装来源确为本客户退税入仓码',current.inbounds?.length===1&&current.inbounds.every(row=>
row.taxMode==='TAX_REFUND'&&id(row.inboundId)===proof.inboundId))
const source=await upload('source',`${MERCHANT}/loading-plans/${proof.loadingPlanId}/documents/upload?inboundIds=${proof.inboundId}`,token)
proof.documentId=id(source.documentId)
check(context,'F40 客户原始报关文件精确绑定本票',Boolean(proof.documentId)&&source.source?.fileHash===proof.files.source.sha256
&&id(source.tenantId)===proof.customerId&&source.inboundIds?.map(id).includes(proof.inboundId))
for(const action of ['start','send-to-broker']){
current=await detail()
const next=await api(context,'POST',`F40 退税真实${action}`,`${base}/${action}`,
{expectedVersion:Number(current.version),confirm:true,remark:context.marker},context.token,key(action))
proof.stages.push({action,status:next.status,version:next.version})
}
const receipt=await upload('receipt',`${PLATFORM}/documents/${proof.documentId}/receipt/upload?expectedVersion=0`,context.token)
await api(context,'POST','F40 客户确认本票报关回执',`${MERCHANT}/documents/${proof.documentId}/receipt/confirm`,
{expectedVersion:Number(receipt.receipt.version),confirm:true,remark:context.marker},token,key('confirm'))
current=await detail()
await api(context,'POST','F40 平台发送已确认回执执行报关',`${base}/submit-to-broker`,
{expectedVersion:Number(current.version),confirm:true,remark:context.marker},context.token,key('execute'))
current=await detail()
const batch=await api(context,'POST','F40 人工创建真实正式申报批次',`${base}/declaration-batches`,
{expectedVersion:Number(current.version),batchNo:`${context.marker}-DECL`.slice(-64),remark:context.marker},context.token,key('declaration'))
proof.declarationBatchId=id(batch.id)
const submitted=await api(context,'POST','F40 人工提交正式申报',`${PLATFORM}/declaration-batches/${batch.id}/submit`,
{expectedVersion:Number(batch.version),customsEntryNo:`F40TAX-${proof.loadingPlanId}`,remark:context.marker},context.token,key('submit-declaration'))
const now=new Date(),local=new Date(now.getTime()-now.getTimezoneOffset()*60000).toISOString().slice(0,19)
const release=await api(context,'POST','F40 人工记录海关放行',`${PLATFORM}/declaration-batches/${batch.id}/results`,
{expectedVersion:Number(submitted.version),resultType:'RELEASED',occurredAt:local,resolution:'本轮退税验收人工放行',resolved:true},context.token,key('release'))
check(context,'F40 真实申报已提交且放行已处理',submitted.status==='SUBMITTED'&&release.resultType==='RELEASED'&&release.resultStatus==='RESOLVED')
await upload('final',`${PLATFORM}/documents/${proof.documentId}/final/upload?expectedVersion=0`,context.token)
proof.completedCustoms=await detail()
check(context,'F40 退税完成文件齐全才完成报关',taxDocumentMatches(proof.completedCustoms,proof))
proof.status='CUSTOMS_COMPLETED'
context.createdResources.phase3Customs={...loading,parentOrderId:loading.orderId,finalStatus:'CLEARANCE_COMPLETED'}
return proof
}
export async function verifyTaxDocumentsAfterCompletion(context,proofs,merchantToken){
check(context,'F40 完成后验收具有真实退税报关证据',proofs.length>0)
for(const proof of proofs){
const order=await api(context,'GET','F40 客户回查退税原订单最终完成',`/api/merchant/v1/orders/${proof.orderId}`,undefined,merchantToken)
check(context,'F40 退税订单最终COMPLETED且税制未变',id(order.summary?.orderId)===proof.orderId&&order.summary.mainStatus==='COMPLETED'&&order.summary.taxMode==='TAX_REFUND')
proof.afterCompletedView=await api(context,'GET','F40 COMPLETED后客户仍可读取本票报关资料',`${MERCHANT}/orders/${proof.orderId}`,undefined,merchantToken)
check(context,'F40 完成后报关资料客户范围及文件哈希保持一致',taxDocumentMatches(proof.afterCompletedView,proof))
proof.downloads=[]
const customer=Object.create(context);customer.token=merchantToken
for(const [kind,suffix] of [['source','download'],['receipt','receipt/download'],['final','final/download']]){
const response=await customer.requestBinary('GET',`${MERCHANT}/documents/${proof.documentId}/${suffix}`)
const fact={kind,http:response.http,byteCount:response.bytes?.length||0,sha256:sha(response.bytes||new Uint8Array())}
proof.downloads.push(fact)
check(context,`F40 COMPLETED后客户真实下载${kind}字节保持一致`,fact.http===200&&fact.byteCount===proof.files[kind].byteCount&&fact.sha256===proof.files[kind].sha256,
{...response,bytes:undefined})
}
proof.status='ORDER_COMPLETED_CUSTOMS_READ_DOWNLOAD_VERIFIED'
}
}