fix(approval): replay immutable action responses
This commit is contained in:
@@ -15,6 +15,7 @@ from app.models.audit_log import AuditLog
|
||||
from app.models.employee import Employee
|
||||
from app.models.expense_case import BusinessEvent
|
||||
from app.models.financial_record import ExpenseClaim
|
||||
from app.schemas.reimbursement import ExpenseClaimRead
|
||||
from app.services.approval_action_protocol import (
|
||||
ApprovalActionConflictError,
|
||||
ApprovalActionProtocol,
|
||||
@@ -117,6 +118,9 @@ def test_approve_replay_persists_one_ledger_event_and_audit(
|
||||
)
|
||||
|
||||
assert first is not None and replay is not None
|
||||
assert ExpenseClaimRead.model_validate(replay).model_dump(mode="json") == (
|
||||
ExpenseClaimRead.model_validate(first).model_dump(mode="json")
|
||||
)
|
||||
assert replay.approval_stage == "财务审批"
|
||||
assert db.scalar(select(func.count()).select_from(ApprovalActionLedger)) == 1
|
||||
assert db.scalar(select(func.count()).select_from(BusinessEvent)) == 1
|
||||
@@ -130,6 +134,61 @@ def test_approve_replay_persists_one_ledger_event_and_audit(
|
||||
)
|
||||
|
||||
|
||||
def test_approve_replay_returns_original_snapshot_after_claim_moves_forward(
|
||||
session_factory: sessionmaker[Session],
|
||||
) -> None:
|
||||
with session_factory() as db:
|
||||
claim = _seed_claim(db, claim_id="claim-action-original-response")
|
||||
service = ExpenseClaimService(db)
|
||||
first = service.approve_claim(
|
||||
claim.id,
|
||||
_manager_user(),
|
||||
opinion="同意",
|
||||
request_id="approve-original-response-1",
|
||||
expected_status="submitted",
|
||||
expected_approval_stage="直属领导审批",
|
||||
)
|
||||
assert first is not None
|
||||
first_json = ExpenseClaimRead.model_validate(first).model_dump(mode="json")
|
||||
|
||||
current = db.get(ExpenseClaim, claim.id)
|
||||
assert current is not None
|
||||
current.status = "pending_payment"
|
||||
current.approval_stage = "待付款"
|
||||
current.risk_flags_json = [
|
||||
*list(current.risk_flags_json or []),
|
||||
{"source": "future_state", "message": "首次响应之后产生的内部状态"},
|
||||
]
|
||||
db.commit()
|
||||
|
||||
replay = service.approve_claim(
|
||||
claim.id,
|
||||
_manager_user(),
|
||||
opinion="同意",
|
||||
request_id="approve-original-response-1",
|
||||
expected_status="submitted",
|
||||
expected_approval_stage="直属领导审批",
|
||||
)
|
||||
|
||||
assert replay is not None
|
||||
assert ExpenseClaimRead.model_validate(replay).model_dump(mode="json") == first_json
|
||||
assert replay.approval_stage == "财务审批"
|
||||
assert all(
|
||||
item.get("source") != "future_state" for item in replay.risk_flags_json or []
|
||||
)
|
||||
persisted = db.get(ExpenseClaim, claim.id)
|
||||
assert persisted is not None
|
||||
assert persisted.status == "pending_payment"
|
||||
assert persisted.approval_stage == "待付款"
|
||||
ledger = db.scalar(
|
||||
select(ApprovalActionLedger).where(
|
||||
ApprovalActionLedger.request_id == "approve-original-response-1"
|
||||
)
|
||||
)
|
||||
assert ledger is not None
|
||||
assert ledger.response_json == first_json
|
||||
|
||||
|
||||
def test_request_id_payload_mismatch_and_stale_preconditions_return_conflict(
|
||||
session_factory: sessionmaker[Session],
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user