59 lines
6.9 KiB
TypeScript
59 lines
6.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
const api = vi.hoisted(() => ({ get: vi.fn(), post: vi.fn() }))
|
|
vi.mock('@/shared/api/http', async importOriginal => ({ ...(await importOriginal<typeof import('@/shared/api/http')>()), http: api }))
|
|
import { submitProcurementCommand, uploadProcurementFile, downloadProcurementFile, type ProcurementCommand } from './procurementCommands'
|
|
const envelope = <T>(data: T) => ({ code: '200', message: 'success', data })
|
|
beforeEach(() => vi.clearAllMocks())
|
|
describe('procurement node HTTP contract', () => {
|
|
it.each<ProcurementCommand>(['review', 'purchase', 'purchase/confirm', 'budget-authorizations', 'planned-warehouse', 'cancel', 'overseas-receipts', 'domestic-receipts', 'overseas-receipts/complete', 'domestic-receipts/complete', 'packing', 'packing/confirm', 'packing/withdraw', 'dispositions', 'labels', 'international-estimate', 'labels/apply', 'domestic-repack', 'overseas-shipment', 'customer-shipment', 'charges/confirm', 'control-notes', 'actual-warehouse/correct', 'exception-close'])('sends %s to the scoped order endpoint without interpreting money success', async command => {
|
|
api.post.mockResolvedValue(envelope({ status: 'WAIT_OVERSEAS_RECEIPT', purchase: { walletStatus: 'FREEZE_PENDING' } }))
|
|
const payload = { version: 3, idempotencyKey: 'same-command', reason: '已核对', amount: '9007199254740993.01' }
|
|
await submitProcurementCommand('OP/1', '10001', command, payload)
|
|
expect(api.post).toHaveBeenCalledWith(`/platform/v1/last-leg/overseas-procurements/OP%2F1/${command}`, payload, { params: { customerId: '10001' } })
|
|
})
|
|
it('posts additional expenses without a fee route id or inherited correction fields', async () => {
|
|
api.post.mockResolvedValue(envelope({}))
|
|
const payload = { version: 3, idempotencyKey: 'additional', reason: 'invoice', name: 'Storage', amount: '3.25', currency: 'USD' as const, businessDate: '2020-01-03', voucherFileId: '501' }
|
|
await submitProcurementCommand('OP/1', '10001', 'fees/additional', { ...payload, feeId: '11', targetOriginalAmount: '99' })
|
|
expect(api.post).toHaveBeenCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/fees/additional', payload, { params: { customerId: '10001' } })
|
|
})
|
|
it('resolves the exact order-owned blocker and does not send the internal route reference as a mutable field', async () => {
|
|
api.post.mockResolvedValue(envelope({}))
|
|
await submitProcurementCommand('OP/1', '10001', 'control-notes/resolve', { version: 3, idempotencyKey: 'resolve-one', reason: '核对完成', noteId: '71', voucherFileId: '501' })
|
|
expect(api.post).toHaveBeenCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/control-notes/71/resolve', { version: 3, idempotencyKey: 'resolve-one', reason: '核对完成', voucherFileId: '501' }, { params: { customerId: '10001' } })
|
|
})
|
|
it('uses the exact fee endpoint and sends only fields belonging to that financial operation', async () => {
|
|
api.post.mockResolvedValue(envelope({}))
|
|
const base = { version: 3, idempotencyKey: 'finance-one', reason: '核对', feeId: '11' }
|
|
await submitProcurementCommand('OP/1', '10001', 'fees/correct-target', { ...base, targetOriginalAmount: '12.00', voucherFileId: '501', amount: 'unrelated' })
|
|
expect(api.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/fees/11/correct-target', { version: 3, idempotencyKey: 'finance-one', reason: '核对', targetOriginalAmount: '12.00', voucherFileId: '501' }, { params: { customerId: '10001' } })
|
|
await submitProcurementCommand('OP/1', '10001', 'fees/replace-failed', { ...base, targetOriginalAmount: '12.00', voucherFileId: '501' })
|
|
expect(api.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/fees/11/replace-failed', { version: 3, idempotencyKey: 'finance-one', reason: '核对', targetOriginalAmount: '12.00', voucherFileId: '501' }, { params: { customerId: '10001' } })
|
|
await submitProcurementCommand('OP/1', '10001', 'fees/refund', { ...base, amount: '1.01', voucherFileId: '501' })
|
|
expect(api.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/fees/11/refund', { version: 3, idempotencyKey: 'finance-one', reason: '核对', amount: '1.01', voucherFileId: '501' }, { params: { customerId: '10001' } })
|
|
for (const command of ['fees/retry', 'fees/terminate', 'fees/release-estimate'] as const) {
|
|
await submitProcurementCommand('OP/1', '10001', command, base)
|
|
expect(api.post).toHaveBeenLastCalledWith(`/platform/v1/last-leg/overseas-procurements/OP%2F1/fees/11/${command.slice(5)}`, { version: 3, idempotencyKey: 'finance-one', reason: '核对' }, { params: { customerId: '10001' } })
|
|
}
|
|
})
|
|
it('targets an original receipt and nests only the receipt contract plus correction evidence', async () => {
|
|
api.post.mockResolvedValue(envelope({}))
|
|
const receipt = { version: 3, idempotencyKey: 'correct-one', reason: '实收复核', leg: 'OVERSEAS' as const, warehouseId: '20', goodQuantity: 5, damagedQuantity: 1, extraQuantity: 0, receivedAt: '2020-01-02T10:00:00' }
|
|
await submitProcurementCommand('OP/1', '10001', 'receipts/correct', { ...receipt, receiptId: '11', voucherFileId: '901' })
|
|
expect(api.post).toHaveBeenCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/receipts/11/correct', { receipt: { ...receipt, sourceCartonId: undefined }, voucherFileId: '901' }, { params: { customerId: '10001' } })
|
|
await submitProcurementCommand('OP/1', '10001', 'dispositions/revoke', { version: 3, idempotencyKey: 'revoke-one', reason: '撤销错误处置', dispositionId: '71', voucherFileId: '901' })
|
|
expect(api.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/dispositions/71/revoke', { version: 3, idempotencyKey: 'revoke-one', reason: '撤销错误处置', voucherFileId: '901' }, { params: { customerId: '10001' } })
|
|
})
|
|
it('uses purchase-purpose multipart evidence and normalizes file references', async () => {
|
|
api.post.mockResolvedValue(envelope({ id: 11, fileId: '9007199254740993', category: 'PURCHASE', fileName: 'purchase.pdf' }))
|
|
const file = new File(['pdf'], 'purchase.pdf', { type: 'application/pdf' })
|
|
expect(await uploadProcurementFile('OP/1', '10001', 'PURCHASE', file)).toMatchObject({ id: '11', fileId: '9007199254740993' })
|
|
const call = api.post.mock.calls[0]; expect(call[0]).toBe('/platform/v1/last-leg/overseas-procurements/OP%2F1/files/PURCHASE'); expect(call[1].get('file')).toBe(file); expect(call[2]).toEqual({ params: { customerId: '10001' } })
|
|
})
|
|
it('downloads binary files through the authenticated order boundary', async () => {
|
|
const blob = new Blob(['pdf'], { type: 'application/pdf' }); api.get.mockResolvedValue(blob)
|
|
expect(await downloadProcurementFile('OP/1', '10001', '901')).toBe(blob)
|
|
expect(api.get).toHaveBeenCalledWith('/platform/v1/last-leg/overseas-procurements/OP%2F1/files/901/content', { params: { customerId: '10001' }, responseType: 'blob' })
|
|
})
|
|
})
|