feat(customs): show broker submission state in admin
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { http } from '@/shared/api/http'
|
||||
import { completeAllBuyContainerCustoms, downloadContainerCustomsReceipt, downloadContainerCustomsDocument, getContainerCustoms, startContainerCustoms, uploadContainerCustomsReceipt } from './platformContainerCustoms'
|
||||
import { completeAllBuyContainerCustoms, downloadContainerCustomsReceipt, downloadContainerCustomsDocument, getContainerCustoms, sendContainerCustomsToBroker, startContainerCustoms, uploadContainerCustomsReceipt } from './platformContainerCustoms'
|
||||
|
||||
const customs = {
|
||||
aggregateId: 2092438843630391401n, loadingPlanId: 2092438843630391402n, containerBookingId: 2092438843630391403n,
|
||||
planNo: 'LP-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', status: 'READY_TO_START', hasActualCargo: true, version: '3', taxRefundCount: 1, coveredTaxRefundCount: 1, actualBoxCount: 12,
|
||||
inbounds: [{ inboundId: 2092438843630391404n, orderId: 2092438843630391405n, orderNo: 'O-1', inboundCode: 'RK-1', tenantId: 2092438843630391406n, customerName: '客户一', taxMode: 'TAX_REFUND', covered: true }],
|
||||
documents: [{ documentId: 2092438843630391407n, tenantId: 2092438843630391406n, source: { fileId: '2092438843630391408', fileName: 'source.pdf', fileHash: 'hash', sizeBytes: 1, version: 2, uploadedAt: '2026-08-28T10:00:00' }, inboundIds: ['2092438843630391404'], inboundCodes: ['RK-1'], frozen: true, receipt: null, finalDocument: null }],
|
||||
documents: [{ documentId: 2092438843630391407n, tenantId: 2092438843630391406n, source: { fileId: '2092438843630391408', fileName: 'source.pdf', fileHash: 'hash', sizeBytes: 1, version: 2, uploadedAt: '2026-08-28T10:00:00' }, inboundIds: ['2092438843630391404'], inboundCodes: ['RK-1'], frozen: true, receipt: null, finalDocument: null, confirmationHistory: [] }],
|
||||
brokerSubmission: null,
|
||||
startedAt: null, completedAt: null,
|
||||
}
|
||||
|
||||
@@ -39,6 +40,13 @@ describe('platform container customs API', () => {
|
||||
expect(post.mock.calls[0]?.[2]).toMatchObject({ headers: { 'Idempotency-Key': expect.any(String) } })
|
||||
})
|
||||
|
||||
it('sends the frozen source documents to the broker through the first-send action', async () => {
|
||||
const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: customs })
|
||||
await sendContainerCustomsToBroker('2092438843630391402', 3)
|
||||
expect(post.mock.calls[0]?.slice(0, 2)).toEqual(['/platform/v1/customs/loading-plans/2092438843630391402/send-to-broker', { expectedVersion: 3, confirm: true, remark: '已将冻结退税报关资料首次发送给报关行' }])
|
||||
expect(post.mock.calls[0]?.[2]).toMatchObject({ headers: { 'Idempotency-Key': expect.any(String) } })
|
||||
})
|
||||
|
||||
it('uploads one receipt file without completing the container', async () => {
|
||||
const document = { ...customs.documents[0], documentId: '2092438843630391407', tenantId: '2092438843630391406' }
|
||||
const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: document })
|
||||
|
||||
@@ -19,6 +19,29 @@ export interface ContainerCustomsReceiptVersion {
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type ContainerCustomsConfirmationAction = 'CONFIRM' | 'REJECT'
|
||||
|
||||
export interface ContainerCustomsConfirmationHistory {
|
||||
id: string
|
||||
receiptVersion: number
|
||||
action: ContainerCustomsConfirmationAction
|
||||
reason: string | null
|
||||
operatorName: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface ContainerCustomsBrokerSubmission {
|
||||
id: string
|
||||
customsContainerId: string
|
||||
loadingPlanId: string
|
||||
batchNo: string
|
||||
status: 'SENT'
|
||||
sourceVersion: number
|
||||
sentByName: string
|
||||
sentAt: string
|
||||
version: number
|
||||
}
|
||||
|
||||
export interface ContainerCustomsInbound {
|
||||
inboundId: string
|
||||
orderId: string
|
||||
@@ -42,6 +65,7 @@ export interface ContainerCustomsDocument {
|
||||
receiptRejectReason?: string | null
|
||||
finalDocument: ContainerCustomsFile | null
|
||||
receiptHistory?: ContainerCustomsReceiptVersion[]
|
||||
confirmationHistory: ContainerCustomsConfirmationHistory[]
|
||||
}
|
||||
|
||||
export interface ContainerCustomsView {
|
||||
@@ -59,6 +83,7 @@ export interface ContainerCustomsView {
|
||||
actualBoxCount: number
|
||||
inbounds: ContainerCustomsInbound[]
|
||||
documents: ContainerCustomsDocument[]
|
||||
brokerSubmission: ContainerCustomsBrokerSubmission | null
|
||||
startedAt: string | null
|
||||
completedAt: string | null
|
||||
brokerSubmittedAt?: string | null
|
||||
@@ -119,7 +144,24 @@ 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)!, receipt: file(item.receipt), finalDocument: file(item.finalDocument), receiptHistory: (item.receiptHistory || []).map((version) => ({ ...version, version: Number(version.version), file: file(version.file)! })) })),
|
||||
brokerSubmission: value.brokerSubmission ? {
|
||||
...value.brokerSubmission,
|
||||
id: String(value.brokerSubmission.id),
|
||||
customsContainerId: String(value.brokerSubmission.customsContainerId),
|
||||
loadingPlanId: String(value.brokerSubmission.loadingPlanId),
|
||||
sourceVersion: Number(value.brokerSubmission.sourceVersion),
|
||||
version: Number(value.brokerSubmission.version),
|
||||
} : null,
|
||||
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),
|
||||
receiptHistory: (item.receiptHistory || []).map((version) => ({ ...version, version: Number(version.version), file: file(version.file)! })),
|
||||
confirmationHistory: (item.confirmationHistory || []).map((history) => ({ ...history, id: String(history.id), receiptVersion: Number(history.receiptVersion), reason: history.reason ?? null })),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +216,7 @@ 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) {
|
||||
async function action(loadingPlanId: string, name: 'start' | 'send-to-broker' | '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 },
|
||||
@@ -187,6 +229,10 @@ export function startContainerCustoms(loadingPlanId: string, expectedVersion: nu
|
||||
return action(loadingPlanId, 'start', expectedVersion)
|
||||
}
|
||||
|
||||
export function sendContainerCustomsToBroker(loadingPlanId: string, expectedVersion: number) {
|
||||
return action(loadingPlanId, 'send-to-broker', expectedVersion, '已将冻结退税报关资料首次发送给报关行')
|
||||
}
|
||||
|
||||
export function completeAllBuyContainerCustoms(loadingPlanId: string, expectedVersion: number) {
|
||||
return action(loadingPlanId, 'complete-all-buy', expectedVersion)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user