feat(expenses): add transactional expense case events
This commit is contained in:
@@ -6,6 +6,7 @@ from decimal import Decimal, InvalidOperation
|
||||
from typing import Any
|
||||
|
||||
from app.api.deps import CurrentUserContext
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.services.budget import BudgetService
|
||||
from app.services.expense_claim_workflow_constants import (
|
||||
APPLICATION_LINK_STATUS_STAGE,
|
||||
@@ -45,6 +46,7 @@ class ExpenseClaimApprovalFlowMixin:
|
||||
next_budget_manager = None
|
||||
merged_budget_approval = False
|
||||
route_decision_flag: dict[str, Any] | None = None
|
||||
generated_draft = None
|
||||
if previous_stage == DIRECT_MANAGER_APPROVAL_STAGE:
|
||||
if not self._access_policy.can_approve_claim(current_user, claim):
|
||||
raise ValueError("只有当前直属领导审批人可以审批通过该单据。")
|
||||
@@ -254,6 +256,48 @@ class ExpenseClaimApprovalFlowMixin:
|
||||
business_stage=business_stage,
|
||||
)
|
||||
|
||||
correlation_id = str(approval_flag.get("approval_event_id") or uuid.uuid4())
|
||||
structured_event_type = "approval_stage_completed"
|
||||
if is_application_claim and next_status == "approved":
|
||||
structured_event_type = "application_approved"
|
||||
elif not is_application_claim and next_status == PAYMENT_PENDING_STATUS:
|
||||
structured_event_type = "claim_approved"
|
||||
expense_case, _event = self._expense_cases.record_claim_event(
|
||||
claim,
|
||||
event_type=structured_event_type,
|
||||
actor_id=current_user.username,
|
||||
tenant_id=getattr(current_user, "tenant_id", None),
|
||||
correlation_id=correlation_id,
|
||||
idempotency_key=correlation_id,
|
||||
previous_status=str(before_json.get("status") or ""),
|
||||
previous_approval_stage=previous_stage,
|
||||
extra_payload={
|
||||
"workflow_event_type": event_type,
|
||||
"opinion": approval_opinion,
|
||||
"route_requires_budget_review": bool(
|
||||
route_decision_flag and route_decision_flag.get("requires_budget_review")
|
||||
),
|
||||
},
|
||||
)
|
||||
if generated_draft is not None:
|
||||
self._expense_cases.record_claim_event(
|
||||
generated_draft,
|
||||
event_type="reimbursement_draft_generated",
|
||||
actor_id="system",
|
||||
tenant_id=getattr(current_user, "tenant_id", None),
|
||||
correlation_id=correlation_id,
|
||||
idempotency_key=correlation_id,
|
||||
causation_id=_event.id,
|
||||
previous_status="",
|
||||
previous_approval_stage="",
|
||||
extra_payload={
|
||||
"application_claim_id": claim.id,
|
||||
"application_claim_no": claim.claim_no,
|
||||
},
|
||||
expense_case=expense_case,
|
||||
relation_type="generated_reimbursement",
|
||||
)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(claim)
|
||||
self._access_policy.attach_budget_approval_snapshot(claim)
|
||||
@@ -326,6 +370,47 @@ class ExpenseClaimApprovalFlowMixin:
|
||||
claim.approval_stage = PAYMENT_PAID_STAGE
|
||||
claim.risk_flags_json = [*list(claim.risk_flags_json or []), payment_flag]
|
||||
|
||||
payment_correlation_id = str(payment_flag.get("payment_event_id") or uuid.uuid4())
|
||||
expense_case, payment_event = self._expense_cases.record_claim_event(
|
||||
claim,
|
||||
event_type="payment_completed",
|
||||
actor_id=current_user.username,
|
||||
tenant_id=getattr(current_user, "tenant_id", None),
|
||||
correlation_id=payment_correlation_id,
|
||||
idempotency_key=payment_correlation_id,
|
||||
previous_status=str(before_json.get("status") or ""),
|
||||
previous_approval_stage=previous_stage,
|
||||
extra_payload={"archived_applications": archived_applications},
|
||||
)
|
||||
for archived_application in archived_applications:
|
||||
application_claim = self.db.get(
|
||||
ExpenseClaim,
|
||||
str(archived_application.get("application_claim_id") or ""),
|
||||
)
|
||||
if application_claim is None:
|
||||
raise RuntimeError("付款关联的申请单已不存在,无法记录归档事件。")
|
||||
archive_event_id = str(archived_application.get("archive_event_id") or "").strip()
|
||||
self._expense_cases.record_claim_event(
|
||||
application_claim,
|
||||
event_type="application_archived",
|
||||
actor_id=current_user.username,
|
||||
tenant_id=getattr(current_user, "tenant_id", None),
|
||||
correlation_id=payment_correlation_id,
|
||||
idempotency_key=archive_event_id,
|
||||
causation_id=payment_event.id,
|
||||
previous_status=str(archived_application.get("previous_status") or ""),
|
||||
previous_approval_stage=str(
|
||||
archived_application.get("previous_approval_stage") or ""
|
||||
),
|
||||
extra_payload={
|
||||
"reimbursement_claim_id": claim.id,
|
||||
"reimbursement_claim_no": claim.claim_no,
|
||||
},
|
||||
expense_case=expense_case,
|
||||
relation_type="application",
|
||||
update_case_state=False,
|
||||
)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(claim)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user