feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
@@ -25,6 +25,7 @@ from app.services.expense_claim_workflow_constants import (
|
||||
DIRECT_MANAGER_APPROVAL_STAGE,
|
||||
)
|
||||
from app.services.expense_claims import ExpenseClaimService
|
||||
from app.services.tenant_registry import TenantRegistryService
|
||||
|
||||
|
||||
def build_session() -> Session:
|
||||
@@ -316,12 +317,15 @@ def test_submit_blocks_fixable_pre_review_before_budget_and_submission_event(
|
||||
).all()
|
||||
)
|
||||
assert len(pre_review_events) == 1
|
||||
assert db.scalar(
|
||||
select(BusinessEvent).where(
|
||||
BusinessEvent.aggregate_id == claim.id,
|
||||
BusinessEvent.event_type == "claim_submitted",
|
||||
assert (
|
||||
db.scalar(
|
||||
select(BusinessEvent).where(
|
||||
BusinessEvent.aggregate_id == claim.id,
|
||||
BusinessEvent.event_type == "claim_submitted",
|
||||
)
|
||||
)
|
||||
) is None
|
||||
is None
|
||||
)
|
||||
assert db.scalar(select(BudgetReservation)) is None
|
||||
assert db.scalar(select(BudgetTransaction)) is None
|
||||
|
||||
@@ -400,12 +404,15 @@ def test_submit_rechecks_dynamic_risk_context_and_rejects_stale_ready_review(
|
||||
assert error_info.value.review["review_id"] != ready_flag["review_id"]
|
||||
assert error_info.value.review["decision"] == "needs_fix"
|
||||
assert db.get(ExpenseClaim, claim.id).status == "draft"
|
||||
assert db.scalar(
|
||||
select(BusinessEvent).where(
|
||||
BusinessEvent.aggregate_id == claim.id,
|
||||
BusinessEvent.event_type == "claim_submitted",
|
||||
assert (
|
||||
db.scalar(
|
||||
select(BusinessEvent).where(
|
||||
BusinessEvent.aggregate_id == claim.id,
|
||||
BusinessEvent.event_type == "claim_submitted",
|
||||
)
|
||||
)
|
||||
) is None
|
||||
is None
|
||||
)
|
||||
pre_review_events = list(
|
||||
db.scalars(
|
||||
select(BusinessEvent).where(
|
||||
@@ -429,6 +436,7 @@ def test_legacy_bootstrap_excludes_migration_owned_tables(
|
||||
monkeypatch.setattr(service, "_seed_agent_assets", lambda: None)
|
||||
monkeypatch.setattr(service, "_sync_demo_financial_records", lambda: None)
|
||||
monkeypatch.setattr(service, "_seed_runs_and_logs", lambda: None)
|
||||
monkeypatch.setattr(TenantRegistryService, "ensure_builtin", lambda self: None)
|
||||
|
||||
service._prepare_foundation()
|
||||
|
||||
@@ -473,6 +481,7 @@ def test_event_write_is_idempotent_for_same_business_operation() -> None:
|
||||
|
||||
def test_submit_claim_creates_case_link_and_structured_event() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
tenant_id="default",
|
||||
username="employee-case@example.com",
|
||||
name="张三",
|
||||
role_codes=[],
|
||||
@@ -536,6 +545,7 @@ def test_payment_event_failure_rolls_back_payment_archive_and_nested_audit(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
current_user = CurrentUserContext(
|
||||
tenant_id="default",
|
||||
username="finance-case@example.com",
|
||||
name="财务付款",
|
||||
role_codes=["finance"],
|
||||
@@ -639,6 +649,7 @@ def test_application_approval_links_generated_reimbursement_to_same_case() -> No
|
||||
approved = ExpenseClaimService(db).approve_claim(
|
||||
application_claim.id,
|
||||
CurrentUserContext(
|
||||
tenant_id="default",
|
||||
username=manager.email,
|
||||
name=manager.name,
|
||||
role_codes=["manager"],
|
||||
@@ -680,6 +691,7 @@ def test_application_approval_links_generated_reimbursement_to_same_case() -> No
|
||||
|
||||
def test_payment_records_application_archive_event_in_same_case() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
tenant_id="default",
|
||||
username="finance-archive@example.com",
|
||||
name="财务付款",
|
||||
role_codes=["finance"],
|
||||
|
||||
Reference in New Issue
Block a user