feat: 完善报销单审批流程及退回原因追踪
新增直属领导审批通过接口和审批待办列表查询,报销单退回 支持原因码分类和审批环节标记,优化票据附件去重和路径 回退查找,前端新增退回原因对话框、审批收件箱和工作台 图标组件,补充工具函数和单元测试覆盖。
This commit is contained in:
@@ -546,11 +546,11 @@ def test_user_agent_guides_implicit_expense_draft_request() -> None:
|
||||
assert slot_map["amount"].value == "1000.00元"
|
||||
|
||||
|
||||
def test_user_agent_guides_narrative_with_day_before_yesterday() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
def test_user_agent_guides_narrative_with_day_before_yesterday() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query="我前天请客户吃饭花了200元",
|
||||
user_id="pytest",
|
||||
context_json={
|
||||
@@ -571,15 +571,106 @@ def test_user_agent_guides_narrative_with_day_before_yesterday() -> None:
|
||||
|
||||
assert response.review_payload is not None
|
||||
slot_map = {item.key: item for item in response.review_payload.slot_cards}
|
||||
assert slot_map["time_range"].raw_value == "前天"
|
||||
assert slot_map["time_range"].value == "2026-05-11"
|
||||
assert "时间为 2026-05-11" in response.review_payload.intent_summary
|
||||
|
||||
|
||||
def test_user_agent_attachment_only_upload_uses_generic_scene_reason_without_fabrication() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
assert slot_map["time_range"].raw_value == "前天"
|
||||
assert slot_map["time_range"].value == "2026-05-11"
|
||||
assert "时间为 2026-05-11" in response.review_payload.intent_summary
|
||||
|
||||
|
||||
def test_user_agent_guides_riding_fare_as_transport_expense() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
message = "业务发生时间:2026-03-04,送客户去林萃小区办事,请报销乘车费用"
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query=message,
|
||||
user_id="pytest",
|
||||
)
|
||||
)
|
||||
response = UserAgentService(db).respond(
|
||||
UserAgentRequest(
|
||||
run_id=ontology.run_id,
|
||||
user_id="pytest",
|
||||
message=message,
|
||||
ontology=ontology,
|
||||
tool_payload={"draft_only": True},
|
||||
)
|
||||
)
|
||||
|
||||
assert response.review_payload is not None
|
||||
slot_map = {item.key: item for item in response.review_payload.slot_cards}
|
||||
assert slot_map["expense_type"].value == "交通费"
|
||||
assert slot_map["expense_type"].normalized_value == "transport"
|
||||
assert slot_map["time_range"].value == "2026-03-04"
|
||||
assert slot_map["reason"].value == "送客户去林萃小区办事,请报销乘车费用"
|
||||
assert "业务发生时间" not in slot_map["reason"].raw_value
|
||||
assert "“交通费”" in response.review_payload.intent_summary
|
||||
|
||||
|
||||
def test_user_agent_does_not_treat_draft_saved_message_as_precheck_risk_for_transport() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
message = "业务发生时间:2026-03-04,送客户去林萃小区办事,打车花了32元,请报销乘车费用"
|
||||
context_json = {
|
||||
"name": "赵六",
|
||||
"attachment_names": ["didi-trip.png"],
|
||||
"attachment_count": 1,
|
||||
"ocr_documents": [
|
||||
{
|
||||
"filename": "didi-trip.png",
|
||||
"summary": "滴滴出行 支付金额 32 元",
|
||||
"text": "滴滴出行 支付金额 32 元",
|
||||
"document_type": "taxi_receipt",
|
||||
"scene_code": "transport",
|
||||
"document_fields": [
|
||||
{"key": "amount", "label": "支付金额", "value": "32.00"},
|
||||
],
|
||||
"warnings": [],
|
||||
}
|
||||
],
|
||||
}
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query=message,
|
||||
user_id="pytest",
|
||||
context_json=context_json,
|
||||
)
|
||||
)
|
||||
response = UserAgentService(db).respond(
|
||||
UserAgentRequest(
|
||||
run_id=ontology.run_id,
|
||||
user_id="pytest",
|
||||
message=message,
|
||||
ontology=ontology,
|
||||
context_json=context_json,
|
||||
tool_payload={
|
||||
"draft_only": True,
|
||||
"claim_id": "claim-1",
|
||||
"claim_no": "EXP-202603-001",
|
||||
"status": "draft",
|
||||
"message": (
|
||||
"已创建报销草稿 EXP-202603-001,当前状态为 draft。"
|
||||
"你可以继续补充费用明细、客户单位和票据附件。"
|
||||
),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
assert response.review_payload is not None
|
||||
assert response.review_payload.can_proceed is True
|
||||
assert "客户名称" not in response.review_payload.missing_slots
|
||||
assert "参与人员" not in response.review_payload.missing_slots
|
||||
assert "票据附件" not in response.review_payload.missing_slots
|
||||
risk_text = "\n".join(
|
||||
f"{item.title}\n{item.content}" for item in response.review_payload.risk_briefs
|
||||
)
|
||||
assert "AI预审未通过" not in risk_text
|
||||
assert "已创建报销草稿" not in risk_text
|
||||
|
||||
|
||||
def test_user_agent_attachment_only_upload_uses_generic_scene_reason_without_fabrication() -> None:
|
||||
session_factory = build_session_factory()
|
||||
with session_factory() as db:
|
||||
ontology = SemanticOntologyService(db).parse(
|
||||
OntologyParseRequest(
|
||||
query="我上传了 1 份票据,请结合附件名称给出报销建议并尽量生成草稿。",
|
||||
user_id="pytest",
|
||||
|
||||
Reference in New Issue
Block a user