import { beforeEach, describe, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' import { getConfirmedLoadingList, confirmPreloadPlan, uploadLoadingDeclaration, completeLoadingDeclaration, getPreloadPlan, listConfirmedLoadingLists, listPreloadPlans, updatePreloadTransport, } from './platformLoadingLists' const preloadSummary = { planId: 1900000000000000001n as unknown as string, containerBookingId: 1900000000000000004n as unknown as string, preloadNo: 'PL-001', containerNo: 'MSCU1234567', cabinetNo: 'YG-001', customers: [{ accountId: 1900000000000000002n as unknown as string, customerName: '测试客户' }], warehouseId: 1900000000000000003n as unknown as string, warehouseName: '义乌仓', vesselName: 'MATSON', voyageNo: '001E', containerType: '40HC', status: 'LOADING', skuCount: '2', totalPackageCount: '10', totalVolumeCbm: 12.3, totalGrossWeightKg: 1200, createdAt: '2026-08-10T10:00:00', version: 3, readiness: { preloadPushed: true, containerNoReady: true, preloadLinkCount: 1, confirmedPreloadCount: 0, oceanBookingId: 1900000000000000005n as unknown as string, oceanBookingStatus: 'RELEASED', pdaTaskId: null, pdaTaskStatus: null, confirmationStatus: 'PENDING', canConfirm: true, readyForPda: false, blockingReasons: [], }, } const detail = { summary: preloadSummary, items: [{ itemId: 11, accountId: 12, inboundId: 13, inboundDetailId: 14, productQuantity: 20, customerName: '测试客户', inboundCode: 'RK001', skuCode: 'SKU001', destinationWarehouse: 'ONT8', chineseName: '产品', englishName: null, goodsImageUrl: null, packageCount: 2, palletCount: 1, grossWeightKg: 10, netWeightKg: 9, volumeCbm: 1, dimensionalWeightKg: 2, unitsPerPackage: 10, fbaTrackingNo: null, shipmentNo: null, shippingMark: null, internalRemark: null, customerRemark: null, warehouseRemark: null, loadSequence: 1, frozenAt: null, }], histories: [{ historyId: 21, itemId: 11, actionType: 'UPDATE_TRANSPORT', fromStatus: 'LOADING', toStatus: 'LOADING', beforeSnapshot: null, afterSnapshot: null, operatorId: 22, operatorName: '操作员', operationReason: '调整', occurredAt: '2026-08-10T11:00:00' }], } describe('platform loading lists API', () => { beforeEach(() => vi.restoreAllMocks()) it('queries preload plans without forcing a status so EDITING and LOADING are returned', async () => { const get = vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'ok', data: { records: [preloadSummary], page: '1', size: '20', total: '1', pages: '1' } }) const result = await listPreloadPlans({ page: 1, size: 20, keyword: 'PL' }) expect(get).toHaveBeenCalledWith('/platform/v1/first-leg/loading-plans', { params: { page: 1, size: 20, keyword: 'PL' } }) expect(result.total).toBe(1) expect(result.records[0]?.planId).toBe('1900000000000000001') expect(result.records[0]?.containerBookingId).toBe('1900000000000000004') expect(result.records[0]?.readiness.oceanBookingId).toBe('1900000000000000005') expect(result.records[0]?.customers[0]?.accountId).toBe('1900000000000000002') }) it('normalizes long ids in preload detail items and history', async () => { vi.spyOn(http, 'get').mockResolvedValue({ code: '200', message: 'ok', data: detail }) const result = await getPreloadPlan('100', '300') expect(result.items[0]).toMatchObject({ itemId: '11', accountId: '12', inboundId: '13', inboundDetailId: '14', productQuantity: '20' }) expect(result.summary.containerBookingId).toBe('1900000000000000004') expect(result.histories[0]).toMatchObject({ historyId: '21', itemId: '11', operatorId: '22' }) }) it('updates transport with expectedVersion, warehouse and idempotency key', async () => { const patch = vi.spyOn(http, 'patch').mockResolvedValue({ code: '200', message: 'ok', data: detail }) await updatePreloadTransport('100', '300', { containerNo: 'MSCU1234567', voyageNo: '001E', expectedVersion: 3, operationReason: '调整' }) expect(patch.mock.calls[0]?.[0]).toBe('/platform/v1/first-leg/loading-plans/100/transport') expect(patch.mock.calls[0]?.[1]).toMatchObject({ expectedVersion: 3, voyageNo: '001E' }) expect(patch.mock.calls[0]?.[2]).toMatchObject({ params: { warehouseId: '300' }, headers: { 'Idempotency-Key': expect.any(String) } }) }) it('uses distinct completed list and detail endpoints', async () => { const confirmed = { ...preloadSummary, loadingListNo: 'LL-001', totalNetWeightKg: 1100, totalQuantity: '20', remark: null, createdBy: 9, createdByName: '操作员', status: 'COMPLETED' } const get = vi.spyOn(http, 'get') .mockResolvedValueOnce({ code: '200', message: 'ok', data: { records: [confirmed], page: '1', size: '20', total: '1', pages: '1' } }) .mockResolvedValueOnce({ code: '200', message: 'ok', data: { ...detail, summary: confirmed } }) const page = await listConfirmedLoadingLists({ page: 1, size: 20 }) const loaded = await getConfirmedLoadingList('100', '300') expect(get.mock.calls[0]?.[0]).toBe('/platform/v1/first-leg/loading-lists') expect(get.mock.calls[0]?.[1]).toEqual({ params: { page: 1, size: 20 } }) expect(get.mock.calls[1]?.[0]).toBe('/platform/v1/first-leg/loading-lists/100') expect(page.records[0]?.createdBy).toBe('9') expect(page.records[0]?.containerBookingId).toBe('1900000000000000004') expect(loaded.summary.status).toBe('COMPLETED') }) it('confirms the whole preload plan with expectedVersion and the caller operation id', async () => { const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: { planId: 1900000000000000001n, containerBookingId: 1900000000000000004n, confirmationStatus: 'CONFIRMED', version: 4, readiness: { ...preloadSummary.readiness, confirmedPreloadCount: 1, confirmationStatus: 'CONFIRMED', canConfirm: false, readyForPda: true, pdaTaskId: 1900000000000000006n, pdaTaskStatus: 'PENDING', blockingReasons: ['PRELOAD_ALREADY_CONFIRMED'], }, }, }) const result = await confirmPreloadPlan('1900000000000000001', 3, 'confirm-operation-1') expect(post).toHaveBeenCalledWith( '/platform/v1/first-leg/loading-plans/1900000000000000001/confirm', { expectedVersion: 3 }, { headers: { 'Idempotency-Key': 'confirm-operation-1' } }, ) expect(result).toMatchObject({ planId: '1900000000000000001', containerBookingId: '1900000000000000004', confirmationStatus: 'CONFIRMED', version: 4, readiness: { pdaTaskId: '1900000000000000006', readyForPda: true }, }) }) it('uses stable plan and warehouse ids for customs document actions', async () => { const post = vi.spyOn(http, 'post').mockResolvedValue({ code: '200', message: 'ok', data: { id: 6, documentType: 'CUSTOMS_DECLARATION', fileId: 9, fileName: 'declaration.pdf', version: 2 } }) const file = new File(['declaration'], 'declaration.pdf', { type: 'application/pdf' }) await uploadLoadingDeclaration('100', '300', file, 1, '上传资料') await completeLoadingDeclaration('100', '300', 2, '完成报关') expect(post.mock.calls[0]?.[0]).toBe('/platform/v1/first-leg/loading-lists/100/customs/document/upload') const body = post.mock.calls[0]?.[1] as FormData expect(body.get('file')).toBe(file) expect(body.get('expectedVersion')).toBe('1') expect(body.get('operationReason')).toBe('上传资料') expect(post.mock.calls[0]?.[2]).toMatchObject({ params: { warehouseId: '300' }, headers: { 'Content-Type': 'multipart/form-data', 'Idempotency-Key': expect.any(String) } }) expect(post.mock.calls[1]?.[0]).toBe('/platform/v1/first-leg/loading-lists/100/customs/complete') }) })