feat(expenses): backfill historical claims into expense cases
This commit is contained in:
@@ -26,6 +26,7 @@ const BUSINESS_STATUS_LABELS = {
|
||||
const EVENT_PRESENTATION = {
|
||||
claim_draft_created: { label: '报销草稿已创建', icon: 'mdi mdi-file-plus-outline', tone: 'info' },
|
||||
claim_draft_updated: { label: '报销草稿已更新', icon: 'mdi mdi-file-edit-outline', tone: 'info' },
|
||||
historical_claim_imported: { label: '历史单据已纳入统一费用事件', icon: 'mdi mdi-history', tone: 'info' },
|
||||
application_submitted: { label: '费用申请已提交', icon: 'mdi mdi-send-outline', tone: 'info' },
|
||||
claim_submitted: { label: '报销单已提交', icon: 'mdi mdi-send-outline', tone: 'info' },
|
||||
approval_stage_completed: { label: '审批节点已完成', icon: 'mdi mdi-account-check-outline', tone: 'success' },
|
||||
@@ -83,7 +84,30 @@ function buildStateTransitionSummary(payload) {
|
||||
return transition || (nextStage ? `当前节点:${nextStage}` : '')
|
||||
}
|
||||
|
||||
function buildHistoricalClaimImportSummary(payload) {
|
||||
const currentStatus = resolveStatusLabel(readField(payload, 'next_status', 'nextStatus'))
|
||||
const currentStage = compactText(readField(payload, 'next_approval_stage', 'nextApprovalStage'))
|
||||
const currentStateParts = []
|
||||
|
||||
if (currentStatus) {
|
||||
currentStateParts.push(`纳入时状态:${currentStatus}`)
|
||||
}
|
||||
if (currentStage) {
|
||||
currentStateParts.push(`纳入时节点:${currentStage}`)
|
||||
}
|
||||
|
||||
const currentStateSummary = currentStateParts.length
|
||||
? `${currentStateParts.join(' · ')}。`
|
||||
: ''
|
||||
|
||||
return `系统已根据原单据建立费用事件档案。${currentStateSummary}导入前办理记录以原单据为准,后续动作将在此持续记录。`
|
||||
}
|
||||
|
||||
function buildEventSummary(eventType, payload) {
|
||||
if (eventType === 'historical_claim_imported') {
|
||||
return buildHistoricalClaimImportSummary(payload)
|
||||
}
|
||||
|
||||
const reason = compactText(payload?.reason)
|
||||
if (reason) {
|
||||
return reason
|
||||
@@ -159,7 +183,7 @@ function normalizeEvent(event, index) {
|
||||
icon: presentation.icon,
|
||||
tone: presentation.tone,
|
||||
summary: buildEventSummary(eventType, payload),
|
||||
actorLabel: resolveActorLabel(event),
|
||||
actorLabel: eventType === 'historical_claim_imported' ? '系统' : resolveActorLabel(event),
|
||||
occurredAt: compactText(occurredAt),
|
||||
occurredAtLabel: formatOccurredAt(occurredAt),
|
||||
sortTimestamp: Number.isNaN(timestamp) ? null : timestamp,
|
||||
|
||||
@@ -100,6 +100,64 @@ test('expense case timeline gives returned events an actionable reason', () => {
|
||||
assert.equal(timeline.events[0].summary, '住宿票据缺少入住人信息')
|
||||
})
|
||||
|
||||
test('expense case timeline explains a historical import without pretending it is a business action', () => {
|
||||
const timeline = buildExpenseCaseTimelineViewModel({
|
||||
events: [
|
||||
{
|
||||
id: 'historical-import-1',
|
||||
event_type: 'historical_claim_imported',
|
||||
actor_id: 'migration-worker',
|
||||
actor_type: 'user',
|
||||
occurred_at: '2026-07-14T03:00:00Z',
|
||||
correlation_id: 'private-correlation',
|
||||
delivery_status: 'pending',
|
||||
payload_json: {
|
||||
reason: '不应覆盖历史导入事件的专用说明',
|
||||
next_status: 'submitted',
|
||||
next_approval_stage: '直属领导审批',
|
||||
migration_batch_id: 'private-batch',
|
||||
source_table: 'expense_claims'
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const [event] = timeline.events
|
||||
assert.equal(event.label, '历史单据已纳入统一费用事件')
|
||||
assert.equal(event.icon, 'mdi mdi-history')
|
||||
assert.equal(event.tone, 'info')
|
||||
assert.equal(event.actorLabel, '系统')
|
||||
assert.equal(
|
||||
event.summary,
|
||||
'系统已根据原单据建立费用事件档案。纳入时状态:已提交 · 纳入时节点:直属领导审批。导入前办理记录以原单据为准,后续动作将在此持续记录。'
|
||||
)
|
||||
assert.doesNotMatch(
|
||||
JSON.stringify(event),
|
||||
/不应覆盖|private-correlation|pending|private-batch|expense_claims/
|
||||
)
|
||||
})
|
||||
|
||||
test('expense case timeline keeps the historical import summary honest when state is missing', () => {
|
||||
const timeline = buildExpenseCaseTimelineViewModel({
|
||||
events: [
|
||||
{
|
||||
id: 'historical-import-without-state',
|
||||
event_type: 'historical_claim_imported',
|
||||
actor_id: 'system',
|
||||
occurred_at: '2026-07-14T03:00:00Z',
|
||||
payload_json: {}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const [event] = timeline.events
|
||||
assert.equal(
|
||||
event.summary,
|
||||
'系统已根据原单据建立费用事件档案。导入前办理记录以原单据为准,后续动作将在此持续记录。'
|
||||
)
|
||||
assert.doesNotMatch(event.summary, /纳入时状态|纳入时节点|undefined|null/)
|
||||
})
|
||||
|
||||
test('expense case timeline describes application archive against the paid reimbursement', () => {
|
||||
const timeline = buildExpenseCaseTimelineViewModel({
|
||||
events: [
|
||||
|
||||
Reference in New Issue
Block a user