feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
@@ -439,26 +439,34 @@ def test_claim_standard_adjustment_endpoint_recalculates_and_marks_reviewer_noti
|
||||
"item_id": item_id,
|
||||
"title": "住宿超标待说明",
|
||||
"risk": "住宿票据金额超过职级标准。",
|
||||
"application_days": 2,
|
||||
"original_amount": "1000.00",
|
||||
"reimbursable_amount": "1000.00",
|
||||
"application_days": 99,
|
||||
"original_amount": "1.00",
|
||||
"reimbursable_amount": "999999.00",
|
||||
}
|
||||
]
|
||||
],
|
||||
"request_id": "standard-adjustment-endpoint-1",
|
||||
},
|
||||
headers={"x-auth-username": "emp-1", "x-auth-name": "Zhang San", "x-auth-grade": "P4"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
payload = response.json()
|
||||
assert payload["amount"] == "900.00"
|
||||
assert payload["amount"] == "450.00"
|
||||
standard_flag = next(
|
||||
flag
|
||||
for flag in payload["risk_flags_json"]
|
||||
if isinstance(flag, dict) and flag.get("source") == "reimbursement_standard_adjustment"
|
||||
)
|
||||
assert standard_flag["original_amount"] == "1000.00"
|
||||
assert standard_flag["reimbursable_amount"] == "900.00"
|
||||
assert standard_flag["employee_absorbed_amount"] == "100.00"
|
||||
assert standard_flag["reimbursable_amount"] == "450.00"
|
||||
assert standard_flag["employee_absorbed_amount"] == "550.00"
|
||||
assert standard_flag["calculation_source"] == "server_policy"
|
||||
assert standard_flag["policy_days"] == 1
|
||||
assert standard_flag["policy_grade"] == "P4"
|
||||
assert standard_flag["policy_matched_city"] == "北京"
|
||||
assert standard_flag["policy_hotel_rate"] == "450.00"
|
||||
assert standard_flag["policy_rule_name"]
|
||||
assert standard_flag["policy_rule_version"]
|
||||
assert standard_flag["visibility_scope"] == "leader"
|
||||
|
||||
|
||||
@@ -781,6 +789,82 @@ def test_approve_claim_endpoint_routes_direct_manager_claim_to_finance_review()
|
||||
assert ledgers[0].completed_at is not None
|
||||
|
||||
|
||||
def test_claim_owner_cannot_discover_or_operate_manager_approval_task() -> None:
|
||||
client, session_factory = build_client()
|
||||
with session_factory() as db:
|
||||
superior = Employee(
|
||||
id="manager-self-approval-superior",
|
||||
employee_no="E-SELF-APPROVAL-SUPERIOR",
|
||||
name="王总",
|
||||
email="superior-self-approval-api@example.com",
|
||||
)
|
||||
manager = Employee(
|
||||
id="manager-self-approval-owner",
|
||||
employee_no="E-SELF-APPROVAL-OWNER",
|
||||
name="李经理",
|
||||
email="manager-self-approval-api@example.com",
|
||||
manager=superior,
|
||||
)
|
||||
claim = ExpenseClaim(
|
||||
id="claim-self-approval-api",
|
||||
claim_no="EXP-SELF-APPROVAL-API",
|
||||
employee_id=manager.id,
|
||||
employee_name=manager.name,
|
||||
department_name="市场部",
|
||||
expense_type="transport",
|
||||
reason="交通报销",
|
||||
location="上海",
|
||||
amount=Decimal("66.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 13, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 13, 10, 0, tzinfo=UTC),
|
||||
status="submitted",
|
||||
approval_stage="直属领导审批",
|
||||
risk_flags_json=[],
|
||||
)
|
||||
db.add_all([superior, manager, claim])
|
||||
db.commit()
|
||||
|
||||
headers = {
|
||||
"X-Auth-Username": "manager-self-approval-api@example.com",
|
||||
"X-Auth-Name": "manager-self-approval-api@example.com",
|
||||
"X-Auth-Role-Codes": "manager",
|
||||
}
|
||||
approve_response = client.post(
|
||||
"/api/v1/reimbursements/claims/claim-self-approval-api/approve",
|
||||
json={
|
||||
"opinion": "同意",
|
||||
"request_id": "claim-self-approval-approve",
|
||||
"expected_status": "submitted",
|
||||
"expected_approval_stage": "直属领导审批",
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
return_response = client.post(
|
||||
"/api/v1/reimbursements/claims/claim-self-approval-api/return",
|
||||
json={
|
||||
"reason": "退回",
|
||||
"request_id": "claim-self-approval-return",
|
||||
"expected_status": "submitted",
|
||||
"expected_approval_stage": "直属领导审批",
|
||||
},
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
assert approve_response.status_code == 404
|
||||
assert return_response.status_code == 404
|
||||
assert approve_response.json()["detail"] == "Approval task not found."
|
||||
assert return_response.json()["detail"] == "Approval task not found."
|
||||
with session_factory() as db:
|
||||
claim = db.get(ExpenseClaim, "claim-self-approval-api")
|
||||
assert claim is not None
|
||||
assert claim.status == "submitted"
|
||||
assert claim.approval_stage == "直属领导审批"
|
||||
assert claim.risk_flags_json == []
|
||||
assert db.scalar(select(func.count(ApprovalActionLedger.id))) == 0
|
||||
|
||||
|
||||
def test_approve_claim_endpoint_blocks_open_high_risk_with_machine_readable_detail() -> None:
|
||||
client, session_factory = build_client()
|
||||
with session_factory() as db:
|
||||
|
||||
Reference in New Issue
Block a user