Compare commits

...

3 Commits

6 changed files with 1540 additions and 2 deletions

View File

@ -264,6 +264,10 @@ flowchart TD
- 旧系统将“柜台收费”和“柜台结账”拆分为两个菜单,结账阶段包含未结/已结查询、结账红冲、追加抄表和打印动作。
- 当前设计可继续采用统一收费核销模型,但必须补出“收费记录 → 班结结果 → 打印/红冲/查询”的业务闭环,避免柜面日终处理缺口。
- 迁移时需保留结账时间、结账人、网点、收费汇总口径和结账后红冲痕迹,保证财务对账与审计连续。
- 当前正式实现中,柜台红冲分为已结账红冲与未结账红冲两条路径:
- 已结账红冲以 `biz_settle_record_detail` 为红冲状态源,要求收费记录已结账且结账明细为正常状态,红冲后同步更新结账明细、支付主单和营业账结清状态。
- 未结账红冲仅允许柜台收费、账单缴费、收款方向、未结账且未绑定结账单的收费记录;执行正式反向支付流水后,将原支付主单标记为 `REVERSED`,将营业账恢复为未收费,并写入未结账红冲投影表用于红冲记录查询。
- 已结账与未结账收费记录不得混批红冲,前端应在未结账列表提供单笔红冲入口,后端仍以支付主单状态进行最终校验。
#### 账单打印服务
@ -276,6 +280,7 @@ flowchart TD
- 旧系统支持红冲记录查询、导出与明细展开,是收费差错追溯的重要入口。
- 当前设计可将红冲视为收费核销后的修正场景,不强制要求独立实体表,但必须提供历史只读查询口径。
- 红冲迁移最小保留信息应包括原收费记录、红冲时间、红冲金额、原因、经办人、关联账单和后续账务状态。
- 当前正式实现中,红冲记录查询合并两个来源:已结账红冲来自结账明细状态,未结账红冲来自 `biz_counter_unsettled_red_flush_record` 查询投影;两类记录在查询出口统一按红冲时间排序并支持导出。
### 接口映射
@ -285,8 +290,8 @@ flowchart TD
### 落地边界
- **已落地**:营业账主明细、交易流水、回调、异常、托收/代扣主对象。
- **部分落地**:柜台班结、部分收费汇总类对象可能通过业务流程与报表实现,不一定存在独立表。
- **文档先行**部分红冲、实时收费汇总类台账暂不表述为已确认独立实体表。
- **部分落地**:柜台班结、部分收费汇总类对象可能通过业务流程与报表实现,不一定存在独立表;柜台未结账红冲已落地为正式反向支付流水 + 状态回写 + 查询投影
- **文档先行**:实时收费汇总类台账暂不表述为已确认独立实体表。
<a id="mod-rev-004"></a>

View File

@ -1172,6 +1172,31 @@ retrieval_priority: P0
>
> 边界说明:旧数据字典中的“跨周期水量、特账、红冲、已销调整、呆坏账、实时收费日志”等对象,在当前 backend 范围内未全部识别到独立实体表;数据库专项中统一按“在线骨架承接 / 历史只读保留 / 映射对象追溯”三层口径描述,不误写为已明确落地的真实表。
### biz_counter_unsettled_red_flush_record (柜台未结账红冲查询投影表)
| 字段 | 说明 |
| :--- | :--- |
| `id` | 主键,使用 `biz_counter_unsettled_red_flush_record_seq` |
| `payment_record_id``payment_no` | 原未结账收费支付主单及单号 |
| `reverse_payment_record_id``reverse_payment_no` | 正式反向支付流水及单号 |
| `charge_id` | 关联营业账 |
| `source_cust_id``source_cust_code``source_cust_name` | 客户快照 |
| `cashier_id` | 原收费员,用于柜台红冲记录查询权限和筛选 |
| `reversed_amount``reversed_time``reverse_reason` | 红冲金额、时间、原因 |
| `proc_person``proc_type` | 经办人与处理类型,未结账红冲固定为 `COUNTER_UNSETTLED_RED_FLUSH` |
| `tenant_id``creator``create_time``updater``update_time``deleted` | 租户、审计和逻辑删除字段 |
约束与索引:
- `uk_counter_unsettled_red_flush_payment(payment_record_id, tenant_id)`:防止同一原收费支付主单重复写入未结账红冲投影。
- `idx_counter_unsettled_red_flush_cashier_time(cashier_id, reversed_time, tenant_id)`:支撑按收费员和红冲时间查询。
- `idx_counter_unsettled_red_flush_cust_time(source_cust_code, reversed_time, tenant_id)`:支撑按客户编号和红冲时间查询。
设计边界:
- 该表不是红冲业务主账本,主状态仍以 `biz_payment_record``biz_charge` 和正式反向支付流水为准。
- 该表只承接未结账红冲的查询/导出投影;已结账红冲仍以 `biz_settle_record_detail` 的红冲状态为查询来源。
### 旧系统历史台账迁移与只读查询口径
#### 在线主模型承接范围

