This commit is contained in:
2026-08-20 16:13:55 +08:00
parent 4df6a5f571
commit c93eea89c8
4 changed files with 226 additions and 46 deletions
@@ -10,8 +10,8 @@ import { useAuthStore } from '@/core/auth/auth'
import { createPlatformExportTask } from '@/modules/downloads'
import { listPlatformTenants } from '@/modules/iam'
import { showExportTaskCreatedDialog } from '@/shared/components/exportTaskCreatedDialog'
import { getReconciliationStatement, listFinanceStatements } from '../api/finance'
import type { FinanceReconciliationDetail, FinanceStatementReadModel } from '../types'
import { createReconciliationStatement, getReconciliationStatement, listFinanceFeeSources, listFinanceStatements } from '../api/finance'
import type { FinanceFeeSourceSummary, FinanceReconciliationDetail, FinanceStatementReadModel } from '../types'
import FinanceCustomerStatementsView from './FinanceCustomerStatementsView.vue'
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() }))
@@ -19,7 +19,7 @@ vi.mock('@/modules/downloads', () => ({ createPlatformExportTask: vi.fn() }))
vi.mock('@/shared/components/exportTaskCreatedDialog', () => ({ showExportTaskCreatedDialog: vi.fn() }))
vi.mock('../api/finance', () => ({
actionReconciliationStatement: vi.fn(), createReconciliationStatement: vi.fn(),
getReconciliationStatement: vi.fn(), listFinanceStatements: vi.fn(),
getReconciliationStatement: vi.fn(), listFinanceFeeSources: vi.fn(), listFinanceStatements: vi.fn(),
}))
class ResizeObserverStub implements ResizeObserver { observe() {} unobserve() {} disconnect() {} }
@@ -44,6 +44,13 @@ const detail: FinanceReconciliationDetail = {
paymentHistory: [{ transactionId: '701', transactionType: 'RECEIPT', transactionDate: '2026-08-20', amount: 100, method: 'BANK_TRANSFER', referenceNo: 'PAY-701', voucherFileIds: ['9001'], createdBy: '9', createdByName: '财务员', occurredAt: '2026-08-20 10:00:00' }],
revisions: [{ revisionNo: 1, action: 'ADJUST', beforeAmount: 320, afterAmount: 300, reason: '优惠调整', operatorId: '10', operatorName: '调整人', occurredAt: '2026-08-20 09:00:00' }],
}
const eligibleFeeSource: FinanceFeeSourceSummary = {
poolItemId: '42', feeSourceId: '42', sourceSystem: 'FIRSTLEG', sourceType: 'INBOUND_FEE', sourceBusinessId: 'IN-21',
sourceId: '81', sourceVersion: 2, customerId: '10001', customerName: '客户A', direction: 'RECEIVABLE', supplierId: null,
supplierName: '', orderId: '11', inboundId: '21', inboundCode: 'IN-21', containerId: null, containerNo: '', businessDate: '2026-08-15',
feeItemName: '运费', quantity: 10, unitPrice: 30, amount: 320, currency: 'CNY', reviewStatus: 'APPROVED',
sourceStatus: 'AVAILABLE', statementGenerated: false, status: 'AVAILABLE', createdAt: '2026-08-15',
}
async function mountPage(permissions = ['platform:finance:customer-statement:list'], initialPath = '/finance/customer-statements') {
const pinia = createPinia(); setActivePinia(pinia)
@@ -73,6 +80,8 @@ describe('FinanceCustomerStatementsView', () => {
vi.mocked(listPlatformTenants).mockResolvedValue({ records: [{ id: '10001', tenantName: '客户A', status: 'ACTIVE' } as never], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(listFinanceStatements).mockResolvedValue([statement])
vi.mocked(getReconciliationStatement).mockResolvedValue(detail)
vi.mocked(listFinanceFeeSources).mockReset().mockResolvedValue({ records: [eligibleFeeSource], total: 1, pages: 1 })
vi.mocked(createReconciliationStatement).mockReset().mockResolvedValue({ ...detail, id: '502', billNo: 'REC-502', status: 'DRAFT' })
vi.mocked(createPlatformExportTask).mockReset().mockResolvedValue({ taskName: '客户对账单', taskNo: 'EXP-STMT-1' } as never)
vi.mocked(showExportTaskCreatedDialog).mockReset().mockResolvedValue(false)
})
@@ -255,6 +264,34 @@ describe('FinanceCustomerStatementsView', () => {
expect(listPlatformTenants).not.toHaveBeenCalled()
})
it('accepts selected fee sources from customer arap and creates them in the statement page', async () => {
const permissions = [
'platform:finance:customer-statement:list',
'platform:finance:customer-arap:list',
'platform:finance:reconciliation:create',
'platform:customer:list',
]
const { wrapper, router } = await mountPage(permissions, '/finance/customer-statements?createMode=SELECTED&customerId=10001&feeSourceIds=42&periodStart=2026-08-15&periodEnd=2026-08-15')
const vm = wrapper.vm as unknown as {
createOpen: boolean
createForm: { generationMode: string; customerId: string; feeSourceIds: string[]; periodStart: string; periodEnd: string }
createStatement(): Promise<void>
}
expect(vm.createOpen).toBe(true)
expect(vm.createForm).toMatchObject({ generationMode: 'SELECTED', customerId: '10001', feeSourceIds: ['42'], periodStart: '2026-08-15', periodEnd: '2026-08-15' })
expect(listFinanceFeeSources).toHaveBeenCalledWith(expect.objectContaining({
customerId: '10001', direction: 'RECEIVABLE', status: 'AVAILABLE', reviewStatus: 'APPROVED', statementGenerated: false,
businessDateFrom: '2026-08-15', businessDateTo: '2026-08-15',
}))
await vm.createStatement()
expect(createReconciliationStatement).toHaveBeenCalledWith({
customerId: '10001', currency: 'CNY', generationMode: 'SELECTED', feeSourceIds: ['42'],
periodStart: '2026-08-15', periodEnd: '2026-08-15', remark: undefined,
})
expect(router.currentRoute.value).toMatchObject({ name: 'finance-customer-statement-detail', params: { statementId: '502' } })
})
it('uses statement detail snapshots without a second fee-source request or technical trace fields', async () => {
const { wrapper } = await mountPage(['platform:finance:customer-statement:list'])
await (wrapper.vm as unknown as { openDetail(row: FinanceStatementReadModel): Promise<void> }).openDetail(statement)