import assert from 'node:assert/strict' import test from 'node:test' import { mapExpenseClaimToRequest } from '../src/composables/useRequests.js' test('progress steps show approval operator time and current stay duration', () => { const originalNow = Date.now Date.now = () => new Date('2026-05-20T05:00:00.000Z').getTime() try { const request = mapExpenseClaimToRequest({ id: 'claim-1', claim_no: 'EXP-202605-001', employee_name: '张三', department_name: '市场部', expense_type: 'transport', reason: '交通报销', location: '上海', amount: 88, invoice_count: 1, occurred_at: '2026-05-20T01:00:00.000Z', submitted_at: '2026-05-20T02:00:00.000Z', created_at: '2026-05-20T01:30:00.000Z', updated_at: '2026-05-20T03:30:00.000Z', status: 'submitted', approval_stage: '财务审批', risk_flags_json: [ { source: 'manual_approval', operator: '李经理', previous_approval_stage: '直属领导审批', next_approval_stage: '财务审批', created_at: '2026-05-20T03:30:00.000Z' } ], items: [] }) const leaderStep = request.progressSteps.find((step) => step.label === '直属领导审批') const financeStep = request.progressSteps.find((step) => step.label === '财务审批') const aiStep = request.progressSteps.find((step) => step.label === 'AI预审') const firstStep = request.progressSteps[0] assert.equal(firstStep.label, '创建单据') assert.equal(leaderStep.time, '李经理通过') assert.match(leaderStep.detail, /2026-05-20/) assert.match(leaderStep.title, /李经理审批通过/) assert.equal(aiStep.time, 'AI预审通过') assert.match(aiStep.detail, /2026-05-20/) assert.equal(financeStep.current, true) assert.equal(financeStep.time, '停留 1小时30分钟') } finally { Date.now = originalNow } }) test('progress steps do not expose approver email when manager name is available', () => { const originalNow = Date.now Date.now = () => new Date('2026-05-20T05:00:00.000Z').getTime() try { const request = mapExpenseClaimToRequest({ id: 'claim-email-operator', claim_no: 'EXP-202605-003', employee_name: '张三', department_name: '市场部', manager_name: '李经理', expense_type: 'transport', reason: '交通报销', location: '上海', amount: 88, invoice_count: 1, occurred_at: '2026-05-20T01:00:00.000Z', submitted_at: '2026-05-20T02:00:00.000Z', created_at: '2026-05-20T01:30:00.000Z', updated_at: '2026-05-20T03:30:00.000Z', status: 'submitted', approval_stage: '财务审批', risk_flags_json: [ { source: 'manual_approval', operator: 'manager@example.com', operator_username: 'manager@example.com', previous_approval_stage: '直属领导审批', next_approval_stage: '财务审批', created_at: '2026-05-20T03:30:00.000Z' } ], items: [] }) const leaderStep = request.progressSteps.find((step) => step.label === '直属领导审批') assert.equal(leaderStep.time, '李经理通过') assert.ok(!leaderStep.title.includes('manager@example.com')) } finally { Date.now = originalNow } }) test('travel expense items describe departure return and lodging time below the date', () => { const request = mapExpenseClaimToRequest({ id: 'claim-travel-time-labels', claim_no: 'EXP-202605-TRAVEL', employee_name: '张三', department_name: '市场部', expense_type: 'travel', reason: '北京客户现场出差', location: '北京', amount: 1108, invoice_count: 3, occurred_at: '2026-05-13T01:00:00.000Z', created_at: '2026-05-13T01:30:00.000Z', updated_at: '2026-05-13T03:30:00.000Z', status: 'draft', approval_stage: '待提交', risk_flags_json: [], items: [ { id: 'outbound-train', item_type: 'train_ticket', item_reason: '从广州南到北京南', item_location: '北京', item_date: '2026-05-13', item_amount: 354, invoice_id: 'outbound.png' }, { id: 'hotel', item_type: 'hotel_ticket', item_reason: '北京全季酒店', item_location: '北京', item_date: '2026-05-14', item_amount: 100, invoice_id: 'hotel.png' }, { id: 'return-train', item_type: 'train_ticket', item_reason: '从北京南到广州南', item_location: '广州', item_date: '2026-05-15', item_amount: 354, invoice_id: 'return.png' }, { id: 'allowance', item_type: 'travel_allowance', item_reason: '系统自动计算出差补贴', item_location: '北京', item_date: '2026-05-15', item_amount: 300, invoice_id: '', is_system_generated: true } ] }) assert.equal(request.expenseItems.find((item) => item.id === 'outbound-train')?.dayLabel, '出发时间') assert.equal(request.expenseItems.find((item) => item.id === 'return-train')?.dayLabel, '返回时间') assert.equal(request.expenseItems.find((item) => item.id === 'hotel')?.dayLabel, '住宿时间') assert.equal(request.expenseItems.at(-1)?.id, 'allowance') assert.equal(request.expenseItems.at(-1)?.dayLabel, '系统自动计算') }) test('completed finance approval marks finance and archive progress steps', () => { const request = mapExpenseClaimToRequest({ id: 'claim-finance-completed', claim_no: 'EXP-202605-004', employee_name: '张三', department_name: '市场部', expense_type: 'transport', reason: '交通报销', location: '上海', amount: 88, invoice_count: 1, occurred_at: '2026-05-20T01:00:00.000Z', submitted_at: '2026-05-20T02:00:00.000Z', created_at: '2026-05-20T01:30:00.000Z', updated_at: '2026-05-20T04:00:00.000Z', status: 'approved', approval_stage: '归档入账', risk_flags_json: [ { source: 'manual_approval', operator: '李经理', previous_approval_stage: '直属领导审批', next_approval_stage: '财务审批', created_at: '2026-05-20T03:00:00.000Z' }, { source: 'finance_approval', operator: '财务复核', previous_approval_stage: '财务审批', next_approval_stage: '归档入账', created_at: '2026-05-20T04:00:00.000Z' } ], items: [] }) const financeStep = request.progressSteps.find((step) => step.label === '财务审批') const archiveStep = request.progressSteps.find((step) => step.label === '归档入账') assert.equal(request.workflowNode, '归档入账') assert.equal(financeStep.time, '财务复核通过') assert.match(financeStep.detail, /2026-05-20/) assert.equal(archiveStep.time, '归档入账') assert.equal(archiveStep.done, true) }) test('current direct manager step shows how long the claim has stayed there', () => { const originalNow = Date.now Date.now = () => new Date('2026-05-20T05:15:00.000Z').getTime() try { const request = mapExpenseClaimToRequest({ id: 'claim-2', claim_no: 'EXP-202605-002', employee_name: '王五', department_name: '市场部', expense_type: 'office', reason: '办公用品', location: '上海', amount: 128, invoice_count: 1, occurred_at: '2026-05-20T01:00:00.000Z', submitted_at: '2026-05-20T02:00:00.000Z', created_at: '2026-05-20T01:30:00.000Z', updated_at: '2026-05-20T02:00:00.000Z', status: 'submitted', approval_stage: '直属领导审批', risk_flags_json: [], items: [] }) const leaderStep = request.progressSteps.find((step) => step.label === '直属领导审批') const submitStep = request.progressSteps.find((step) => step.label === '待提交') assert.equal(submitStep.time, '王五提交') assert.match(submitStep.detail, /2026-05-20/) assert.equal(leaderStep.current, true) assert.equal(leaderStep.time, '停留 3小时15分钟') } finally { Date.now = originalNow } })