文件
hl-api-changelog/changelogs-v2/2026-09/22_8127_应付款付款支持冲抵供应商预付款-修改接口-管理后台.md
T
Mimingguang ea7a01e674
changelog-filename-gate / validate (push) Failing after 1s
chore(changelog): #8127 frontmatter 回写 verified(ref d0a264ec)
2026-09-22 10:24:44 +08:00

11 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 8127 应付款付款支持冲抵供应商预付款(差额付款 + 占用/付讫/回冲状态机) admin yst(GIT) 修改接口 deployed not_required verified mmg d0a264ec33214617f5ee3ca500b0f9ebd76b4bb6 v2.1 2026-09-22 backend_status: deployed - hl-order-service-v3 已部署测试服(dev-v3,含 finance 同进程),E2E 全链路 PASS(改单加冲抵→审批→出纳差额付款,往来净额/预付余额/冲抵明细 SETTLED 全对,2026-09-22 取证); gateway_status: not_required - 零网关改动,/admin/finance/** 走 hl-gateway 既有通配路由; frontend_status: pending - 前端适配情况未知,后端不代填。 前端核验(2026-09-22): 已交付 hl-admin d0a264ec——payable.js 加 getOffsettablePrepayments+PREPAY_OFFSET_STATUS meta+4 写口 JSDoc(单笔 create 前端无调用方仅补文档);新共享组件 PrepayOffsetPicker(勾选+金额≤availableAmount+598812/598813 前置校验);PaymentDetailPanel 详情冲抵金额/实付金额+offsets 状态机明细卡、编辑草稿冲抵区块(回显仅 OCCUPYING,整体置换恒传,空数组清占用);GroupPayPanel 按供应商分组上送、SupplierPayPanel 单元素分组;出纳页零改动(#7815 锁 actualAmount 槽位);598812-598817 透 message;原型 pay-adv 冲抵记录子 tab 契约无列表读口,未造数留痕待后端;Vitest 23/23+checkpoint 全量 13 项全绿。 2026-09-22 dev-v3

财务:应付款付款支持冲抵供应商预付款(Epic #8127)

应付款付款时可用「该供应商已付讫且有余额的预付款」冲抵,出纳只付差额现金。涉及 4 个建单/改单接口入参新增、付款单详情出参新增、新增 1 个可冲抵预付查询接口。

① 接口背景

供应商常有预付(先打款后结算)。此前预付款与应付款是两条独立线,应付款付款只能全额现金支出,无法用已预付的余额抵扣,资金占用高。本变更让应付款付款单可勾选「该供应商的预付款」做冲抵:冲抵部分无现金流出,出纳仅按「应付金额 − 冲抵金额 = 实付金额」付差额。

三个口径决策(后端已定):

  • 冲抵算 paid:冲抵部分随现金一并计入应付台账已付,清偿方式不影响应付债务的付讫认定。
  • 占用时点=建单时:建单/改单(PENDING 草稿)即扣减预付可用余额并落「占用中」明细,驳回/删改草稿回冲,付讫转「已冲抵」。
  • 往来差额归零:付讫时补一对抵销分录,付款单文档净额=0、预付单文档净额=剩余可用余额,供应商往来对账自清。

② 变更清单

类型 接口 变更
修改 POST /admin/finance/payments 创建付款单 入参新增 prepayOffsets[]
修改 POST /admin/finance/payments/batch 按订单批量创建 入参新增 prepayOffsets[](按供应商分组)
修改 POST /admin/finance/payments/batch-by-supplier 按供应商合并创建 入参新增 prepayOffsets[](按供应商分组)
修改 PUT /admin/finance/payments/{id} 编辑草稿 入参新增 prepayOffsets[](整体置换语义)
修改 GET /admin/finance/payments/{id} 付款单详情 出参新增 offsets[];prepayOffsetAmount/actualPayAmount 由恒 0 真值化
新增 GET /admin/finance/payments/offsettable-prepays 可冲抵预付查询 新接口

③ 接口详情

3.1 新增:可冲抵预付查询

GET /admin/finance/payments/offsettable-prepays?supplierId={supplierId}

供付款申请页勾选「用哪笔预付冲抵」。返回该供应商下已付讫(PAID)且可用余额 > 0 的预付款,按付款日期升序。

3.2 创建/编辑付款单(冲抵)

在既有入参基础上加 prepayOffsets,提交即占用预付余额;不冲抵则该字段不传或传空数组(行为与旧版完全一致)。

3.3 付款单详情

详情返回本单的冲抵明细 offsets[] 及冲抵状态;actualPayAmount(实付)为出纳真正付现的金额。

④ 入参

4.1 prepayOffsets[](创建/编辑付款单新增,可空)

字段 类型 必填 说明
prepayId Long(string) 是 预付款申请单 ID(须为本供应商 PAID 且余额充足)
offsetAmount number 是 该笔预付冲抵金额(>0,≤预付可用余额)

批量接口(按订单批量/按供应商合并)按供应商分组传:prepayOffsets: [{ supplierId, offsets: [{ prepayId, offsetAmount }] }]。 编辑草稿为整体置换语义:传入的 prepayOffsets 全量替换旧占用(先回冲旧占用再按新入参重占)。

4.2 可冲抵预付查询入参

字段 类型 必填 说明
supplierId Long(string) 是 供应商 ID(查询其可冲抵预付)

⑤ 出参

5.1 付款单详情 GET /admin/finance/payments/{id}(新增/真值化字段)

字段 类型 说明
amount number 应付金额(= Σ明细行,不随冲抵变化)
prepayOffsetAmount number 冲抵金额合计(= Σoffsets.offsetAmount;原恒 0,本期真值化)
actualPayAmount number 实付金额 = amount − prepayOffsetAmount(出纳按此现金付款;原恒等于 amount)
offsets array 本单冲抵明细列表(新增,无冲抵时为 [])

offsets[] 元素:

字段 类型 说明
offsetId Long(string) 冲抵明细 ID
prepayId Long(string) 预付款申请单 ID
prepayNo string 预付款单号(YF- 前缀)
offsetAmount number 该笔冲抵金额
status string 冲抵状态:OCCUPYING 占用中 / SETTLED 已冲抵(付讫)/ CANCELLED 已取消(驳回/删改草稿回冲)
settleTime string(datetime) 付讫冲抵时间(仅 SETTLED 有值,否则 null)

5.2 可冲抵预付查询 offsettable-prepays 出参(数组)

字段 类型 说明
prepayId Long(string) 预付款申请单 ID
prepayNo string 预付款单号
amount number 预付本金
availableAmount number 可用余额(可被冲抵的上限)
payDate string(date) 付款日期
supplierId Long(string) 供应商 ID
supplierName string 供应商名称

⑥ 枚举/数据字典

冲抵明细状态 offsets[].status:

值 含义
OCCUPYING 占用中(建单~付讫前)
SETTLED 已冲抵(付讫,终态)
CANCELLED 已取消(驳回/删草稿/改草稿回冲,留痕)

paymentType 仍走既有 fin_payment_type 字典标签(住宿/门票·游玩/餐食/车辆/导游/摄影/保险/其他支出/退款/其他应付),本次未新增。

⑦ 错误码

码 语义
598812 冲抵入参非法(金额≤0 / 同一预付单重复传)
598813 冲抵合计超应付金额(Σoffset > amount)
598814 预付单不可用(不存在 / 非已付讫 / 可用余额不足 / 金额非法)
598815 预付单冲抵单位与付款供应商不一致(跨供应商拦截)
598816 预付可冲抵余额并发不足(CAS 扣减失败,整批回滚)
598817 冲抵回冲账实不符(回冲 CAS 失败,fail-fast)

⑧ 示例

8.1 典型:创建付款单并冲抵预付

请求 POST /admin/finance/payments:

{
  "supplierId": "2096854417461403650",
  "payeeAccountId": "2096854417490763777",
  "amount": 800.00,
  "paymentType": "住宿",
  "reason": "9月羊和远方住宿结算",
  "orderId": "2100482290093039617",
  "prepayOffsets": [
    { "prepayId": "2100473583917580290", "offsetAmount": 500.00 }
  ]
}

响应(data.paymentId 为新单 ID)。此时预付 available_amount 1000→500,prepayOffsetAmount=500、actualPayAmount=300,offset 行 OCCUPYING。

详情出参(付讫后):

{
  "code": 200,
  "data": {
    "id": "2100485199803359234",
    "paymentNo": "FK-202609170057",
    "amount": 800.00,
    "prepayOffsetAmount": 500.00,
    "actualPayAmount": 300.00,
    "status": "PAID",
    "offsets": [
      {
        "offsetId": "2102204...",
        "prepayId": "2100473583917580290",
        "prepayNo": "YF-202609170002",
        "offsetAmount": 500.00,
        "status": "SETTLED",
        "settleTime": "2026-09-22 09:15:35"
      }
    ]
  }
}

8.2 边界:可冲抵预付查询

请求 GET /admin/finance/payments/offsettable-prepays?supplierId=2096854417461403650:

{
  "code": 200,
  "data": [
    {
      "prepayId": "2100473583917580290",
      "prepayNo": "YF-202609170002",
      "amount": 1000.00,
      "availableAmount": 500.00,
      "payDate": "2026-09-17",
      "supplierId": "2096854417461403650",
      "supplierName": "呼伦贝尔羊和远方牧业有限公司"
    }
  ]
}

(该预付已被冲抵 500,故 availableAmount 由 1000 降为 500。)

8.3 异常:冲抵超应付 / 跨供应商

{ "code": 598813, "message": "冲抵合计超应付金额", "success": false }
{ "code": 598815, "message": "预付单冲抵单位与付款供应商不一致", "success": false }
{ "code": 598814, "message": "预付单不可用(不存在/非已付讫/余额不足)", "success": false }

⑨ 业务边界

  • 可冲抵资格:预付单须 status=PAID(钱真出了)且 availableAmount>0 且其「冲抵单位」= 付款供应商;未付讫(APPROVED 未出钱)不可勾选。
  • 出纳只付差额:付款金额锁死 = actualPayAmount,冲抵部分无现金流出、无资金流水。
  • 回冲时机:仅 PENDING 草稿可编辑/删除;驳回(REJECTED)、删除、改草稿均回冲预付余额并置 offset 行 CANCELLED。付讫(PAID)后无回冲——已消耗预付不回退(供应商退回走退回形态,只退现金)。
  • 台账外手工单:无台账锚点的手工付款单同样支持冲抵,行级/头表回写跳过(WARN),不影响付款主流程。

⑩ 修改前后对比

项 修改前 修改后
付款方式 只能全额现金 可勾选预付冲抵,出纳付差额
prepayOffsetAmount 恒 0 真值化 = Σ冲抵
actualPayAmount 恒等于 amount = amount − 冲抵
详情 offsets 无 返回冲抵明细及状态机

⑪ 影响评估 / 回滚

  • 向后兼容:不冲抵(不传 prepayOffsets 或传空)时行为与旧版完全一致(offset=0、actualPay=amount),旧前端不感知。
  • 回滚:代码回滚即恢复全额现金逻辑;已产生的冲抵数据(fin_prepay_offset)保留不影响。

⑫ 注意事项

  • 列表行 PaymentRowRespVO 暂未加 prepayOffsetAmount,列表「实付」列如需区分冲抵单,另提需求(详情已全量返回)。
  • 冲抵不改变应付台账 applied 占用口径(建单占全额,付讫全额转 paid)。

⑬ 关联 / 联系人