This commit is contained in:
2026-08-12 09:25:55 +08:00
parent ffb2116741
commit fb542e0b6f
24 changed files with 983 additions and 75 deletions
@@ -0,0 +1,143 @@
import ElementPlus from 'element-plus'
import { createPinia, setActivePinia } from 'pinia'
import { enableAutoUnmount, flushPromises, mount } from '@vue/test-utils'
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useAuthStore } from '@/core/auth/auth'
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
import {
actionReconciliationStatement,
createFinanceReceipt,
getFinanceSettlement,
getReconciliationStatement,
listFinancePayableSettlements,
listFinanceReceivableSettlements,
uploadFinanceVoucher,
} from '../api/finance'
import type { FinanceReconciliationDetail, FinanceSettlementDetail, FinanceStatementReadModel } from '../types'
import FinanceCustomerStatementsView from './FinanceCustomerStatementsView.vue'
import FinanceFormalSettlementPage from './FinanceFormalSettlementPage.vue'
vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() }))
vi.mock('../api/finance', () => ({
actionReconciliationStatement: vi.fn(), createFinanceReceipt: vi.fn(), createFinanceSettlementPayment: vi.fn(),
createPayableSettlement: vi.fn(), createReceivableSettlement: vi.fn(), createReconciliationStatement: vi.fn(),
getFinanceSettlement: vi.fn(), getReconciliationStatement: vi.fn(), listFinanceFeePool: vi.fn(),
listFinancePayableSettlements: vi.fn(), listFinanceReceivableSettlements: vi.fn(), listFinanceStatements: vi.fn(),
uploadFinanceVoucher: vi.fn(),
}))
class ResizeObserverStub implements ResizeObserver {
observe() {}
unobserve() {}
disconnect() {}
}
function createMemoryStorage(): Storage {
const values = new Map<string, string>()
return {
get length() { return values.size }, clear: () => values.clear(), getItem: (key) => values.get(key) ?? null,
key: (index) => [...values.keys()][index] ?? null, removeItem: (key) => values.delete(key), setItem: (key, value) => values.set(key, value),
}
}
function piniaWithPermissions(permissions: string[]) {
const pinia = createPinia()
setActivePinia(pinia)
const auth = useAuthStore()
auth.token = 'token'
auth.permissions = permissions
useAccessPreviewStore().initializeAfterLogin()
return pinia
}
const statement: FinanceStatementReadModel = {
id: '501', customerId: '10001', billNo: 'REC-501', customerName: '客户A', billDate: '2026-08-31',
dueDate: '2026-09-30', totalAmount: 300, receivedAmount: 0, outstandingAmount: 300,
currency: 'CNY', status: 'ISSUED', version: 3,
}
const reconciliation: FinanceReconciliationDetail = {
...statement, paidAmount: 0, periodStart: '2026-08-01', periodEnd: '2026-08-31', creditDays: 30, settled: false,
remark: '', lines: [
{ statementLineId: '601', sourceFeeId: '41', sourceFeeVersion: 1, orderId: null, inboundId: '91', feeItemName: '运费', amount: 100 },
{ statementLineId: '602', sourceFeeId: '42', sourceFeeVersion: 1, orderId: null, inboundId: '92', feeItemName: '操作费', amount: 200 },
], paymentHistory: [], revisions: [],
}
const receivable: FinanceSettlementDetail = {
id: '701', settlementNo: 'AR-701', customerId: '10001', reconciliationStatementId: '501', direction: 'RECEIVABLE',
amount: 300, paidAmount: 0, outstandingAmount: 300, currency: 'CNY', status: 'ISSUED', version: 0,
lines: [], transactions: [], revisions: [],
}
enableAutoUnmount(afterEach)
afterAll(() => vi.unstubAllGlobals())
describe('finance G3/G4 page operations', () => {
beforeEach(() => {
vi.stubGlobal('ResizeObserver', ResizeObserverStub)
vi.stubGlobal('localStorage', createMemoryStorage())
vi.stubGlobal('sessionStorage', createMemoryStorage())
document.body.innerHTML = ''
const customer = { id: '10001', tenantName: '客户A', status: 'ACTIVE' } as PlatformTenantResource
vi.mocked(listPlatformTenants).mockReset().mockResolvedValue({ records: [customer], page: 1, size: 200, total: 1, pages: 1 })
vi.mocked(actionReconciliationStatement).mockReset().mockResolvedValue(reconciliation)
vi.mocked(getReconciliationStatement).mockReset().mockResolvedValue(reconciliation)
vi.mocked(getFinanceSettlement).mockReset().mockResolvedValue(receivable)
vi.mocked(listFinanceReceivableSettlements).mockReset().mockResolvedValue([])
vi.mocked(listFinancePayableSettlements).mockReset().mockResolvedValue([])
vi.mocked(uploadFinanceVoucher).mockReset().mockResolvedValue({ fileId: '9001', customerId: '10001', fileName: 'receipt.png', contentType: 'image/png', sizeBytes: 100 })
vi.mocked(createFinanceReceipt).mockReset().mockResolvedValue({ id: '801', customerId: '10001', customerName: '客户A', receivedDate: '2026-08-11', amount: 300, currency: 'CNY', paymentMethod: 'BANK_TRANSFER', voucherFileIds: ['9001'], allocationCount: 1 })
})
it('loads frozen G3 detail and submits multiple selected adjustment lines', async () => {
const pinia = piniaWithPermissions(['platform:finance:bill:view', 'platform:finance:reconciliation:adjust'])
const wrapper = mount(FinanceCustomerStatementsView, { attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
const vm = wrapper.vm as unknown as {
openAdjustment(row: FinanceStatementReadModel): Promise<void>
submitAdjustment(): Promise<void>
adjustReason: string
adjustLines: Array<{ selected: boolean; adjustedAmount: number; reason: string }>
}
await vm.openAdjustment(statement)
vm.adjustReason = '账期复核调整'
vm.adjustLines[0]!.selected = true; vm.adjustLines[0]!.adjustedAmount = 90; vm.adjustLines[0]!.reason = '运费更正'
vm.adjustLines[1]!.selected = true; vm.adjustLines[1]!.adjustedAmount = 180; vm.adjustLines[1]!.reason = '操作费更正'
await vm.submitAdjustment()
expect(getReconciliationStatement).toHaveBeenCalledWith('501')
expect(actionReconciliationStatement).toHaveBeenCalledWith('501', {
action: 'ADJUST', reason: '账期复核调整', expectedVersion: 3,
adjustmentLines: [
{ statementLineId: '601', adjustedAmount: 90, reason: '运费更正' },
{ statementLineId: '602', adjustedAmount: 180, reason: '操作费更正' },
],
})
})
it('loads AR linkage, uploads a voucher and allocates a real receipt to its statement', async () => {
const pinia = piniaWithPermissions(['platform:finance:settlement-ar:view', 'platform:finance:receipt:create'])
const wrapper = mount(FinanceFormalSettlementPage, { props: { kind: 'receivable' }, attachTo: document.body, global: { plugins: [pinia, ElementPlus] } })
await flushPromises()
const vm = wrapper.vm as unknown as {
openReceipt(row: Record<string, unknown>): Promise<void>
submitReceipt(): Promise<void>
receiptFile: File | null
receiptForm: { receivedDate: string; amount: number; paymentMethod: 'BANK_TRANSFER'; remark: string }
}
await vm.openReceipt({ id: '701' })
vm.receiptFile = new File(['receipt'], 'receipt.png', { type: 'image/png' })
vm.receiptForm.receivedDate = '2026-08-11'; vm.receiptForm.amount = 300; vm.receiptForm.remark = '全额到账'
await vm.submitReceipt()
expect(uploadFinanceVoucher).toHaveBeenCalledWith('10001', vm.receiptFile)
expect(createFinanceReceipt).toHaveBeenCalledWith({
customerId: '10001', receivedDate: '2026-08-11', amount: 300, paymentMethod: 'BANK_TRANSFER', remark: '全额到账',
allocations: [{ billId: '501', amount: 300 }],
}, ['9001'])
})
})