2026-08-11 19:35:28 +08:00
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { resolve } from 'node:path'
|
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
|
|
|
|
|
const settlement = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceFormalSettlementPage.vue'), 'utf8')
|
2026-08-13 12:22:41 +08:00
|
|
|
const financeRoutes = readFileSync(resolve(process.cwd(), 'src/modules/finance/routes.ts'), 'utf8')
|
2026-08-14 12:50:42 +08:00
|
|
|
const accounts = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerAccountsView.vue'), 'utf8')
|
2026-08-11 19:59:15 +08:00
|
|
|
const statements = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerStatementsView.vue'), 'utf8')
|
2026-08-11 19:35:28 +08:00
|
|
|
const arap = readFileSync(resolve(process.cwd(), 'src/modules/finance/pages/FinanceCustomerArapView.vue'), 'utf8')
|
|
|
|
|
|
|
|
|
|
describe('finance presentation governance', () => {
|
|
|
|
|
it('shows customer names instead of internal customer IDs in formal settlements', () => {
|
2026-08-13 12:22:41 +08:00
|
|
|
expect(settlement).toContain('customerName(detail.value.customerId, detail.value.customerName)')
|
|
|
|
|
expect(settlement).toContain('detailPartyName()')
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(settlement).toContain('activeCustomers.value.map')
|
|
|
|
|
expect(settlement).not.toContain('{{ selected.customerId }}')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-13 12:22:41 +08:00
|
|
|
it('uses prototype-defined standalone settlement details and modal partial payments', () => {
|
|
|
|
|
expect(financeRoutes).toContain("finance/settlement-receivables/:settlementId")
|
|
|
|
|
expect(financeRoutes).toContain("finance/settlement-payables/:settlementId")
|
|
|
|
|
expect(financeRoutes).toContain('showPageHeading: false')
|
2026-08-17 12:31:27 +08:00
|
|
|
expect(settlement).toContain('class="settlement-detail-page surface-panel"')
|
2026-08-13 12:22:41 +08:00
|
|
|
expect(settlement).toContain('返回列表')
|
|
|
|
|
expect(settlement).not.toContain('AppDrawer')
|
|
|
|
|
expect(settlement).toContain('登记后剩余余额')
|
|
|
|
|
expect(settlement).toContain('remainingAfter(receiptForm.amount)')
|
|
|
|
|
expect(settlement).toContain('remainingAfter(paymentForm.amount)')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-17 12:31:27 +08:00
|
|
|
it('uses one continuous desktop detail surface with balanced summary and scroll-safe tables', () => {
|
|
|
|
|
expect(settlement).toContain('<DetailPageLayout v-else-if="detailMode" class="settlement-detail-page surface-panel"')
|
|
|
|
|
expect(settlement).toContain('class="settlement-detail-content"')
|
2026-08-19 09:14:35 +08:00
|
|
|
expect(settlement).toContain('.settlement-detail-header { display: flex; min-height: 68px;')
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(settlement).toContain('align-items: center;')
|
|
|
|
|
expect(settlement).toContain('justify-content: space-between')
|
2026-08-17 12:31:27 +08:00
|
|
|
expect(settlement).toContain('<el-descriptions :column="4" class="settlement-detail-info">')
|
|
|
|
|
for (const title of ['基本信息', '费用明细', '修订记录']) expect(settlement).toContain(`title="${title}"`)
|
|
|
|
|
expect(settlement).toContain(':title="kind === \'receivable\' ? \'收款记录\' : \'付款记录\'"')
|
|
|
|
|
expect(settlement.match(/class="detail-table-scroll"/g)).toHaveLength(3)
|
|
|
|
|
expect(settlement).toContain('grid-template-columns: repeat(4, minmax(0, 1fr))')
|
|
|
|
|
expect(settlement).toContain(':deep(.detail-table-scroll .el-table) { min-width: 920px; }')
|
|
|
|
|
expect(settlement).not.toContain('class="detail-section surface-panel"')
|
|
|
|
|
expect(settlement.match(/width="min\(760px, 94vw\)"/g)).toHaveLength(2)
|
|
|
|
|
expect(settlement.match(/class="settlement-operation-form"/g)).toHaveLength(2)
|
|
|
|
|
expect(settlement).toContain('grid-template-columns: repeat(5, minmax(0, 1fr))')
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(settlement).toContain('grid-template-columns: repeat(2, minmax(0, 1fr));')
|
|
|
|
|
expect(settlement).toContain('column-gap: 14px;')
|
2026-08-17 12:31:27 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-19 09:14:35 +08:00
|
|
|
it('keeps settlement totals and list actions in the table toolbar', () => {
|
|
|
|
|
expect(settlement).toContain('<template #toolbar-start>')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(settlement).toContain('class="settlement-summary" aria-label="结算汇总"')
|
2026-08-19 09:14:35 +08:00
|
|
|
expect(settlement).toContain('<template #toolbar-actions>')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(settlement).toContain('class="settlement-list-actions"')
|
|
|
|
|
expect(settlement).toContain('.settlement-summary .summary-item + .summary-item')
|
2026-08-19 09:14:35 +08:00
|
|
|
expect(settlement).not.toContain('class="settlement-list-toolbar"')
|
2026-08-17 13:24:55 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps settlement details focused on finance-facing labels and fields', () => {
|
|
|
|
|
expect(settlement).toContain('financeSourceTypeLabel(scope.row.sourceType)')
|
|
|
|
|
expect(settlement).toContain('financeOperationActionLabel(scope.row.action)')
|
|
|
|
|
expect(settlement).toContain('displayDateTime(scope.row.occurredAt)')
|
|
|
|
|
expect(settlement).not.toContain('label="来源版本"')
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-12 17:29:35 +08:00
|
|
|
it('uses optional all-customer filters without a customer-selection blocker', () => {
|
|
|
|
|
for (const source of [settlement, statements, arap]) {
|
|
|
|
|
expect(source).toContain("placeholder: '全部客户'")
|
|
|
|
|
expect(source).not.toContain('全客户查询暂不可用')
|
|
|
|
|
expect(source).not.toContain('empty-text="请选择客户查询')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-14 12:50:42 +08:00
|
|
|
it('uses active-customer name selectors across all formal finance pages', () => {
|
|
|
|
|
for (const source of [accounts, arap, statements, settlement]) {
|
|
|
|
|
expect(source).toContain("type: 'select'")
|
|
|
|
|
expect(source).toContain("label: '客户名称'")
|
|
|
|
|
expect(source).toContain('label: item.tenantName')
|
|
|
|
|
expect(source).not.toContain('tenantCode} · ${item.tenantName')
|
|
|
|
|
}
|
|
|
|
|
expect(accounts).not.toContain("key: 'keyword', type: 'input'")
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-11 19:35:28 +08:00
|
|
|
it('translates source codes and provides reason placeholders', () => {
|
2026-08-15 09:47:23 +08:00
|
|
|
expect(arap).toContain('sourceTypeDisplay(detail.sourceType)')
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(arap).toContain('financeOperationActionLabel(log.action)')
|
|
|
|
|
expect(arap).toContain('financeOperationResultLabel(log.result)')
|
2026-08-11 19:59:15 +08:00
|
|
|
expect(arap).toContain('financeDirectionLabel(row.direction)')
|
|
|
|
|
expect(arap).toContain('financeDirectionLabel(detail.direction)')
|
|
|
|
|
expect(arap).not.toContain("detail.direction === 'RECEIVABLE' ? '应收' : '应付'")
|
2026-08-11 19:35:28 +08:00
|
|
|
expect(arap).toContain('请填写驳回原因')
|
2026-08-19 09:14:35 +08:00
|
|
|
expect(arap).toContain('请填写操作原因')
|
2026-08-11 19:35:28 +08:00
|
|
|
})
|
2026-08-11 19:59:15 +08:00
|
|
|
|
2026-08-12 09:25:55 +08:00
|
|
|
it('exposes frozen G3 controls without unavailable placeholders', () => {
|
2026-08-11 19:59:15 +08:00
|
|
|
expect(settlement).not.toContain('生成{{ title }}')
|
|
|
|
|
expect(settlement).not.toContain('接口尚未提供,暂不可操作')
|
2026-08-12 09:25:55 +08:00
|
|
|
expect(statements).toContain('生成对账单')
|
|
|
|
|
expect(statements).toContain('createReconciliationStatement')
|
|
|
|
|
expect(statements).toContain('actionReconciliationStatement')
|
|
|
|
|
expect(statements).toContain('<template #toolbar-actions>')
|
|
|
|
|
expect(statements).toContain('aria-label="更多对账单操作"')
|
|
|
|
|
expect(statements).toContain('adjustmentLines: selected.map')
|
2026-08-11 19:59:15 +08:00
|
|
|
expect(statements).not.toContain('disabled title="接口尚未提供')
|
|
|
|
|
})
|
2026-08-12 19:04:48 +08:00
|
|
|
|
|
|
|
|
it('uses bordered striped tables for statement business details', () => {
|
|
|
|
|
expect(statements).toContain(':data="detail.lines" size="small" border stripe')
|
|
|
|
|
expect(statements).toContain(':data="detail.revisions" size="small" border stripe')
|
|
|
|
|
expect(statements).toContain(':data="adjustLines" size="small" max-height="420" border stripe')
|
|
|
|
|
})
|
2026-08-17 13:24:55 +08:00
|
|
|
|
2026-08-17 16:45:46 +08:00
|
|
|
it('opens statement details on a compact dedicated route instead of below the list', () => {
|
|
|
|
|
expect(statements).toContain('v-else-if="isDetailRoute" class="surface-panel statement-detail-page"')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(statements).toContain('@row-click="openDetail"')
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(statements).toContain("name: 'finance-customer-statement-detail'")
|
|
|
|
|
expect(statements).toContain('.statement-detail-page :deep(.detail-page-layout) { gap: 0; }')
|
|
|
|
|
expect(statements).toContain('返回客户对账单')
|
|
|
|
|
expect(statements).not.toContain('aria-label="所选对账单联动明细"')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(statements).not.toContain('核对账期、金额和结清状态后查看下方业务明细。')
|
|
|
|
|
expect(statements).not.toContain('展示当前对账单版本中的业务与计费明细。')
|
|
|
|
|
expect(statements).not.toContain('只读展示当前对账单关联的有效收款记录。')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('uses a compact financial summary row for customer accounts', () => {
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(accounts).toContain('.finance-account-detail__layout :deep(.detail-page-layout)')
|
|
|
|
|
expect(accounts).toContain('gap: 0;')
|
|
|
|
|
expect(accounts).toContain('.account-metric + .account-metric')
|
|
|
|
|
expect(accounts).toContain('border-left: 1px solid')
|
|
|
|
|
expect(accounts).toContain('.account-metric::before')
|
|
|
|
|
expect(accounts).toContain('display: none;')
|
|
|
|
|
expect(accounts).toContain(':deep(.ledger-query.query-toolbar)')
|
|
|
|
|
expect(accounts).toContain('padding: 0;')
|
|
|
|
|
expect(accounts).toContain('border: 0;')
|
|
|
|
|
expect(accounts).toContain('background: transparent;')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(accounts).not.toContain('展示应收、应付及每次变动后的未收金额。')
|
|
|
|
|
expect(accounts).not.toContain('到期日按账单日期加客户账期计算;每个客户独立设置,默认 30 天。')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps fee-source details continuous without nested metric and section cards', () => {
|
2026-08-17 16:45:46 +08:00
|
|
|
expect(arap).toContain('.finance-arap-detail__layout :deep(.detail-page-layout)')
|
|
|
|
|
expect(arap).toContain('.arap-metric + .arap-metric')
|
|
|
|
|
expect(arap).toContain('border-left: 1px solid')
|
|
|
|
|
expect(arap).toContain('.arap-settlement {')
|
|
|
|
|
expect(arap).toContain('padding-left: 16px;')
|
|
|
|
|
expect(arap).toContain('border-left: 1px solid')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(arap).not.toContain('.arap-metric.is-amount { border-color:')
|
|
|
|
|
expect(arap).not.toContain('description="费用来源与关联业务摘要"')
|
2026-08-19 09:14:35 +08:00
|
|
|
expect(arap).not.toContain('description="当前审核和出账状态"')
|
2026-08-17 13:24:55 +08:00
|
|
|
expect(arap).not.toContain('description="按时间查看审核与结算调整记录"')
|
|
|
|
|
})
|
2026-08-11 19:35:28 +08:00
|
|
|
})
|