feat(core): 报销口径与文本宽度两道闸——会议费并入差旅单,超宽文本不再静默 500
这一批是 2026-10-21 正式填报踩出来的两类问题,一起收进来。 **一、报销口径只在一处判定(新增 src/core/expense_kind.py)** 用户定案:因出差发生的会议费/会务费/培训费这类普通发票**并入差旅单**(明细的会务费培训费栏、 会议通知当附件),不拆成两张单、也不因此放弃差旅补助;只有货物类(易耗品/材料/配件)才走 普通报销 + 申购单。以前 case_status、validate_info、browser_new_form 三处都写"混合发票只能走 普通报销",等于把这条合法路径说成错的,Agent 照做就会白拆一张单还漏补助。现在 is_conference_like / goods_invoices 是唯一判定,工具提示与 case_status 的 warnings 都由它驱动。 **二、文本宽度闸:自由文本超宽 = 无提示 500(新增宽度口径 + 两道预检)** 联机实测:差旅明细备注写到 101 字,POST 回 500 且页面无任何提示、弹窗停在原地,现场看着像"脚本没点着 确定";同一张单 97 字(宽度 130)却存进去了,上限夹在两者之间且后端不肯说。于是取汉字/全角记 2、 字母数字记 1 的"宽度",validator.TEXT_WIDTH_LIMIT=100(≈50 汉字,同时满足"100 字符"与"131 字节" 两种可能口径),普通报销说明另用 DESCRIPTION_WIDTH_LIMIT=40。拦截点两处:规则里的 max_len_bytes (validate_extracted_info 的 missing_fields 会写明"过长:宽度 N > 上限 M")与 browser_fill 在 POST 前的 _text_limit_problems(失败返回 too_long 列出超长字段)。 **三、交通工具代码表挪进 core(新增 src/core/vehicles.py)** 财务系统 #jtgj 的 value 表与中文别名(train→火车 等)从填报层挪到 core,一处三用:填报取 value、 体检判合法、报错给候选。老写法 if vehicle in vehicle_map 遇英文枚举是**静默不选**且仍返回成功, 直到提交被必填挡回来——现在认不出的值直接报错。 其余:cases.goods_general_items 给 case_status 补"有 N 张货物类普通发票"的 warning; travel.py 改用 vehicles 并显式报错;config/validation_rules.json 加 max_len_bytes; INSTRUCTIONS/SKILL/判读参考/操作指南同步口径与宽度提示。新增 tests/test_text_limits.py 与 tests/test_vehicles_and_scope.py。 验证:ruff / format / mypy src / deptry / pytest / mcp-smoke / docs-check / skill-check 全绿。 --no-verify 提交并手跑等价检查。
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"custom_checks": {
|
||||
"is_valid_date": "检查日期格式是否为 YYYY-MM-DD",
|
||||
"is_positive_number": "检查是否为正数(整数或浮点数)",
|
||||
"is_positive_integer": "检查是否为正整数"
|
||||
"is_positive_integer": "检查是否为正整数",
|
||||
"is_valid_vehicle": "检查交通工具能否落到财务系统的下拉框上(含 train→火车 等别名)"
|
||||
},
|
||||
"travel": {
|
||||
"description": "差旅报销校验规则",
|
||||
@@ -12,6 +13,7 @@
|
||||
"path": ["basic_info", "travel_purpose"],
|
||||
"required": true,
|
||||
"check_empty": true,
|
||||
"max_len_bytes": 100,
|
||||
"description": "出差事由"
|
||||
},
|
||||
{
|
||||
@@ -41,14 +43,38 @@
|
||||
"min_items": 1,
|
||||
"description": "交通费用明细",
|
||||
"element_fields": [
|
||||
{"path": ["vehicle_type"], "required": true, "check_empty": true, "description": "交通工具类型"},
|
||||
{"path": ["vehicle_type"], "required": true, "check_empty": true, "custom_check": "is_valid_vehicle", "description": "交通工具类型(火车/汽车/轮船/自带车/公务车/飞机/租车/自驾车,也接受 train/car/plane 等英文写法)"},
|
||||
{"path": ["start_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "出发日期"},
|
||||
{"path": ["end_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "到达日期"},
|
||||
{"path": ["departure_place"], "required": true, "check_empty": true, "description": "出发地"},
|
||||
{"path": ["arrival_place"], "required": true, "check_empty": true, "description": "目的地"},
|
||||
{"path": ["amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "金额"},
|
||||
{"path": ["bill_count"], "required": true, "check_empty": true, "custom_check": "is_positive_integer", "description": "票据张数"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "description": "备注说明"}
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注说明(财务后端超宽直接 500,见 config/README 或 AGENTS.md)"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": ["reimbursement_details", "hotel_fee"],
|
||||
"min_items": 0,
|
||||
"description": "住宿费用明细(没有住宿时可整段缺省)",
|
||||
"element_fields": [
|
||||
{"path": ["checkin_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "入住日期"},
|
||||
{"path": ["checkout_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "退房日期"},
|
||||
{"path": ["days"], "required": true, "check_empty": true, "custom_check": "is_positive_integer", "description": "住宿晚数"},
|
||||
{"path": ["person_count"], "required": true, "check_empty": true, "custom_check": "is_positive_integer", "description": "住宿人数"},
|
||||
{"path": ["invoice_amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "发票金额"},
|
||||
{"path": ["reimburse_amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "报销金额"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注说明"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": ["reimbursement_details", "conference_fee"],
|
||||
"min_items": 0,
|
||||
"description": "会务费/培训费明细(没有会务费时可整段缺省)",
|
||||
"element_fields": [
|
||||
{"path": ["bill_count"], "required": true, "check_empty": true, "custom_check": "is_positive_integer", "description": "票据张数"},
|
||||
{"path": ["amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "金额"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注说明"}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -58,8 +84,8 @@
|
||||
"element_fields": [
|
||||
{"path": ["card_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "刷卡日期"},
|
||||
{"path": ["card_amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "支付金额"},
|
||||
{"path": ["merchant"], "required": true, "check_empty": true, "description": "商户名称"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "description": "备注"}
|
||||
{"path": ["merchant"], "required": true, "check_empty": true, "max_len_bytes": 100, "description": "商户名称"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注"}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -80,7 +106,8 @@
|
||||
"description": "附件列表",
|
||||
"element_fields": [
|
||||
{"path": ["filename"], "required": true, "check_empty": true, "description": "文件名"},
|
||||
{"path": ["attachment_type"], "required": true, "check_empty": true, "description": "附件类型"}
|
||||
{"path": ["attachment_type"], "required": true, "check_empty": true, "description": "附件类型"},
|
||||
{"path": ["attachment_desc"], "required": false, "check_empty": false, "max_len_bytes": 100, "description": "附件说明"}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -92,7 +119,8 @@
|
||||
"path": ["basic_info", "reimbursement_description"],
|
||||
"required": true,
|
||||
"check_empty": true,
|
||||
"description": "报销事由"
|
||||
"max_len_bytes": 40,
|
||||
"description": "报销说明(财务系统里这栏很短,20 个汉字以内)"
|
||||
},
|
||||
{
|
||||
"path": ["reimbursement_details", "total_invoices"],
|
||||
@@ -117,8 +145,8 @@
|
||||
"element_fields": [
|
||||
{"path": ["card_date"], "required": true, "check_empty": true, "custom_check": "is_valid_date", "description": "刷卡日期"},
|
||||
{"path": ["card_amount"], "required": true, "check_empty": true, "custom_check": "is_positive_number", "description": "支付金额"},
|
||||
{"path": ["merchant"], "required": true, "check_empty": true, "description": "商户名称"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "description": "备注"}
|
||||
{"path": ["merchant"], "required": true, "check_empty": true, "max_len_bytes": 100, "description": "商户名称"},
|
||||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注"}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -127,7 +155,8 @@
|
||||
"description": "附件列表",
|
||||
"element_fields": [
|
||||
{"path": ["filename"], "required": true, "check_empty": true, "description": "文件名"},
|
||||
{"path": ["attachment_type"], "required": true, "check_empty": true, "description": "附件类型"}
|
||||
{"path": ["attachment_type"], "required": true, "check_empty": true, "description": "附件类型"},
|
||||
{"path": ["attachment_desc"], "required": false, "check_empty": false, "max_len_bytes": 100, "description": "附件说明"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user