feat: 完善文档中心与报销申请交互及侧边栏重构
后端优化编排器报销查询和本体检测精度,增强报销单草稿保 存和附件回填逻辑,前端重构侧边栏组件支持折叠和图标导 航,完善文档中心状态筛选和详情提示,报销创建和审批详情 页优化会话管理和费用明细交互,新增助手应用服务和预设动 作工具函数,补充单元测试覆盖。
This commit is contained in:
@@ -2883,6 +2883,133 @@ def test_direct_manager_can_approve_subordinate_claim_to_finance_review() -> Non
|
||||
)
|
||||
|
||||
|
||||
def test_application_submit_skips_ai_review_and_receipt_requirements(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="application-owner@example.com",
|
||||
name="张三",
|
||||
role_codes=["employee"],
|
||||
is_admin=True,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
claim = ExpenseClaim(
|
||||
claim_no="APP-20260525-SUBMIT",
|
||||
employee_name="张三",
|
||||
department_name="交付部",
|
||||
project_code="PRJ-A",
|
||||
expense_type="travel_application",
|
||||
reason="支撑国网服务器上线部署",
|
||||
location="上海",
|
||||
amount=Decimal("12000.00"),
|
||||
currency="CNY",
|
||||
invoice_count=0,
|
||||
occurred_at=datetime(2026, 5, 25, 9, 0, tzinfo=UTC),
|
||||
submitted_at=None,
|
||||
status="draft",
|
||||
approval_stage="待提交",
|
||||
risk_flags_json=[
|
||||
{
|
||||
"source": "submission_review",
|
||||
"severity": "medium",
|
||||
"message": "旧 AI 预审提示不应保留到申请单提交结果。",
|
||||
}
|
||||
],
|
||||
)
|
||||
db.add(claim)
|
||||
db.commit()
|
||||
claim_id = claim.id
|
||||
service = ExpenseClaimService(db)
|
||||
|
||||
def fail_ai_review(_claim: ExpenseClaim) -> dict[str, object]:
|
||||
raise AssertionError("费用申请提交不应进入 AI 预审")
|
||||
|
||||
monkeypatch.setattr(service, "_run_ai_submission_review", fail_ai_review)
|
||||
|
||||
submitted = service.submit_claim(claim_id, current_user)
|
||||
|
||||
assert submitted is not None
|
||||
assert submitted.status == "submitted"
|
||||
assert submitted.approval_stage == "直属领导审批"
|
||||
assert submitted.invoice_count == 0
|
||||
assert submitted.items == []
|
||||
assert not any(
|
||||
isinstance(flag, dict) and flag.get("source") == "submission_review"
|
||||
for flag in submitted.risk_flags_json
|
||||
)
|
||||
assert any(
|
||||
isinstance(flag, dict)
|
||||
and flag.get("source") == "application_submission"
|
||||
and flag.get("event_type") == "expense_application_submission"
|
||||
and flag.get("next_approval_stage") == "直属领导审批"
|
||||
for flag in submitted.risk_flags_json
|
||||
)
|
||||
|
||||
|
||||
def test_direct_manager_can_approve_application_claim_to_completed_stage() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="manager-application-approve@example.com",
|
||||
name="李经理",
|
||||
role_codes=["manager"],
|
||||
is_admin=False,
|
||||
)
|
||||
|
||||
with build_session() as db:
|
||||
manager = Employee(
|
||||
employee_no="E8112",
|
||||
name="李经理",
|
||||
email="manager-application-approve@example.com",
|
||||
)
|
||||
employee = Employee(
|
||||
employee_no="E8113",
|
||||
name="张三",
|
||||
email="zhangsan-application-approve@example.com",
|
||||
manager=manager,
|
||||
)
|
||||
db.add_all([manager, employee])
|
||||
db.flush()
|
||||
claim = ExpenseClaim(
|
||||
claim_no="APP-20260525-APPROVE",
|
||||
employee_id=employee.id,
|
||||
employee_name="张三",
|
||||
department_name="交付部",
|
||||
project_code="PRJ-A",
|
||||
expense_type="travel_application",
|
||||
reason="支撑国网服务器上线部署",
|
||||
location="上海",
|
||||
amount=Decimal("12000.00"),
|
||||
currency="CNY",
|
||||
invoice_count=0,
|
||||
occurred_at=datetime(2026, 5, 25, 9, 0, tzinfo=UTC),
|
||||
submitted_at=datetime(2026, 5, 25, 10, 0, tzinfo=UTC),
|
||||
status="submitted",
|
||||
approval_stage="直属领导审批",
|
||||
risk_flags_json=[],
|
||||
)
|
||||
db.add(claim)
|
||||
db.commit()
|
||||
claim_id = claim.id
|
||||
|
||||
approved = ExpenseClaimService(db).approve_claim(
|
||||
claim_id,
|
||||
current_user,
|
||||
opinion="业务必要,同意申请。",
|
||||
)
|
||||
|
||||
assert approved is not None
|
||||
assert approved.status == "approved"
|
||||
assert approved.approval_stage == "审批完成"
|
||||
assert any(
|
||||
isinstance(flag, dict)
|
||||
and flag.get("source") == "manual_approval"
|
||||
and flag.get("event_type") == "expense_application_approval"
|
||||
and flag.get("opinion") == "业务必要,同意申请。"
|
||||
and flag.get("previous_approval_stage") == "直属领导审批"
|
||||
and flag.get("next_status") == "approved"
|
||||
and flag.get("next_approval_stage") == "审批完成"
|
||||
for flag in approved.risk_flags_json
|
||||
)
|
||||
|
||||
|
||||
def test_finance_can_approve_claim_to_archive_stage() -> None:
|
||||
current_user = CurrentUserContext(
|
||||
username="finance-approve@example.com",
|
||||
|
||||
Reference in New Issue
Block a user