View File

@ -1777,6 +1777,30 @@ sequenceDiagram
| 水表生命周期、检定与库存流转 | `IF-METER-001``IF-METER-003` | `biz_meter*` + 历史仓储/检定单据 | 表号、当前状态、单据类型、仓库、检定结论、证书编号、时间 | 支撑表务迁移核查与历史作业追溯 |
| 页面参数、业务字段、微信配置 | `IF-REV-012``IF-CS-006` | `biz_parameter_settings``biz_page_settings*``sys_wechat_app_settings` | 参数编码、原值、新值、启用状态、生效时间、适用渠道 | 支撑微网厅后台配置迁移核查 |
### 柜台红冲接口约束补充
当前柜台结账红冲统一复用 `POST /business/charge/counter-settle/red-flush`,请求参数为 `paymentRecordIds` 与必填 `reason`
接口按支付主单结账状态分流:
| 场景 | 请求约束 | 状态变化 | 查询出口 |
| :--- | :--- | :--- | :--- |
| 已结账红冲 | 所有 `paymentRecordIds` 必须存在正常 `biz_settle_record_detail`,支付主单为 `SETTLED` | 写正式反向支付流水;结账明细标记红冲;原支付主单标记 `REVERSED`;营业账清除结账态 | 红冲记录页从结账明细读取 |
| 未结账红冲 | 所有 `paymentRecordIds` 必须为柜台收费、账单缴费、收款方向、`UNSETTLED`、未绑定 `settle_id` | 写正式反向支付流水;原支付主单标记 `REVERSED`;营业账恢复未收费;写入 `biz_counter_unsettled_red_flush_record` | 红冲记录页从未结账红冲投影读取 |
接口拒绝以下情况:
- 已结账与未结账支付主单混批提交。
- 未结账记录不是柜台账单缴费,或缺少可恢复的营业账。
- 原支付主单已红冲、已结账状态并发变化,或已存在未结账红冲投影。
- 当前登录收费员尝试红冲其他收费员的收费记录。
前端约束:
- 柜台结账“未结账”列表对单笔可红冲收费记录提供红冲按钮,仅对有 `paymentRecordId`、有 `chargeId` 且业务场景为 `CHARGE_PAYMENT` 的行启用。
- 柜台结账“已结账”明细继续提供已结账红冲入口。
- 两类入口都必须采集非空红冲原因后再提交。
### 迁移验收对账接口要求
| 验收场景 | 推荐挂靠接口 | 最低查询维度 | 输出要求 |

View File

