feat: 同步报销流程与工作台改动

This commit is contained in:
caoxiaozhu
2026-06-09 08:32:00 +00:00
parent e124e4bbcb
commit 25724c354f
64 changed files with 6518 additions and 687 deletions

View File

@@ -234,6 +234,124 @@ def test_budget_warning_application_still_skips_budget_manager_when_not_over_bud
)
def test_application_routes_to_budget_manager_when_usage_reaches_90_percent() -> None:
with build_session() as db:
department, manager, _budget_manager, employee = _seed_people(db, suffix="OVER-90-APP")
_seed_budget_allocation(
db,
department_id=department.id,
department_name=department.name,
amount=Decimal("10000.00"),
)
claim = ExpenseClaim(
claim_no="APP-20260530-OVER-90",
employee_id=employee.id,
employee_name=employee.name,
department_id=department.id,
department_name=department.name,
project_code=None,
expense_type="travel_application",
reason="客户现场支持",
location="上海",
amount=Decimal("9500.00"),
currency="CNY",
invoice_count=0,
occurred_at=datetime(2026, 5, 30, 9, 0, tzinfo=UTC),
submitted_at=datetime(2026, 5, 30, 10, 0, tzinfo=UTC),
status="submitted",
approval_stage=DIRECT_MANAGER_APPROVAL_STAGE,
risk_flags_json=[],
)
db.add(claim)
db.commit()
routed = ExpenseClaimService(db).approve_claim(
claim.id,
CurrentUserContext(
username=manager.email,
name=manager.name,
role_codes=["manager"],
is_admin=False,
),
opinion="业务必要,同意申请。",
)
assert routed is not None
assert routed.status == "submitted"
assert routed.approval_stage == BUDGET_MANAGER_APPROVAL_STAGE
assert any(
isinstance(flag, dict)
and flag.get("source") == "approval_routing"
and flag.get("requires_budget_review") is True
and flag.get("route") == "budget_manager"
and any("90%" in item or "90" in item for item in flag.get("reasons", []))
for flag in routed.risk_flags_json
)
def test_application_stage_risk_under_90_percent_does_not_route_to_budget_manager() -> None:
with build_session() as db:
department, manager, _budget_manager, employee = _seed_people(db, suffix="RISK-APP")
_seed_budget_allocation(
db,
department_id=department.id,
department_name=department.name,
amount=Decimal("10000.00"),
)
claim = ExpenseClaim(
claim_no="APP-20260530-RISK-UNDER-90",
employee_id=employee.id,
employee_name=employee.name,
department_id=department.id,
department_name=department.name,
project_code=None,
expense_type="travel_application",
reason="客户现场支持",
location="上海",
amount=Decimal("500.00"),
currency="CNY",
invoice_count=0,
occurred_at=datetime(2026, 5, 30, 9, 0, tzinfo=UTC),
submitted_at=datetime(2026, 5, 30, 10, 0, tzinfo=UTC),
status="submitted",
approval_stage=DIRECT_MANAGER_APPROVAL_STAGE,
risk_flags_json=[
{
"source": "submission_review",
"severity": "high",
"label": "申请信息风险",
"message": "申请事由需要领导关注。",
"business_stage": "expense_application",
}
],
)
db.add(claim)
db.commit()
approved = ExpenseClaimService(db).approve_claim(
claim.id,
CurrentUserContext(
username=manager.email,
name=manager.name,
role_codes=["manager"],
is_admin=False,
),
opinion="业务必要,同意申请。",
)
assert approved is not None
assert approved.status == "approved"
assert approved.approval_stage == APPLICATION_LINK_STATUS_STAGE
route_flag = [
flag
for flag in approved.risk_flags_json
if isinstance(flag, dict) and flag.get("source") == "approval_routing"
][0]
assert route_flag["requires_budget_review"] is False
assert route_flag["route"] == "approval_done"
assert route_flag["current_risk_count"] == 1
def test_application_route_ignores_reimbursement_stage_current_risks() -> None:
with build_session() as db:
department, manager, _budget_manager, employee = _seed_people(db, suffix="MIXED-STAGE")