feat: 新增员工行为画像算法与费用风险标签体系
后端新增员工行为画像算法模块,支持标签规则引擎和评分计算, 完善员工模型、银行信息、序列化和导入逻辑,优化报销审批流 和工作流常量,增强 Hermes 同步和知识同步能力,前端新增费 用画像详情弹窗、雷达图和风险卡片组件,完善登录页和工作台 样式,优化文档中心和归档中心交互,补充单元测试。
This commit is contained in:
@@ -2680,6 +2680,23 @@ def test_list_archived_claims_returns_company_archived_records_for_finance() ->
|
||||
approval_stage="财务审批",
|
||||
risk_flags_json=[],
|
||||
),
|
||||
ExpenseClaim(
|
||||
claim_no="EXP-ARCH-PAID",
|
||||
employee_name="丙",
|
||||
department_name="C部",
|
||||
project_code="PRJ-C",
|
||||
expense_type="office",
|
||||
reason="C 报销",
|
||||
location="深圳",
|
||||
amount=Decimal("180.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 11, 14, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 11, 15, 0, tzinfo=UTC),
|
||||
status="paid",
|
||||
approval_stage="已付款",
|
||||
risk_flags_json=[],
|
||||
),
|
||||
ExpenseClaim(
|
||||
claim_no="AP-20260525120000-ABCDEFGH",
|
||||
employee_name="丙",
|
||||
@@ -2722,6 +2739,7 @@ def test_list_archived_claims_returns_company_archived_records_for_finance() ->
|
||||
|
||||
assert {claim.claim_no for claim in claims} == {
|
||||
"EXP-ARCH-101",
|
||||
"EXP-ARCH-PAID",
|
||||
"AP-20260525120000-ABCDEFGH",
|
||||
}
|
||||
|
||||
@@ -3894,7 +3912,7 @@ def test_finance_cannot_operate_own_claim_in_finance_stage() -> None:
|
||||
assert claim.risk_flags_json == []
|
||||
|
||||
|
||||
def test_finance_can_approve_claim_to_archive_stage() -> None:
|
||||
def test_finance_can_approve_claim_to_pending_payment_stage() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance-approve@example.com",
|
||||
name="财务复核",
|
||||
@@ -3931,19 +3949,65 @@ def test_finance_can_approve_claim_to_archive_stage() -> None:
|
||||
)
|
||||
|
||||
assert approved is not None
|
||||
assert approved.status == "approved"
|
||||
assert approved.approval_stage == "归档入账"
|
||||
assert approved.status == "pending_payment"
|
||||
assert approved.approval_stage == "待付款"
|
||||
assert any(
|
||||
isinstance(flag, dict)
|
||||
and flag.get("source") == "finance_approval"
|
||||
and flag.get("event_type") == "expense_claim_finance_approval"
|
||||
and flag.get("opinion") == "票据与明细一致,同意入账。"
|
||||
and flag.get("previous_approval_stage") == "财务审批"
|
||||
and flag.get("next_approval_stage") == "归档入账"
|
||||
and flag.get("next_status") == "pending_payment"
|
||||
and flag.get("next_approval_stage") == "待付款"
|
||||
for flag in approved.risk_flags_json
|
||||
)
|
||||
|
||||
|
||||
def test_finance_can_mark_pending_payment_claim_as_paid() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance-pay@example.com",
|
||||
name="财务付款",
|
||||
role_codes=["finance"],
|
||||
is_admin=False,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
claim = ExpenseClaim(
|
||||
claim_no="EXP-FIN-PAY-201",
|
||||
employee_name="张三",
|
||||
department_name="市场部",
|
||||
project_code="PRJ-A",
|
||||
expense_type="transport",
|
||||
reason="交通报销",
|
||||
location="上海",
|
||||
amount=Decimal("66.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 12, 9, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 12, 10, 0, tzinfo=UTC),
|
||||
status="pending_payment",
|
||||
approval_stage="待付款",
|
||||
risk_flags_json=[],
|
||||
)
|
||||
db.add(claim)
|
||||
db.commit()
|
||||
|
||||
paid = ExpenseClaimService(db).mark_claim_paid(claim.id, current_user)
|
||||
|
||||
assert paid is not None
|
||||
assert paid.status == "paid"
|
||||
assert paid.approval_stage == "已付款"
|
||||
assert any(
|
||||
isinstance(flag, dict)
|
||||
and flag.get("source") == "payment"
|
||||
and flag.get("event_type") == "expense_claim_payment_completed"
|
||||
and flag.get("previous_status") == "pending_payment"
|
||||
and flag.get("next_status") == "paid"
|
||||
and flag.get("next_approval_stage") == "已付款"
|
||||
for flag in paid.risk_flags_json
|
||||
)
|
||||
|
||||
|
||||
def test_return_claim_rejects_already_returned_claim_without_adding_event() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance-returned@example.com",
|
||||
|
||||
Reference in New Issue
Block a user