feat: 财务看板口径重构与半年模拟数据及报销状态注册表
- 重构 finance_dashboard 口径计算,新增模拟公司画像数据生成与筛选 - 引入 expense_claim_status_registry 统一报销状态流转 - 完善报销草稿流程、Item Sync 与本体解析器 - 优化总览页趋势图、分页组件与请求进度步骤 - 增强报销申请快速预览、本体工具与详情展示 - 新增半年报销模拟数据种子脚本与状态审计工具 - 补充财务看板、报销状态注册与模拟数据测试覆盖
This commit is contained in:
@@ -2,6 +2,10 @@ import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { mapExpenseClaimToRequest } from '../src/composables/useRequests.js'
|
||||
import {
|
||||
buildApplicationDetailFactItems,
|
||||
buildRelatedApplicationFactItems
|
||||
} from '../src/utils/expenseApplicationDetail.js'
|
||||
|
||||
const CREATE_APPLICATION = '\u521b\u5efa\u7533\u8bf7'
|
||||
const DIRECT_MANAGER_APPROVAL = '\u76f4\u5c5e\u9886\u5bfc\u5ba1\u6279'
|
||||
@@ -110,6 +114,55 @@ test('application claims are mapped as application documents', () => {
|
||||
assert.equal(request.progressSteps.find((step) => step.label === WAIT_LEADER_LI_APPROVAL)?.current, true)
|
||||
})
|
||||
|
||||
test('travel application detail splits trip time into departure and return rows', () => {
|
||||
const request = mapExpenseClaimToRequest({
|
||||
id: 'claim-application-trip-time',
|
||||
claim_no: 'AP-20260602103045-TRIPTIME',
|
||||
employee_name: '张三',
|
||||
department_name: '交付部',
|
||||
manager_name: 'Leader Li',
|
||||
expense_type: 'travel_application',
|
||||
reason: '支撑国网仿生产环境部署',
|
||||
location: '上海',
|
||||
amount: 3000,
|
||||
invoice_count: 0,
|
||||
occurred_at: '2026-02-20T00:00:00.000Z',
|
||||
submitted_at: '2026-02-20T02:00:00.000Z',
|
||||
created_at: '2026-02-20T01:30:00.000Z',
|
||||
updated_at: '2026-02-20T02:00:00.000Z',
|
||||
status: 'submitted',
|
||||
approval_stage: '直属领导审批',
|
||||
risk_flags_json: [
|
||||
{
|
||||
source: 'application_detail',
|
||||
application_detail: {
|
||||
application_type: '差旅费用申请',
|
||||
time: '2026-02-20 至 2026-02-23',
|
||||
location: '上海',
|
||||
reason: '支撑国网仿生产环境部署',
|
||||
days: '4 天',
|
||||
transport_mode: '火车',
|
||||
amount: '3000'
|
||||
}
|
||||
}
|
||||
],
|
||||
items: []
|
||||
})
|
||||
|
||||
const factItems = buildApplicationDetailFactItems(request)
|
||||
assert.deepEqual(
|
||||
factItems
|
||||
.filter((item) => ['trip_start_time', 'trip_return_time'].includes(item.key))
|
||||
.map((item) => [item.label, item.value]),
|
||||
[
|
||||
['出发时间', '2026-02-20'],
|
||||
['返回时间', '2026-02-23']
|
||||
]
|
||||
)
|
||||
assert.equal(factItems.some((item) => item.label === '发生时间'), false)
|
||||
assert.equal(factItems.some((item) => item.label === '行程时间'), false)
|
||||
})
|
||||
|
||||
test('application claims wait for department P8 budget monitor after leader approval', () => {
|
||||
const request = mapExpenseClaimToRequest({
|
||||
id: 'claim-application-budget',
|
||||
@@ -679,11 +732,17 @@ test('paid reimbursement marks payment progress step as complete', () => {
|
||||
application_type: '差旅费用申请',
|
||||
application_content: '差旅费用申请 / 北京',
|
||||
application_reason: '支撑国网仿生产环境部署',
|
||||
application_days: '3 天',
|
||||
application_days: '4 天',
|
||||
application_location: '北京',
|
||||
application_amount: '3000',
|
||||
application_time: '2026-05-20T00:00:00.000Z',
|
||||
application_transport_mode: '高铁'
|
||||
application_time: '2026-05-20 至 2026-05-23',
|
||||
application_transport_mode: '高铁',
|
||||
application_lodging_daily_cap: '600元/天',
|
||||
application_subsidy_daily_cap: '120元/天',
|
||||
application_transport_policy: '按真实票据复核',
|
||||
application_policy_estimate: '交通按真实票据 + 住宿 2,400元 + 补贴 480元',
|
||||
application_rule_name: '差旅标准规则',
|
||||
application_rule_version: '2026.05'
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -709,8 +768,36 @@ test('paid reimbursement marks payment progress step as complete', () => {
|
||||
assert.equal(linkedStep.time, '已关联 APP-20260520-001')
|
||||
assert.equal(request.relatedApplication.claimNo, 'APP-20260520-001')
|
||||
assert.equal(request.relatedApplication.reason, '支撑国网仿生产环境部署')
|
||||
assert.equal(request.relatedApplication.days, '3 天')
|
||||
assert.equal(request.relatedApplication.days, '4 天')
|
||||
assert.equal(request.relatedApplication.time, '2026-05-20 至 2026-05-23')
|
||||
assert.equal(request.relatedApplication.tripStartDate, '2026-05-20')
|
||||
assert.equal(request.relatedApplication.tripEndDate, '2026-05-23')
|
||||
assert.equal(request.relatedApplication.transportMode, '高铁')
|
||||
assert.equal(request.relatedApplication.lodgingDailyCap, '600元/天')
|
||||
assert.equal(request.relatedApplication.subsidyDailyCap, '120元/天')
|
||||
assert.equal(request.relatedApplication.transportPolicy, '按真实票据复核')
|
||||
assert.equal(request.relatedApplication.policyEstimate, '交通按真实票据 + 住宿 2,400元 + 补贴 480元')
|
||||
assert.equal(request.relatedApplication.ruleLabel, '差旅标准规则 / 2026.05')
|
||||
assert.equal(request.relatedApplication.amountLabel, '¥3,000')
|
||||
assert.deepEqual(
|
||||
buildRelatedApplicationFactItems(request).map((item) => [item.label, item.value]),
|
||||
[
|
||||
['关联单据单号', 'APP-20260520-001'],
|
||||
['申请内容', '差旅费用申请 / 北京'],
|
||||
['出发时间', '2026-05-20'],
|
||||
['返回时间', '2026-05-23'],
|
||||
['申请天数', '4 天'],
|
||||
['申请事由', '支撑国网仿生产环境部署'],
|
||||
['申请地点', '北京'],
|
||||
['出行方式', '高铁'],
|
||||
['住宿上限/天', '600元/天'],
|
||||
['补贴标准/天', '120元/天'],
|
||||
['交通费用口径', '按真实票据复核'],
|
||||
['规则测算参考', '交通按真实票据 + 住宿 2,400元 + 补贴 480元'],
|
||||
['规则依据', '差旅标准规则 / 2026.05'],
|
||||
['预计金额', '¥3,000']
|
||||
]
|
||||
)
|
||||
})
|
||||
|
||||
test('reimbursement detail resolves linked application from guided entry context', () => {
|
||||
@@ -739,7 +826,14 @@ test('reimbursement detail resolves linked application from guided entry context
|
||||
application_reason: '支撑国网仿生产环境部署',
|
||||
application_location: '北京',
|
||||
application_amount: '3000',
|
||||
application_amount_label: '¥3,000'
|
||||
application_amount_label: '¥3,000',
|
||||
application_business_time: '2026-05-20 至 2026-05-23',
|
||||
application_days: '4 天',
|
||||
application_transport_mode: '高铁',
|
||||
application_lodging_daily_cap: '600元/天',
|
||||
application_subsidy_daily_cap: '120元/天',
|
||||
application_transport_policy: '按真实票据复核',
|
||||
application_policy_estimate: '交通按真实票据 + 住宿 2,400元 + 补贴 480元'
|
||||
},
|
||||
expense_scene_selection: {
|
||||
application_claim_no: 'AP-202605-001'
|
||||
@@ -752,7 +846,136 @@ test('reimbursement detail resolves linked application from guided entry context
|
||||
assert.equal(request.relatedApplication.claimNo, 'AP-202605-001')
|
||||
assert.equal(request.relatedApplication.reason, '支撑国网仿生产环境部署')
|
||||
assert.equal(request.relatedApplication.location, '北京')
|
||||
assert.equal(request.relatedApplication.time, '2026-05-20 至 2026-05-23')
|
||||
assert.equal(request.relatedApplication.tripStartDate, '2026-05-20')
|
||||
assert.equal(request.relatedApplication.tripEndDate, '2026-05-23')
|
||||
assert.equal(request.relatedApplication.days, '4 天')
|
||||
assert.equal(request.relatedApplication.transportMode, '高铁')
|
||||
assert.equal(request.relatedApplication.lodgingDailyCap, '600元/天')
|
||||
assert.equal(request.relatedApplication.subsidyDailyCap, '120元/天')
|
||||
assert.equal(request.relatedApplication.policyEstimate, '交通按真实票据 + 住宿 2,400元 + 补贴 480元')
|
||||
assert.equal(request.relatedApplication.amountLabel, '¥3,000')
|
||||
assert.deepEqual(request.expenseItems, [])
|
||||
})
|
||||
|
||||
test('reimbursement detail hides stale application placeholder and allowance rows before receipts', () => {
|
||||
const request = mapExpenseClaimToRequest({
|
||||
id: 'claim-linked-stale-placeholder',
|
||||
claim_no: 'EXP-20260520-010',
|
||||
employee_name: '张三',
|
||||
department_name: '交付部',
|
||||
expense_type: 'travel',
|
||||
reason: '支撑国网仿生产环境部署',
|
||||
location: '上海',
|
||||
amount: 3480,
|
||||
invoice_count: 0,
|
||||
occurred_at: '2026-02-20T00:00:00.000Z',
|
||||
created_at: '2026-05-20T01:30:00.000Z',
|
||||
updated_at: '2026-05-20T02:00:00.000Z',
|
||||
status: 'draft',
|
||||
approval_stage: '待提交',
|
||||
risk_flags_json: [
|
||||
{
|
||||
source: 'application_link',
|
||||
event_type: 'expense_reimbursement_application_linked',
|
||||
review_form_values: {
|
||||
application_claim_id: 'application-guided-stale',
|
||||
application_claim_no: 'AP-202605-010',
|
||||
application_reason: '支撑国网仿生产环境部署',
|
||||
application_location: '上海',
|
||||
application_amount: '3000',
|
||||
application_amount_label: '¥3,000',
|
||||
application_business_time: '2026-02-20 至 2026-02-23',
|
||||
application_days: '4 天',
|
||||
application_transport_mode: '火车'
|
||||
}
|
||||
}
|
||||
],
|
||||
items: [
|
||||
{
|
||||
id: 'placeholder-travel',
|
||||
item_type: 'travel',
|
||||
item_reason: '支撑国网仿生产环境部署',
|
||||
item_location: '上海',
|
||||
item_amount: 3000,
|
||||
item_date: '2026-02-20',
|
||||
invoice_id: ''
|
||||
},
|
||||
{
|
||||
id: 'stale-allowance',
|
||||
item_type: 'travel_allowance',
|
||||
item_reason: '系统自动计算出差补贴:上海市,1天,120.00元/天',
|
||||
item_location: '直辖市/特区',
|
||||
item_amount: 120,
|
||||
item_date: '2026-02-20',
|
||||
invoice_id: ''
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert.equal(request.relatedApplication.claimNo, 'AP-202605-010')
|
||||
assert.equal(request.relatedApplication.days, '4 天')
|
||||
assert.equal(request.amount, 0)
|
||||
assert.deepEqual(request.expenseItems, [])
|
||||
assert.equal(request.expenseTableSummary, '暂无费用明细')
|
||||
})
|
||||
|
||||
test('reimbursement detail hides stale allowance when linked application days differ', () => {
|
||||
const request = mapExpenseClaimToRequest({
|
||||
id: 'claim-linked-stale-allowance',
|
||||
claim_no: 'EXP-20260520-011',
|
||||
employee_name: '张三',
|
||||
department_name: '交付部',
|
||||
expense_type: 'travel',
|
||||
reason: '支撑国网仿生产环境部署',
|
||||
location: '上海',
|
||||
amount: 474,
|
||||
invoice_count: 1,
|
||||
occurred_at: '2026-02-20T00:00:00.000Z',
|
||||
created_at: '2026-05-20T01:30:00.000Z',
|
||||
updated_at: '2026-05-20T02:00:00.000Z',
|
||||
status: 'draft',
|
||||
approval_stage: '待提交',
|
||||
risk_flags_json: [
|
||||
{
|
||||
source: 'application_link',
|
||||
event_type: 'expense_reimbursement_application_linked',
|
||||
application_claim_no: 'AP-202605-011',
|
||||
application_detail: {
|
||||
application_reason: '支撑国网仿生产环境部署',
|
||||
application_location: '上海',
|
||||
application_time: '2026-02-20 至 2026-02-23',
|
||||
application_days: '4 天',
|
||||
application_transport_mode: '火车'
|
||||
}
|
||||
}
|
||||
],
|
||||
items: [
|
||||
{
|
||||
id: 'outbound-train',
|
||||
item_type: 'train_ticket',
|
||||
item_reason: '武汉-上海',
|
||||
item_location: '上海',
|
||||
item_amount: 354,
|
||||
item_date: '2026-02-20',
|
||||
invoice_id: 'ticket-1.png'
|
||||
},
|
||||
{
|
||||
id: 'stale-allowance',
|
||||
item_type: 'travel_allowance',
|
||||
item_reason: '系统自动计算出差补贴:上海市,1天,120.00元/天',
|
||||
item_location: '直辖市/特区',
|
||||
item_amount: 120,
|
||||
item_date: '2026-02-20',
|
||||
invoice_id: ''
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
assert.equal(request.relatedApplication.days, '4 天')
|
||||
assert.deepEqual(request.expenseItems.map((item) => item.id), ['outbound-train'])
|
||||
assert.equal(request.amount, 354)
|
||||
assert.equal(request.expenseTableSummary, '共 1 条费用明细,已关联 1 张票据')
|
||||
})
|
||||
|
||||
test('current direct manager step shows how long the claim has stayed there', () => {
|
||||
|
||||
Reference in New Issue
Block a user