统一付款详情与批次短编号(08-01-reimbursement-notification-progress-clarity)
这个提交包含在:
@@ -117,3 +117,30 @@
|
||||
- 不改 Prisma schema、历史批次或报销事实;既有 `draft` 批次部署后直接按“待付款复核”展示。
|
||||
- 若新创建合同需回滚,客户端可恢复旧表单,服务端仍接受显式名称和可选默认字段;状态机与历史数据不变。
|
||||
- 任何真实批次复核、回单上传、付款登记仍由业务人员操作,不纳入部署自动化。
|
||||
|
||||
## 9. 付款详情与日序号收口
|
||||
|
||||
### 9.1 页面信息层级
|
||||
|
||||
- 保留 `/payment-batches?id=<batch-id>` 的现有显式选择详情,不为低频付款批次增加独立路由。
|
||||
- 详情连续顺序固定为:批次概要、当前处理/下一步、报销明细、银行回单。状态和职责先于长明细,避免财务再次寻找“现在该做什么”。
|
||||
- 顶部操作组是不变的主动作坐标:`draft` 的合格非制单人可“复核通过”;`reviewed + bank + 无有效回单` 为“上传银行回单”;回单具备后为“登记付款完成”。回单章节只承载证据与次要说明,不复制同等级主按钮。
|
||||
- 当前责任只显示职责,不预猜未来姓名。状态信息缺失时降级为可理解的“待处理”,不让下游对象缺失打空详情。
|
||||
|
||||
### 9.2 金额单一展示合同
|
||||
|
||||
- `client/src/lib/displayFormat.ts` 的 `formatMoney()` 是付款页唯一人民币格式化入口。
|
||||
- 列表、移动卡片、详情总额、批次项、复核/付款确认文案和高风险阈值均传原始数值给共享函数;币种字段仍可作为业务事实保留,但 CNY 不再以裸值加代码拼接。
|
||||
|
||||
### 9.3 编号和自动标题
|
||||
|
||||
- 新格式为 `PB-YYYYMMDD-NNN`,日期使用 `fastify.config.appTimezone`,序号为当日三位正整数。既有长编号不迁移,导出、AP 与外部交接继续把编号视为不透明字符串。
|
||||
- 自动标题只能在取得编号后生成:`YYYY-MM-DD 付款批次 NNN`。请求显式传 `name` 时保持原值,不追加序号。
|
||||
- 当日序号大于 999 时返回稳定校验错误并回滚,不改成四位或随机后缀,避免格式在边界时静默漂移。
|
||||
|
||||
### 9.4 咨询锁与事务边界
|
||||
|
||||
- 使用 PostgreSQL 双 int4 事务级咨询锁:第一参数为项目登记的固定命名空间“付款批次日序号”,第二参数为公司时区 `YYYYMMDD`。
|
||||
- 在同一个 Prisma 交互事务中按固定顺序执行:取得咨询锁 → 查询当日严格新格式 `^PB-YYYYMMDD-[0-9]{3}$` 的最大序号 → 生成编号/自动标题 → 创建批次及既有项、审计、通知。
|
||||
- 不使用 `hashtext`,不把旧长格式纳入最大值,不在 P2002 后原事务内重试;PostgreSQL 唯一冲突会使事务进入 aborted 状态,任何兜底只能重试整个事务。本设计通过咨询锁消除正常同日并发冲突。
|
||||
- 无 schema 迁移;回滚时可恢复旧生成器,已经生成的新短编号仍满足唯一字符串合同,无需改写。
|
||||
|
||||
@@ -97,3 +97,20 @@
|
||||
本地验证(2026-08-01):付款/报销详情/通知/导出/导航聚焦 Vitest 85 passed;隔离 PostgreSQL 付款并发门禁覆盖双执行、执行/取消、重复建批次和直接付款竞争并全部通过;客户端 typecheck/build 通过;付款权限 5 条、高风险动作 8 条 Playwright 共 13 passed。reviewer 首轮未发现数据安全或状态机阻断项,指出“兼任复核职责的制单人仍显示自审按钮”和“回单查询失败误报为空”两项交互问题;已修为前端同步排除制单人、失败态只显示错误与重新加载,复查未发现新 blocker。既有真实批次未被读取正文、修改、复核、上传回单或登记付款。
|
||||
|
||||
生产发布(2026-08-02,脱敏):提交 `7c39f8b` 已推送并通过既有安装固化入口部署;同步前远端源码、普通环境文件、PGDATA 和附件目录合同通过,未同步或覆盖 `.env`。63 项 migration 为 up to date,init/server 依赖层命中缓存;`/ready` 构建短标识、external deps smoke、runtime smoke、付款能力 env/flag 常开、公网前端资源扫描和付款懒加载块四项关键文案均通过。当前 shell 未配置 live-smoke 专用凭据,因此没有宣称登录后业务链自动验收;部署未替任何人复核批次、上传回单或登记付款。
|
||||
|
||||
## 9. 付款详情与批次短编号(已实施,未部署)
|
||||
|
||||
- [x] 将付款详情重排为“批次概要 → 当前处理/下一步 → 报销明细 → 银行回单”,保留现有内联详情和顶部唯一主动作,不新增独立路由。
|
||||
- [x] 付款页列表、移动卡片、详情总额、批次项、确认弹窗和高风险阈值全部改用共享 `formatMoney()`。
|
||||
- [x] 使用公司时区生成 `PB-YYYYMMDD-NNN`,在同一 Prisma 交互事务中先取得双 int4 `pg_advisory_xact_lock`,再查询严格新格式最大日序号并创建批次。
|
||||
- [x] 自动标题改为 `YYYY-MM-DD 付款批次 NNN`;显式名称保持原值;既有编号不迁移;日序号超过 999 时 fail-closed。
|
||||
- [x] 更新 route 单测,覆盖时区、自动标题、显式名称、同日递增、跨日归零、同日旧长编号隔离和边界错误;更新真实 PostgreSQL 并发门禁,证明同日并发编号唯一递增。
|
||||
- [x] 更新付款主链 Playwright,断言详情信息顺序、顶部主动作和全页金额格式;运行前端 typecheck/build。
|
||||
- [x] 运行 Prisma validate、node syntax、task/governance/process/sensitive/diff 门禁;因涉及付款创建并发和原始 SQL,执行只读 reviewer,不允许 reviewer 改代码。
|
||||
- [x] 把双参数咨询锁命名空间、锁顺序、错误矩阵和测试要求沉淀到 backend spec;通过后中文提交,不在未获新批准时部署。
|
||||
|
||||
本地验证(2026-08-02):付款 route 单测 39 passed;隔离 PostgreSQL 付款并发门禁覆盖双执行、执行/取消、重复建批次、同日两来源日序号、直接付款竞争和缺失制单人场景并全部通过;付款权限与高风险动作 Playwright 14 passed;客户端 typecheck/build、Prisma validate、相关脚本语法、Trellis task/governance/process/sensitive/diff 门禁通过。文档注册审计保持 0 error,既有 22 个未登记 docs 文件 warning 与本轮无关。
|
||||
|
||||
实现中发现 Prisma 7.8 driver adapter 无法反序列化 `SELECT pg_advisory_xact_lock(...)` 的 PostgreSQL `void` 列并返回 `P2010`;锁调用改用 `$executeRaw` 后真实 PostgreSQL 并发门禁通过。该陷阱、双 int4 命名空间登记、固定锁顺序和禁止在已 aborted 事务内重试 `P2002` 已写入 backend spec。
|
||||
|
||||
只读审查(2026-08-02):未发现权限、maker-checker、付款状态机或历史编号兼容阻断项。新编号分配在既有报销锁定、状态/占用和复核人校验之后、所有批次/批次项/审计/通知写入之前;咨询锁、严格新格式查询和创建保持在同一交互事务。旧长编号不参与日序号,超过 999 时零业务写入;付款页未新增路由或可操作权限,回单缺失时顶部仍只有一个主动作。审查未修改生产数据,也未执行部署。
|
||||
|
||||
@@ -45,6 +45,10 @@
|
||||
- 付款复核仍是一轮 maker-checker;责任收件人只选一个,优先具备复核权限的 active `ceo`,没有时再按稳定顺序选择 active `manager`,始终排除制单人。
|
||||
- 付款页同一时刻只显示一个主动作:银行渠道复核通过但尚无有效回单时显示“上传银行回单”;上传后才显示“登记付款完成”,不能让用户先撞到 `bank_receipt_required` 才知道前置条件。
|
||||
- 付款批次状态标签必须使用共享展示映射;列表、详情、移动响应式视图和相关测试不得各自复制 `draft/reviewed/executed/failed/cancelled` 中文文案。
|
||||
- 付款详情保持现有列表内显式选择后的详情形态,不新增独立详情路由;信息顺序固定为“批次概要 → 当前处理/下一步 → 报销明细 → 银行回单”。顶部操作组始终承载当前唯一主动作,回单章节后移不得把“上传银行回单”推到明细列表之后。
|
||||
- 付款页的列表、详情总额、批次项、确认弹窗和高风险阈值统一使用共享 `formatMoney()`;不得继续裸输出金额或在页面内新增货币格式化副本。
|
||||
- 新建付款批次编号使用公司时区日期和三位日序号 `PB-YYYYMMDD-NNN`;既有编号不迁移。未显式传名称时自动标题为 `YYYY-MM-DD 付款批次 NNN`,显式名称原样保留。
|
||||
- 日序号必须在创建批次的同一交互事务中,先取得双参数 PostgreSQL 事务级咨询锁,再查询当日严格新格式最大序号并创建;咨询锁使用固定 int4 命名空间和 int4 `YYYYMMDD`,不得依赖唯一冲突后的原事务内重试。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
@@ -67,6 +71,10 @@
|
||||
- [ ] 无合格复核人的错误明确说明“复核人必须是非制单人且在职,请联系管理员”,不把配置问题伪装成财务人员操作错误。
|
||||
- [ ] 既有真实批次不改 ID、不改金额、不改报销绑定、不改复核/付款事实;部署后仅获得新的业务文案和下一步说明,不替任何人推进状态。
|
||||
- [ ] 既有报销批次渠道为空时按银行转账展示并执行回单门禁,但不回写历史记录;当前真实批次同样先上传回单再登记付款完成。
|
||||
- [ ] 付款详情按“批次概要、当前处理/下一步、报销明细、银行回单”连续展示;各状态唯一主动作位于顶部,银行回单章节内不出现第二个同等级主按钮。
|
||||
- [ ] 付款页所有人民币金额统一显示为 `¥1,234.56` 形式,列表、详情、批次项、确认弹窗和高风险阈值无裸金额残留。
|
||||
- [ ] 新批次编号为公司时区 `PB-YYYYMMDD-NNN`,同日并发创建得到唯一递增序号,跨日从 `001` 开始;超过 `999` 时 fail-closed,不改变编号格式。
|
||||
- [ ] 自动标题在编号生成后使用相同日序号;调用方显式提供名称时不追加序号;旧编号保持原值且导出/AP/外部传递继续按不透明字符串兼容。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户