文件
hl-api-changelog/changelogs-v2/2026-09/17_7398_应付款已付超额冲正-供应商退回-新增接口-管理后台.md
T
2026-09-17 18:59:49 +08:00

21 KiB
原始文件 Blame 文件历史

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref target_release verified_at status_note updated_at base
hl-changelog/v2 7398 应付款已付超额冲正(供应商退回):申请 / 分次确认到账 / 撤销剩余额度 / 分页 admin jw(GIT) 新增接口 deployed verified verified mmg 0fa6574f9cceb2f41f5e4208424ce64190da2014 2026-09-17 后端交付(hl-finance,随 hl-order-service-v3 部署)。新增 4 个冲正接口与错误码 598814/598815/598816/598817/598818;付款明细新增 REVERSAL 负明细语义;资金流水新增 bizType=PAYMENT_REVERSAL。网关前缀 /admin/finance/** 已存在,无需新路由。前端需新增冲正申请与出纳确认到账两个界面。[mmg 2026-09-17 交付] 原型无冲正 UI,以后端契约为准嵌入应付款模块:①payable.js 冲正节 4 端点+四态字典(分页路径无 /page 后缀,598814-818 等拦截器透 message);②可冲正额=reversibleAmount(netPaidAmount−amount 2dp,非 −diffAmount,unpaidAmount 不参与,≥0.01 才显入口);③ReversalApplyModal 申请弹窗(sourceRefKey 原样回传、amount 预填可冲正额、reason 必填,文案明示确认到账后生效);④PaymentReversalPanel 冲正管理(orderId/supplierId/status 筛选分页,PENDING/PARTIAL 出确认到账/撤销,确认六字段含 ACTIVE 公司户下拉+默认今天+凭证号必填);⑤三处申请入口(ap-detail 构成明细+两建单面板超额行),统计页头部「冲正管理」进新视图返回强制刷新。api spec +5 例+组件 spec 11 例,scoped checkpoint 13 项全绿。 2026-09-17 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<PaymentReversalIdRespVO>

使用场景

建议行或按团统计显示某团期住宿付款身份已付超出当前应付(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,字符串输出)

请求示例

{
  "sourceRefType": "GROUP_BATCH_STAY",
  "sourceRefKey": "2100482262209380353:2027-01-05:2029926133256929282:2029944767501012994",
  "orderId": 2100482262209380353,
  "amount": 800.00,
  "reason": "该户退团,酒店同意退回已付房费"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": { "id": "2100483176424972289" },
  "success": true
}

空数据 / 降级响应

无查询数据。申请成功后不产生资金流水、不写付款明细;统计、建议、再建单的结果与申请前逐字段相同(再建单仍 598812)。

错误响应

{
  "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<Boolean>

使用场景

出纳收到酒店退款回单后登记。酒店分几次退就调几次,每次填本次实际到账金额与入账的公司账户。

入参字段表

字段 位置 类型 必填 约束 说明
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

请求示例

{
  "amount": 400.00,
  "fundAccountId": 2100482405474148354,
  "receivedDate": "2026-09-17",
  "voucherNo": "HD-20260917-001",
  "voucherUrl": "https://oss.example.com/voucher/001.png",
  "remark": "酒店财务转账"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": true,
  "success": true
}

空数据 / 降级响应

无查询数据。成功后:申请 confirmedAmount 累加、状态变 PARTIAL(未满额)或 CONFIRMED(满额);资金流水新增一条 direction=IN、bizType=PAYMENT_REVERSAL、bizId=冲正申请ID;付款明细新增一条 itemKind=REVERSAL、金额为负的明细。

错误响应

{
  "code": 598817,
  "message": "确认到账金额超出冲正申请剩余额度 0.00 元",
  "data": null,
  "success": false
}
{
  "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<Boolean>

使用场景

酒店明确不再退款(或应付已恢复、钱没有退)时,作废申请剩余未到账的额度。

入参字段表

字段 位置 类型 必填 约束 说明
id Path Long 是 - 冲正申请 ID
reason Body String 是 ≤512,非空白 撤销原因

出参字段表

字段 类型 说明
data Boolean 恒 true

请求示例

{
  "reason": "酒店余款不再退回"
}

响应示例

{
  "code": 200,
  "message": "成功",
  "data": true,
  "success": true
}

空数据 / 降级响应

无查询数据。成功后状态变 CANCELLED,confirmedAmount 不变,已登记的流水与负明细不变。

错误响应

{
  "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<PageResult<PaymentReversalRespVO>>

使用场景

冲正申请列表 / 出纳待确认列表(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 分页信息

请求示例

GET /admin/finance/payment-reversals?orderId=2100482262209380353&status=CONFIRMED&page=1&pageSize=20

响应示例

{
  "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
}

空数据 / 降级响应

{
  "code": 200,
  "message": "成功",
  "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 },
  "success": true
}

错误响应

{
  "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
  • PR: #7889
  • 关联: #7396(598812 的来源)、#7325(团期单户退团 / 取消)

联系人

  • 后端负责人: @jw
  • 前端负责人: @mmg