记录真实报销首跑风险(07-27-yang-jun-reimbursement-readiness)

这个提交包含在:
2026-07-27 21:23:15 +08:00
父节点 9694179c25
当前提交 4147a6960f
共修改 3 个文件,包含 27 行新增和 0 行删除
@@ -32,6 +32,8 @@ duplicate detection or attachment behavior.
- Database: `InvoicePool.ocr_provider` is a non-null `OcrProvider` with schema
default `tencent`; `ocr_attempts`, `ocr_last_requested_at`,
`ocr_completed_at` and extracted invoice fields carry the execution evidence.
- Duplicate index: `@@unique([file_sha256, ocr_provider, ocr_type])` partitions
the SHA-256 namespace by provider and OCR type.
### 3. Contracts
@@ -47,6 +49,13 @@ duplicate detection or attachment behavior.
finance checks the PDF original manually. SHA-256 duplicate detection remains
active; invoice-code/number duplicate detection is unavailable for blank
metadata.
- A future nullable/off-provider migration must not change only the stored
provider value. Backfill historical rows to one canonical namespace or replace
the composite unique key with an intentional file-level uniqueness contract;
otherwise the same bytes can be uploaded once before and once after migration.
- Before changing provider semantics, count historical `mock` rows uploaded by
non-test employees. Any such rows need an explicit dedupe/review plan because
their provider namespace already differs from current default rows.
- A config-only provider switch keeps `BUILD_SHA` unchanged. Recreate every
process that actually loads OCR config: server only when `JOBS_ENABLED=true`
and no worker exists; server plus worker when an independent worker already
@@ -65,6 +74,8 @@ duplicate detection or attachment behavior.
- `ocr_provider=tencent` with attempts `0`, null request/completion timestamps
and null extracted fields -> historical schema-default finding, not evidence
of a Tencent request.
- Provider enum/default or unique-key change without backfill/index redesign ->
block migration review because it splits the duplicate-detection namespace.
- Blank metadata cannot bind or submit -> keep the test draft, clean it through
normal APIs, then plan a code change; do not add an emergency production
database edit.
@@ -84,6 +95,9 @@ duplicate detection or attachment behavior.
- Assert upload status is `available` under `OCR_PROVIDER=off`.
- Assert invoice code, number, date, buyer, seller and all amount fields are
null; assert attempts are `0` and request/completion timestamps are null.
- Migration tests for provider semantics must upload identical bytes on both
sides of the migration and assert that the second upload is still rejected or
reuses the canonical row.
- Bind to a reimbursement with one valid item, submit through the real workflow,
return instead of approve, delete the draft and archive the released invoice.
- Final read-only checks: test accounts archived, active sessions `0`, test
@@ -105,6 +105,7 @@
- 用户选择并批准关闭 mock。现场拓扑没有独立 worker,`JOBS_ENABLED=true`,因此只重建实际承载 Web 与后台任务的 server;不得为了“同时重启 worker”而凭空创建第二个作业执行器。
- `OCR_PROVIDER=off` 复验确认:新 PDF 直接进入 `available`,发票代码、号码、日期、购销方和金额字段均为空,OCR 尝试为 0、请求/完成时间为空;票据可以绑定到单明细 0.01 元测试报销并正常提交、退回和清理。
- `InvoicePool.ocr_provider` 是非空枚举且 schema 默认 `tencent`;off 路径传入 `undefined` 后数据库仍写入该默认值。该字段只是历史 schema 默认,不代表发生过 OCR 调用;当前 UI 不展示 provider。是否在后续迁移中改为 nullable 应与腾讯 OCR/人工录入产品决策一起规划,不阻断本次真实报销。
- `InvoicePool` 当前使用 `@@unique([file_sha256, ocr_provider, ocr_type])`。后续若把 provider 改成 nullable 或新增 `off` 枚举,必须同步回填历史行或重设计唯一键;只改新行 provider 会把同一文件拆进两个查重命名空间。生产只读核查显示历史 `mock` 票据共 1 张且仅由测试账号上传,非测试员工上传数为 0。
- off 模式下发票号为空,基于 `invoice_code + invoice_number` 的查重不可用,但文件 SHA-256 查重仍生效;当前小团队短期由财务查看 PDF 原件并人工核对,不在本任务增加强制补录。
- 草稿删除成功后,详情页在导航完成前仍短暂请求已删除的详情与 precheck,浏览器产生预期 404 控制台噪声,但用户已回到空草稿列表。这是非阻断前端时序 finding,不影响数据清理结果。
@@ -135,3 +136,11 @@
- 不创建法体、不补银行账号、不导入其余成员。
- 不把首次真实业务扩成管理员代办的生产自动化测试。
- 不创建新的企微测试成员,不给测试主管财务或管理员权限,不让测试单通过审批或进入付款链路。
## 8. 真实首跑补充风险复核
- 生产三名真实参与人均 active 且企微绑定唯一,但尚无 `AUTH_WECOM_LOGIN_SUCCESS` 记录;企微客户端入口仍需本人首登验证。
- 当前 active 费用标准为 0、杨俊部门适用预算池为 0;真实金额不会触发费用标准或预算阻断。active 法体为 0、杨俊未绑定法体,但现有报销提交和付款批次候选不以法体为硬门禁。
- 金额审批规则不是同一条:`[0,500)` 为 `manager → finance`,`[500,5000)` 为 `manager → finance_mgr`,`[5000,+∞)` 为 `manager → ceo → finance_mgr`。
- 当前 active 角色持有人:`finance=1`、`ceo=1`、`finance_mgr=0`。因此低于 500 元可路由到马建勇后再到王芝玲;达到 500 元的单据会生成无人可处理的 `finance_mgr` 角色待办。修复应调整审批规则到既有最小角色,不给王芝玲扩为权限过宽的 `finance_mgr`。
- 最终审批只把报销置为 `approved`,不会自动创建付款批次或付款记录;付款批次仍由王芝玲主动创建并由马建勇复核。
@@ -64,6 +64,8 @@
## 6. 用户真实验收
- [ ] 在杨俊提交前修正金额审批规则:`[500,5000)` 的末级由 `finance_mgr` 改为 `finance`,`[5000,+∞)` 保留 `ceo` 且末级由 `finance_mgr` 改为 `finance`;不得通过给王芝玲增加 `finance_mgr` 绕过。
- [ ] 规则写入前后只读核对三档金额边界(499.99 / 500 / 5000)及 active 角色持有人,确认每个角色节点都至少有一名实际处理人。
- [ ] 请杨俊本人从企业微信进入 OA,确认身份解析成功并能看到报销入口。
- [ ] 杨俊使用真实业务数据创建报销,至少添加一条费用明细并上传/绑定状态有效的真实票据后提交。
- [ ] 确认马建勇能看到该真实审批待办并按业务事实处理。
@@ -92,3 +94,5 @@
- finding:生产 OCR 为 mock 且真实腾讯 OCR 未配置,合成 PDF 自动生成 Mock 字段;草稿删除后页面导航前产生已删除详情/precheck 的 404 控制台噪声。前者阻断杨俊真实票据上传,后者不阻断业务。
- OCR off 复验:配置切换后未改构建;三次有业务写入的复验/诊断均按“退回 → 删除 → 归档 → 停用”收口。累计 4 张合成测试票据(含第一轮)均已归档,未归档 0;测试报销与 pending 待办均为 0。
- OCR off 结论:空发票业务字段可以绑定并提交,主管退回成功;真实使用暂由财务查看 PDF 原件并人工核对。文件 SHA-256 查重仍有效,发票号查重暂不可用。
- 真实首跑复核:三名参与人 active 且企微绑定唯一,但企微首登均未发生;费用标准 0、适用预算池 0、active 法体 0,法体不阻断当前报销。历史 mock 票据仅 1 张且为测试账号上传,非测试员工为 0。
- 审批规则 finding:`[0,500)` 可由马建勇和唯一 finance 王芝玲处理;`[500,5000)` 与 `[5000,+∞)` 均以 `finance_mgr` 收尾,但 active `finance_mgr=0`,达到 500 元会形成无人处理待办。未修正规则前不得笼统通知杨俊可提交。