From dbadd1b685439dfa80cbd6202121889ca83763dc Mon Sep 17 00:00:00 2001 From: jw Date: Thu, 17 Sep 2026 15:26:05 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog-v2):=20#7398=20=E5=BA=94?= =?UTF-8?q?=E4=BB=98=E6=AC=BE=E5=B7=B2=E4=BB=98=E8=B6=85=E9=A2=9D=E5=86=B2?= =?UTF-8?q?=E6=AD=A3=EF=BC=88=E4=BE=9B=E5=BA=94=E5=95=86=E9=80=80=E5=9B=9E?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94=E7=94=B3=E8=AF=B7=20/=20=E5=88=86?= =?UTF-8?q?=E6=AC=A1=E7=A1=AE=E8=AE=A4=E5=88=B0=E8=B4=A6=20/=20=E6=92=A4?= =?UTF-8?q?=E9=94=80=E5=89=A9=E4=BD=99=E9=A2=9D=E5=BA=A6=20/=20=E5=88=86?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- ...¾已付超额冲正-供应商退回-新增接口-管理后台.md | 516 ++++++++++++++++++ 1 file changed, 516 insertions(+) create mode 100644 changelogs-v2/2026-09/17_7398_应付款已付超额冲正-供应商退回-新增接口-管理后台.md diff --git a/changelogs-v2/2026-09/17_7398_应付款已付超额冲正-供应商退回-新增接口-管理后台.md b/changelogs-v2/2026-09/17_7398_应付款已付超额冲正-供应商退回-新增接口-管理后台.md new file mode 100644 index 00000000..11f6e68e --- /dev/null +++ b/changelogs-v2/2026-09/17_7398_应付款已付超额冲正-供应商退回-新增接口-管理后台.md @@ -0,0 +1,516 @@ +--- +schema: "hl-changelog/v2" +ticket: "7398" +title: "应付款已付超额冲正(供应商退回):申请 / 分次确认到账 / 撤销剩余额度 / 分页" +consumer: "admin" +author: "jw(GIT)" +change_type: "新增接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "后端交付(hl-finance,随 hl-order-service-v3 部署)。新增 4 个冲正接口与错误码 598814/598815/598816/598817/598818;付款明细新增 REVERSAL 负明细语义;资金流水新增 bizType=PAYMENT_REVERSAL。网关前缀 /admin/finance/** 已存在,无需新路由。前端需新增冲正申请与出纳确认到账两个界面。" +updated_at: "2026-09-17" +base: "dev-v3" +--- + +# finance: 应付款已付超额冲正(供应商退回) + +> **存放目录**: changelogs-v2/{YYYY-MM}/(管理后台,二期 finance) +> +> **服务**: hl-order-service-v3(hl-finance 模块同进程) +> **PR**: #7889 +> **Issue**: #7398 +> **日期**: 2026-09-17 +> **影响范围**: 应付款团期住宿付款身份的「已付超出当前应付」处置(申请冲正 → 出纳登记到账 → 统计与建议自动对平) + +--- + +## 关键变化(给前端 mmg 的一句话) + +1. **598812 有出口了**:团期住宿某付款身份「已付 > 当前应付」时(统计 `status=OVERPAID`、建议行 `netPaidAmount > amount`),财务可发起**冲正申请**,额度 ≤ `netPaidAmount − amount`。 +2. **申请不改任何金额**:申请后统计、建议、再建单的结果都与申请前一样;**只有出纳确认到账后**才生效。 +3. **确认到账可分次**:酒店分几次退就登记几次,每次一条入账流水 + 一条负明细;剩下不再退的额度可以**撤销**,已登记的部分保留。 +4. **确认到账不看当前应付**:钱已经到了就必须能记进来,哪怕房又加回来、应付回升;应付回升产生的新欠付照常在建议里以差额行出现。 +5. **统计读法不变**:`paidAmount`(原始已付,不减少)/ `refundedAmount`(已确认退款)/ `netPaidAmount`(净已付)三字段 #7396 已有,冲正确认后自动变化。 +6. **凭证号必填且在同一申请内唯一**:重复提交同一次确认返回 598802,账户只入账一次。 + +--- + +## 一、背景 + +#7396 让团期住宿按付款身份 `{orderId}:{stayDate}:{hotelId}:{roomTypeId}` 做金额对账,但「钱已付出、应付降到已付之下」只能报 598812,没有处理终态。本次补上「供应商退回」形态:申请 = 批准冲正额度,出纳每登记一次真实到账,就记一条资金流水(入账)和一条挂在原付款单下的负明细,原付款单不做任何修改。「抵扣下次付款」形态依赖预付域,本次不做。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 申请冲正 | POST | `/admin/finance/payment-reversals` | 新增 | 对已付超额的团期住宿付款身份申请退回额度,落 PENDING | +| 2 | 确认本次到账 | PUT | `/admin/finance/payment-reversals/{id}/confirm` | 新增 | 出纳登记一次真实到账(可分次),记入账流水 + 负明细 | +| 3 | 撤销剩余额度 | PUT | `/admin/finance/payment-reversals/{id}/cancel` | 新增 | 作废未到账的剩余额度,已确认部分保留 | +| 4 | 冲正申请分页 | GET | `/admin/finance/payment-reversals` | 新增 | 按订单 / 供应商 / 状态过滤 | + +--- + +## 三、接口详情 + +### 1. 申请冲正 `POST /admin/finance/payment-reversals` + +**VO**: `PaymentReversalCreateReqVO → Result` + +#### 使用场景 + +建议行或按团统计显示某团期住宿付款身份已付超出当前应付(`status=OVERPAID` / `netPaidAmount > amount`),财务与酒店协商退回后发起申请。`sourceRefKey` 取建议行的 `sourceRefKey`,`orderId` 取建议行 / 统计行的 `orderId`。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| sourceRefType | Body | String | 是 | 本期仅 `GROUP_BATCH_STAY` | 来源类型 | +| sourceRefKey | Body | String | 是 | ≤96;格式 `{orderId}:{yyyy-MM-dd}:{hotelId}:{roomTypeId}`,其中订单须等于 orderId | 付款身份业务键 | +| orderId | Body | Long | 是 | - | 来源订单 ID | +| amount | Body | BigDecimal | 是 | ≥0.01,两位小数,≤ 净已付 − 当前应付 | 申请冲正额度 | +| reason | Body | String | 是 | ≤512,非空白 | 冲正原因 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data.id | String | 新冲正申请 ID(雪花 ID,字符串输出) | + +#### 请求示例 + +```json +{ + "sourceRefType": "GROUP_BATCH_STAY", + "sourceRefKey": "2100482262209380353:2027-01-05:2029926133256929282:2029944767501012994", + "orderId": 2100482262209380353, + "amount": 800.00, + "reason": "该户退团,酒店同意退回已付房费" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { "id": "2100483176424972289" }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无查询数据。申请成功后不产生资金流水、不写付款明细;统计、建议、再建单的结果与申请前逐字段相同(再建单仍 598812)。 + +#### 错误响应 + +```json +{ + "code": 598814, + "message": "冲正金额超出可冲正额 0.00 元(已付超出当前应付的部分)", + "data": null, + "success": false +} +``` + +| code | 触发 | +|------|------| +| 400 | 必填缺失 / amount ≤ 0 / 长度超限 | +| 598808 | sourceRefType 不是 GROUP_BATCH_STAY,或 sourceRefKey 格式非法 / 与 orderId 不一致 | +| 598814 | amount > 净已付 − 当前应付(未超额时可冲正额为 0;已全额退回后再申请同样命中) | +| 598815 | 同一付款身份已有未完结申请(PENDING / PARTIAL) | +| 100503 | 同订单付款写操作正在进行,稍后重试 | + +#### 业务边界 + +- 只处理「已付 > 当前应付」;「未付占用超额」仍走 #7396 的删除 / 驳回 / 撤销批准(598813)。 +- 冲正挂在该付款身份下最近一次付讫的原付款单上;原付款单金额、状态、付款时间不变。 +- 同一付款身份同时只允许一张未完结申请。 +- 与付款单创建 / 编辑共用同一把订单锁,并发时串行执行。 + +--- + +### 2. 确认本次到账 `PUT /admin/finance/payment-reversals/{id}/confirm` + +**VO**: `PaymentReversalConfirmReqVO → Result` + +#### 使用场景 + +出纳收到酒店退款回单后登记。酒店分几次退就调几次,每次填本次实际到账金额与入账的公司账户。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | 是 | - | 冲正申请 ID | +| amount | Body | BigDecimal | 是 | ≥0.01,两位小数 | 本次实际到账金额 | +| fundAccountId | Body | Long | 是 | 须为启用中的公司资金账户 | 入账账户(不会用原付款账户兜底) | +| receivedDate | Body | String | 是 | `yyyy-MM-dd` | 到账日期(写入流水备注) | +| voucherNo | Body | String | 是 | ≤64,非空白;同一申请内唯一 | 银行回单 / 凭证号 | +| voucherUrl | Body | String | 否 | ≤500 | 凭证影像 URL(写入流水) | +| remark | Body | String | 否 | ≤200 | 备注(写入流水备注) | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | Boolean | 恒 true | + +#### 请求示例 + +```json +{ + "amount": 400.00, + "fundAccountId": 2100482405474148354, + "receivedDate": "2026-09-17", + "voucherNo": "HD-20260917-001", + "voucherUrl": "https://oss.example.com/voucher/001.png", + "remark": "酒店财务转账" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": true, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无查询数据。成功后:申请 `confirmedAmount` 累加、状态变 PARTIAL(未满额)或 CONFIRMED(满额);资金流水新增一条 `direction=IN`、`bizType=PAYMENT_REVERSAL`、`bizId=冲正申请ID`;付款明细新增一条 `itemKind=REVERSAL`、金额为负的明细。 + +#### 错误响应 + +```json +{ + "code": 598817, + "message": "确认到账金额超出冲正申请剩余额度 0.00 元", + "data": null, + "success": false +} +``` + +```json +{ + "code": 595006, + "message": "账户已停用", + "data": null, + "success": false +} +``` + +| code | 触发 | +|------|------| +| 400 | 必填缺失(含 fundAccountId / voucherNo / receivedDate)/ amount ≤ 0 | +| 598818 | 冲正申请不存在 | +| 598802 | 申请已撤销;或同一申请下该凭证号已登记(重复提交);或并发下状态已被推进 | +| 598817 | 累计到账超过申请额度(含已全额确认后再确认) | +| 598816 | 本次到账超过该付款身份当前净已付 | +| 595001 / 595006 | 入账账户不存在 / 已停用 | +| 100503 | 同订单付款写操作正在进行,稍后重试 | + +#### 业务边界 + +- **不复核当前应付**:房务把房加回、应付回升后仍可登记到账;由此产生的欠付在建议里 `diffAmount > 0`,按 #7396 规则再建单。 +- 任一校验失败整体回滚:不入账、不写明细、不累加额度。 +- 两次不同凭证号的到账并发提交会先后成功;同凭证号只会成功一次。 +- 入账账户余额按本次金额增加,原付款账户不变。 + +--- + +### 3. 撤销剩余额度 `PUT /admin/finance/payment-reversals/{id}/cancel` + +**VO**: `PaymentReversalCancelReqVO → Result` + +#### 使用场景 + +酒店明确不再退款(或应付已恢复、钱没有退)时,作废申请剩余未到账的额度。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| id | Path | Long | 是 | - | 冲正申请 ID | +| reason | Body | String | 是 | ≤512,非空白 | 撤销原因 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| data | Boolean | 恒 true | + +#### 请求示例 + +```json +{ + "reason": "酒店余款不再退回" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": true, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无查询数据。成功后状态变 CANCELLED,`confirmedAmount` 不变,已登记的流水与负明细不变。 + +#### 错误响应 + +```json +{ + "code": 598802, + "message": "应付单状态非法,当前状态不允许此操作", + "data": null, + "success": false +} +``` + +| code | 触发 | +|------|------| +| 400 | reason 缺失或空白 | +| 598818 | 冲正申请不存在 | +| 598802 | 申请已 CONFIRMED 或已 CANCELLED;或与确认并发时确认先完成 | + +#### 业务边界 + +- 仅 PENDING / PARTIAL 可撤销;已全额确认的申请不能撤销(已发生的资金事实只能另行红冲,本期不提供)。 +- 与确认并发时只有一个成功。 + +--- + +### 4. 冲正申请分页 `GET /admin/finance/payment-reversals` + +**VO**: `PaymentReversalPageReqVO → Result>` + +#### 使用场景 + +冲正申请列表 / 出纳待确认列表(`status=PENDING` 或 `PARTIAL`)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| orderId | Query | Long | 否 | - | 订单 ID | +| supplierId | Query | Long | 否 | - | 供应商 ID | +| status | Query | String | 否 | PENDING / PARTIAL / CONFIRMED / CANCELLED | 状态 | +| page | Query | Integer | 否 | ≥1,默认 1 | 页码 | +| pageSize | Query | Integer | 否 | 1~100,默认 20 | 每页条数 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| records[].reversalId | String | 冲正申请 ID | +| records[].reversalNo | String | 冲正单号(RF-yyyyMMdd####) | +| records[].sourceRefType | String | 来源类型 | +| records[].sourceRefKey | String | 付款身份业务键 | +| records[].orderId | String | 订单 ID | +| records[].teamNo | String | 团号快照 | +| records[].supplierId | String | 供应商 ID | +| records[].supplierName | String | 供应商全称快照 | +| records[].paymentId | String | 被冲的原付款单 ID | +| records[].amount | BigDecimal | 申请额度 | +| records[].confirmedAmount | BigDecimal | 累计已确认到账 | +| records[].remainingAmount | BigDecimal | 剩余可确认额度(CANCELLED / CONFIRMED 为 0) | +| records[].status | String | PENDING / PARTIAL / CONFIRMED / CANCELLED | +| records[].reason | String | 冲正原因 | +| records[].appliedBy | String | 申请人 ID | +| records[].appliedByName | String | 申请人姓名 | +| records[].cancelReason | String | 撤销原因 | +| records[].cancelledBy | String | 撤销人 ID | +| records[].cancelledAt | String | 撤销时间 | +| records[].createTime | String | 申请时间 | +| total / page / pageSize | Long / Integer / Integer | 分页信息 | + +#### 请求示例 + +```http +GET /admin/finance/payment-reversals?orderId=2100482262209380353&status=CONFIRMED&page=1&pageSize=20 +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "records": [ + { + "reversalId": "2100483176424972289", + "reversalNo": "RF-202609170002", + "sourceRefType": "GROUP_BATCH_STAY", + "sourceRefKey": "2100482262209380353:2027-01-05:2029926133256929282:2029944767501012994", + "orderId": "2100482262209380353", + "supplierId": "2096854417461403650", + "paymentId": "2100482264935604226", + "amount": 800.00, + "confirmedAmount": 800.00, + "remainingAmount": 0, + "status": "CONFIRMED", + "reason": "#7398 验收冲正" + } + ], + "total": 1, + "page": 1, + "pageSize": 20 + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +```json +{ + "code": 200, + "message": "成功", + "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 }, + "success": true +} +``` + +#### 错误响应 + +```json +{ + "code": 400, + "message": "每页条数最大为100", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 按申请 ID 倒序(最新在前)。 +- 单次到账明细(入账账户、金额、时间、凭证、经办人)在资金流水查询中按 `bizType=PAYMENT_REVERSAL`、`bizId=reversalId` 查看。 + +--- + +## 四、契约约束与正确调用方式 + +- 申请的 `sourceRefKey` / `orderId` 必须原样取自建议行或统计行,不要前端拼接。 +- 可冲正额 = 建议行 `netPaidAmount − amount`(≤0 时不应展示申请入口)。 +- 确认到账时 `amount` 填**本次**实际到账金额,不是申请额;分次到账逐次调用,每次换一个凭证号。 +- 所有 ID 按字符串处理,避免精度丢失。 +- 统计与建议无需新接口:确认后 `refundedAmount` 增加、`netPaidAmount` 减少,`paidAmount` 不变。 + +--- + +## 五、数据库行为 + +| 表 | 动作 | 说明 | +|---|---|---| +| `fin_payment_reversal` | 新建表(V20260917_114) | 冲正申请:amount 额度、confirmed_amount 累计到账、status 四态;申请 INSERT,确认 / 撤销按旧值条件更新 | +| `fin_payment_item` | 新增列 `reversal_id` + 索引 `idx_reversal`(V20260917_114) | 确认时 INSERT 一条 `item_kind=REVERSAL`、金额为负、`payment_id`=原付款单、`reversal_id`=申请、`remark`=凭证号 | +| `fin_fund_flow` | INSERT | 确认时一条 `direction=IN`、`biz_type=PAYMENT_REVERSAL`、`biz_id`=申请 ID | +| `fin_fund_account` | UPDATE balance | 入账账户余额 + 本次金额 | +| `fin_payment` | 不变 | 原付款单任何字段不改 | + +--- + +## 六、边界行为 + +- 申请后、到账前:金额口径完全不变,再建单仍 598812。 +- 到账后应付回升:确认照常成功,建议 `diffAmount` 出现正差额,按 #7396 建单。 +- 部分到账后剩余不退:调撤销;统计中已退部分保留。 +- 合并付款单(一张付多户):冲正只作用于申请的那一户,负明细 `orderId` 为该户,挂在合并付款单下;另一户统计不变。 +- 已全额确认后不可撤销、不可再确认(598802 / 598817)。 + +## 六.5、枚举 + +### 冲正申请状态(status) + +| 值 | 含义 | +|---|---| +| PENDING | 待到账 | +| PARTIAL | 部分到账 | +| CONFIRMED | 全额到账 | +| CANCELLED | 已撤销剩余额度 | + +### 新增取值 + +| 字段 | 新值 | 含义 | +|---|---|---| +| 付款明细 `itemKind` | REVERSAL | 冲正负明细(金额为负) | +| 资金流水 `bizType` | PAYMENT_REVERSAL | 应付款冲正入账 | + +--- + +## 七、不影响范围 + +- 付款单创建 / 编辑 / 提交 / 批准 / 驳回 / 撤销批准 / 出纳付款的入参出参与判据不变。 +- 建议与统计接口结构不变(相关字段 #7396 已提供)。 +- 非团期住宿来源(行程节点 / 配房 / 手工)不支持冲正。 +- 前端:新增「冲正申请」(统计 / 建议超额行入口)与「出纳确认到账」两个界面,由 mmg 承接。 + +--- + +## 八、测试环境已验证 + +TEST(`https://api.test.1814.love:9443`,`hl-order-service-v3` = **dev-v3@977257515** 合并提交之后的构建;真实网关 + 管理员 token),2026-09-17: + +| 场景 | 结果 | +|---|---| +| 迁移 | `V20260917_114` 执行成功;`fin_payment_item.reversal_id` 与 `idx_reversal` 存在;新表不含账户 / 到账日 / 凭证列 | +| 真实取消户闭环 | 已付 800、订单已取消应付 0:申请前后建单均 598812;申请 800 → 确认 800 → 入账流水 800、负明细 −800、Σ=0、统计 PAID、建议 overpaid 归零;原付款单逐字段不变 | +| 申请不生效 | 申请后付款明细零新增,建议 / 统计 / 再建单结果与申请前逐字段相同 | +| 入账账户 | 入 Y:Y +800、原付款账户 X 不变;fundAccountId 为空 → 400;停用账户 → 595006 且零流水零明细;重复提交同凭证 → 598802、余额只加一次 | +| 上界 | 超额申请 598814;重复申请 598815;全额确认后再申请 598814、再确认 598817;净已付不足时确认 598816(零流水零明细);已确认 400 再确认 400 → 200 | +| 分次到账 | 800 分两次 400:PARTIAL → CONFIRMED,两条流水两条负明细,统计 800/400/400 → 800/800/0 | +| 应付回升 | 申请后未到账、应付恢复 → 撤销 200、零流水、再建单 598809;已到账、应付恢复 → 确认 200、统计 OWED 欠付 800、差额单 800 → 200;部分到账叠加回升同样可确认,剩余可撤销 | +| 退后再建 | 退完后应付变 400 → 四入口差额 400 → 建单提交后统计欠付 400、在途 400、OWED → 再建 598809 → 付款后 PAID | +| 合并付款 | 1600 合并单只退户 1:户 1 800/800/0、户 2 800/0/800;负明细挂 1600 主单、订单为户 1;供应商统计已退 +800 | +| 并发 | 同申请并发两次确认 400 均成功、流水恰 2;三次并发恰 2 成功 1 次 598817;并发确认与申请 → 申请 598815;撤销与确认并发 8 轮均恰一方成功(PENDING / PARTIAL 两个分支、CANCELLED / CONFIRMED 两个终态都出现),撤销后已确认部分保留 | + +``` +POST /admin/finance/payment-reversals → 200 {"id":"2100483176424972289"} +PUT /admin/finance/payment-reversals/2100483176424972289/confirm → 200 true +PUT /admin/finance/payment-reversals/2100483176424972289/confirm(同凭证)→ 598802 +PUT /admin/finance/payment-reversals/2100483176424972289/cancel → 598802 +``` + +逐条验收记录见工单 #7398 验收评论。 + +--- + +## 十、相关文档 + +- Issue: [#7398](https://git.1814.love:8443/wx/HL/issues/7398) +- PR: [#7889](https://git.1814.love:8443/wx/HL/pulls/7889) +- 前置: [#7396](https://git.1814.love:8443/wx/HL/issues/7396)(付款身份与金额对账) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7398](https://git.1814.love:8443/wx/HL/issues/7398) +- **PR**: [#7889](https://git.1814.love:8443/wx/HL/pulls/7889) +- **关联**: #7396(598812 的来源)、#7325(团期单户退团 / 取消) + +### 联系人 + +- **后端负责人**: @jw +- **前端负责人**: @mmg