feat(admin): manage customs receipt and final files

This commit is contained in:
2026-09-26 13:28:48 +08:00
parent 391369a3d0
commit d2e063ea3f
4 changed files with 109 additions and 49 deletions
@@ -30,9 +30,10 @@ export interface ContainerCustomsDocument {
inboundIds: string[]
inboundCodes: string[]
frozen: boolean
completion: ContainerCustomsFile | null
receipt: ContainerCustomsFile | null
receiptStatus?: 'PENDING' | 'CONFIRMED' | 'REJECTED' | null
receiptRejectReason?: string | null
finalDocument: ContainerCustomsFile | null
}
export interface ContainerCustomsView {
@@ -60,7 +61,7 @@ interface RawContainerCustomsView extends Omit<ContainerCustomsView, 'aggregateI
containerBookingId: string | number
version: string | number
inbounds: Array<Omit<ContainerCustomsInbound, 'inboundId' | 'orderId' | 'tenantId'> & { inboundId: string | number; orderId: string | number; tenantId: string | number }>
documents: Array<Omit<ContainerCustomsDocument, 'documentId' | 'tenantId' | 'source' | 'completion'> & { documentId: string | number; tenantId: string | number; source: ContainerCustomsFile; completion: ContainerCustomsFile | null }>
documents: Array<Omit<ContainerCustomsDocument, 'documentId' | 'tenantId' | 'source' | 'receipt' | 'finalDocument'> & { documentId: string | number; tenantId: string | number; source: ContainerCustomsFile; receipt: ContainerCustomsFile | null; finalDocument: ContainerCustomsFile | null }>
}
const basePath = '/platform/v1/customs/loading-plans'
@@ -73,7 +74,7 @@ function normalize(value: RawContainerCustomsView): ContainerCustomsView {
...value,
aggregateId: String(value.aggregateId), loadingPlanId: String(value.loadingPlanId), containerBookingId: String(value.containerBookingId), version: Number(value.version),
inbounds: value.inbounds.map((item) => ({ ...item, inboundId: String(item.inboundId), orderId: String(item.orderId), tenantId: String(item.tenantId) })),
documents: value.documents.map((item) => ({ ...item, documentId: String(item.documentId), tenantId: String(item.tenantId), source: file(item.source)!, completion: file(item.completion) })),
documents: value.documents.map((item) => ({ ...item, documentId: String(item.documentId), tenantId: String(item.tenantId), source: file(item.source)!, receipt: file(item.receipt), finalDocument: file(item.finalDocument) })),
}
}
@@ -87,11 +88,15 @@ export function downloadContainerCustomsDocument(documentId: string) {
return http.get<Blob, Blob>(`${basePath}/documents/${encodeURIComponent(documentId)}/download`, { responseType: 'blob' })
}
export function downloadContainerCustomsCompletion(documentId: string) {
return http.get<Blob, Blob>(`${basePath}/documents/${encodeURIComponent(documentId)}/completion/download`, { responseType: 'blob' })
export function downloadContainerCustomsReceipt(documentId: string) {
return http.get<Blob, Blob>(`${basePath}/documents/${encodeURIComponent(documentId)}/receipt/download`, { responseType: 'blob' })
}
async function action(loadingPlanId: string, name: 'start' | 'complete-all-buy', expectedVersion: number, remark?: string) {
export function downloadContainerCustomsFinal(documentId: string) {
return http.get<Blob, Blob>(`${basePath}/documents/${encodeURIComponent(documentId)}/final/download`, { responseType: 'blob' })
}
async function action(loadingPlanId: string, name: 'start' | 'submit-to-broker' | 'complete-all-buy', expectedVersion: number, remark?: string) {
const response = await http.post<ApiEnvelope<RawContainerCustomsView>, ApiEnvelope<RawContainerCustomsView>>(
`${basePath}/${encodeURIComponent(loadingPlanId)}/${name}`,
{ expectedVersion, confirm: true, remark: remark?.trim() || null },
@@ -108,14 +113,26 @@ export function completeAllBuyContainerCustoms(loadingPlanId: string, expectedVe
return action(loadingPlanId, 'complete-all-buy', expectedVersion)
}
export async function uploadContainerCustomsCompletion(documentId: string, fileValue: File, expectedVersion: number) {
export function submitContainerCustomsToBroker(loadingPlanId: string, expectedVersion: number) {
return action(loadingPlanId, 'submit-to-broker', expectedVersion, '已将客户确认回执发送给报关行')
}
async function uploadFile(documentId: string, fileValue: File, expectedVersion: number, kind: 'receipt' | 'final') {
const body = new FormData()
body.append('file', fileValue)
const response = await http.post<ApiEnvelope<ContainerCustomsDocument>, ApiEnvelope<ContainerCustomsDocument>>(
`${basePath}/documents/${encodeURIComponent(documentId)}/completion/upload`,
`${basePath}/documents/${encodeURIComponent(documentId)}/${kind}/upload`,
body,
{ params: { expectedVersion }, headers: { 'Content-Type': 'multipart/form-data', 'Idempotency-Key': operationId('container-customs-completion') } },
{ params: { expectedVersion }, headers: { 'Content-Type': 'multipart/form-data', 'Idempotency-Key': operationId(`container-customs-${kind}`) } },
)
const result = unwrapApiResponse(response)
return { ...result, documentId: String(result.documentId), tenantId: String(result.tenantId), source: file(result.source)!, completion: file(result.completion) }
return { ...result, documentId: String(result.documentId), tenantId: String(result.tenantId), source: file(result.source)!, receipt: file(result.receipt), finalDocument: file(result.finalDocument) }
}
export function uploadContainerCustomsReceipt(documentId: string, fileValue: File, expectedVersion: number) {
return uploadFile(documentId, fileValue, expectedVersion, 'receipt')
}
export function uploadContainerCustomsFinal(documentId: string, fileValue: File, expectedVersion: number) {
return uploadFile(documentId, fileValue, expectedVersion, 'final')
}