R78 A3-A5:跑通本地支付尾链回归并回填报告

这个提交包含在:
2026-04-29 10:55:55 +08:00
父节点 300420aa25
当前提交 c25a223945
共修改 5 个文件,包含 278 行新增和 18 行删除
+27 -1
查看文件
@@ -90,4 +90,30 @@ R70 初次试运行暂停项已在 2026-04-28 的 R71 定向修复后关闭。
- 最新 batch 票据结果:`fb29cdd8-0c10-4254-8feb-d3c3c1be86c3`,销售方 `北京悦海堂餐饮管理有限公司`,总金额 `1683.00`,状态 `available`。
- 试运行结论:
- 员工侧报销 / 票据中心 / OCR 主链路 `上传 -> OCR -> available -> 绑定 -> 提交报销` 已具备本地试运行条件。
- 本轮仍未覆盖真实企微 OAuth、HTTPS cookie secure、多角色并发协同,以及财务支付完成后的下游链路;这些项继续保留在外部或后续 focused regression。
- 本轮仍未覆盖真实企微 OAuth、HTTPS cookie secure、多角色并发协同、真实 ERP 外发回执,以及高风险二次复核;这些项继续保留在外部或后续 focused regression。
## R78 财务支付尾链 focused regression
- 回归时间:`2026-04-29 10:37 - 10:52 CST`
- 自动化环境:`http://127.0.0.1:3002`
- 自动化数据库:`hyb_oa_manual_trialrun_20260429093000`
- 本地 focused 开关:
- 运行时 env:`PAYMENT_BATCH_ENABLED=true`、`ERP_SYNC_ENABLED=true`
- DB feature flag:通过 `TRIALRUN_EXTRA_FORCE_ON_FLAGS='payment_batch_v1 erp_sync'` 重复执行 `seed:trialrun` 显式打开,仅用于本地 focused 财务回归,不改变默认 trialrun 上线建议。
- 回归命令:
- `cd server && ./scripts/regression-payment-batches.sh`
- `cd client && E2E_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list`
- 结果:
- server focused regression:`3 passed`
- Playwright focused E2E:`1 passed (58.0s)`
- 已验证链路:
- employee:创建空白报销 -> 上传 mock 票据 -> 绑定 -> 提交
- manager / finance:完成两级审批
- finance:创建 reimbursement payment batch,并在复核后执行
- finance_mgr:在 AP 工作台完成快捷复核
- execute 后:`payment batch = executed`、`reimbursement = paid`、`invoice = archived`
- ERP preview:可见最新 `pending` outbox
- reviewer 结论:
- `paymentBatches` 复核路由仍拦截 maker 自审;低风险批次维持 `finance_mgr` 复核、`finance` 执行的边界。
- 失败路径单测确认不会错误归档已绑定发票,也不会误入 ERP / 通知 follow-up。
- `PaymentBatchesPage` 与 `ApOperationsPage` 没有新增越权入口,按钮仍按 `payment.batch.manage` 与 `risk.review.manage` 分离。
+2 -2
查看文件
@@ -1,7 +1,7 @@
"Domain","Tier","Routes","Primary_Roles","Env_Gates","Feature_Flags","Coverage_Status","Evidence","Validated_Flows","Gaps","Next_Action"
"认证与 Portal 导航","must_cover","/login|/portal|主/管/备导航","employee|manager|admin","APP_ORIGIN|COOKIE_SECURE|PORTAL_HOME_ENABLED|WECOM_ENTRY_ENABLED","portal_home_v1|wecom_entry_v1","done","artifacts/r69/acceptance-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/login-portal-ux.spec.ts|client/tests/e2e/wecom-entry.spec.ts|client/tests/e2e/manual/r70-trialrun-assist.spec.ts","登录、Portal 首屏、员工/经理/管理员导航分组、普通浏览器与企微 WebView CTA 提示均有自动化或人工证据。","真实企微 OAuth 绑定与回跳未做端到端;HTTPS 部署态 cookie 行为未在本轮验证。","保持主链路 done;真实企微与 HTTPS 转 A5 外部专项。"
"请假与审批","must_cover","/leave-requests|/workflow/tasks","employee|manager|admin","none","leave_holiday_calendar","done","artifacts/r69/acceptance-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/leave-flow.spec.ts|client/tests/e2e/workflow-quick-approve.spec.ts|client/tests/e2e/leave-team-calendar.spec.ts","请假草稿、提交、撤回、经理快捷审批、团队日历权限边界与经理部门视角已自动化并在 R70 人工复测通过。","多审批人链路、代理审批、真实并发多人协同未在 trialrun 轮次验证。","维持 done;多人协同与代理审批并入 A5 或后续专项。"
"报销、票据中心与 OCR","must_cover","/reimbursements|/reimbursements/:id|/invoice-pool","employee|manager|finance|admin","OCR_PROVIDER|TENCENT_SECRET_ID|TENCENT_SECRET_KEY","invoice_verification","partial_done","artifacts/r70/manual-trialrun-report.md|artifacts/r70/live-ocr-batch-report.md|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv|issues/2026-04-29_09-19-07-r77-ocr-pdf-first.csv|client/tests/e2e/reimbursement-workflow-progress.spec.ts|client/tests/e2e/invoice-ocr-flow.spec.ts|client/tests/e2e/manual/live-invoice-ocr.spec.ts|client/tests/e2e/manual/live-invoice-ocr-batch.spec.ts","报销提交后审批进度、票据中心缺字段修正、上传后自动 OCR 进入可绑定、绑定后提交已自动化;R70 人工链路也通过。R77 已在干净本地 trialrun DB 上用真实 PDF 发票跑通 `上传 -> OCR -> available`,并完成 live 单张 + batch Playwright 复测,runner 正常退出。","旧项目 `.env` 中一组腾讯凭证已在当前应用链路内验证可用;`.env.local` 仍是无效凭证。当前剩余缺口已不再是 OCR 鉴权、PDF 参数或 batch runner 收尾,而是财务支付完成后的下游链路尚未纳入本轮小团队 trialrun。","员工侧报销 / 票据 / OCR 主链路已具备本地试运行条件;如后续将财务支付后链路纳入试运行,再补一轮 focused regression。PDF 转图片 fallback 仅在新增真实票样兼容性问题出现时再评估。"
"报销、票据中心与 OCR","must_cover","/reimbursements|/reimbursements/:id|/invoice-pool","employee|manager|finance|admin","OCR_PROVIDER|TENCENT_SECRET_ID|TENCENT_SECRET_KEY|PAYMENT_BATCH_ENABLED|ERP_SYNC_ENABLED","invoice_verification|payment_batch_v1|erp_sync","done","artifacts/r70/manual-trialrun-report.md|artifacts/r70/live-ocr-batch-report.md|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv|issues/2026-04-29_09-19-07-r77-ocr-pdf-first.csv|issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|client/tests/e2e/reimbursement-workflow-progress.spec.ts|client/tests/e2e/invoice-ocr-flow.spec.ts|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|client/tests/e2e/manual/live-invoice-ocr.spec.ts|client/tests/e2e/manual/live-invoice-ocr-batch.spec.ts|server/scripts/regression-payment-batches.sh","报销提交后审批进度、票据中心缺字段修正、上传后自动 OCR 进入可绑定、绑定后提交已自动化;R70 人工链路也通过。R77 已在干净本地 trialrun DB 上用真实 PDF 发票跑通 `上传 -> OCR -> available`。R78 已补 employee->manager->finance->finance_mgr->payment batch->execute->reimbursement paid->invoice archived->ERP preview pending outbox focused regression。","真实 ERP 投递回执、高风险两轮复核、大额或混合付款批次仍未进入本轮本地试运行;腾讯 OCR 凭证仍依赖旧项目运行时注入。","维持 done;真实 ERP 与高风险付款转外部/专项回归,PDF 转图片 fallback 仅在新增票样兼容性问题出现时再评估。"
"周报与待办","must_cover","/weekly-reports|/todos","employee|manager","WEEKLY_REPORT_ENABLED|TODO_BOARD_ENABLED","weekly_report_v1|todo_board_v1","done","artifacts/r70/auto-trialrun-deep-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/weekly-todo-flow.spec.ts|client/tests/e2e/manual/r70-trialrun-deep-assist.spec.ts","周报保存、生成待办、提交周报、完成待办闭环已自动化且在真实 trialrun 数据上复核通过。","经理侧未提交周报聚合与催办策略未单独验证。","维持 done;如进入管理场景增强,再补经理视角聚合。"
"公告、搜索与通知","must_cover","/announcements|/notifications|全局搜索","employee|manager|admin","ANNOUNCEMENTS_ENABLED|GLOBAL_SEARCH_ENABLED","announcements_v1|global_search_v1","done","artifacts/r70/auto-trialrun-deep-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/announcements-presence.spec.ts|client/tests/e2e/global-search.spec.ts|client/tests/e2e/manual/r70-trialrun-deep-assist.spec.ts|client/tests/e2e/r75-automation-gap-fill.spec.ts","公告发布、员工可见、Portal 汇总、真实搜索命中公告并跳转,以及通知中心未读筛选、单条已读、全部已读与待办跳转均有自动化或人工证据。","无当前试运行阻断缺口;真实外发渠道投递与通知渠道配置验证另见系统治理和外部环境项。","站内通知维持 done;外发渠道留在 A5/治理专项。"
"团队状态与考勤","must_cover","/team|/attendance","employee|manager|admin","PRESENCE_BOARD_ENABLED|ATTENDANCE_ENABLED","presence_board_v1|attendance_v1","done","artifacts/r70/manual-trialrun-report.md|client/tests/e2e/announcements-presence.spec.ts|issues/2026-04-09_r59-attendance-clockin.csv|client/tests/e2e/r75-automation-gap-fill.spec.ts","团队状态更新、Portal 汇总、经理查看团队状态,以及员工今日上班/下班打卡、月视图记录、管理员团队考勤补卡最小链路均已验证。","排班、节假日豁免、跨日班次等复杂考勤规则未做专项回归,但不构成本轮 trialrun 阻断。","维持 done;复杂考勤规则按需要独立专项。"
@@ -10,7 +10,7 @@
"采购","enabled_admin","/purchase-requests","employee|manager|finance","none","none","partial_done","artifacts/r69/acceptance-report.md|client/tests/e2e/procurement-flow.spec.ts|issues/2026-03-24_17-00-26-r33-3-transaction-timeout-tail-cleanup.csv","员工提单、经理审批、财务归档链路已自动化通过,且事务超时问题已收口。","未进入 R70 人工试运行,当前 trialrun DB 下没有业务体验反馈。","若采购进入试运行范围,先补一次人工 happy path;否则保留自动化证据即可。"
"资产与盘点","enabled_admin","/assets|/inventory-checks|/mobile/assets/scan","employee|asset admin","none","none","partial_done","artifacts/r69/acceptance-report.md|client/tests/e2e/assets-flow.spec.ts|issues/2026-03-24_17-00-26-r33-3-transaction-timeout-tail-cleanup.csv","资产建卡、领用归还、调拨、盘点主链路已自动化通过。","未进入 R70 人工试运行;移动扫描真实设备与摄像头场景未验证。","保持自动化证据;真实移动扫描转 A5 或独立移动专项。"
"差旅与移动审批","enabled_admin","/travel-requests|/mobile/approvals|/mobile/capture","employee|manager|finance","WECOM_ENTRY_ENABLED","wecom_entry_v1","partial_done","artifacts/r69/acceptance-report.md|client/tests/e2e/travel-payment-flow.spec.ts|client/tests/e2e/travel-reconciliation-flow.spec.ts|client/tests/e2e/mobile-approvals-offline.spec.ts|artifacts/r70/manual-trialrun-report.md","差旅申请、审批、支付、绑票、对账批次,以及移动审批 deep link 离线重放都有自动化证据;R70 人工仅确认移动入口可理解。","真实 IM deep link、真实移动设备拍照上传、多人并发审批未验证。","保留自动化为主;真实设备和 IM 深链并入 A5。"
"预算、支付批次与 AP 运营","optional","/budgets|/payment-batches|/ap-operations","finance|admin","BUDGET_PRECHECK_ENABLED|PAYMENT_BATCH_ENABLED","budget_precheck_v1|payment_batch_v1","partial","issues/2026-03-29_08-07-14-p25-lite-budget-ap.csv|issues/2026-04-06_r52-ap-operations.csv","预算预检、支付批次 maker-checker、AP 工作台四 Tab 与快捷操作有实现、review 与浏览器级 QA 证据。","不属于当前小团队 trialrun 主路径,R69-R74 没有重新执行一轮本地场景回归。","若财务域纳入试运行,再补一轮 focused regression;本轮仅保留历史验证。"
"预算、支付批次与 AP 运营","optional","/budgets|/payment-batches|/ap-operations","finance|admin","BUDGET_PRECHECK_ENABLED|PAYMENT_BATCH_ENABLED|ERP_SYNC_ENABLED","budget_precheck_v1|payment_batch_v1|erp_sync","partial_done","issues/2026-03-29_08-07-14-p25-lite-budget-ap.csv|issues/2026-04-06_r52-ap-operations.csv|issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|server/scripts/regression-payment-batches.sh","支付批次 maker-checker、AP 待付款快捷复核、finance 执行报销付款批次,以及 reimbursement paid 后 ERP preview pending outbox 已在本地 focused regression 跑通;历史 budget/AP 视图与 review 证据仍保留。","预算预检页面级联动、高风险两轮复核、大额批次与非报销实体付款未在本轮 focused local 回归中重跑。","若财务域继续扩大试运行,再补 budget precheck 与高风险二次复核专项;当前 payment batch/AP happy path 已具备本地回归证据。"
"ERP 集成与切换控制台","external","/system/integrations/erp|/system/cutover","integration admin|system.settings","ERP_SYNC_ENABLED|CUTOVER_CONSOLE_ENABLED","erp_sync|cutover_console_v1","partial_external","issues/2026-03-08_14-10-53-p19-erp-1-l5-masterdata-journal-bridge.csv|issues/2026-03-21_11-10-00-p29-replacement-readiness-refresh.csv|client/tests/e2e/live-smoke.spec.ts","ERP 管理页、mock/http 回归和只读 live smoke 有历史证据。","当前本地 trialrun 没有真实 ERP 出入站或 cutover 操作验证。","仅在外部环境专项下执行,不进入默认 trialrun gate。"
"系统治理、功能开关与审计","enabled_admin","/system/workspace|/system/settings|/system/feature-flags|/system/workflow-definitions|/audit-logs|/system/notification-channels","admin","WORKFLOW_DEFINITION_ADMIN_ENABLED","workflow_definition_admin_v1","partial_done","artifacts/r70/manual-trialrun-report.md|client/tests/e2e/settings-governance.spec.ts|client/tests/e2e/live-smoke.spec.ts|server/src/scripts/smoke-trialrun-runtime.js|client/tests/e2e/r75-automation-gap-fill.spec.ts","工作区设置、系统设置、功能开关、审计日志打开与导航边界有自动化和人工证据;R74 本地门禁已检查 workspace/flag/navigation 基线,R75 已补功能开关备注保存并恢复写路径。","流程定义、通知渠道、工作区其他写操作和审计留痕写入效果仍未在当前 trialrun 做完整专项。","保留 partial_done;后续若治理域纳入试运行,再补通知渠道与流程定义写路径。"
"企微、HTTPS、部署态与并发","external","/login(企微入口)|部署运行时|多账号并发","all roles","APP_ORIGIN|COOKIE_SECURE|WECOM_*|PUBLIC_SECURITY_HARDENING_ENABLED","wecom_entry_v1|public_security_hardening_v1","blocked_external","deploy/trialrun/runbook.md|client/tests/e2e/wecom-entry.spec.ts|client/tests/e2e/live-smoke.spec.ts|server/src/scripts/smoke-trialrun-runtime.js|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv","本地/只读 smoke、env contract、导航/入口提示与 local runtime gate 已就绪。","真实企微 OAuth、HTTPS cookie secure、多用户并发协同仍缺环境级成功证据。腾讯 OCR 鉴权已确认不是当前主阻塞点:旧项目 `.env` 中至少一组凭证可用,当前主阻塞已转为应用层未传 PDF 参数导致的 `ImageDecodeFailed`。","企微/HTTPS/并发仍按 A3/A4 外部专项推进;OCR 从“外部凭证阻塞”切换为 R77 内部修复项,修完后再回到 live trialrun。"
1 Domain Tier Routes Primary_Roles Env_Gates Feature_Flags Coverage_Status Evidence Validated_Flows Gaps Next_Action
2 认证与 Portal 导航 must_cover /login|/portal|主/管/备导航 employee|manager|admin APP_ORIGIN|COOKIE_SECURE|PORTAL_HOME_ENABLED|WECOM_ENTRY_ENABLED portal_home_v1|wecom_entry_v1 done artifacts/r69/acceptance-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/login-portal-ux.spec.ts|client/tests/e2e/wecom-entry.spec.ts|client/tests/e2e/manual/r70-trialrun-assist.spec.ts 登录、Portal 首屏、员工/经理/管理员导航分组、普通浏览器与企微 WebView CTA 提示均有自动化或人工证据。 真实企微 OAuth 绑定与回跳未做端到端;HTTPS 部署态 cookie 行为未在本轮验证。 保持主链路 done;真实企微与 HTTPS 转 A5 外部专项。
3 请假与审批 must_cover /leave-requests|/workflow/tasks employee|manager|admin none leave_holiday_calendar done artifacts/r69/acceptance-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/leave-flow.spec.ts|client/tests/e2e/workflow-quick-approve.spec.ts|client/tests/e2e/leave-team-calendar.spec.ts 请假草稿、提交、撤回、经理快捷审批、团队日历权限边界与经理部门视角已自动化并在 R70 人工复测通过。 多审批人链路、代理审批、真实并发多人协同未在 trialrun 轮次验证。 维持 done;多人协同与代理审批并入 A5 或后续专项。
4 报销、票据中心与 OCR must_cover /reimbursements|/reimbursements/:id|/invoice-pool employee|manager|finance|admin OCR_PROVIDER|TENCENT_SECRET_ID|TENCENT_SECRET_KEY OCR_PROVIDER|TENCENT_SECRET_ID|TENCENT_SECRET_KEY|PAYMENT_BATCH_ENABLED|ERP_SYNC_ENABLED invoice_verification invoice_verification|payment_batch_v1|erp_sync partial_done done artifacts/r70/manual-trialrun-report.md|artifacts/r70/live-ocr-batch-report.md|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv|issues/2026-04-29_09-19-07-r77-ocr-pdf-first.csv|client/tests/e2e/reimbursement-workflow-progress.spec.ts|client/tests/e2e/invoice-ocr-flow.spec.ts|client/tests/e2e/manual/live-invoice-ocr.spec.ts|client/tests/e2e/manual/live-invoice-ocr-batch.spec.ts artifacts/r70/manual-trialrun-report.md|artifacts/r70/live-ocr-batch-report.md|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv|issues/2026-04-29_09-19-07-r77-ocr-pdf-first.csv|issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|client/tests/e2e/reimbursement-workflow-progress.spec.ts|client/tests/e2e/invoice-ocr-flow.spec.ts|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|client/tests/e2e/manual/live-invoice-ocr.spec.ts|client/tests/e2e/manual/live-invoice-ocr-batch.spec.ts|server/scripts/regression-payment-batches.sh 报销提交后审批进度、票据中心缺字段修正、上传后自动 OCR 进入可绑定、绑定后提交已自动化;R70 人工链路也通过。R77 已在干净本地 trialrun DB 上用真实 PDF 发票跑通 `上传 -> OCR -> available`,并完成 live 单张 + batch Playwright 复测,runner 正常退出。 报销提交后审批进度、票据中心缺字段修正、上传后自动 OCR 进入可绑定、绑定后提交已自动化;R70 人工链路也通过。R77 已在干净本地 trialrun DB 上用真实 PDF 发票跑通 `上传 -> OCR -> available`。R78 已补 employee->manager->finance->finance_mgr->payment batch->execute->reimbursement paid->invoice archived->ERP preview pending outbox focused regression。 旧项目 `.env` 中一组腾讯凭证已在当前应用链路内验证可用;`.env.local` 仍是无效凭证。当前剩余缺口已不再是 OCR 鉴权、PDF 参数或 batch runner 收尾,而是财务支付完成后的下游链路尚未纳入本轮小团队 trialrun。 真实 ERP 投递回执、高风险两轮复核、大额或混合付款批次仍未进入本轮本地试运行;腾讯 OCR 凭证仍依赖旧项目运行时注入。 员工侧报销 / 票据 / OCR 主链路已具备本地试运行条件;如后续将财务支付后链路纳入试运行,再补一轮 focused regression。PDF 转图片 fallback 仅在新增真实票样兼容性问题出现时再评估。 维持 done;真实 ERP 与高风险付款转外部/专项回归,PDF 转图片 fallback 仅在新增票样兼容性问题出现时再评估。
5 周报与待办 must_cover /weekly-reports|/todos employee|manager WEEKLY_REPORT_ENABLED|TODO_BOARD_ENABLED weekly_report_v1|todo_board_v1 done artifacts/r70/auto-trialrun-deep-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/weekly-todo-flow.spec.ts|client/tests/e2e/manual/r70-trialrun-deep-assist.spec.ts 周报保存、生成待办、提交周报、完成待办闭环已自动化且在真实 trialrun 数据上复核通过。 经理侧未提交周报聚合与催办策略未单独验证。 维持 done;如进入管理场景增强,再补经理视角聚合。
6 公告、搜索与通知 must_cover /announcements|/notifications|全局搜索 employee|manager|admin ANNOUNCEMENTS_ENABLED|GLOBAL_SEARCH_ENABLED announcements_v1|global_search_v1 done artifacts/r70/auto-trialrun-deep-report.md|artifacts/r70/manual-trialrun-report.md|client/tests/e2e/announcements-presence.spec.ts|client/tests/e2e/global-search.spec.ts|client/tests/e2e/manual/r70-trialrun-deep-assist.spec.ts|client/tests/e2e/r75-automation-gap-fill.spec.ts 公告发布、员工可见、Portal 汇总、真实搜索命中公告并跳转,以及通知中心未读筛选、单条已读、全部已读与待办跳转均有自动化或人工证据。 无当前试运行阻断缺口;真实外发渠道投递与通知渠道配置验证另见系统治理和外部环境项。 站内通知维持 done;外发渠道留在 A5/治理专项。
7 团队状态与考勤 must_cover /team|/attendance employee|manager|admin PRESENCE_BOARD_ENABLED|ATTENDANCE_ENABLED presence_board_v1|attendance_v1 done artifacts/r70/manual-trialrun-report.md|client/tests/e2e/announcements-presence.spec.ts|issues/2026-04-09_r59-attendance-clockin.csv|client/tests/e2e/r75-automation-gap-fill.spec.ts 团队状态更新、Portal 汇总、经理查看团队状态,以及员工今日上班/下班打卡、月视图记录、管理员团队考勤补卡最小链路均已验证。 排班、节假日豁免、跨日班次等复杂考勤规则未做专项回归,但不构成本轮 trialrun 阻断。 维持 done;复杂考勤规则按需要独立专项。
10 采购 enabled_admin /purchase-requests employee|manager|finance none none partial_done artifacts/r69/acceptance-report.md|client/tests/e2e/procurement-flow.spec.ts|issues/2026-03-24_17-00-26-r33-3-transaction-timeout-tail-cleanup.csv 员工提单、经理审批、财务归档链路已自动化通过,且事务超时问题已收口。 未进入 R70 人工试运行,当前 trialrun DB 下没有业务体验反馈。 若采购进入试运行范围,先补一次人工 happy path;否则保留自动化证据即可。
11 资产与盘点 enabled_admin /assets|/inventory-checks|/mobile/assets/scan employee|asset admin none none partial_done artifacts/r69/acceptance-report.md|client/tests/e2e/assets-flow.spec.ts|issues/2026-03-24_17-00-26-r33-3-transaction-timeout-tail-cleanup.csv 资产建卡、领用归还、调拨、盘点主链路已自动化通过。 未进入 R70 人工试运行;移动扫描真实设备与摄像头场景未验证。 保持自动化证据;真实移动扫描转 A5 或独立移动专项。
12 差旅与移动审批 enabled_admin /travel-requests|/mobile/approvals|/mobile/capture employee|manager|finance WECOM_ENTRY_ENABLED wecom_entry_v1 partial_done artifacts/r69/acceptance-report.md|client/tests/e2e/travel-payment-flow.spec.ts|client/tests/e2e/travel-reconciliation-flow.spec.ts|client/tests/e2e/mobile-approvals-offline.spec.ts|artifacts/r70/manual-trialrun-report.md 差旅申请、审批、支付、绑票、对账批次,以及移动审批 deep link 离线重放都有自动化证据;R70 人工仅确认移动入口可理解。 真实 IM deep link、真实移动设备拍照上传、多人并发审批未验证。 保留自动化为主;真实设备和 IM 深链并入 A5。
13 预算、支付批次与 AP 运营 optional /budgets|/payment-batches|/ap-operations finance|admin BUDGET_PRECHECK_ENABLED|PAYMENT_BATCH_ENABLED BUDGET_PRECHECK_ENABLED|PAYMENT_BATCH_ENABLED|ERP_SYNC_ENABLED budget_precheck_v1|payment_batch_v1 budget_precheck_v1|payment_batch_v1|erp_sync partial partial_done issues/2026-03-29_08-07-14-p25-lite-budget-ap.csv|issues/2026-04-06_r52-ap-operations.csv issues/2026-03-29_08-07-14-p25-lite-budget-ap.csv|issues/2026-04-06_r52-ap-operations.csv|issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|server/scripts/regression-payment-batches.sh 预算预检、支付批次 maker-checker、AP 工作台四 Tab 与快捷操作有实现、review 与浏览器级 QA 证据。 支付批次 maker-checker、AP 待付款快捷复核、finance 执行报销付款批次,以及 reimbursement paid 后 ERP preview pending outbox 已在本地 focused regression 跑通;历史 budget/AP 视图与 review 证据仍保留。 不属于当前小团队 trialrun 主路径,R69-R74 没有重新执行一轮本地场景回归。 预算预检页面级联动、高风险两轮复核、大额批次与非报销实体付款未在本轮 focused local 回归中重跑。 若财务域纳入试运行,再补一轮 focused regression;本轮仅保留历史验证。 若财务域继续扩大试运行,再补 budget precheck 与高风险二次复核专项;当前 payment batch/AP happy path 已具备本地回归证据。
14 ERP 集成与切换控制台 external /system/integrations/erp|/system/cutover integration admin|system.settings ERP_SYNC_ENABLED|CUTOVER_CONSOLE_ENABLED erp_sync|cutover_console_v1 partial_external issues/2026-03-08_14-10-53-p19-erp-1-l5-masterdata-journal-bridge.csv|issues/2026-03-21_11-10-00-p29-replacement-readiness-refresh.csv|client/tests/e2e/live-smoke.spec.ts ERP 管理页、mock/http 回归和只读 live smoke 有历史证据。 当前本地 trialrun 没有真实 ERP 出入站或 cutover 操作验证。 仅在外部环境专项下执行,不进入默认 trialrun gate。
15 系统治理、功能开关与审计 enabled_admin /system/workspace|/system/settings|/system/feature-flags|/system/workflow-definitions|/audit-logs|/system/notification-channels admin WORKFLOW_DEFINITION_ADMIN_ENABLED workflow_definition_admin_v1 partial_done artifacts/r70/manual-trialrun-report.md|client/tests/e2e/settings-governance.spec.ts|client/tests/e2e/live-smoke.spec.ts|server/src/scripts/smoke-trialrun-runtime.js|client/tests/e2e/r75-automation-gap-fill.spec.ts 工作区设置、系统设置、功能开关、审计日志打开与导航边界有自动化和人工证据;R74 本地门禁已检查 workspace/flag/navigation 基线,R75 已补功能开关备注保存并恢复写路径。 流程定义、通知渠道、工作区其他写操作和审计留痕写入效果仍未在当前 trialrun 做完整专项。 保留 partial_done;后续若治理域纳入试运行,再补通知渠道与流程定义写路径。
16 企微、HTTPS、部署态与并发 external /login(企微入口)|部署运行时|多账号并发 all roles APP_ORIGIN|COOKIE_SECURE|WECOM_*|PUBLIC_SECURITY_HARDENING_ENABLED wecom_entry_v1|public_security_hardening_v1 blocked_external deploy/trialrun/runbook.md|client/tests/e2e/wecom-entry.spec.ts|client/tests/e2e/live-smoke.spec.ts|server/src/scripts/smoke-trialrun-runtime.js|issues/2026-04-29_08-39-22-r76-external-trialrun-closeout.csv 本地/只读 smoke、env contract、导航/入口提示与 local runtime gate 已就绪。 真实企微 OAuth、HTTPS cookie secure、多用户并发协同仍缺环境级成功证据。腾讯 OCR 鉴权已确认不是当前主阻塞点:旧项目 `.env` 中至少一组凭证可用,当前主阻塞已转为应用层未传 PDF 参数导致的 `ImageDecodeFailed`。 企微/HTTPS/并发仍按 A3/A4 外部专项推进;OCR 从“外部凭证阻塞”切换为 R77 内部修复项,修完后再回到 live trialrun。
@@ -0,0 +1,190 @@
import { test, expect } from "./helpers/fixtures";
import {
apiGetData,
createAuthenticatedApiClient,
EMPLOYEE,
FINANCE,
FINANCE_MGR,
login,
logout,
MANAGER,
} from "./helpers/auth";
import { openNavLink } from "./helpers/navigation";
import { TEST_IDS } from "./helpers/testIds";
function extractId(text: string, label: string) {
const match = text.match(new RegExp(`${label}:([0-9a-f-]{36})`));
if (!match?.[1]) {
throw new Error(`Failed to parse ${label} from text: ${text}`);
}
return match[1];
}
test("E2E 报销支付批次主链路:提交 -> 审批 -> 创建批次 -> 复核 -> 执行 -> 下游收口", async ({
page,
reimbursementDetail,
workflowDetail,
}) => {
test.setTimeout(300_000);
const employeeApi = await createAuthenticatedApiClient(EMPLOYEE);
const financeMgrApi = await createAuthenticatedApiClient(FINANCE_MGR);
const paymentDate = new Date().toISOString().slice(0, 10);
try {
await login(page, EMPLOYEE);
await (await openNavLink(page, "我的报销")).click();
await expect(page).toHaveURL(/\/reimbursements/);
const createResponsePromise = page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/reimbursements" && response.ok();
});
await page.getByTestId("reimbursements-create").click();
await page.getByRole("menuitem", { name: "空白报销" }).click();
const createPayload = (await (await createResponsePromise).json()) as { data?: { id?: string } };
const reimbursementId = createPayload.data?.id;
if (!reimbursementId) {
throw new Error(`创建报销单后未返回 id: ${JSON.stringify(createPayload)}`);
}
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await (await openNavLink(page, "票据中心")).click();
const marker = `R78_BATCH_${Date.now()}_${Math.random().toString(16).slice(2)}`;
await page.getByTestId("invoice-upload-file").setInputFiles({
name: `r78-${Date.now()}.png`,
mimeType: "image/png",
buffer: Buffer.from(marker, "utf8"),
});
await page.getByTestId("invoice-upload-submit").click();
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
await expect(page.getByTestId("invoice-detail-panel")).toContainText("状态:available", { timeout: 90_000 });
const invoiceDetailText = await page.getByTestId("invoice-detail-panel").innerText();
const invoiceId = extractId(invoiceDetailText, "票据ID");
await page.getByTestId("invoice-bind-invoice").selectOption(invoiceId);
await page.getByTestId("invoice-bind-reimbursement").selectOption(reimbursementId);
await page.getByTestId("invoice-bind-submit").click();
await expect(page.getByText("绑定成功")).toBeVisible({ timeout: 30_000 });
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await reimbursementDetail.submitReimbursement();
await expect(page.getByText(/状态:(reviewing|审批中)/)).toBeVisible();
await logout(page, EMPLOYEE.name);
await login(page, MANAGER);
await (await openNavLink(page, "待我处理")).click();
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
const managerTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
await expect(managerTaskRow).toBeVisible();
await managerTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
await workflowDetail.assertLoaded();
await workflowDetail.approve();
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
await logout(page, MANAGER.name);
await login(page, FINANCE);
await (await openNavLink(page, "待我处理")).click();
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
const financeTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
await expect(financeTaskRow).toBeVisible();
await financeTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
await workflowDetail.assertLoaded();
await workflowDetail.approve();
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
const batchName = `R78-报销付款批次-${Date.now()}`;
await page.goto("/payment-batches");
await expect(page).toHaveURL(/\/payment-batches/);
const createCard = page.locator(".card-std").filter({ hasText: "新建批次" }).first();
const createBatchResponsePromise = page.waitForResponse((response) => {
const url = new URL(response.url());
return response.request().method() === "POST" && url.pathname === "/api/payment-batches" && response.ok();
});
await createCard.locator("select").nth(0).selectOption("reimbursement");
await createCard.getByPlaceholder("例如:2026Q2-财务付款批次-01").fill(batchName);
await createCard.getByPlaceholder("用逗号或换行分隔 reimbursement / travel_order ID").fill(reimbursementId);
await createCard.locator('input[type="date"]').fill(paymentDate);
await createCard.getByPlaceholder("例如 PBX").fill("R78PB");
await createCard.getByRole("button", { name: "创建批次" }).click();
const createBatchPayload = (await (await createBatchResponsePromise).json()) as {
data?: { item?: { id?: string } };
};
const batchId = createBatchPayload.data?.item?.id;
if (!batchId) {
throw new Error(`创建支付批次后未返回 id: ${JSON.stringify(createBatchPayload)}`);
}
await page.goto(`/payment-batches?id=${batchId}`);
const batchDetailCard = page.locator(".card-std").filter({ hasText: batchName }).filter({ hasText: "复核进度:" }).last();
await expect(batchDetailCard).toBeVisible();
await expect(batchDetailCard).toContainText("状态:draft");
await expect(batchDetailCard).toContainText("复核进度:0/1");
await logout(page, FINANCE.name);
await login(page, FINANCE_MGR);
await page.goto("/ap-operations");
await expect(page.getByRole("tab", { name: /待付款/ })).toBeVisible();
const pendingRow = page.locator("tr").filter({ hasText: batchName }).first();
await expect(pendingRow).toBeVisible();
await pendingRow.getByRole("button", { name: "快捷复核" }).click();
await expect(page.getByRole("dialog")).toContainText(batchName);
await page.getByPlaceholder("请输入复核意见").fill("R78 focused regression review");
await page.getByRole("button", { name: "提交复核" }).click();
await expect(pendingRow).toContainText("复核 1/1");
await logout(page, FINANCE_MGR.name);
await login(page, FINANCE);
await page.goto(`/payment-batches?id=${batchId}`);
const executedBatchDetailCard = page.locator(".card-std").filter({ hasText: batchName }).filter({ hasText: "复核进度:" }).last();
await expect(executedBatchDetailCard).toBeVisible();
await expect(executedBatchDetailCard).toContainText("复核进度:1/1");
await executedBatchDetailCard.getByRole("button", { name: "执行" }).click();
await expect(executedBatchDetailCard).toContainText("状态:executed", { timeout: 30_000 });
await page.goto(`/reimbursements/${reimbursementId}`);
await reimbursementDetail.assertLoaded();
await expect(page.getByText(/状态:(paid|已支付)/)).toBeVisible({ timeout: 30_000 });
await page.goto("/ap-operations");
await expect(page.getByRole("tab", { name: /待付款/ })).toBeVisible();
await expect(page.getByText(batchName)).toHaveCount(0);
await logout(page, FINANCE.name);
const invoiceDetail = await apiGetData<{
status: string;
archivedAt: string | null;
linkedReimbursementId: string | null;
}>(
employeeApi.api,
`/api/invoice-pool/${invoiceId}`,
);
expect(invoiceDetail.status).toBe("archived");
expect(invoiceDetail.archivedAt).toBeTruthy();
expect(invoiceDetail.linkedReimbursementId).toBe(reimbursementId);
const erpPreview = await apiGetData<{
exportState: {
lastOutboxId: string | null;
lastOutboxStatus: string | null;
};
}>(
financeMgrApi.api,
`/api/integrations/erp/journals/reimbursements/${reimbursementId}/preview`,
);
expect(erpPreview.exportState.lastOutboxId).toBeTruthy();
expect(erpPreview.exportState.lastOutboxStatus).toBe("pending");
} finally {
await Promise.all([employeeApi.dispose(), financeMgrApi.dispose()]);
}
});
@@ -0,0 +1,6 @@
"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes"
"A1","R78 执行合同建档与范围冻结","创建并校验 R78 合同,范围仅覆盖“报销 approved -> 创建支付批次 -> 复核 -> 执行 -> 报销 paid -> AP/支付批次页可见结果”本地 focused regression;不包含真实 ERP 出站、企微、HTTPS、高风险二次复核。","issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv 列完整并通过校验;A2-A5 依赖顺序清晰;Notes 明确本轮无 Prisma 迁移、无外部环境专项。","命令:`python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv`。检查:12 列完整、Dependencies 为 JSON 数组字符串、Files 为实际路径或哨兵值。","manual","DONE","DONE","DONE","issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv","[]","回滚路径:删除本 CSV;不影响业务运行。2026-04-29:合同已写入并通过 validate_issues_csv.py 校验。"
"A2","支付批次报销付款后端回归补齐","围绕 `paymentBatches` 的 reimbursement execute 路径补 focused 后端测试,覆盖 approved 报销进入批次后执行支付、报销变 paid、已绑定发票归档、失败条件与 maker-checker 边界;必要时补最小修复与专用回归脚本。","新增或补强的后端测试能稳定覆盖 reimbursement payment batch happy path;执行后报销状态/付款字段/发票归档行为正确;不引入 schema 变更。","命令:`cd server && npm run prisma:validate && node --check src/routes/paymentBatches.js && node --check src/routes/reimbursements.js && npm test -- tests/unit/routes/paymentBatches.reimbursement.test.js`。业务:approved 报销进入 payment batch,execute 后 reimbursement=paid,关联发票 archived。","manual","DONE","DONE","DONE","server/src/routes/paymentBatches.js|server/src/routes/reimbursements.js|server/tests/unit/routes/paymentBatches.reimbursement.test.js|server/scripts/regression-payment-batches.sh","[""A1""]","涉及付款状态流与票据归档,回滚路径:回退 payment batch reimbursement 执行逻辑改动、删除新增测试与脚本。2026-04-29:已补 payment batch reimbursement route 回归 3 条用例,覆盖 happy path、maker-checker 边界与 invalid pay status 失败路径;并补 `server/scripts/regression-payment-batches.sh`。当前 reimbursement payment batch 成功路径会在支付后补 ERP outbox 与已付款通知。"
"A3","报销支付批次 Playwright 主链路补齐","新增 focused E2E:员工创建并提交流程、经理/财务审批、finance 创建 reimbursement payment batch、finance_mgr 复核、finance 执行、验证 PaymentBatchesPage / ApOperationsPage / reimbursement detail 的下游结果一致;若本地 trialrun 默认种子未打开 payment batch / erp sync,则补最小可复现的本地开关引导。","新增 Playwright 用例通过;至少验证 1) 批次从 draft/reviewed 到 executed;2) 报销单状态变 paid;3) 支付后票据进入 archived;4) AP/支付批次页能看到执行结果;5) ERP preview 可见 pending outbox;6) 本地 focused 回归可通过显式开关重复启动,不依赖手工改库。","命令:`cd client && E2E_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list`。业务:employee->manager->finance->finance_mgr 四角色完成低风险报销支付批次闭环。","playwright","DONE","DONE","DONE","client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|client/tests/e2e/helpers/auth.ts|client/tests/e2e/helpers/navigation.ts|client/src/pages/PaymentBatchesPage.tsx|client/src/pages/ApOperationsPage.tsx|server/src/scripts/seed-trialrun.js","[""A2""]","优先复用现有 reimbursement / workflow / invoice helper;若现有 UI 标识不足,仅补最小测试可观测性。本地 focused finance 回归不改变默认试运行上线建议,只允许显式 opt-in 打开 `payment_batch_v1`、`erp_sync`。2026-04-29:已补 `reimbursement-payment-batch-flow.spec.ts`,并通过 `TRIALRUN_EXTRA_FORCE_ON_FLAGS='payment_batch_v1 erp_sync'` 重复 seed 在 `http://127.0.0.1:3002` 跑通 focused E2E;覆盖 draft/reviewed/executed、reimbursement=paid、invoice=archived、ERP preview pending outbox。"
"A4","付款状态流与权限 reviewer 复核","对 A2-A3 做独立复核,重点检查 maker-checker、payment.batch.manage 与 risk.review.manage 权限边界、报销 paid 与发票 archived 状态流、以及 AP/批次页是否误放大入口。","review 结论覆盖:1) finance 不能自审自己创建的批次;2) finance_mgr 复核后 finance 可执行;3) 失败路径不会错误归档发票;4) AP/批次页未新增越权操作。","人工 review:对照 server/src/routes/paymentBatches.js、server/src/routes/reimbursements.js 与新增 Playwright/单测断言输出 reviewer 结论;若发现权限/状态回归,A5 前必须先修复。","manual","DONE","DONE","DONE","server/src/routes/paymentBatches.js|server/src/routes/reimbursements.js|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts","[""A2"",""A3""]","本 Issue 只输出 review 结论,不扩展新需求。2026-04-29 reviewer 结论:`paymentBatches` 复核路由继续拦截 maker 自审与二次复核人冲突;`PaymentBatchesPage` / `ApOperationsPage` 仍按 `payment.batch.manage` 与 `risk.review.manage` 分离入口;focused 单测确认 invalid reimbursement status 不会错误归档发票,也不会误入 ERP / 通知 follow-up;本轮无阻塞问题。"
"A5","R78 回归收口与覆盖矩阵回填","执行 R78 post-check,回填合同、coverage matrix 与试运行报告,明确‘报销/票据/OCR’域剩余缺口是否仅剩外部专项与高风险付款场景。","A2-A4 通过后,R78 CSV 状态回填完整;coverage matrix 与 manual trialrun report 结论一致;可明确说明本地试运行对财务付款后链路的覆盖边界。","命令:`cd server && ./scripts/regression-payment-batches.sh`;`cd client && E2E_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list`。业务:汇总批次执行、报销 paid、AP/支付批次结果与报告回填。","manual|playwright","DONE","DONE","DONE","issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|artifacts/r75/coverage-matrix.csv|artifacts/r70/manual-trialrun-report.md","[""A4""]","若 R78 通过,下一步再决定是否单开高风险二次复核专项;本轮不进入真实 ERP/企微/HTTPS。2026-04-29:`./scripts/regression-payment-batches.sh` 与 focused Playwright 均通过;coverage matrix 已将报销/票据/OCR 域提升为 done,并把 payment batch / AP happy path 收口到 partial_done;manual trialrun report 已补 R78 财务支付尾链结论。"
1 ID Title Description Acceptance Test_Method Tools Dev_Status Review1_Status Regression_Status Files Dependencies Notes
2 A1 R78 执行合同建档与范围冻结 创建并校验 R78 合同,范围仅覆盖“报销 approved -> 创建支付批次 -> 复核 -> 执行 -> 报销 paid -> AP/支付批次页可见结果”本地 focused regression;不包含真实 ERP 出站、企微、HTTPS、高风险二次复核。 issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv 列完整并通过校验;A2-A5 依赖顺序清晰;Notes 明确本轮无 Prisma 迁移、无外部环境专项。 命令:`python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv`。检查:12 列完整、Dependencies 为 JSON 数组字符串、Files 为实际路径或哨兵值。 manual DONE DONE DONE issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv [] 回滚路径:删除本 CSV;不影响业务运行。2026-04-29:合同已写入并通过 validate_issues_csv.py 校验。
3 A2 支付批次报销付款后端回归补齐 围绕 `paymentBatches` 的 reimbursement execute 路径补 focused 后端测试,覆盖 approved 报销进入批次后执行支付、报销变 paid、已绑定发票归档、失败条件与 maker-checker 边界;必要时补最小修复与专用回归脚本。 新增或补强的后端测试能稳定覆盖 reimbursement payment batch happy path;执行后报销状态/付款字段/发票归档行为正确;不引入 schema 变更。 命令:`cd server && npm run prisma:validate && node --check src/routes/paymentBatches.js && node --check src/routes/reimbursements.js && npm test -- tests/unit/routes/paymentBatches.reimbursement.test.js`。业务:approved 报销进入 payment batch,execute 后 reimbursement=paid,关联发票 archived。 manual DONE DONE DONE server/src/routes/paymentBatches.js|server/src/routes/reimbursements.js|server/tests/unit/routes/paymentBatches.reimbursement.test.js|server/scripts/regression-payment-batches.sh ["A1"] 涉及付款状态流与票据归档,回滚路径:回退 payment batch reimbursement 执行逻辑改动、删除新增测试与脚本。2026-04-29:已补 payment batch reimbursement route 回归 3 条用例,覆盖 happy path、maker-checker 边界与 invalid pay status 失败路径;并补 `server/scripts/regression-payment-batches.sh`。当前 reimbursement payment batch 成功路径会在支付后补 ERP outbox 与已付款通知。
4 A3 报销支付批次 Playwright 主链路补齐 新增 focused E2E:员工创建并提交流程、经理/财务审批、finance 创建 reimbursement payment batch、finance_mgr 复核、finance 执行、验证 PaymentBatchesPage / ApOperationsPage / reimbursement detail 的下游结果一致;若本地 trialrun 默认种子未打开 payment batch / erp sync,则补最小可复现的本地开关引导。 新增 Playwright 用例通过;至少验证 1) 批次从 draft/reviewed 到 executed;2) 报销单状态变 paid;3) 支付后票据进入 archived;4) AP/支付批次页能看到执行结果;5) ERP preview 可见 pending outbox;6) 本地 focused 回归可通过显式开关重复启动,不依赖手工改库。 命令:`cd client && E2E_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list`。业务:employee->manager->finance->finance_mgr 四角色完成低风险报销支付批次闭环。 playwright DONE DONE DONE client/tests/e2e/reimbursement-payment-batch-flow.spec.ts|client/tests/e2e/helpers/auth.ts|client/tests/e2e/helpers/navigation.ts|client/src/pages/PaymentBatchesPage.tsx|client/src/pages/ApOperationsPage.tsx|server/src/scripts/seed-trialrun.js ["A2"] 优先复用现有 reimbursement / workflow / invoice helper;若现有 UI 标识不足,仅补最小测试可观测性。本地 focused finance 回归不改变默认试运行上线建议,只允许显式 opt-in 打开 `payment_batch_v1`、`erp_sync`。2026-04-29:已补 `reimbursement-payment-batch-flow.spec.ts`,并通过 `TRIALRUN_EXTRA_FORCE_ON_FLAGS='payment_batch_v1 erp_sync'` 重复 seed 在 `http://127.0.0.1:3002` 跑通 focused E2E;覆盖 draft/reviewed/executed、reimbursement=paid、invoice=archived、ERP preview pending outbox。
5 A4 付款状态流与权限 reviewer 复核 对 A2-A3 做独立复核,重点检查 maker-checker、payment.batch.manage 与 risk.review.manage 权限边界、报销 paid 与发票 archived 状态流、以及 AP/批次页是否误放大入口。 review 结论覆盖:1) finance 不能自审自己创建的批次;2) finance_mgr 复核后 finance 可执行;3) 失败路径不会错误归档发票;4) AP/批次页未新增越权操作。 人工 review:对照 server/src/routes/paymentBatches.js、server/src/routes/reimbursements.js 与新增 Playwright/单测断言输出 reviewer 结论;若发现权限/状态回归,A5 前必须先修复。 manual DONE DONE DONE server/src/routes/paymentBatches.js|server/src/routes/reimbursements.js|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts ["A2","A3"] 本 Issue 只输出 review 结论,不扩展新需求。2026-04-29 reviewer 结论:`paymentBatches` 复核路由继续拦截 maker 自审与二次复核人冲突;`PaymentBatchesPage` / `ApOperationsPage` 仍按 `payment.batch.manage` 与 `risk.review.manage` 分离入口;focused 单测确认 invalid reimbursement status 不会错误归档发票,也不会误入 ERP / 通知 follow-up;本轮无阻塞问题。
6 A5 R78 回归收口与覆盖矩阵回填 执行 R78 post-check,回填合同、coverage matrix 与试运行报告,明确‘报销/票据/OCR’域剩余缺口是否仅剩外部专项与高风险付款场景。 A2-A4 通过后,R78 CSV 状态回填完整;coverage matrix 与 manual trialrun report 结论一致;可明确说明本地试运行对财务付款后链路的覆盖边界。 命令:`cd server && ./scripts/regression-payment-batches.sh`;`cd client && E2E_BASE_URL=http://127.0.0.1:3002 npx playwright test tests/e2e/reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list`。业务:汇总批次执行、报销 paid、AP/支付批次结果与报告回填。 manual|playwright DONE DONE DONE issues/2026-04-29_10-14-27-r78-reimbursement-payment-tail.csv|artifacts/r75/coverage-matrix.csv|artifacts/r70/manual-trialrun-report.md ["A4"] 若 R78 通过,下一步再决定是否单开高风险二次复核专项;本轮不进入真实 ERP/企微/HTTPS。2026-04-29:`./scripts/regression-payment-batches.sh` 与 focused Playwright 均通过;coverage matrix 已将报销/票据/OCR 域提升为 done,并把 payment batch / AP happy path 收口到 partial_done;manual trialrun report 已补 R78 财务支付尾链结论。
+53 -15
查看文件
@@ -6,6 +6,30 @@ const { createPrismaClient } = require("../prismaClient");
const { hashPassword } = require("../utils/crypto");
const { validatePassword } = require("../utils/passwordPolicy");
const DEFAULT_FORCE_ON_FLAGS = Object.freeze([
"portal_home_v1",
"announcements_v1",
"global_search_v1",
"presence_board_v1",
"attendance_v1",
"weekly_report_v1",
"employee_lifecycle_v1",
"todo_board_v1",
"project_domain_v1",
"project_linking_v1",
"project_communication_log_v1",
"project_closure_checklist_v1",
"confidential_doc_refs_v1",
]);
const OPTIONAL_FORCE_ON_FLAGS = new Set([
"erp_sync",
"budget_precheck_v1",
"payment_batch_v1",
"workflow_definition_admin_v1",
"cutover_console_v1",
]);
function requiredEnv(name) {
const value = process.env[name];
if (typeof value !== "string" || !value.trim()) {
@@ -14,6 +38,29 @@ function requiredEnv(name) {
return value.trim();
}
function parseExtraForceOnFlags() {
const raw = String(process.env.TRIALRUN_EXTRA_FORCE_ON_FLAGS ?? "").trim();
if (!raw) {
return [];
}
const parts = raw
.split(/[\s,]+/)
.map((item) => item.trim())
.filter(Boolean);
const unique = Array.from(new Set(parts));
const invalid = unique.filter((flagKey) => !OPTIONAL_FORCE_ON_FLAGS.has(flagKey));
if (invalid.length > 0) {
throw new Error(
`Unsupported TRIALRUN_EXTRA_FORCE_ON_FLAGS: ${invalid.join(", ")}. Allowed: ${Array.from(
OPTIONAL_FORCE_ON_FLAGS,
).join(", ")}`,
);
}
return unique;
}
async function ensureDepartment(prisma, payload) {
const now = new Date();
const existing = await prisma.department.findFirst({
@@ -255,21 +302,10 @@ async function main() {
await ensureWorkspaceSetting(prisma);
for (const flagKey of [
"portal_home_v1",
"announcements_v1",
"global_search_v1",
"presence_board_v1",
"attendance_v1",
"weekly_report_v1",
"employee_lifecycle_v1",
"todo_board_v1",
"project_domain_v1",
"project_linking_v1",
"project_communication_log_v1",
"project_closure_checklist_v1",
"confidential_doc_refs_v1",
]) {
const extraForceOnFlags = parseExtraForceOnFlags();
const forceOnFlags = [...DEFAULT_FORCE_ON_FLAGS, ...extraForceOnFlags];
for (const flagKey of forceOnFlags) {
await ensureFeatureFlag(prisma, {
flag_key: flagKey,
override_mode: "force_on",
@@ -287,6 +323,8 @@ async function main() {
console.log(`- phone: ${adminPhone}`);
// eslint-disable-next-line no-console
console.log(`- created: ${result.created}`);
// eslint-disable-next-line no-console
console.log(`- force_on flags: ${forceOnFlags.join(", ")}`);
if (result.password) {
// eslint-disable-next-line no-console
console.log(`- one-time password: ${result.password}`);