@ -0,0 +1,28 @@
# 柜台未结账正式红冲验证记录
## 背景
柜台结账历史口径要求未结账和已结账记录均可红冲。原实现仅覆盖已结账红冲,本次补齐未结账正式红冲,并在柜台结账未结账列表提供前端入口。
## 实现范围
- 后端 `POST /business/charge/counter-settle/red-flush` 按支付主单状态分流已结账/未结账红冲。
- 未结账红冲仅支持柜台收费、账单缴费、收款方向、`UNSETTLED``settleId=null` 的支付主单。
- 未结账红冲生成正式反向支付流水,原支付主单更新为 `REVERSED`,营业账恢复未收费。
- 新增 `biz_counter_unsettled_red_flush_record` 查询投影,红冲记录查询/导出合并已结账与未结账红冲。
- 前端柜台结账“未结账”列表新增单笔红冲按钮;已结账明细与未结账入口均要求非空红冲原因。
## 验证命令
| 范围 | 命令 | 结果 | 说明 |
| :--- | :--- | :--- | :--- |
| 后端单测 | `mvn -pl sw-business/sw-business-server -am -Dtest=CounterSettleApplicationServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test` | 通过 | `CounterSettleApplicationServiceImplTest` 共 31 个用例通过,覆盖未结账红冲成功、混批拒绝、重复/并发拒绝、越权拒绝和红冲记录查询。 |
| 后端最终回归 | `mvn -pl sw-business/sw-business-server -am -Dtest=CounterSettleApplicationServiceImplTest,PaymentRecordServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test` | 通过 | 共 50 个用例通过,其中柜台结账 32 个、支付记录 18 个。 |
| 前端依赖 | `pnpm install` | 通过 | 前端 worktree 初始缺少 `node_modules`,安装依赖后继续验证。 |
| 前端类型检查 | `pnpm ts:check` | 未通过 | 首次执行因 Node 默认 4GB 堆内存 OOM 退出。 |
| 前端类型检查 | `NODE_OPTIONS=--max-old-space-size=8192 pnpm ts:check` | 未通过 | 全仓存在既有 TS 基线错误,主要为自动导入类型未识别和无关业务类型问题。 |
| 前端局部过滤 | `NODE_OPTIONS=--max-old-space-size=8192 pnpm ts:check 2>&1 \| rg "counterCheckout\|counterSettle"` | 通过本次改动检查 | 过滤后仅剩未触碰的 `CheckoutTopSummary.vue``CounterSettleConfirmDialog.vue``CounterSettledPanel.vue` 自动导入基线错误;本次触碰的 `CounterUnsettledPanel.vue``CounterSettledDetailDialog.vue``counterCheckout/index.vue``counterSettle.ts` 无新增错误。 |
## 结论
柜台未结账正式红冲已完成后端最小闭环验证。红冲后原收费支付主单不再进入待结账候选,营业账恢复未收费,红冲记录查询可追溯未结账红冲投影。前端入口已补齐,但全仓 `vue-tsc` 仍受既有自动导入类型基线影响,需要另行治理。

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,142 @@
# 柜台未结账正式红冲设计
## 背景
当前 `water-backend` 已实现柜台已结账红冲:`POST /admin-api/business/charge/counter-settle/red-flush` 先查 `biz_settle_record_detail`,再校验支付主单 `settleStatus=SETTLED`,因此只能处理已结清记录。历史操作手册要求柜台结账模块的“未结账”和“已结账”页签均可红冲。
本设计补齐“未结账正式红冲”能力。用户已确认业务口径:未结账红冲也按正式红冲处理;红冲后原收费记录不再参与后续柜台结账汇总,只能通过红冲记录查询和导出追溯。
## 目标
- 支持对未结账柜台收费记录执行正式红冲。
- 生成正式反向支付流水,保留原收费主单与反向流水关联。
- 红冲后原收费主单从待结清列表消失,后续结账确认不再统计。
- 红冲记录查询和导出能同时覆盖已结账红冲与未结账红冲。
- 保留红冲原因、经办人、红冲时间、客户、金额、原收费单号等审计字段。
## 非目标
- 不把未结账红冲伪造成普通结账单。
- 不改变已结账红冲的现有业务语义。
- 不在本次设计中扩展发票红冲、渠道退款、银行对账红冲等其他场景。
- 不修改 `.specify/` 流程工件。
## 推荐方案
采用“新增未结账正式红冲路径 + 独立红冲投影”的方案。
已结账红冲继续复用现有 `biz_settle_record` / `biz_settle_record_detail` 路径。未结账红冲不创建普通结账单,而是在支付主单和账单完成红冲状态流转后,写入一条未结账红冲投影记录。红冲记录查询将已结账明细和未结账投影合并返回。
不推荐先生成“红冲专用结清单”,因为这会让未结账业务产生结账单号,污染结账汇总、打印和财务日结语义。
## 接口设计
继续使用现有接口:
- `POST /admin-api/business/charge/counter-settle/red-flush`
请求对象 `CounterRedFlushReqVO` 保持兼容:
- `paymentRecordIds`:待红冲支付主单 ID 列表。
- `reason`:红冲原因。
- `operatorId`:兼容字段,实际操作人以当前登录用户为准。
接口行为按支付主单状态分流:
- `SETTLED` 且存在 `biz_settle_record_detail`:走现有已结账红冲逻辑。
- `UNSETTLED``settleId=null`:走新增未结账正式红冲逻辑。
- 同一批次中不建议混合已结账和未结账记录;若混合会增加事务回滚和结果解释复杂度,推荐直接拒绝并提示分批操作。
## 数据设计
新增未结账红冲投影表,建议命名为 `biz_counter_unsettled_red_flush_record`
核心字段:
- `id`
- `payment_record_id`:原收费支付主单 ID唯一约束。
- `payment_no`:原收费单号。
- `reverse_payment_record_id`:反向支付主单 ID。
- `reverse_payment_no`:反向支付单号。
- `charge_id`
- `source_cust_id`
- `source_cust_code`
- `source_cust_name`
- `cashier_id`
- `reversed_amount`
- `reversed_time`
- `reverse_reason`
- `proc_person`
- `proc_type`:固定为 `COUNTER_UNSETTLED_RED_FLUSH`
- `tenant_id``creator``create_time``updater``update_time``deleted`
约束:
- `payment_record_id + tenant_id` 唯一,防止重复红冲。
- 按 `cashier_id + reversed_time``source_cust_code + reversed_time` 建普通索引,支撑红冲记录查询。
## 流程设计
未结账红冲流程:
1. 根据 `paymentRecordIds` 查询支付主单。
2. 校验每条记录均为柜台收费来源、收入方向、收费业务场景、`settleStatus=UNSETTLED``settleId=null`
3. 校验当前登录收费员只能红冲自己的收费记录;具备管理权限的后台角色可按既有权限模型扩展。
4. 查询关联账单 `ChargeDO`,要求账单仍能与该收费主单匹配。
5. 调用现有 `PaymentCommandApplicationService.reverseChargePayment(charge)` 生成反向流水。
6. 将原支付主单更新为 `settleStatus=REVERSED`,并要求更新条件仍是 `UNSETTLED``settleId=null`
7. 将账单恢复到可再次收费状态,状态规则与现有收费红冲保持一致。
8. 写入 `biz_counter_unsettled_red_flush_record`
9. 返回红冲笔数、现金退回金额、预存抵扣退回金额等汇总结果。
待结清列表继续只查询 `settleStatus=UNSETTLED``settleId=null` 的收入主单。原收费主单改为 `REVERSED` 后自然不再出现,也不会被结清确认统计。
## 红冲记录查询
现有红冲记录查询从 `biz_settle_record_detail.detail_status=REVERSED` 读取已结账红冲记录。新增后查询逻辑改为合并两个来源:
- 已结账红冲:沿用现有 `biz_settle_record` + `biz_settle_record_detail`
- 未结账红冲:读取 `biz_counter_unsettled_red_flush_record`
响应 VO 可保持兼容:
- 未结账红冲的 `settleId``settleNo` 为空。
- `paymentRecordId``paymentNo`、客户、收费员、红冲金额、红冲时间、红冲原因正常返回。
导出逻辑使用同一合并查询,确保页面查询和 Excel 导出一致。
## 异常处理
- 支付主单不存在:拒绝。
- 已红冲:拒绝重复处理。
- 已结账与未结账记录混批:拒绝,提示分批红冲。
- 未结账记录已被其他事务结清:更新原支付主单为 `REVERSED` 时影响行数为 0拒绝并回滚。
- 未结账记录已被其他事务红冲:唯一约束或状态更新失败,拒绝并回滚。
- 反向流水生成失败:拒绝并回滚,不写红冲投影。
## 测试设计
后端单测覆盖:
- 未结账柜台收费红冲成功:生成反向流水、原主单改为 `REVERSED`、写红冲投影、待结清不再出现。
- 未结账红冲记录查询和导出可查到记录,且 `settleNo` 为空。
- 已结账红冲现有行为不回归。
- 已结账和未结账混批被拒绝。
- 重复红冲被拒绝。
- 当前收费员红冲他人未结账收费被拒绝。
- 并发结账或并发红冲导致状态更新失败时回滚。
最小验证建议:
- `mvn -pl sw-business/sw-business-server -Dtest=CounterSettleApplicationServiceImplTest test`
- 如涉及 mapper SQL 或集成数据,补充受环境变量控制的柜台结账接口集成测试。
## 文档与证据
实现后需要回写:
- `docs/design/02_Detailed_Design/12_REV_Detailed.md`:明确未结账红冲与已结账红冲的状态边界。
- `docs/design/03_Technical_Design/01_Database_Design.md`:补充未结账红冲投影表。
- `docs/design/03_Technical_Design/03_Interface_Design.md`:补充 `counter-settle/red-flush` 对未结账记录的处理约束。
- `docs/evidence/bugfix/` 或对应模块 evidence记录编译、单测、最小 smoke 结果。