# 报销状态空隙与财务处理视角 ## Goal 消除报销从审批通过到付款批次创建之间、以及报销回到草稿状态之后的两个运营空隙:财务应能在付款页直接发现并处理尚未组批的已通过报销,同时“全范围”不得暴露他人从未提交的私人草稿,但必须保留对已经进入过流程的退回或撤回草稿的追踪能力。 本任务来自真实业务单据 `0014` 的首次使用反馈。两个 Requirement 共享“状态空隙必须有明确责任人和可见入口”的产品原则,但必须保持代码边界、测试证据和回滚点独立,不能因一项回滚而被迫撤销另一项。 ## Scope - R1 覆盖付款页待组批视图、报销详情下一步深链及报销处理进度中的组批/复核阶段区分。 - R2 覆盖报销列表、详情、材料、提交前检查、全局搜索、导出和报销统计的“本人草稿或已进入流程”服务端读取边界。 - 两项分别实施、分别测试、分别提交和回滚;具体行为以 Requirements 为准,排除项以 Out of Scope 为准。 ## Background - 2026-09-04 对生产做了只读核验:目标单据唯一匹配,状态为 `approved`、未付款、无任何付款批次关联,`payment_batch_v1` 为 `force_on/global`,满足现有 `/payment-batches/candidates` 候选合同;未创建批次或改动单据。 - 同次脱敏统计显示:生产当前只有 1 张 `approved` 且未付款、未被活动批次占用的报销,即本次目标单据;当前 1 张 `draft` 从未产生 workflow 实例,暂无“有历史 workflow 的 draft”。这些计数只用于评估本次行为,不作为测试 fixture 或实现常量。 - 付款首页只查询已经创建的 `PaymentBatch`;候选报销仅在财务点击“新建批次”后才加载。`待我审批` 查询 workflow task,而付款批次复核不是报销 workflow task;组批前也不存在可复核的付款批次。 - 报销详情目前把无批次的 `approved` 投影为“付款复核 / 待安排 / 待财务安排付款”,同时页面下一步仅写“等待财务处理”。批次 `draft` 才真正表示“已创建并提交付款复核”。当前投影把批次级阶段提前套在尚未组批的报销上。 - 报销列表的状态与范围相互独立。具备 `expense.view_all` 的财务、财务负责人、CEO、HR、管理员可在 `草稿 + 全范围` 查看全公司的草稿;经理可查看本人、代填及直属下属草稿。现有详情和材料读取辅助函数也对 `expense.view_all` 无条件放行。 - 报销 `return` 和申请人 `withdraw` 都会把业务状态改回 `draft`,并保留 workflow 实例;从未提交的草稿没有 workflow 实例。仓库统计查询已使用 workflow 实例创建时间作为提交事实,因此“是否存在 workflow 实例”是现有、可推导且无需 schema 变更的“是否进入过流程”判据。 ## Requirements ### R1:已通过但未组批的报销必须成为财务可见、可执行的队列 1. 报销 `approved` 且未付款、未进入任何非取消付款批次、付款能力对其部门有效时,必须出现在付款页一级“待组批”视图;候选资格继续由服务端现有候选合同决定,前端不得自行复制状态或部门门禁。 2. “待组批”只对具备 `payment.batch.manage` 的人员显示和查询;仅具备 `payment.batch.review` 的复核人继续查看已创建的付款批次,不获得组批候选或创建能力。 3. 付款页在无批次详情参数时提供“待组批”和“付款批次”两个明确视图。对组批管理员默认进入“待组批”,标签显示服务端返回的候选总数;复核只读角色默认进入“付款批次”。URL 必须可表达当前视图并支持刷新、返回和报销详情深链。 4. “待组批”复用现有候选 API、搜索、分页和创建合同,显示单号、报销人、报销日期、权威总额及摘要。它不是第二套候选计算,也不得同时保留一份隐藏在“新建批次”里的重复候选列表。 5. 财务可从待组批列表选择一张或多张报销并进入现有“创建并提交复核”流程;创建成功后候选立即从待组批队列消失,付款批次视图出现新批次,其状态为“待付款复核”。不得自动组批、自动选择、自动复核或改动 maker-checker 规则。 6. 报销详情在无批次的 `approved` 状态下,把下一步明确写为“等待财务组建付款批次”。具备 `payment.batch.manage` 时提供进入付款页待组批视图的单一入口,并以当前报销编号预置搜索;其他用户只看到状态说明,不暴露付款管理能力。 7. 报销处理进度必须区分“组建付款批次”和“付款复核”: - 无批次的 `approved`:组批阶段为当前,说明“待财务组建付款批次”;付款复核仍为待处理。 - 已有 `draft` 批次:组批阶段已完成,付款复核为当前,继续使用“待付款复核”。 - 后续 `reviewed/executed/failed/cancelled` 继续由共享付款阶段语义投影,不改变付款写状态机。 8. 列表必须有加载、错误、空态和分页反馈;空态只说明当前没有待组批报销,不把“系统正常”写成解释性噪音。PC 与现有响应式付款页保持同一数据源,移动端不新建第二套付款状态机。 ### R2:全范围只展示已经成为业务事实的他人草稿 1. “业务事实”判据固定为:报销单至少存在一个 `entity_type=reimbursement` 的 workflow 实例。不得用当前 workflow 状态、最后一次 outcome、页面标签、单号是否为空或更新时间猜测。 2. 报销人为本人或提交人为本人时,仍可查看自己的全部草稿,不要求存在 workflow 实例。 3. 对他人的 `draft`: - 从未进入流程、无 workflow 实例:从全范围列表、全局搜索、导出和按报销聚合的统计中排除,直接访问详情或材料接口返回无权限。 - 曾经进入流程、有 workflow 实例:继续按既有全范围角色边界可见,包括审批人退回和申请人撤回后回到 `draft` 的记录。 4. 非 `draft` 状态的既有可见性不变;经理仍限本人、提交人及直属下属,财务/财务负责人/CEO/HR/管理员仍限现有全局范围。本任务不新增角色、不授予 `expense.view_all`、不改变编辑权限。 5. 服务端必须统一强制该判据。前端可以隐藏不可见记录,但不得作为授权边界;详情、普通附件下载、附件预览 URL、完整材料预览和 `POST /reimbursements/:id/precheck` 必须共同收紧,避免通过已知 ID 绕过列表。`precheck` 返回的预算提示、费用标准、审批规则和风险标记同样属于受保护的报销内容。 6. 报销统计与导出不得把他人从未提交的草稿当作正式台账事实;本人草稿是否进入个人视角统计沿用既有行为。付款候选只接受 `approved`,不受本条授权筛选影响。 7. 被排除的记录对无权读取者使用既有自然中文无权限合同,不泄露“该 ID 存在但尚未提交”、workflow 计数或内部判据。 ## Acceptance Criteria - [ ] AC1(R1):以 `approved + unpaid + no active batch + gate enabled` fixture 请求待组批视图,能看到目标报销;任一条件不满足时不出现。生产只读复核能在待组批视图搜索到 `0014`,但自动化不得创建真实付款批次。 - [ ] AC2(R1):付款批次管理员默认看到“待组批”及准确总数,搜索/分页/空态/错误态可用;仅复核角色看不到待组批和创建入口,原有批次复核能力不变。 - [ ] AC3(R1):从报销详情点击入口进入可刷新、可返回的待组批 URL,并预置当前单号;无管理权限时入口为 0,但“等待财务组建付款批次”仍准确显示。 - [ ] AC4(R1):无批次时进度显示“组建付款批次”为当前且“付款复核”为待处理;创建批次后前者完成、后者当前。`draft/reviewed/executed/failed/cancelled` 付款批次的既有语义、通知、权限和写状态机回归通过。 - [ ] AC5(R1):创建成功只刷新候选与批次查询,不出现重复候选列表,不自动执行任何付款写动作;PC 1280px 无横向溢出,移动端可完成搜索、选择和进入创建流程,交互目标不小于现有 44px 约束。 - [ ] AC6(R2):表驱动服务端测试覆盖本人/提交人、全局角色、经理直属团队、无关人员 × `draft` 无 workflow、`draft` 有 returned workflow、`draft` 有 cancelled/withdrawn workflow、所有非 draft 状态;只有“他人且从未进入流程的 draft”被排除。另有负向保护证明当前或历史 workflow 审批人读取自己任务关联的报销不受新过滤误伤。 - [ ] AC7(R2):同一未提交他人草稿在列表、全局搜索、CSV/XLSX 导出和报销统计中均不可见/不计入;直接详情、普通附件、预览 URL、完整材料预览和 `precheck` 均返回无权限。不得只做前端筛选。 - [ ] AC8(R2):退回或撤回后为 `draft` 且有 workflow 实例的报销仍可由原授权范围内人员查看;既有审批历史、只读投影和申请人编辑重提/再次提交路径不变。 - [ ] AC9(独立性):R1 与 R2 分成独立业务提交或至少独立可逆提交单元、独立 focused 测试组;分别回滚任一项时另一项仍可构建并通过自己的测试,不需要 schema、migration、seed、flag 或生产数据修正。 - [ ] AC10(门禁):前后端 focused 测试、前端 typecheck/build、UI strict/self-test、Prisma validate、workflow/权限过程级回归、Trellis governance 和 `git diff --check` 通过;涉及权限和 workflow 投影,实施后必须进行只读 review。 - [ ] AC11(部署后):`/ready` 与 bundle marker 等于候选短 SHA,公网文案扫描 findings=0;真实账号只读确认 `0014` 位于待组批且未被自动修改,未提交他人草稿不再可见,退回/撤回历史草稿 fixture 仍可见。真实组批、复核和付款仍由财务人工执行。 ## Test Plan - R1 focused:付款候选资格、manage/review 权限矩阵、待组批 URL/搜索/分页/空态、创建后查询刷新,以及无批次到五种批次状态的进度投影。 - R2 focused:角色与归属 × draft/非 draft × workflow 无/有的表驱动矩阵,并覆盖列表、详情、材料、`precheck`、全局搜索、CSV/XLSX 和统计旁路;单列 workflow reviewer 待办读取的负向保护。 - 回归与门禁:付款 maker-checker、报销退回/撤回/重提、B1/B2、非报销 workflow、前端 typecheck/build、UI strict/self-test、Prisma validate、Trellis governance 和 `git diff --check`。 - 生产验收只读检查候选可见性和授权结果;不自动创建、复核或执行真实付款批次。 ## Out of Scope - 不在本任务替财务创建 `0014` 的付款批次、复核或登记付款。 - 不修改 Prisma schema,不新增 `submitted_at`,不回写历史报销或 workflow 数据。 - 不合并报销 workflow 与付款批次状态机,不把付款复核塞进“待我审批”。 - 不修改 maker-checker、付款账号、回单、网银制单、B1/B2、预算、票据或通知 routing。 - 不改变采购、差旅、请假、补卡的列表、草稿权限或工作流。 - 不借本任务调整 `expense.view_all` 的角色授予;HR/CEO 是否应继续拥有该权限另行评估。 - 不新增批量自动组批、自动付款、真实支付接口或第二套移动付款页。 ## Risks and Deferred Items - workflow 实例存在表示“曾进入流程”,因此会有意保留退回和撤回草稿的全范围可见性;若后续业务要求撤回后重新私有化,必须新增独立产品判据,不能悄悄改成 outcome 白名单。 - 当前数据库无 `submitted_at`;workflow 关系查询需要通过 focused 性能测试确认列表、搜索、导出和统计没有不可接受退化。本任务不以冗余字段或 migration 预优化。 - 全局搜索、导出和统计是列表之外的旁路;若实现评估发现某一旁路无法在本候选安全收紧,必须阻断 R2 发布或拆出明确依赖任务,不能把列表隐藏冒充权限修复完成。 - 真实付款批次创建属于财务业务动作,不作为自动化验收。部署后只证明候选可见、入口可用和数据未被自动修改。