0d9cc7b4ca
这一批是 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 提交并手跑等价检查。
164 lines
9.4 KiB
JSON
164 lines
9.4 KiB
JSON
{
|
||
"version": "1.1",
|
||
"custom_checks": {
|
||
"is_valid_date": "检查日期格式是否为 YYYY-MM-DD",
|
||
"is_positive_number": "检查是否为正数(整数或浮点数)",
|
||
"is_positive_integer": "检查是否为正整数",
|
||
"is_valid_vehicle": "检查交通工具能否落到财务系统的下拉框上(含 train→火车 等别名)"
|
||
},
|
||
"travel": {
|
||
"description": "差旅报销校验规则",
|
||
"fields": [
|
||
{
|
||
"path": ["basic_info", "travel_purpose"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"max_len_bytes": 100,
|
||
"description": "出差事由"
|
||
},
|
||
{
|
||
"path": ["basic_info", "travel_location"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"description": "出差地点"
|
||
},
|
||
{
|
||
"path": ["basic_info", "start_date"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"custom_check": "is_valid_date",
|
||
"description": "出差开始日期"
|
||
},
|
||
{
|
||
"path": ["basic_info", "end_date"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"custom_check": "is_valid_date",
|
||
"description": "出差结束日期"
|
||
}
|
||
],
|
||
"arrays": [
|
||
{
|
||
"path": ["reimbursement_details", "transport_fee"],
|
||
"min_items": 1,
|
||
"description": "交通费用明细",
|
||
"element_fields": [
|
||
{"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, "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": "备注说明"}
|
||
]
|
||
},
|
||
{
|
||
"path": ["payment_methods"],
|
||
"min_items": 1,
|
||
"description": "支付方式记录",
|
||
"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, "max_len_bytes": 100, "description": "商户名称"},
|
||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注"}
|
||
]
|
||
},
|
||
{
|
||
"path": ["subsidy_list"],
|
||
"min_items": 1,
|
||
"description": "补助清单",
|
||
"element_fields": [
|
||
{"path": ["person_id"], "required": true, "check_empty": true, "description": "人员工号"},
|
||
{"path": ["person_name"], "required": true, "check_empty": true, "description": "人员姓名"},
|
||
{"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": ["days"], "required": true, "check_empty": true, "custom_check": "is_positive_integer", "description": "补助天数"}
|
||
]
|
||
},
|
||
{
|
||
"path": ["attachments"],
|
||
"min_items": 0,
|
||
"description": "附件列表",
|
||
"element_fields": [
|
||
{"path": ["filename"], "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": "附件说明"}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
"normal": {
|
||
"description": "普通报销校验规则",
|
||
"fields": [
|
||
{
|
||
"path": ["basic_info", "reimbursement_description"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"max_len_bytes": 40,
|
||
"description": "报销说明(财务系统里这栏很短,20 个汉字以内)"
|
||
},
|
||
{
|
||
"path": ["reimbursement_details", "total_invoices"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"custom_check": "is_positive_integer",
|
||
"description": "发票总数"
|
||
},
|
||
{
|
||
"path": ["reimbursement_details", "total_amount"],
|
||
"required": true,
|
||
"check_empty": true,
|
||
"custom_check": "is_positive_number",
|
||
"description": "总金额"
|
||
}
|
||
],
|
||
"arrays": [
|
||
{
|
||
"path": ["payment_methods"],
|
||
"min_items": 1,
|
||
"description": "支付方式记录",
|
||
"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, "max_len_bytes": 100, "description": "商户名称"},
|
||
{"path": ["remark"], "required": true, "check_empty": false, "max_len_bytes": 100, "description": "备注"}
|
||
]
|
||
},
|
||
{
|
||
"path": ["attachments"],
|
||
"min_items": 0,
|
||
"description": "附件列表",
|
||
"element_fields": [
|
||
{"path": ["filename"], "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": "附件说明"}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
} |