feat: 新增归档中心页面并完善知识库与报销查询能力
新增前端归档中心视图及相关工具函数,扩充知识库文档分类和 提取器支持多种格式,增强编排器报销查询的多维度检索,优 化本体规则和用户代理审核消息,前端完善报销创建和审批详 情交互细节,补充单元测试覆盖。
This commit is contained in:
@@ -1516,8 +1516,21 @@ def test_upload_hotel_attachment_flags_amount_over_travel_policy(monkeypatch, tm
|
||||
analysis = uploaded_meta["analysis"]
|
||||
assert analysis["severity"] == "high"
|
||||
assert analysis["headline"] == "AI提示:住宿金额超出报销标准"
|
||||
assert "保留在单据中" in analysis["summary"]
|
||||
assert "特殊情况" in analysis["summary"]
|
||||
assert any("住宿标准" in point and "800.00 元" in point for point in analysis["points"])
|
||||
assert any("住宿费按员工职级" in basis for basis in analysis["rule_basis"])
|
||||
db.refresh(claim)
|
||||
hotel_item = next(item for item in claim.items if str(item.invoice_id or "").strip())
|
||||
assert hotel_item.item_amount == Decimal("800.00")
|
||||
assert claim.invoice_count == 1
|
||||
assert any(
|
||||
isinstance(flag, dict)
|
||||
and str(flag.get("source") or "").strip() == "attachment_analysis"
|
||||
and flag.get("item_id") == hotel_item.id
|
||||
and str(flag.get("severity") or "").strip() == "high"
|
||||
for flag in list(claim.risk_flags_json or [])
|
||||
)
|
||||
|
||||
|
||||
def test_attachment_analysis_does_not_compare_business_purpose_with_ticket_scene() -> None:
|
||||
@@ -2433,8 +2446,8 @@ def test_list_claims_allows_finance_to_view_all_records() -> None:
|
||||
|
||||
claims = ExpenseClaimService(db).list_claims(current_user)
|
||||
|
||||
assert len(claims) == 2
|
||||
assert {claim.claim_no for claim in claims} == {"EXP-FIN-101", "EXP-FIN-102"}
|
||||
assert len(claims) == 1
|
||||
assert claims[0].claim_no == "EXP-FIN-101"
|
||||
|
||||
|
||||
def test_list_claims_allows_executive_to_view_all_records() -> None:
|
||||
@@ -2488,8 +2501,134 @@ def test_list_claims_allows_executive_to_view_all_records() -> None:
|
||||
|
||||
claims = ExpenseClaimService(db).list_claims(current_user)
|
||||
|
||||
assert len(claims) == 2
|
||||
assert {claim.claim_no for claim in claims} == {"EXP-EXE-101", "EXP-EXE-102"}
|
||||
assert len(claims) == 1
|
||||
assert claims[0].claim_no == "EXP-EXE-101"
|
||||
|
||||
|
||||
def test_list_claims_keeps_own_archived_claim_for_finance_applicant() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance@example.com",
|
||||
name="财务",
|
||||
role_codes=["finance"],
|
||||
is_admin=False,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
db.add(
|
||||
ExpenseClaim(
|
||||
claim_no="EXP-FIN-OWN-ARCH",
|
||||
employee_name="财务",
|
||||
department_name="财务部",
|
||||
project_code="PRJ-FIN",
|
||||
expense_type="meal",
|
||||
reason="本人报销",
|
||||
location="上海",
|
||||
amount=Decimal("88.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 12, 9, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 12, 10, 0, tzinfo=UTC),
|
||||
status="approved",
|
||||
approval_stage="归档入账",
|
||||
risk_flags_json=[],
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
claims = ExpenseClaimService(db).list_claims(current_user)
|
||||
|
||||
assert len(claims) == 1
|
||||
assert claims[0].claim_no == "EXP-FIN-OWN-ARCH"
|
||||
|
||||
|
||||
def test_list_archived_claims_returns_company_archived_records_for_finance() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance@example.com",
|
||||
name="财务",
|
||||
role_codes=["finance"],
|
||||
is_admin=False,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
db.add_all(
|
||||
[
|
||||
ExpenseClaim(
|
||||
claim_no="EXP-ARCH-101",
|
||||
employee_name="甲",
|
||||
department_name="A部",
|
||||
project_code="PRJ-A",
|
||||
expense_type="travel",
|
||||
reason="A 报销",
|
||||
location="上海",
|
||||
amount=Decimal("120.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 11, 9, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 11, 10, 0, tzinfo=UTC),
|
||||
status="approved",
|
||||
approval_stage="归档入账",
|
||||
risk_flags_json=[],
|
||||
),
|
||||
ExpenseClaim(
|
||||
claim_no="EXP-ARCH-102",
|
||||
employee_name="乙",
|
||||
department_name="B部",
|
||||
project_code="PRJ-B",
|
||||
expense_type="meal",
|
||||
reason="B 报销",
|
||||
location="杭州",
|
||||
amount=Decimal("300.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 11, 12, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 11, 13, 0, tzinfo=UTC),
|
||||
status="submitted",
|
||||
approval_stage="财务审批",
|
||||
risk_flags_json=[],
|
||||
),
|
||||
]
|
||||
)
|
||||
db.commit()
|
||||
|
||||
claims = ExpenseClaimService(db).list_archived_claims(current_user)
|
||||
|
||||
assert len(claims) == 1
|
||||
assert claims[0].claim_no == "EXP-ARCH-101"
|
||||
|
||||
|
||||
def test_list_archived_claims_is_empty_for_regular_employee() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="zhangsan@example.com",
|
||||
name="张三",
|
||||
role_codes=["employee"],
|
||||
is_admin=False,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
db.add(
|
||||
ExpenseClaim(
|
||||
claim_no="EXP-ARCH-EMP",
|
||||
employee_name="张三",
|
||||
department_name="研发部",
|
||||
project_code="PRJ-EMP",
|
||||
expense_type="travel",
|
||||
reason="本人报销",
|
||||
location="北京",
|
||||
amount=Decimal("200.00"),
|
||||
currency="CNY",
|
||||
invoice_count=1,
|
||||
occurred_at=datetime(2026, 5, 10, 9, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 10, 10, 0, tzinfo=UTC),
|
||||
status="approved",
|
||||
approval_stage="归档入账",
|
||||
risk_flags_json=[],
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
claims = ExpenseClaimService(db).list_archived_claims(current_user)
|
||||
|
||||
assert claims == []
|
||||
|
||||
|
||||
def test_finance_can_return_but_cannot_delete_submitted_claim() -> None:
|
||||
|
||||
Reference in New Issue
Block a user