from __future__ import annotations import uuid from datetime import UTC, datetime from decimal import Decimal from typing import Any from app.models.financial_record import ExpenseClaim APPLICATION_REIMBURSEMENT_TYPE_MAP = { "travel_application": "travel", "purchase_application": "office", "meeting_application": "meeting", "expense_application": "other", "application": "other", } class ExpenseClaimApplicationHandoffMixin: @staticmethod def _resolve_reimbursement_type_from_application(expense_type: str | None) -> str: normalized = str(expense_type or "").strip().lower() if normalized in APPLICATION_REIMBURSEMENT_TYPE_MAP: return APPLICATION_REIMBURSEMENT_TYPE_MAP[normalized] if normalized.endswith("_application"): return normalized.removesuffix("_application") or "other" return normalized or "other" def _create_reimbursement_draft_from_application( self, *, application_claim: ExpenseClaim, approval_flag: dict[str, Any], operator: str, ) -> ExpenseClaim: occurred_at = application_claim.occurred_at or datetime.now(UTC) created_at = datetime.now(UTC) draft_claim = ExpenseClaim( claim_no=self._generate_claim_no(occurred_at), employee_id=application_claim.employee_id, employee_name=application_claim.employee_name, department_id=application_claim.department_id, department_name=application_claim.department_name, project_code=application_claim.project_code, expense_type=self._resolve_reimbursement_type_from_application(application_claim.expense_type), reason=application_claim.reason, location=application_claim.location, amount=application_claim.amount or Decimal("0.00"), currency=application_claim.currency or "CNY", invoice_count=0, occurred_at=occurred_at, submitted_at=None, status="draft", approval_stage="待提交", risk_flags_json=[ { "source": "application_handoff", "event_type": "expense_application_to_reimbursement_draft", "handoff_event_id": str(uuid.uuid4()), "severity": "info", "label": "申请转报销草稿", "message": ( f"费用申请 {application_claim.claim_no} 已由 {operator} 确认审核," "系统已生成报销草稿。" ), "application_claim_id": application_claim.id, "application_claim_no": application_claim.claim_no, "application_budget_amount": str(application_claim.amount or Decimal("0.00")), "application_approval_event_id": str(approval_flag.get("approval_event_id") or ""), "leader_opinion": str( approval_flag.get("leader_opinion") or approval_flag.get("opinion") or "" ).strip(), "budget_opinion": str(approval_flag.get("budget_opinion") or "").strip(), "created_at": created_at.isoformat(), } ], ) self.db.add(draft_claim) self.db.flush() approval_flag["generated_draft_claim_id"] = draft_claim.id approval_flag["generated_draft_claim_no"] = draft_claim.claim_no approval_flag["handoff_event_type"] = "expense_application_to_reimbursement_draft" approval_flag["handoff_message"] = f"已生成报销草稿 {draft_claim.claim_no}。" return draft_claim