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

@@ -17,6 +17,7 @@ from app.services.expense_claim_risk_stage import (
class ExpenseClaimApprovalRoutingMixin:
_APPLICATION_BUDGET_REVIEW_USAGE_THRESHOLD = Decimal("90.00")
_BUDGET_REVIEW_RATINGS = {"block"}
_BUDGET_REVIEW_RISK_LEVELS = {"high", "critical"}
_ROUTE_RISK_SEVERITIES = {"medium", "high", "critical", "danger"}
@@ -63,7 +64,11 @@ class ExpenseClaimApprovalRoutingMixin:
) -> dict[str, Any]:
business_stage = risk_business_stage_for_claim(is_application_claim=is_application_claim)
budget_result = BudgetService(self.db).analyze_claim_budget(claim)
budget_reasons = self._collect_budget_route_reasons(budget_result)
budget_reasons = (
self._collect_application_budget_route_reasons(budget_result)
if is_application_claim
else self._collect_budget_route_reasons(budget_result)
)
current_risk_reasons = self._collect_current_route_risk_reasons(
claim.risk_flags_json,
business_stage=business_stage,
@@ -75,7 +80,9 @@ class ExpenseClaimApprovalRoutingMixin:
else []
)
reasons = self._dedupe_reasons(
[*budget_reasons, *current_risk_reasons, *historical_risk_reasons]
budget_reasons
if is_application_claim
else [*budget_reasons, *current_risk_reasons, *historical_risk_reasons]
)
requires_budget_review = bool(reasons)
route = (
@@ -86,11 +93,18 @@ class ExpenseClaimApprovalRoutingMixin:
else "finance"
)
label = "需要预算管理者复核" if requires_budget_review else "跳过预算管理者复核"
message = (
"系统根据预算、当前风险和历史风险判断,该单据需要预算管理者二次确认。"
if requires_budget_review
else "系统根据预算、当前风险和历史风险判断,该单据可跳过预算管理者复核。"
)
if is_application_claim:
message = (
"系统根据预算占用阈值判断,该申请单达到 90% 预算复核线,需要预算管理者二次确认。"
if requires_budget_review
else "系统根据预算占用阈值判断,该申请单未达到 90% 预算复核线,可跳过预算管理者复核。"
)
else:
message = (
"系统根据预算、当前风险和历史风险判断,该单据需要预算管理者二次确认。"
if requires_budget_review
else "系统根据预算、当前风险和历史风险判断,该单据可跳过预算管理者复核。"
)
return with_risk_business_stage(
{
@@ -136,6 +150,20 @@ class ExpenseClaimApprovalRoutingMixin:
reasons.append(f"预计超预算 {over_budget_amount}")
return self._dedupe_reasons(reasons)
def _collect_application_budget_route_reasons(self, budget_result: dict[str, Any]) -> list[str]:
metrics = budget_result.get("metrics") if isinstance(budget_result.get("metrics"), dict) else {}
over_budget_amount = self._decimal(metrics.get("over_budget_amount"))
if over_budget_amount > Decimal("0.00"):
return [f"预计超预算 {over_budget_amount}"]
after_usage_rate = self._decimal(metrics.get("after_usage_rate"))
claim_amount_ratio = self._decimal(metrics.get("claim_amount_ratio"))
budget_usage_rate = max(after_usage_rate, claim_amount_ratio)
if budget_usage_rate >= self._APPLICATION_BUDGET_REVIEW_USAGE_THRESHOLD:
return [f"审批后预算占用达到 {budget_usage_rate}%,触发 90% 预算复核线"]
return []
def _collect_current_route_risk_reasons(
self,
risk_flags: list[Any] | None,