feat: 增强风险规则生成引擎与预算中心页面
后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块, 优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强 报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图 组件,重构审计页面和风险规则测试对话框交互,完善文档中心 和报销创建页面细节,补充单元测试覆盖。
This commit is contained in:
@@ -33,9 +33,11 @@ from app.services.agent_asset_spreadsheet import RISK_RULES_LIBRARY
|
||||
from app.services.agent_foundation import AgentFoundationService
|
||||
from app.services.audit import AuditLogService
|
||||
from app.services.document_intelligence import build_document_insight
|
||||
from app.services.document_numbering import is_application_claim_no
|
||||
from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy
|
||||
from app.services.expense_claim_attachment_presentation import ExpenseClaimAttachmentPresentation
|
||||
from app.services.expense_claim_attachment_storage import ExpenseClaimAttachmentStorage
|
||||
from app.services.expense_claim_application_handoff import ExpenseClaimApplicationHandoffMixin
|
||||
from app.services.expense_claim_attachment_analysis import ExpenseClaimAttachmentAnalysisMixin
|
||||
from app.services.expense_claim_attachment_document import ExpenseClaimAttachmentDocumentMixin
|
||||
from app.services.expense_claim_attachment_operations import ExpenseClaimAttachmentOperationsMixin
|
||||
@@ -124,6 +126,7 @@ from app.services.ocr import OcrService
|
||||
|
||||
|
||||
class ExpenseClaimService(
|
||||
ExpenseClaimApplicationHandoffMixin,
|
||||
ExpenseClaimAttachmentOperationsMixin,
|
||||
ExpenseClaimReviewPreviewMixin,
|
||||
ExpenseClaimDraftFlowMixin,
|
||||
@@ -153,7 +156,7 @@ class ExpenseClaimService(
|
||||
or ""
|
||||
).strip().lower()
|
||||
return (
|
||||
claim_no.startswith("APP-")
|
||||
is_application_claim_no(claim_no)
|
||||
or expense_type == "application"
|
||||
or expense_type.endswith("_application")
|
||||
or document_type in {"application", "expense_application"}
|
||||
@@ -492,9 +495,28 @@ class ExpenseClaimService(
|
||||
|
||||
def delete_claim(self, claim_id: str, current_user: CurrentUserContext) -> ExpenseClaim | None:
|
||||
claim = self.get_claim(claim_id, current_user)
|
||||
if claim is None and (
|
||||
current_user.is_admin or self._access_policy.has_archive_center_access(current_user)
|
||||
):
|
||||
candidate_claim = self.db.scalar(
|
||||
select(ExpenseClaim)
|
||||
.options(
|
||||
selectinload(ExpenseClaim.items),
|
||||
selectinload(ExpenseClaim.employee).selectinload(Employee.manager),
|
||||
selectinload(ExpenseClaim.employee).selectinload(Employee.roles),
|
||||
)
|
||||
.where(ExpenseClaim.id == claim_id)
|
||||
)
|
||||
if candidate_claim is not None and (
|
||||
current_user.is_admin or self._access_policy.is_archived_claim(candidate_claim)
|
||||
):
|
||||
claim = candidate_claim
|
||||
if claim is None:
|
||||
return None
|
||||
|
||||
if self._access_policy.is_archived_claim(claim) and not current_user.is_admin:
|
||||
raise ValueError("已归档单据不能删除,只有高级管理员可以执行删除。")
|
||||
|
||||
if not self._access_policy.has_claim_delete_access(current_user):
|
||||
self._ensure_draft_claim(claim)
|
||||
if not self._access_policy.is_claim_owned_by_current_user(claim, current_user):
|
||||
@@ -642,7 +664,7 @@ class ExpenseClaimService(
|
||||
label = "领导审批通过"
|
||||
next_status = "approved"
|
||||
next_stage = "审批完成"
|
||||
default_message = "{operator} 已审批通过,申请流程完成。"
|
||||
default_message = "{operator} 已确认审核,申请流程完成并生成报销草稿。"
|
||||
else:
|
||||
event_type = "expense_claim_approval"
|
||||
label = "领导审批通过"
|
||||
@@ -663,9 +685,12 @@ class ExpenseClaimService(
|
||||
else:
|
||||
raise ValueError("当前节点不支持审批通过。")
|
||||
|
||||
approval_opinion = str(opinion or "").strip()
|
||||
if previous_stage == "直属领导审批" and not approval_opinion:
|
||||
raise ValueError("领导审核意见不能为空,请填写意见后再确认审核。")
|
||||
|
||||
before_json = self._serialize_claim(claim)
|
||||
operator = self._access_policy.resolve_current_user_display_name(current_user)
|
||||
approval_opinion = str(opinion or "").strip()
|
||||
approval_flag = {
|
||||
"source": approval_source,
|
||||
"event_type": event_type,
|
||||
@@ -692,6 +717,12 @@ class ExpenseClaimService(
|
||||
claim.approval_stage = next_stage
|
||||
if claim.submitted_at is None:
|
||||
claim.submitted_at = datetime.now(UTC)
|
||||
if is_application_claim and previous_stage == "直属领导审批":
|
||||
generated_draft = self._create_reimbursement_draft_from_application(
|
||||
application_claim=claim,
|
||||
approval_flag=approval_flag,
|
||||
operator=operator,
|
||||
)
|
||||
claim.risk_flags_json = [*list(claim.risk_flags_json or []), approval_flag]
|
||||
|
||||
self.db.commit()
|
||||
|
||||
Reference in New Issue
Block a user