changelog-filename-gate / validate (push) Failing after 2s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1141 行
42 KiB
Markdown
1141 行
42 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "7396"
|
||
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 个应付款读接口返回扩大(新 sourceType=GROUP_BATCH_STAY、sourceRefKey、金额对账字段、统计新字段与 OVERPAID 状态),3 个创建口新增入参 sourceRefKey 与 598812/598813 两个新错误码,草稿编辑口新增归属与额度守卫,新增 PUT /admin/finance/payments/{id}/revoke-approval。网关前缀 /admin/finance/payments 已存在,无需新路由。前端需接新 sourceType / 新字段 / 新状态 / 新错误码 / 撤销批准按钮。"
|
||
updated_at: "2026-09-17"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# finance: 应付款四入口接团期住宿 + 团期住宿付款身份金额对账 + 撤销批准
|
||
|
||
> **存放目录**: changelogs-v2/{YYYY-MM}/(管理后台,二期 finance)
|
||
>
|
||
> **服务**: hl-order-service-v3(hl-finance 模块同进程)
|
||
> **PR**: #7866
|
||
> **Issue**: #7396
|
||
> **日期**: 2026-09-17
|
||
> **影响范围**: 应付款建议(按订单 / 按供应商)、应付款统计(按团 / 按供应商)、付款单创建(单笔 / 按团批量 / 按供应商批量)、草稿编辑;新增撤销批准
|
||
|
||
---
|
||
|
||
## 关键变化(给前端 mmg 的一句话)
|
||
|
||
1. **团期住宿进应付款了**:团期房务(整团订房 + 系统分房到户)的酒店应付,现在在四个入口都能看到,行的 `sourceType` 为 **`GROUP_BATCH_STAY`**,并带 **`sourceRefKey`**(付款身份业务键)。勾选建单时 **`sourceRefKey` 必须原样回传**,`sourceRefId` 回传行上的 `sourceId`。
|
||
2. **团期住宿按金额对账防重**:同一付款身份(同订单、同晚、同酒店、同房型)已建单后,建议行 `alreadyGenerated=true`;若应付金额变了,`amountChanged=true`、`diffAmount` 给出差额(正数可再建差额行,负数需先删除 / 驳回 / 撤销批准或等冲正)。
|
||
3. **新增两个错误码**:`598812`(已付超出当前应付,需财务冲正)、`598813`(未付占用超出当前应付,提示里列出需处理的单号与状态)。
|
||
4. **统计口径变化**:已申请 / 已付改为按付款明细聚合;新增 `refundedAmount` / `netPaidAmount` / `overpaidAmount` / `diffAmount`;`owedAmount` 不再为负;状态新增 **`OVERPAID`**;按供应商统计新增 `unattributedPaidAmount`。
|
||
5. **新增撤销批准** `PUT /admin/finance/payments/{id}/revoke-approval`:已批准未付款的单回到待提交草稿,之后可删除或编辑重提。
|
||
6. **编辑草稿收紧**:有业务来源的草稿禁止改供应商 / 订单(598808);多明细草稿金额必须等于明细合计(598805);团期住宿草稿改金额受额度守卫(598812 / 598813)。
|
||
|
||
---
|
||
|
||
## 一、背景
|
||
|
||
团期房务把住宿从「逐户配房」改成「整团按日订房 + 系统分房到户」后,应付款的三个批量入口(按供应商建议、按团统计、按供应商统计)只读旧配房表,团期住宿应付在这三处恒为 0;按订单建议也没有接入。同时团期分房行 ID 会在人工微调时换新,原「按来源主键判重」可被绕过而重复申请。本次:四入口共用同一读取层;团期住宿以业务键 `{orderId}:{stayDate}:{hotelId}:{roomTypeId}` 作付款身份,并按金额对账防重;统计改从付款明细聚合,已付款不会再算出负欠付。
|
||
|
||
---
|
||
|
||
## 二、变更接口清单
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---|------|------|------|----------|------|
|
||
| 1 | 撤销批准 | PUT | `/admin/finance/payments/{id}/revoke-approval` | 新增 | APPROVED → PENDING,条件更新防并发,记审核流水 UN_APPROVE |
|
||
| 2 | 应付款建议(按订单) | GET | `/admin/finance/payments/suggestions` | 修改 | 新增团期住宿行与孤儿行;新增 sourceRefKey 与金额对账字段 |
|
||
| 3 | 应付款建议(按供应商跨团) | GET | `/admin/finance/payments/suggestions/by-supplier` | 修改 | 同上;已取消但仍有活跃团期占用的订单并入候选 |
|
||
| 4 | 应付款统计(按团) | GET | `/admin/finance/payments/stats/by-team` | 修改 | 该付含团期住宿;已申请/已付改按明细聚合;新字段与 OVERPAID |
|
||
| 5 | 应付款统计(按供应商) | GET | `/admin/finance/payments/stats/by-supplier` | 修改 | 同上;新增 unattributedPaidAmount |
|
||
| 6 | 申请付款(单笔) | POST | `/admin/finance/payments` | 修改 | 新增入参 sourceRefKey;带来源时 orderId 必填;团期住宿金额对账 |
|
||
| 7 | 勾选批量生成(按团) | POST | `/admin/finance/payments/batch-create` | 修改 | 明细新增 sourceRefKey;团期住宿金额对账 |
|
||
| 8 | 按供应商跨团合并建单 | POST | `/admin/finance/payments/batch-create-by-supplier` | 修改 | 明细新增 sourceRefKey;团期住宿金额对账 |
|
||
| 9 | 编辑付款草稿 | PUT | `/admin/finance/payments/{id}` | 修改 | 有来源草稿禁改供应商/订单;多明细金额守恒;团期住宿额度守卫 |
|
||
|
||
---
|
||
|
||
## 三、接口详情
|
||
|
||
### 1. 撤销批准 `PUT /admin/finance/payments/{id}/revoke-approval`
|
||
|
||
**VO**: `PaymentRevokeApprovalReqVO → Result<Void>`
|
||
|
||
#### 使用场景
|
||
|
||
付款单已批准(APPROVED)、出纳尚未付款时,因应付金额减少等原因需要改单:先撤销批准回到草稿(PENDING),再删除或编辑后重新提交。出纳并发付款时两者只会有一个成功。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| id | Path | Long | 是 | - | 付款单 ID |
|
||
| reason | Body | String | 否 | ≤512 字符;请求体整体可省略 | 撤销原因,记入审核流水 opinion |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| code | Integer | 200 成功 |
|
||
| data | Void | 恒为 null |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"reason": "团期减房 1 间,撤销后改单"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": null,
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无查询数据;成功后 `GET /admin/finance/payments/{id}/review-logs` 追加一条 `action=UN_APPROVE`、`fromStatus=APPROVED`、`toStatus=PENDING` 的记录。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 598802,
|
||
"message": "应付单状态非法,当前状态不允许此操作",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598801,
|
||
"message": "应付单不存在",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 仅 `APPROVED` 可撤销;`PENDING` / `SUBMITTED` / `REJECTED` / `PAID` 一律 598802。
|
||
- 条件更新(仅当状态仍为 APPROVED 才改):与出纳付款并发时恰好一个成功,已付款的单绝不会被改回草稿。
|
||
- 撤销后单据退出出纳待付款队列;回到 PENDING 后可删除(`DELETE /admin/finance/payments/{id}`)或编辑后重新提交。
|
||
- 同事务写审核流水,流水写失败整体回滚。
|
||
- 权限口径与批准接口一致。
|
||
|
||
---
|
||
|
||
### 2. 应付款建议(按订单) `GET /admin/finance/payments/suggestions`
|
||
|
||
**VO**: `PaymentSuggestionRespVO{orderId, rows: List<PaymentSuggestionRowVO>}`
|
||
|
||
#### 使用场景
|
||
|
||
填单页按订单拉「该付给供应商」的建议行,勾选后带入创建。本次新增团期住宿行(`sourceType=GROUP_BATCH_STAY`),以及「应付已消失但仍有付款占用」的孤儿行。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| orderId | Query | Long | 是 | - | 订单 ID |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| orderId | Long | 订单 ID |
|
||
| rows[].sourceType | String | `NODE` 行程节点 / `HOTEL_ASSIGNMENT` 配房 / **`GROUP_BATCH_STAY` 团期住宿(新增取值)** |
|
||
| rows[].sourceId | Long | 来源主键;团期住宿为该付款身份下最小分房行 ID,仅定位用 |
|
||
| rows[].sourceRefKey | String | **新增**。仅团期住宿有值:`{orderId}:{yyyy-MM-dd}:{hotelId}:{roomTypeId}`,建单时原样回传 |
|
||
| rows[].resourceId | Long | 资源 ID(团期住宿为酒店 ID) |
|
||
| rows[].resourceName | String | 资源名(团期住宿为 酒店名/房型名) |
|
||
| rows[].quantity | Integer | 数量(团期住宿为该晚该房型分到本户的间数合计) |
|
||
| rows[].unitPrice | BigDecimal | 单价(团期住宿为结算价 元/间·晚;同身份单价不一致时为 null) |
|
||
| rows[].amount | BigDecimal | 当前应付(团期住宿 = Σ 结算价 × 间数;孤儿行为 0) |
|
||
| rows[].paymentMethod | String | `SIGNED` / `COMPANY_PAID` |
|
||
| rows[].paymentType | String | 付款类型字典标签预填(团期住宿同配房 = 住宿) |
|
||
| rows[].supplierId | Long | 预填供应商(按酒店反查) |
|
||
| rows[].supplierName | String | 供应商全称 |
|
||
| rows[].payeeAccountId | Long | 默认收款账户 |
|
||
| rows[].eligible | boolean | 供应商是否可付款 |
|
||
| rows[].eligibleReason | String | 不可付款原因 |
|
||
| rows[].alreadyGenerated | boolean | 是否已有活跃付款占用(团期住宿按业务键判定) |
|
||
| rows[].paidAmount | BigDecimal | **新增**。已付原值(PAID 明细合计,不扣退款);仅团期住宿有值 |
|
||
| rows[].refundedAmount | BigDecimal | **新增**。已确认退款;仅团期住宿有值 |
|
||
| rows[].netPaidAmount | BigDecimal | **新增**。净已付 = paidAmount − refundedAmount;仅团期住宿有值 |
|
||
| rows[].unpaidAmount | BigDecimal | **新增**。未付占用(PENDING/SUBMITTED/APPROVED 明细合计,含草稿);仅团期住宿有值 |
|
||
| rows[].generatedAmount | BigDecimal | **新增**。已生成金额 = netPaidAmount + unpaidAmount;仅团期住宿有值 |
|
||
| rows[].amountChanged | Boolean | **新增**。已有活跃占用且已生成金额 ≠ 当前应付时为 true(未申请过的行恒 false,此时看 diffAmount 即可申请额);仅团期住宿有值 |
|
||
| rows[].diffAmount | BigDecimal | **新增**。当前应付 − 已生成金额(有符号);仅团期住宿有值 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /admin/finance/payments/suggestions?orderId=2100244780104425473
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"orderId": "2100244780104425473",
|
||
"rows": [
|
||
{
|
||
"sourceType": "GROUP_BATCH_STAY",
|
||
"sourceId": "2100244786240692225",
|
||
"sourceRefKey": "2100244780104425473:2026-11-18:2029956939232808961:2029956939333472258",
|
||
"resourceId": "2029956939232808961",
|
||
"resourceName": "测试酒店/标准间",
|
||
"quantity": 1,
|
||
"unitPrice": 519.00,
|
||
"amount": 519.00,
|
||
"paymentMethod": "COMPANY_PAID",
|
||
"paymentType": "住宿",
|
||
"supplierId": null,
|
||
"supplierName": null,
|
||
"payeeAccountId": null,
|
||
"eligible": false,
|
||
"eligibleReason": "资源未关联供应商,请手选",
|
||
"alreadyGenerated": false,
|
||
"paidAmount": 0,
|
||
"refundedAmount": 0,
|
||
"netPaidAmount": 0,
|
||
"unpaidAmount": 0,
|
||
"generatedAmount": 0,
|
||
"amountChanged": false,
|
||
"diffAmount": 519.00
|
||
}
|
||
]
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无应付行返回 `rows: []`。供应商反查 / 可付款判定 / 字典不可用时行仍返回,`eligible=false` 并给出 `eligibleReason`,不打塌整单。
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": { "orderId": "2100244780104425473", "rows": [] },
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "orderId 不能为空",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 团期住宿只取「计划行已确认 + 结算方式为签单/公司付款 + 结算价非空」且分房行有效的数据;现付(cash)不进应付。
|
||
- 团期启用前已冻结的历史旧户只出 `HOTEL_ASSIGNMENT` 行,不出团期住宿行,同订单不会两类并存。
|
||
- 孤儿行:某身份已有活跃付款占用但当前已无应付(如计划行被删)时,输出一行 `amount=0`、`alreadyGenerated=true`、`diffAmount` 为负。
|
||
- `NODE` / `HOTEL_ASSIGNMENT` 行的新增金额字段为 null,判重口径不变。
|
||
- 金额类字段均为数值(BigDecimal),ID 类字段为字符串。
|
||
|
||
---
|
||
|
||
### 3. 应付款建议(按供应商跨团) `GET /admin/finance/payments/suggestions/by-supplier`
|
||
|
||
**VO**: `SupplierSuggestionRespVO{supplierId, rows: List<SupplierSuggestionRowVO>}`
|
||
|
||
#### 使用场景
|
||
|
||
按供应商列出跨团欠付明细,勾选后走按供应商合并建单。本次新增团期住宿行(改前恒无)与孤儿行。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| supplierId | Query | Long | 是 | - | 供应商 ID |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| supplierId | Long | 供应商 ID |
|
||
| rows[] | SupplierSuggestionRowVO | 继承按订单建议行全部字段(含本次新增的 sourceRefKey 与金额对账字段) |
|
||
| rows[].orderId | Long | 来源订单 ID(建单时回传) |
|
||
| rows[].teamNo | String | 来源团号 |
|
||
| rows[].orderNo | String | 来源订单号 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /admin/finance/payments/suggestions/by-supplier?supplierId=88001
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"supplierId": "88001",
|
||
"rows": [
|
||
{
|
||
"orderId": "2100244780104425473",
|
||
"teamNo": "26-3509",
|
||
"orderNo": "HL2026091701",
|
||
"sourceType": "GROUP_BATCH_STAY",
|
||
"sourceId": "2100244786240692225",
|
||
"sourceRefKey": "2100244780104425473:2026-11-18:2029956939232808961:2029956939333472258",
|
||
"amount": 800.00,
|
||
"alreadyGenerated": true,
|
||
"paidAmount": 800.00,
|
||
"refundedAmount": 0,
|
||
"netPaidAmount": 800.00,
|
||
"unpaidAmount": 0,
|
||
"generatedAmount": 800.00,
|
||
"amountChanged": false,
|
||
"diffAmount": 0
|
||
}
|
||
]
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": { "supplierId": "88001", "rows": [] },
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "supplierId 不能为空",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 候选订单 = 未核单且未取消的订单,**另并入「已取消但仍有活跃团期住宿付款占用」的订单**;已取消订单不再计应付,只呈现已付与占用(孤儿行)。
|
||
- 只保留资源反查出的供应商等于入参的行;孤儿行按付款明细上的供应商过滤。
|
||
- 核单已完成的订单不进候选(按订单建议入口仍可见)。
|
||
|
||
---
|
||
|
||
### 4. 应付款统计(按团) `GET /admin/finance/payments/stats/by-team`
|
||
|
||
**VO**: `PaymentStatsByTeamReqVO → PageResult<PaymentStatsByTeamRowVO>`
|
||
|
||
#### 使用场景
|
||
|
||
按订单(团)看该付、已申请、已付、欠付。本次该付含团期住宿;已申请/已付改为按付款**明细**的订单归属聚合(按供应商合并付款的单能正确分摊到各团)。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| page | Query | Integer | 否 | ≥1,默认 1 | 页码 |
|
||
| pageSize | Query | Integer | 否 | 1-100,默认 20 | 每页条数 |
|
||
| keyword | Query | String | 否 | - | 团号 / 产品名模糊 |
|
||
| status | Query | String | 否 | `OWED` / `OVERPAID` / `PAID` | 状态筛选(页内过滤) |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| orderId | Long | 订单 ID |
|
||
| teamNo | String | 团号 |
|
||
| productName | String | 产品名 |
|
||
| customerName | String | 客人 |
|
||
| orderNo | String | 订单号 |
|
||
| departDate | LocalDate | 出团日期 |
|
||
| returnDate | LocalDate | 返团日期 |
|
||
| payableAmount | BigDecimal | 该付(**含团期住宿**;已取消孤儿订单为 0) |
|
||
| appliedAmount | BigDecimal | 已申请(SUBMITTED/APPROVED 明细合计,草稿不计) |
|
||
| paidAmount | BigDecimal | 已付原值(PAID 明细合计,不扣退款、永不减少) |
|
||
| owedAmount | BigDecimal | 欠付 = max(该付 − 净已付, 0),**不再为负** |
|
||
| refundedAmount | BigDecimal | **新增**。已确认退款 |
|
||
| netPaidAmount | BigDecimal | **新增**。净已付 = paidAmount − refundedAmount |
|
||
| overpaidAmount | BigDecimal | **新增**。已付超出 = max(净已付 − 该付, 0) |
|
||
| diffAmount | BigDecimal | **新增**。该付 − 净已付(有符号) |
|
||
| supplierCount | Integer | 供应商数 |
|
||
| status | String | `OWED` 有欠付 / **`OVERPAID` 已付超出待冲正(新增)** / `PAID` 已付清 |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /admin/finance/payments/stats/by-team?page=1&pageSize=20&keyword=26-3509
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"list": [
|
||
{
|
||
"orderId": "2100244780104425473",
|
||
"teamNo": "26-3509",
|
||
"payableAmount": 800.00,
|
||
"appliedAmount": 0,
|
||
"paidAmount": 800.00,
|
||
"owedAmount": 0,
|
||
"refundedAmount": 0,
|
||
"netPaidAmount": 800.00,
|
||
"overpaidAmount": 0,
|
||
"diffAmount": 0,
|
||
"supplierCount": 1,
|
||
"status": "PAID"
|
||
}
|
||
],
|
||
"total": 1,
|
||
"page": 1,
|
||
"pageSize": 20
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无候选订单返回空列表、`total=0`。供应商反查失败只影响 `supplierCount`,该付照常计入。
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": { "list": [], "total": 0, "page": 1, "pageSize": 20 },
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "每页条数最大为100",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 候选订单同按供应商建议(含已取消孤儿订单并入),在 SQL 层合并,`total` 与分页一致。
|
||
- 已付 / 已申请只看付款**明细**,付款主单金额不再参与聚合;按供应商合并付款的单按明细订单分摊到各团。
|
||
- 无订单归属的付款(如合并单的出纳差额行)不进按团统计,只进按供应商统计。
|
||
- `status` 筛选在页内过滤(与改前一致)。
|
||
|
||
---
|
||
|
||
### 5. 应付款统计(按供应商) `GET /admin/finance/payments/stats/by-supplier`
|
||
|
||
**VO**: `PaymentStatsBySupplierReqVO → PageResult<PaymentStatsBySupplierRowVO>`
|
||
|
||
#### 使用场景
|
||
|
||
按供应商看名下各团合计的该付、已申请、已付、欠付。本次该付含团期住宿;已申请/已付改按明细的供应商归属;新增无团归属已付额。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| page | Query | Integer | 否 | ≥1,默认 1 | 页码 |
|
||
| pageSize | Query | Integer | 否 | 1-100,默认 20 | 每页条数 |
|
||
| keyword | Query | String | 否 | - | 供应商名称模糊 |
|
||
| status | Query | String | 否 | `OWED` / `OVERPAID` / `PAID` | 状态筛选 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| supplierId | Long | 供应商 ID |
|
||
| supplierName | String | 供应商全称 |
|
||
| category | String | 类别标签(多类别以 / 拼接) |
|
||
| payableAmount | BigDecimal | 该付(**含团期住宿**) |
|
||
| appliedAmount | BigDecimal | 已申请(SUBMITTED/APPROVED 明细合计) |
|
||
| paidAmount | BigDecimal | 已付原值(PAID 明细合计) |
|
||
| owedAmount | BigDecimal | 欠付 = max(该付 − 净已付, 0) |
|
||
| refundedAmount | BigDecimal | **新增**。已确认退款 |
|
||
| netPaidAmount | BigDecimal | **新增**。净已付 |
|
||
| overpaidAmount | BigDecimal | **新增**。已付超出 |
|
||
| diffAmount | BigDecimal | **新增**。该付 − 净已付(有符号) |
|
||
| unattributedPaidAmount | BigDecimal | **新增**。已付但明细无订单归属的金额;恒等式:Σ 该供应商各团已付 + 本值 = paidAmount |
|
||
| teamCount | Integer | 涉及团数 |
|
||
| status | String | `OWED` / **`OVERPAID`(新增)** / `PAID` |
|
||
|
||
#### 请求示例
|
||
|
||
```http
|
||
GET /admin/finance/payments/stats/by-supplier?page=1&pageSize=20&keyword=呼籁
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"list": [
|
||
{
|
||
"supplierId": "88001",
|
||
"supplierName": "呼籁酒店",
|
||
"category": "住宿",
|
||
"payableAmount": 1600.00,
|
||
"appliedAmount": 0,
|
||
"paidAmount": 1700.00,
|
||
"owedAmount": 0,
|
||
"refundedAmount": 0,
|
||
"netPaidAmount": 1700.00,
|
||
"overpaidAmount": 100.00,
|
||
"diffAmount": -100.00,
|
||
"unattributedPaidAmount": 100.00,
|
||
"teamCount": 2,
|
||
"status": "OVERPAID"
|
||
}
|
||
],
|
||
"total": 1,
|
||
"page": 1,
|
||
"pageSize": 20
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": { "list": [], "total": 0, "page": 1, "pageSize": 20 },
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "页码最小为1",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 已取消孤儿订单的付款占用按明细供应商建桶后并入,已付不会被丢掉。
|
||
- 供应商行为内存聚合后分页,`total` = 筛选后供应商行数。
|
||
- `unattributedPaidAmount` 只来自已付款且明细无订单的行(例如按供应商合并单的存量出纳差额回填行)。
|
||
|
||
---
|
||
|
||
### 6. 申请付款(单笔) `POST /admin/finance/payments`
|
||
|
||
**VO**: `PaymentCreateReqVO → PaymentIdRespVO`
|
||
|
||
#### 使用场景
|
||
|
||
手工直填或从建议清单单条勾选建付款草稿。本次:团期住宿需回传 `sourceRefKey`;任何单笔创建都会同时写出一条付款明细(带来源写来源明细,手工直填写系统内部的 MANUAL 明细),占用即时生效。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| supplierId | Body | Long | 是 | - | 供应商 ID |
|
||
| payeeAccountId | Body | Long | 是 | 供应商生效账户 | 收款账户 |
|
||
| amount | Body | BigDecimal | 是 | >0 | 付款金额 |
|
||
| paymentType | Body | String | 是 | ≤32,字典标签 | 付款类型 |
|
||
| reason | Body | String | 是 | ≤512 | 付款事由 |
|
||
| teamNo | Body | String | 否 | ≤32 | 团号 |
|
||
| orderId | Body | Long | 条件必填 | 带来源时必填 | 关联订单 |
|
||
| resourceId | Body | Long | 否 | - | 关联资源 |
|
||
| sourceRefType | Body | String | 否 | `NODE` / `HOTEL_ASSIGNMENT` / `GROUP_BATCH_STAY`;**不可传 MANUAL** | 来源类型,须与 sourceRefId 成对 |
|
||
| sourceRefId | Body | Long | 否 | 与 sourceRefType 成对 | 来源主键(团期住宿传建议行 sourceId) |
|
||
| sourceRefKey | Body | String | 条件必填 | ≤96;`GROUP_BATCH_STAY` 必填且其中订单须等于 orderId;其余类型必须为空 | **新增**。建议行 sourceRefKey 原样回传 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| paymentId | Long | 新付款单 ID |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"supplierId": "88001",
|
||
"payeeAccountId": "90001",
|
||
"amount": 800.00,
|
||
"paymentType": "住宿",
|
||
"reason": "团期 26-3509 11/18 住宿",
|
||
"teamNo": "26-3509",
|
||
"orderId": "2100244780104425473",
|
||
"resourceId": "2029956939232808961",
|
||
"sourceRefType": "GROUP_BATCH_STAY",
|
||
"sourceRefId": "2100244786240692225",
|
||
"sourceRefKey": "2100244780104425473:2026-11-18:2029956939232808961:2029956939333472258"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": { "paymentId": "2100260000000000001" },
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无查询数据。供应商可付款判定 / 账户接口不可用时失败关闭(598803 / 598804),不落任何数据。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 598809,
|
||
"message": "该来源配置已生成付款单,请勿重复生成",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598813,
|
||
"message": "未付占用超出当前应付 800.00 元,请先删除 / 驳回 / 撤销批准:FK-202609170003(APPROVED)",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598812,
|
||
"message": "已付金额超出当前应付 400.00 元,需财务冲正",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598808,
|
||
"message": "来源配置引用非法",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 团期住宿判据(Σ = 同身份净已付 + 未付占用,A = 当前应付,x = 本次金额):
|
||
- 净已付 > A → **598812**(message 带超出额);
|
||
- Σ > A → **598813**(message 列出未付单「单号(状态)」,PENDING 删除 / SUBMITTED 驳回 / APPROVED 撤销批准即可);
|
||
- Σ = A → **598809**(A 与 Σ 均为 0,即该身份不在应付范围 → 598808);
|
||
- Σ + x > A → **598813**;
|
||
- 否则成功:Σ 为 0 建首笔,Σ > 0 建差额行。
|
||
- `NODE` / `HOTEL_ASSIGNMENT` 仍按来源主键存在性判重(598809),行为不变。
|
||
- 598808 触发:来源类型不在取值域或传了 MANUAL;sourceRefType 与 sourceRefId 不成对;带来源未传 orderId;团期住宿 sourceRefKey 缺失、格式错误、日期非法或其中订单 ≠ orderId;非团期住宿传了 sourceRefKey。
|
||
- 同一订单的四个建单 / 编辑写口串行执行,抢锁超时返回 `100503 资源被占用,请稍后重试`。
|
||
|
||
---
|
||
|
||
### 7. 勾选批量生成(按团) `POST /admin/finance/payments/batch-create`
|
||
|
||
**VO**: `PaymentBatchCreateReqVO → PaymentBatchCreateRespVO`
|
||
|
||
#### 使用场景
|
||
|
||
按订单建议清单勾选多行,按供应商拆单批量生成。本次明细新增 `sourceRefKey`,团期住宿走金额对账。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| orderId | Body | Long | 是 | - | 来源订单 |
|
||
| items | Body | List | 是 | 至少 1 条 | 勾选明细 |
|
||
| items[].sourceRefType | Body | String | 是 | `NODE` / `HOTEL_ASSIGNMENT` / `GROUP_BATCH_STAY` | 来源类型 |
|
||
| items[].sourceRefId | Body | Long | 是 | - | 来源主键(团期住宿传 sourceId) |
|
||
| items[].sourceRefKey | Body | String | 条件必填 | ≤96;团期住宿必填且订单须等于 orderId;其余类型为空 | **新增** |
|
||
| items[].supplierId | Body | Long | 是 | - | 供应商(拆单键) |
|
||
| items[].amount | Body | BigDecimal | 是 | >0 | 明细金额 |
|
||
| items[].paymentType | Body | String | 是 | ≤32 | 付款类型 |
|
||
| items[].resourceId | Body | Long | 否 | - | 资源快照 |
|
||
| items[].resourceName | Body | String | 否 | ≤128 | 资源名快照 |
|
||
| payeeAccountId | Body | Long | 否 | - | 收款账户 |
|
||
| reason | Body | String | 是 | ≤512 | 付款事由 |
|
||
| submit | Body | Boolean | 是 | - | true 直接提交 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| groups[].paymentId | Long | 付款单 ID |
|
||
| groups[].paymentNo | String | 付款单号 |
|
||
| groups[].batchNo | String | 批次号 |
|
||
| groups[].supplierId | Long | 供应商 ID |
|
||
| groups[].supplierName | String | 供应商名 |
|
||
| groups[].amount | BigDecimal | 单据金额 |
|
||
| groups[].status | String | PENDING / SUBMITTED |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"orderId": "2100244780104425473",
|
||
"reason": "团期住宿",
|
||
"submit": false,
|
||
"items": [
|
||
{
|
||
"sourceRefType": "GROUP_BATCH_STAY",
|
||
"sourceRefId": "2100244786240692225",
|
||
"sourceRefKey": "2100244780104425473:2026-11-18:2029956939232808961:2029956939333472258",
|
||
"supplierId": "88001",
|
||
"amount": 400.00,
|
||
"paymentType": "住宿"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"groups": [
|
||
{
|
||
"paymentId": "2100260000000000002",
|
||
"paymentNo": "FK-202609170004",
|
||
"batchNo": "BATCH-2100260000000000003",
|
||
"supplierId": "88001",
|
||
"supplierName": "呼籁酒店",
|
||
"amount": 400.00,
|
||
"status": "PENDING"
|
||
}
|
||
]
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无查询数据;任一明细校验失败整批回滚,不落任何单据。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 598809,
|
||
"message": "该来源配置已生成付款单,请勿重复生成",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598813,
|
||
"message": "未付占用超出当前应付 1200.00 元,请先删除 / 驳回 / 撤销批准:FK-202609170005(PENDING)",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 团期住宿判据同单笔创建;同一请求内重复勾选同一身份 → 598809。
|
||
- 598812 / 598808 触发条件同单笔创建。
|
||
|
||
---
|
||
|
||
### 8. 按供应商跨团合并建单 `POST /admin/finance/payments/batch-create-by-supplier`
|
||
|
||
**VO**: `PaymentBatchCreateBySupplierReqVO → PaymentBatchCreateRespVO`
|
||
|
||
#### 使用场景
|
||
|
||
按供应商建议勾选跨团明细合并成一张付款单。本次明细新增 `sourceRefKey`,团期住宿走金额对账,多订单按订单号升序串行加锁。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| supplierId | Body | Long | 是 | 全部明细须同属该供应商 | 供应商 |
|
||
| items | Body | List | 是 | 至少 1 条 | 勾选明细 |
|
||
| items[].orderId | Body | Long | 是 | - | 来源订单 |
|
||
| items[].sourceRefType | Body | String | 是 | `NODE` / `HOTEL_ASSIGNMENT` / `GROUP_BATCH_STAY` | 来源类型 |
|
||
| items[].sourceRefId | Body | Long | 是 | - | 来源主键 |
|
||
| items[].sourceRefKey | Body | String | 条件必填 | ≤96;团期住宿必填且订单须等于 items[].orderId | **新增** |
|
||
| items[].supplierId | Body | Long | 是 | 等于入参 supplierId | 供应商 |
|
||
| items[].amount | Body | BigDecimal | 是 | >0 | 明细金额 |
|
||
| items[].paymentType | Body | String | 是 | ≤32 | 付款类型 |
|
||
| payeeAccountId | Body | Long | 否 | - | 收款账户 |
|
||
| reason | Body | String | 是 | ≤512 | 付款事由 |
|
||
| submit | Body | Boolean | 是 | - | true 直接提交 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| groups[] | GroupResult | 单元素,字段同按团批量 |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"supplierId": "88001",
|
||
"reason": "11 月团期住宿合并付款",
|
||
"submit": true,
|
||
"items": [
|
||
{
|
||
"orderId": "2100244780104425473",
|
||
"sourceRefType": "GROUP_BATCH_STAY",
|
||
"sourceRefId": "2100244786240692225",
|
||
"sourceRefKey": "2100244780104425473:2026-11-18:2029956939232808961:2029956939333472258",
|
||
"supplierId": "88001",
|
||
"amount": 800.00,
|
||
"paymentType": "住宿"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"groups": [
|
||
{
|
||
"paymentId": "2100260000000000006",
|
||
"paymentNo": "FK-202609170006",
|
||
"batchNo": "BATCH-2100260000000000007",
|
||
"supplierId": "88001",
|
||
"supplierName": "呼籁酒店",
|
||
"amount": 800.00,
|
||
"status": "SUBMITTED"
|
||
}
|
||
]
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无查询数据;校验失败整单回滚。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 598811,
|
||
"message": "勾选明细含其他供应商,请按供应商分批勾选",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598809,
|
||
"message": "该来源配置已生成付款单,请勿重复生成",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 合并单主单无订单归属,金额按明细订单分摊到按团统计。
|
||
- 团期住宿判据同单笔创建(598809 / 598812 / 598813 / 598808)。
|
||
|
||
---
|
||
|
||
### 9. 编辑付款草稿 `PUT /admin/finance/payments/{id}`
|
||
|
||
**VO**: `PaymentUpdateReqVO → Result<Void>`
|
||
|
||
#### 使用场景
|
||
|
||
编辑 PENDING 草稿。本次新增:有业务来源的草稿不允许改供应商与订单;明细金额 / 归属跟随主单同步;团期住宿草稿改金额受额度守卫。
|
||
|
||
#### 入参字段表
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| id | Path | Long | 是 | - | 付款单 ID |
|
||
| supplierId | Body | Long | 是 | 有来源草稿须等于原值 | 供应商(**请回传原值**) |
|
||
| payeeAccountId | Body | Long | 是 | 供应商生效账户 | 收款账户 |
|
||
| amount | Body | BigDecimal | 是 | >0;多明细草稿须等于明细合计 | 付款金额 |
|
||
| paymentType | Body | String | 是 | ≤32 | 付款类型 |
|
||
| reason | Body | String | 是 | ≤512 | 付款事由 |
|
||
| teamNo | Body | String | 否 | ≤32 | 团号 |
|
||
| orderId | Body | Long | 否 | 有来源草稿须等于原值(原值为空则传空) | 关联订单 |
|
||
| resourceId | Body | Long | 否 | - | 关联资源 |
|
||
|
||
#### 出参字段表
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| code | Integer | 200 成功 |
|
||
| data | Void | 恒为 null |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"supplierId": "88001",
|
||
"payeeAccountId": "90001",
|
||
"amount": 300.00,
|
||
"paymentType": "住宿",
|
||
"reason": "差额改为 300",
|
||
"teamNo": "26-3509",
|
||
"orderId": "2100244780104425473"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": null,
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
无查询数据;任一校验失败主单与明细零变化。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 598808,
|
||
"message": "来源配置引用非法",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598805,
|
||
"message": "金额无效(付款金额须大于0)",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 598813,
|
||
"message": "未付占用超出当前应付 1200.00 元,请先删除 / 驳回 / 撤销批准:无未付占用,本次申请金额超出可申请差额",
|
||
"data": null,
|
||
"success": false
|
||
}
|
||
```
|
||
|
||
#### 业务边界
|
||
|
||
- 有来源明细(NODE / HOTEL_ASSIGNMENT / GROUP_BATCH_STAY)的草稿:改 supplierId 或 orderId → 598808;单明细同步明细金额;多明细要求 amount 等于明细合计,否则 598805。
|
||
- 团期住宿单明细草稿:同身份其他占用 + 新金额 ≤ 当前应付,否则按净已付是否超额报 598812 / 598813。
|
||
- 手工草稿(无来源):可自由改供应商 / 订单 / 团号 / 资源,系统同步其内部 MANUAL 明细的归属,资源名快照置空。
|
||
- 仅 PENDING 可编辑,否则 598802。
|
||
|
||
---
|
||
|
||
## 四、契约约束与正确调用方式
|
||
|
||
| 场景 | 做法 |
|
||
|------|------|
|
||
| 团期住宿勾选建单 | `sourceRefType=GROUP_BATCH_STAY`、`sourceRefId=行.sourceId`、`sourceRefKey=行.sourceRefKey` 原样回传,单笔还须带 `orderId` |
|
||
| 团期住宿应付增加 | 行上 `amountChanged=true` 且 `diffAmount>0` → 以 `diffAmount` 为金额再建一笔(差额行) |
|
||
| 团期住宿应付减少、原单未付 | 598813 提示里列出的单:PENDING 删除 / SUBMITTED 驳回 / APPROVED 先调撤销批准再删或改 |
|
||
| 团期住宿应付减少、原单已付 | 598812,需财务冲正(后续能力),前端提示即可 |
|
||
| 编辑有来源草稿 | supplierId / orderId 回传原值,不提供修改入口 |
|
||
| 统计状态筛选 | 新增 `OVERPAID` 选项;`owedAmount` 不会再出现负数 |
|
||
| 建议行新字段 | 仅 `GROUP_BATCH_STAY` 行有值,其余类型为 null,前端需判空 |
|
||
|
||
---
|
||
|
||
## 五、数据库行为
|
||
|
||
| 操作 | 表 | 行为 |
|
||
|------|-----|------|
|
||
| 迁移 V20260917_110 | `fin_payment_item` | 加列 `source_ref_key VARCHAR(96) NULL`、`item_kind VARCHAR(16) NOT NULL DEFAULT 'FULL'`,加索引 `idx_source_ref_key(source_ref_type, source_ref_key)`;幂等 |
|
||
| 迁移 V20260917_111 | `fin_payment_item` | 存量回填:无明细的付款单补一条明细(无来源记 MANUAL);已付单明细合计与出账流水不一致时补一条 `CASHIER_ADJ`;不改付款主单与资金流水,重复执行零新增 |
|
||
| 单笔创建 | `fin_payment` + `fin_payment_item` | 同事务写主单 + 1 条明细(来源明细或 MANUAL) |
|
||
| 批量创建 | `fin_payment` + `fin_payment_item` | 团期住宿明细写 `source_ref_key`;`item_kind` 为 FULL(首笔)或 ADJUST(差额) |
|
||
| 编辑草稿 | `fin_payment` + `fin_payment_item` | 同步唯一明细金额;手工单同步明细归属 |
|
||
| 撤销批准 | `fin_payment` + `fin_payment_review_log` | 条件更新 APPROVED→PENDING + 插入 UN_APPROVE 流水 |
|
||
| 出纳付款 | `fin_payment_item` | 主单实付与明细合计不一致时追加一条 `CASHIER_ADJ` 明细(新数据正常不会触发) |
|
||
| 四个读接口 | 只读 | 新读 `group_batch_room_plan` / `group_batch_room_allocation` / `order_group_batch_house_legacy` |
|
||
|
||
---
|
||
|
||
## 六、边界行为
|
||
|
||
- 未登录 → 401;无权限 → 403。
|
||
- 团期计划行结算价为空 → 该行不进应付(记告警日志)。
|
||
- 同一身份跨计划行单价不一致 → `unitPrice=null`,`amount` 按各行累加。
|
||
- 已核单完成的订单不进三个批量入口的候选,按订单建议仍可见。
|
||
- 抢不到同订单写锁 → 100503。
|
||
- 撤销批准与出纳付款并发 → 只有一个成功,另一方 598802 或出纳侧状态错误。
|
||
|
||
---
|
||
|
||
## 六.5、枚举
|
||
|
||
### 来源类型(sourceType / sourceRefType)
|
||
|
||
**所属字段**: PaymentSuggestionRowVO.sourceType、各创建入参 sourceRefType | **类型**: String
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| NODE | 行程节点 | 既有 |
|
||
| HOTEL_ASSIGNMENT | 配房 | 既有 |
|
||
| GROUP_BATCH_STAY | 团期住宿 | **新增**,按业务键金额对账 |
|
||
| MANUAL | 手工 | 系统内部明细类型,前端不可传、建议行不出现 |
|
||
|
||
### 统计状态(status)
|
||
|
||
**所属字段**: PaymentStatsByTeamRowVO.status、PaymentStatsBySupplierRowVO.status | **类型**: String
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| OWED | 有欠付 | owedAmount > 0 |
|
||
| OVERPAID | 已付超出 | **新增**,overpaidAmount > 0,待冲正 |
|
||
| PAID | 已付清 | 其余 |
|
||
|
||
---
|
||
|
||
## 六.6、修改前后对比
|
||
|
||
| 项 | 改前 | 改后 |
|
||
|----|------|------|
|
||
| 团期住宿应付 | 按订单建议之外三处恒为 0 | 四处一致 |
|
||
| 团期住宿判重 | 按分房行 ID,微调换 ID 可重复申请 | 按业务键金额对账 |
|
||
| 统计已付来源 | 付款主单金额,按主单订单归属 | 付款明细金额,按明细订单归属 |
|
||
| 统计欠付 | 可为负数 | 不为负,超出部分进 overpaidAmount |
|
||
| 统计状态 | OWED / PAID | OWED / OVERPAID / PAID |
|
||
| 单笔创建 | 不写明细,不参与防重 | 同事务写明细,立即占用 |
|
||
| 编辑草稿 | 可任意改供应商/订单/金额,明细不跟随 | 有来源禁改归属;明细跟随;团期住宿守额度 |
|
||
| 已批准单改单 | 无出口(驳回只收 SUBMITTED) | 可撤销批准回到草稿 |
|
||
| 错误码 | 无 598812 / 598813 | 新增 |
|
||
|
||
---
|
||
|
||
## 六.7、影响评估
|
||
|
||
- **是否破坏向后兼容**: 部分。读接口只增字段与取值(`GROUP_BATCH_STAY`、`OVERPAID`);写接口团期住宿必须带 `sourceRefKey`;有来源草稿不能再改供应商 / 订单。
|
||
- **前端是否必须同步上线**: 建议同步。不接也不报错,但团期住宿行无法正确建单(缺 sourceRefKey 会 598808),且看不到金额变化提示与撤销批准入口。
|
||
- **前端需做**: 建议行展示 `GROUP_BATCH_STAY` 与金额对账字段;建单回传 `sourceRefKey`;统计加 `OVERPAID` 与新金额列;按供应商统计展示 `unattributedPaidAmount`;已批准单加「撤销批准」按钮;598812 / 598813 文案映射;有来源草稿的供应商 / 订单置为只读。
|
||
|
||
---
|
||
|
||
## 七、不影响范围
|
||
|
||
- 行程节点、配房两类来源的判重口径不变。
|
||
- 出纳付款接口入参 / 出参不变(仅可能追加内部明细)。
|
||
- 付款单分页、详情、提交、批准、驳回、删除接口契约不变。
|
||
- C 端与小程序零影响。
|
||
|
||
---
|
||
|
||
## 八、测试环境已验证
|
||
|
||
TEST(`https://api.test.1814.love:9443`,`hl-order-service-v3` = **dev-v3@75d77eefe**,含合并提交 `a37bd669f`;真实网关 + 管理员 token),2026-09-17:
|
||
|
||
| 场景 | 结果 |
|
||
|---|---|
|
||
| 迁移 | `V20260917_110` / `V20260917_111` 执行成功;存量无明细单回填 7 条(6 MANUAL + 1 带来源),重复执行第①段零新增 |
|
||
| 四入口金额一致 | 纯团期户 2 间 × 400(sign):按订单建议 / 按供应商建议 / 按团统计均 800,按供应商统计应付 +800;改前按订单建议 0 行 |
|
||
| 客户自付与旧户 | 改 cash 后四处同时归 0;旧户只出 `HOTEL_ASSIGNMENT` 行、不双计 |
|
||
| 防重 | 单笔建单写出 1 行明细;再以单笔 / `batch-create` / `batch-create-by-supplier` 各建一次均 598809;H10 拆行与核单重绑后仍 598809 |
|
||
| 金额变化 | 增额可建差额行(ADJUST);未付超额 598813(列出单号与状态);已付超额 598812;草稿编辑超额 598813、改供应商/订单 598808、多明细改金额 598805 |
|
||
| 撤销批准 | `PUT /admin/finance/payments/{id}/revoke-approval` → 200、回到 PENDING、出纳队列消失、审核流水出现 `UN_APPROVE`;再撤 598802;与出纳付款并发 12 轮均恰一方成功 |
|
||
| 统计口径 | 已付后应付消失 → owed 0 / overpaid 800 / OVERPAID;合并付款按团各归各(800+800),出纳异额合并单 `unattributedPaidAmount=100` 且 800+800+100=1700 |
|
||
| 来源锁 | 同订单并发 create 与 update 8 轮均恰一个成功,Σ 不超过应付 |
|
||
|
||
```
|
||
GET /admin/finance/payments/suggestions?orderId=2100421247786545153 → 200,rows[0].sourceType=GROUP_BATCH_STAY,amount=800
|
||
POST /admin/finance/payments(同 sourceRefKey 第二次)→ 598809
|
||
PUT /admin/finance/payments/{id}/revoke-approval → 200;GET /admin/finance/payments/{id}/review-logs → 含 UN_APPROVE
|
||
```
|
||
|
||
逐条验收记录见工单 #7396 验收评论。
|
||
|
||
---
|
||
|
||
## 十、相关文档
|
||
|
||
- Issue: [#7396](https://git.1814.love:8443/wx/HL/issues/7396)
|
||
- PR: [#7866](https://git.1814.love:8443/wx/HL/pulls/7866)
|
||
- 核查 SQL: `docs/finance/7396-payment-item-reconcile-check.sql`(HL 仓库)
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **Issue**: [#7396](https://git.1814.love:8443/wx/HL/issues/7396)
|
||
- **PR**: [#7866](https://git.1814.love:8443/wx/HL/pulls/7866)
|
||
- **关联**: #7327(团期核单与月报)、#7398(退款 / 冲正,598812 的后续出口)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @jw
|
||
- **前端负责人**: @mmg
|