feat: 选票自动生成报销明细并支持集中分类(08-29-reimbursement-expense-policy-invoice-guard)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-09-09 23:19:02 +08:00
父节点 b87cc55aca
当前提交 e5685f18b2
共修改 28 个文件,包含 1616 行新增和 121 行删除
@@ -7,6 +7,57 @@ duplicate detection or attachment behavior.
## Rules
- 隔离串联的 `RUN_JOB_QUEUE=1` 必须运行真实 startPgBossJobs 与上传入口 enqueue,
等对应 invoiceId 的队列任务 completed,再校验发票状态和页面,不用直接调用
worker 冒充调度。只连接脚本新建数据库;非关注周期任务不得在测试期间改其他对象。
认证使用真实登录/JWT/refresh 和合成账号时,仍需区分导航壳适配、OAuth、
页面登录表单、云端 provider 与生产定时器时序的证据范围。
- 隔离串联可用 `RUN_ISOLATED_DB_TEST=1 RUN_BROWSER_TEST=1 node scripts/qa/test-invoice-first-http.cjs`,
前置为当前候选已构建 client/dist。浏览器业务请求必须走真实后端/数据库;
合成认证、mock provider、手动 worker、真实云端识别分别报告,不能互相替代。
刷新持久化验证先等保存响应、再查库、再刷新定位可见明细,避免隐藏的批量
分类文字或同名“编辑”按钮造成误报;上传与 OCR 完成不能触发隐式绑定。
- 所有报销 bind/unbind 与提交遵循“报销单锁 → 发票锁”,不能只让新动作
遵循而保留旧路径反序。解绑先读关联快照定位报销单,取单锁后再取票锁;
若票据当前关联已变化,409 拒绝,不按旧快照解绑也不转锁其他单据。
验证命令:`RUN_ISOLATED_DB_TEST=1 node scripts/qa/test-invoice-first-http.cjs`,
用真实 PostgreSQL 锁屏障复现并发;正常基线为同单重试仅一条明细,
反例为旧反序路径产生 500,修复后重试/解绑均成功。该脚本合成认证与
OCR 字段不构成真实登录、OCR 调用或完整审批验收证据。
- 报销“我的发票”面板上传/OCR 完成仅默认勾选,点击添加才创建明细或占用。
用户取消的票不得被回调重新勾选,手改目标不得被回调覆盖;退出不删除池票。
已有明细入口只传 linkedItemId,返回整单入口恢复创建新明细的默认值,
成功关闭与手动关闭均须清理目标。部分失败只保留失败项,重试不重复发送成功票。
- 浏览器接口拦截回归证明交互和请求合同,不证明真实 OCR、Prisma 锁或数据库
回滚;这些证据需分别验证,不能把页面成功提示视为业务全链路通过。
- 新选票动作显式传 `createItem: true`,与 linkedItemId 互斥;不复用旧绑定
的单明细/同金额猜测。新动作先锁报销单再锁票据,事务内重验参与人、状态、
票据归属与可用性,一票建一明细并占用;同单重试重验权限与现有明细归属,
不再次创建或覆盖已编辑数据。旧接口未默认切换,页面接入完成前不宣称生效。
- 无票面项目依据时可保存空说明草稿,不用文件名或商户名假装业务用途;
预检和提交集中提示缺少说明的明细。编辑器保存空字符串必须明确传空,
不能转成 undefined 导致 PATCH 没有清除旧值。
- 批量设类必须按用户选择的明细 ID 集合和单据 ID 双重限定、同事务全量校验,
任一异单/失效明细或停用类型拒绝整批;不顺手改金额、日期、说明、批准依据
或发票关系,不扩大对象维护权限。既有资产提示/审批规则仍按实际代码处理,
不以旧文档声称存在未实现的硬门禁。
- 自动建明细后类型无法可靠确定时,允许 draft/rejected 明细保存空费用类型,
不填任意启用类型或“其他”占位。API PATCH 的 null 与省略字段必须区分;
DTO 与 PC/移动编辑器均保留待补值,不因打开编辑器默认选中第一项。
- 待补或已停用费用类型必须在报销提交进入费用、预算、审批服务前按明细 ID
阻断;不能用可选链把缺少类型解释为免票。分类来自持久化 OCR 证据及启用
类型,不来自客户端票种、销售方关键词、文件名、金额或历史 provider 默认值。
- 类型可空迁移不放宽金额、日期和外键约束,也不授权生产应用。保存过待补数据
后回滚旧应用需评估非空假设;恢复 NOT NULL 前必须核实空值,不得猜类型回填。
- 本地结构验收使用显式开启的一次性 PostgreSQL:
`RUN_ISOLATED_DB_TEST=1 bash scripts/qa/test-reimbursement-draft-migration.sh`。
该测试验证迁移和合成记录,不证明浏览器、真实 OCR 或生产恢复通过。
- PDF invoice OCR is the maintained main path.
- Image OCR remains frozen unless a later Trellis task explicitly reopens it.
- Do not write raw invoice images, sensitive form values or full OCR payloads to