import { beforeEach, describe, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' import { approvePlatformUnifiedOrder, batchApprovePlatformUnifiedOrders, getPlatformUnifiedOrderBatchApprovalPreview, addPlatformUnifiedOrderNote, getPlatformUnifiedOrder, downloadPlatformProductImage, listPlatformUnifiedOrders, listPlatformUnifiedOrderFeeDetails, updatePlatformUnifiedOrderDeclaration, updatePlatformUnifiedOrderPlanning, } from './unifiedOrders' vi.mock('@/shared/api/http', () => ({ http: { get: vi.fn(), post: vi.fn(), put: vi.fn() }, unwrapApiResponse: (response: { data: T }) => response.data, })) describe('unified orders API contract', () => { beforeEach(() => { vi.mocked(http.get).mockReset() vi.mocked(http.post).mockReset() vi.mocked(http.put).mockReset() }) it('uses the C02 platform orders endpoint and contract filters', async () => { vi.mocked(http.get).mockResolvedValue({ data: { records: [], page: '2', size: '20', total: '0', pages: '0' } }) await expect(listPlatformUnifiedOrders({ keyword: 'CC', orderNo: 'CC-1', mainStatus: 'WAIT_SPLIT', customerId: '30001', receivingWarehouseId: '11', destinationWarehouseId: '22', inboundCode: 'RK-1', internalOrderStatus: 'ACCEPTED', expectedArrivalDate: '2026-08-10', feeReviewStatus: 'APPROVED', channelName: '海运', containerNo: 'CONT-1', cabinetNo: 'CAB-1', sailingDate: '2026-08-12', loaded: true, fullContainer: false, measurementReviewStatus: 'PENDING', fbaCode: 'FBA-1', createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59', page: 2, size: 20, })).resolves.toMatchObject({ page: 2, size: 20, total: 0, pages: 0 }) expect(http.get).toHaveBeenCalledWith('/platform/v1/orders', { params: { keyword: 'CC', orderNo: 'CC-1', mainStatus: 'WAIT_SPLIT', customerId: '30001', receivingWarehouseId: '11', destinationWarehouseId: '22', inboundCode: 'RK-1', internalOrderStatus: 'ACCEPTED', expectedArrivalDate: '2026-08-10', feeReviewStatus: 'APPROVED', channelName: '海运', containerNo: 'CONT-1', cabinetNo: 'CAB-1', sailingDate: '2026-08-12', loaded: true, fullContainer: false, measurementReviewStatus: 'PENDING', fbaCode: 'FBA-1', createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59', page: 2, size: 20, }, }) }) it('omits empty optional filters while preserving false boolean values', async () => { vi.mocked(http.get).mockResolvedValue({ data: { records: [], page: 1, size: 20, total: 0, pages: 0 } }) await listPlatformUnifiedOrders({ keyword: '', loaded: false, fullContainer: false, page: 1, size: 20 }) expect(http.get).toHaveBeenCalledWith('/platform/v1/orders', { params: { loaded: false, fullContainer: false, page: 1, size: 20 }, }) }) it('forwards every main-status tab with search and pagination unchanged', async () => { const statuses = [ 'PENDING_REVIEW', 'WAIT_MEASURE', 'WAIT_SPLIT', 'LOADING', 'LOADED', 'DELIVERED', ] as const vi.mocked(http.get).mockResolvedValue({ data: { records: [], page: 3, size: 20, total: 0, pages: 0 } }) for (const mainStatus of statuses) { await listPlatformUnifiedOrders({ keyword: 'CC-2026', orderNo: 'FL-77', mainStatus, page: 3, size: 20 }) } expect(vi.mocked(http.get).mock.calls).toEqual(statuses.map((mainStatus) => [ '/platform/v1/orders', { params: { keyword: 'CC-2026', orderNo: 'FL-77', mainStatus, page: 3, size: 20 } }, ])) }) it('normalizes C02 row IDs and keeps inboundId for measurement routing', async () => { vi.mocked(http.get).mockResolvedValue({ data: { records: [{ orderId: '90001', orderVersion: '7', tenantId: '208', inboundId: '77', inboundVersion: 3, mainStatus: 'WAIT_MEASURE', channelCode: ' NY_EXX ', channelName: '纽约渠道', holdFlag: true, currentHoldReason: '待核对货款', locationCodes: 'A0-01, B2-03', estimatedVolumeCbm: '0.4', expectedVolumeCbm: '0.45', actualGrossWeightKg: '12.5', actualVolumeCbm: '0.46', totalPalletCount: '3', totalNetWeightKg: '11.2', chargeableVolumeCbm: '0.5', totalQuantity: '48', skuNos: ['SKU-1', ''], requestedPickupAt: '2026-08-18T09:30:00', customerRemark: '客户备注', latestInternalNote: '最新内部备注', relation: { role: 'PARENT', rootOrderId: 90001, parentOrderId: null, splitSequence: null, childCount: '2' }, measurement: null, preload: null, loading: null, delivery: null, transport: null }], page: 1, size: 20, total: 1, pages: 1, } }) await expect(listPlatformUnifiedOrders({ page: 1, size: 20 })).resolves.toMatchObject({ records: [{ orderId: '90001', orderVersion: 7, tenantId: '208', inboundId: '77', inboundVersion: 3, channelCode: 'NY_EXX', holdFlag: true, currentHoldReason: '待核对货款', locationCodes: 'A0-01, B2-03', estimatedVolumeCbm: 0.4, expectedVolumeCbm: 0.45, actualGrossWeightKg: 12.5, actualVolumeCbm: 0.46, totalPalletCount: 3, totalNetWeightKg: 11.2, chargeableVolumeCbm: 0.5, totalQuantity: 48, skuNos: ['SKU-1'], requestedPickupAt: '2026-08-18T09:30:00', customerRemark: '客户备注', latestInternalNote: '最新内部备注', relation: { role: 'PARENT', rootOrderId: '90001', childCount: 2 } }], }) }) it('loads the C02 detail shape and passes tenantId only on the detail boundary', async () => { vi.mocked(http.get).mockResolvedValue({ data: { summary: { orderId: '90001', tenantId: '208', mainStatus: 'WAIT_SPLIT' }, order: { order: {}, cargoes: [], parties: [], internalNotes: [], statusHistory: [] }, timeline: [], } }) await expect(getPlatformUnifiedOrder('208', '90001')).resolves.toMatchObject({ summary: { orderId: '90001' }, order: { internalNotes: [] }, timeline: [], }) expect(http.get).toHaveBeenCalledWith('/platform/v1/orders/90001', { params: { tenantId: '208' } }) }) it('downloads a tenant-scoped product image for platform order details', async () => { const image = new Blob(['image'], { type: 'image/png' }) vi.mocked(http.get).mockResolvedValue(image) await expect(downloadPlatformProductImage('208/1', '90001')).resolves.toBe(image) expect(http.get).toHaveBeenCalledWith('/platform/v1/files/product-images/208%2F1/90001', { responseType: 'blob' }) }) it('uses the explicit first-leg approval boundary for pending review', async () => { vi.mocked(http.post).mockResolvedValue({ data: null }) await approvePlatformUnifiedOrder('208', '90001') expect(http.post).toHaveBeenCalledWith('/platform/v1/orders/first-leg/90001/approve', undefined, { params: { tenantId: '208' } }) }) it('keeps split rows bound to their own current-container schedule and preserves effective-time semantics', async () => { vi.mocked(http.get).mockResolvedValue({ data: { records: [ { orderId: '90001', tenantId: '208', inboundId: '701', mainStatus: 'LOADED', relation: { role: 'PARENT', rootOrderId: '90001', parentOrderId: null, splitSequence: null, childCount: 1 }, transport: { cabinetNo: 'OLD-CAB', estimatedSailingAt: '2026-08-20T08:00:00', actualSailingAt: '2026-08-21T09:00:00', effectiveSailingAt: '2026-08-21T09:00:00', effectiveSailingAtEstimated: false, estimatedPortArrivalAt: '2026-09-01T08:00:00', actualPortArrivalAt: null, effectivePortArrivalAt: '2026-09-01T08:00:00', effectivePortArrivalAtEstimated: true } }, { orderId: '90002', tenantId: '208', inboundId: '702', mainStatus: 'LOADED', relation: { role: 'CHILD', rootOrderId: '90001', parentOrderId: '90001', splitSequence: 1, childCount: 0 }, transport: { cabinetNo: 'NEW-CAB', estimatedSailingAt: '2026-08-25T08:00:00', actualSailingAt: null, effectiveSailingAt: '2026-08-25T08:00:00', effectiveSailingAtEstimated: true, estimatedPortArrivalAt: null, actualPortArrivalAt: null, effectivePortArrivalAt: null, effectivePortArrivalAtEstimated: null } }, ], page: 1, size: 20, total: 2, pages: 1, } }) const result = await listPlatformUnifiedOrders({ page: 1, size: 20 }) expect(result.records.map((row) => ({ inboundId: row.inboundId, transport: row.transport }))).toMatchObject([ { inboundId: '701', transport: { cabinetNo: 'OLD-CAB', effectiveSailingAt: '2026-08-21T09:00:00', effectiveSailingAtEstimated: false, effectivePortArrivalAt: '2026-09-01T08:00:00', effectivePortArrivalAtEstimated: true } }, { inboundId: '702', transport: { cabinetNo: 'NEW-CAB', effectiveSailingAt: '2026-08-25T08:00:00', effectiveSailingAtEstimated: true, effectivePortArrivalAt: null, effectivePortArrivalAtEstimated: null } }, ]) }) it('uses the tenant-scoped batch approval contract and preserves long IDs', async () => { vi.mocked(http.post).mockResolvedValue({ data: { total: 2, successCount: 1, failureCount: 1, successOrderIds: ['2089223263733518337'], failedOrders: [{ orderId: '2089223263733518338', code: 'A0409', message: '订单状态已变化' }], } }) await expect(batchApprovePlatformUnifiedOrders('2089176869176389634', [ { orderId: '2089223263733518337', orderVersion: 3 }, { orderId: '2089223263733518338', orderVersion: 8 }, ])).resolves.toMatchObject({ successCount: 1, failureCount: 1 }) expect(http.post).toHaveBeenCalledWith( '/platform/v1/orders/first-leg/batch-approve', { items: [ { orderId: '2089223263733518337', orderVersion: 3 }, { orderId: '2089223263733518338', orderVersion: 8 }, ] }, { params: { tenantId: '2089176869176389634' } }, ) }) it('loads one batch-review preview with Chinese goods and normalizes long IDs', async () => { vi.mocked(http.post).mockResolvedValue({ data: { total: 1, approvableCount: 1, items: [{ orderId: '2089223263733518337', orderVersion: '3', inboundCode: 'NYEXX2609080001', customerId: '2089176869176389634', customerName: '测试客户', chineseGoodsNames: [' 蓝牙音箱 ', ''], channelCode: 'NY_EXX', channelName: '纽约海运', destinationWarehouseCode: 'ONT8', destinationWarehouseName: 'ONT8 亚马逊仓', canApprove: true, blockReason: null, }], } }) const items = [{ orderId: '2089223263733518337', orderVersion: 3 }] await expect(getPlatformUnifiedOrderBatchApprovalPreview('2089176869176389634', items)).resolves.toMatchObject({ total: 1, approvableCount: 1, items: [{ orderId: '2089223263733518337', orderVersion: 3, chineseGoodsNames: ['蓝牙音箱'] }], }) expect(http.post).toHaveBeenCalledWith( '/platform/v1/orders/first-leg/batch-review-preview', { items }, { params: { tenantId: '2089176869176389634' } }, ) }) it('writes platform internal notes through the unified order boundary', async () => { vi.mocked(http.post).mockResolvedValue({ data: '91001' }) await expect(addPlatformUnifiedOrderNote('208', '90001', '已确认船期')).resolves.toBe('91001') expect(http.post).toHaveBeenCalledWith('/platform/v1/orders/90001/internal-notes', { content: '已确认船期' }, { params: { tenantId: '208' } }) }) it('updates customer declaration fields through the versioned tenant-scoped contract', async () => { const request = { expectedVersion: 7, customerRemark: '易碎', expectedArrivalAt: '2026-09-06T10:00:00', channelCode: 'US-SEA', channelName: '美西海运', cargoes: [{ itemNo: 1, cargoName: '台灯', packageCount: 2, totalQuantity: 20, lengthCm: 40, widthCm: 30, heightCm: 20, grossWeightKg: 18, volumeCbm: 0.048 }], } vi.mocked(http.put).mockResolvedValue({ data: { orderNo: 'FL-1', inboundCode: 'RK-1', previousVersion: 7, version: 8, channelChangedBefore: false, channelChanged: true, changes: [] } }) await expect(updatePlatformUnifiedOrderDeclaration('208', '90001', request)).resolves.toMatchObject({ version: 8, channelChanged: true }) expect(http.put).toHaveBeenCalledWith('/platform/v1/orders/90001/declaration', request, { params: { tenantId: '208' } }) }) it('updates arrival and operator-estimated volume through the dedicated versioned contract', async () => { const request = { expectedVersion: 7, expectedArrivalAt: '2026-09-10T18:30:00', estimatedVolumeCbm: 0.625, } vi.mocked(http.put).mockResolvedValue({ data: { previousVersion: 7, version: 8, changes: [] } }) await expect(updatePlatformUnifiedOrderPlanning('2089176869176389634', '2089223263733518337', request)) .resolves.toMatchObject({ version: 8 }) expect(http.put).toHaveBeenCalledWith( '/platform/v1/orders/2089223263733518337/planning', request, { params: { tenantId: '2089176869176389634' } }, ) }) it('loads fee detail rows from the inbound fee-detail contract', async () => { vi.mocked(http.get).mockResolvedValue({ data: [{ id: '1', inboundId: '88', reviewStatus: 'APPROVED', amount: 100 }] }) await expect(listPlatformUnifiedOrderFeeDetails('208', '88')).resolves.toHaveLength(1) expect(http.get).toHaveBeenCalledWith('/platform/v1/inbounds/88/fee-details', { params: { accountId: '208' } }) }) })