docs: 新增付款审批履约计划款项金额累加与金蝶付款申请业务流程梳理、付款作废影响分析与补齐方案文档

This commit is contained in:
刘泉佳
2026-10-22 19:06:47 +08:00
parent dcda231c51
commit ced382f3f7
2 changed files with 227 additions and 0 deletions
@@ -0,0 +1,155 @@
# 付款审批履约计划款项金额累加与金蝶付款申请携带履约计划id 业务逻辑与流程梳理
> 需求:① 审批通过后,更新合同履约计划并自动推送合同系统(HCS),款项金额=申请付款金额,多次付款累加更新;② 审批通过自动推送金蝶付款申请,报文携带关联的履约计划id(进度款计划id),用于合同系统关联履约付款数据与履约计划。
> 关键口径:HCS 接口文档(桂物合同集成接口-apifox导入-KeyAuth.json)中履约计划行金额字段 `fdFundAmount` 的定义即**「款项金额,必填」**,本地 `blade_contract_exe_stage.fund_amount` 与之映射——累加直接落在该字段,无需新增数据库列。
> 代码改动日期:2026-10-22,`mvn compile` 通过。
---
## 1. 业务逻辑总览
| # | 规则 | 说明 |
| --- | --- | --- |
| 1 | 款项金额=申请付款金额,多次付款累加 | 付款申请**审批通过**时,按 `coalesce(fund_amount, 0) + applied_amount` 原子累加到匹配的履约计划行(SQL 原子累加,防多笔付款并发审批丢失更新) |
| 2 | 款项性质匹配 | `settlement`(正式结算付款)→ 进度款行 `progress`;`progress_advance`/`project_advance`(预付款)→ 预付款行 `prepayment`(`HcsSyncConstant.fundNatureOfPayment`) |
| 3 | 仅精确性质行累加 | 匹配不到该性质的已同步付款计划行时**跳过累加**(日志留痕),兜底匹配到的异性质行不累加,避免预付款金额误计入进度款计划 |
| 4 | 已同步行重推、未同步行随首推 | 计划行已有合规阶段ID(hcs_stage_fd_id)→ 置 `row_op=update` + 待同步,随付款链凭 fdId 重推 C103-9;未同步行保持 `row_op=add`,合同链路首推时即携带累加后金额 |
| 5 | 金蝶报文携带履约计划id | A103-1/A103-3 付款申请分录备注 `e_remark` 携带 `履约计划id:{hcs_stage_fd_id}`(进度款计划行的合规阶段ID,合同系统凭此关联);原 `remark` 有值时拼接其后 |
| 6 | 无关联不传、异常不阻断 | 无匹配计划行时不携带履约计划id(与 C108 履约记录 §5.6「无关联则不传」同口径);计划行查询异常仅告警,不阻断金蝶推送 |
---
## 2. 业务流程
### 2.1 问题1:付款审批 → 履约计划款项金额累加 → 推送合同系统
```
付款申请审批通过(/approve 或 MK 审批流回调,同一事务内)
│
├─① approve():approvalStatus=approved、hcs_sync_status=unsynced(HCS 零埋点,ADR-0001)
│
├─② accumulatePaidAmountOnApprove()(事务内落库)
│ 按 contractId + performType=payment + fundNature(按付款类型映射)匹配已同步计划行
│ ├─ 精确命中 → fund_amount 原子累加申请金额
│ │ ├─ 行已同步 → row_op=update、hcs_sync_status=unsynced(待重推 C103-9)
│ │ └─ 行未同步 → 保持 row_op=add(合同链路首推即携带累加金额)
│ └─ 未命中/性质不一致 → 跳过累加,仅日志
│
└─③ HCS付款链定时任务 pushHcsPayment(cron 默认 0 2/5 * * * ?,开关 thirdParty.hcs.push.payment.sync-enabled)
扫描「approved + unsynced/failed 未超限」的付款申请 → HcsPaymentSyncSupport.pushPayment
① 特批(如需 C106-3)
② 履约计划重推 C103-9(新增):聚合合同下 row_op≠none 的计划行凭已存 fdId 行级更新
│ ├─ 整体操作类型:有 fdExeplanId → update(首次 add 的判断逻辑不变)
│ ├─ 合同尚未推送合规(hcs_fd_id 空)→ 跳过(计划行随合同链路首推)
│ └─ 推送失败 → 抛出,由付款链统一 markFailed → 下轮自动重试
③ 履约记录 C108-1/2(原有,开关 thirdParty.hcs.push.perf-record…… 独立)
④ 成功 markSynced 回填整体状态
```
### 2.2 问题2:付款审批 → 自动推送金蝶付款申请(携带履约计划id)
```
付款申请审批通过事务提交
│ KingdeeBillApprovedEvent(BIZ_PAYMENT_APPLICATION)
▼
KingdeeApprovalSyncListener(AFTER_COMMIT,失败不影响审批结果)
│ 前置门禁:付款特批未完成(need/pushing/processing)→ 跳过,特批通过后由 HcsPaymentSyncSupport 重新发布事件补推
▼
syncKingdee → doSyncKingdee(单据级事务,幂等预检 + 特批门禁)
├─ 正式结算付款(settlement_payment)→ A103-1 应付下推(前置 A102-4 校验应付单已生成)
└─ 预付款(progress_advance/project_advance)→ A103-3 直接保存
▼
分录备注 e_remark = 「履约计划id:{hcs_stage_fd_id}」(+ 原备注)
├─ 计划行匹配:findPaymentStageRow(contractId, fundNatureOfPayment(paymentType))
│ 优先按款项性质精确匹配已同步付款行 → 无则取首条已同步付款行兜底(与 C108 口径一致)
└─ 无关联不传 / 查询异常仅告警不阻断推送
▼
成功:回填 kingdeeBillId/No、置 synced,联带锁单(lockBusiData)+ 附件上传(A902)
失败:markKingdeeSyncFailed → 定时重推任务(KingdeeAutoSyncRetryTask)与手动「同步金蝶」兜底(重推同样携带履约计划id)
```
### 2.3 履约记录(C108)——合同系统的履约付款数据
履约记录是付款申请在合同系统的**履约付款数据载体**:每笔审批通过的付款申请在 HCS 生成一条履约记录,凭 `fd_id`(来源履约计划项ID = 计划行 hcs_stage_fd_id)挂在对应履约计划行下,与 §2.1 履约计划款项金额累加、§2.2 金蝶付款申请携带计划id 共同完成「合同系统关联履约付款数据和履约计划」。
**生命周期**(HcsPerfRecordSyncSupport.pushPerfRecord):
```
付款申请审批通过(approve 置 hcs_sync_status=unsynced)
│ 付款链定时任务扫描(HcsPaymentSyncSupport.pushPayment 第③步,开关 third-party.hcs.push.perf-record.sync-enabled,默认关)
▼
无 hcs_perf_fd_id → C108-1 新建
│ 返回 fdId 立即回填 hcs_perf_fd_id 并持久化(后续更新全靠它)
▼
实际付款推进:金蝶付款结果回写任务(A103-2,PaymentKingdeeResultTask → applyPayApplyResult)
│ paidAmount 变化且已有 hcs_perf_fd_id → 置 hcs_sync_status=unsynced
▼
下轮付款链扫描 → C108-2 更新(formData.fdId 必填,响应 success 即成功,幂等可重试)
```
**报文字段映射**(formData 动态属性,§5.6):
| 报文字段 | 取值 | 说明 |
| --- | --- | --- |
| fdApplyAmount | appliedAmount | 申请金额(本笔申请付款金额,字符串传值) |
| fdFactAmount | paidAmount | 实际金额(金蝶实付回写后更新) |
| fdApplyNo【必填】 | paymentNo | 付款申请单号 |
| fdPlanDate【必填】 | applyDate | 计划日期(毫秒时间戳) |
| fdPalnType | paymentType | 计划类型(原文拼写 fd_paln_type 必须沿用) |
| fdProperty | 计划行 fundNature | 款项性质(无关联计划行不传) |
| fd_id | 计划行 hcs_stage_fd_id | **来源履约计划项ID**:履约记录与履约计划的挂接键(无关联不传) |
| fdContractId【必填】 | 合同 hcs_fd_id | HCS 合同ID |
| fdBusinessId | payment.id | TMS 付款申请主键(溯源,v4) |
| fdCreateTime / fdXformId | createTime / 配置 | 创建时间戳、表单模板ID(perf-record.xform-id,未配置不传) |
**附件转传**(C108-1):开关 `third-party.hcs.attachment.transfer-enabled` 开启且付款申请有附件时,经 C103-2 转传后挂 `fd_plan_att` 实体键;无附件传空列表。
**状态与重试**:履约记录与特批、履约计划重推共用付款申请的 `hcs_sync_status` 状态机(unsynced→synced/failed→下轮重试,超限人工重推);C108-2 更新按 fdId 幂等,重试无副作用。手动入口:付款申请页「推送合规/重推」按钮(HcsPaymentSyncSupport.repushPayment,重置 unsynced 清零计数立即推送)。
---
## 3. 字段与状态口径
| 字段 | 口径 |
| --- | --- |
| `blade_contract_exe_stage.fund_amount` | 语义修正为**款项金额**(HCS `fdFundAmount` 文档定义):付款审批通过按申请付款金额累加更新;承运商合同默认进度款计划行金额本就留空,首次累加即等于申请付款金额 |
| `row_op` | `none`(已同步无变更)/ `add`(新行/未同步行)/ `update`(已同步行编辑或款项金额累加)/ `delete`(软删行待上报) |
| `hcs_sync_status`(计划行) | 累加后已同步行置 `unsynced`,`hcs_sync_msg=「付款审批款项金额累加,待重推C103-9」`;C103-9 推送成功后清除标记置 synced |
| `hcs_stage_fd_id` | 合规阶段ID(C103-9 返回),行级 update/delete 必传;金蝶备注携带的「履约计划id」即此值——合同系统(HCS)只认自己的阶段ID,不认 TMS 本地主键 |
| 付款类型→款项性质 | `settlement_payment→progress`;`progress_advance`/`project_advance→prepayment` |
---
## 4. 代码改动清单(2026-10-22)
| 文件 | 改动 |
| --- | --- |
| `service/impl/ContractExeStageServiceImpl.java` | 新增 `findPaymentStageRow(contractId, fundNature)`(性质精确匹配→首条付款行兜底)与 `accumulatePaidAmountOnApprove(payment)`(原子累加 + 标记重推) |
| `service/IContractExeStageService.java` | 上述两方法接口声明 |
| `service/impl/PaymentApplicationServiceImpl.java` | `approve()` 审批通过事务内调用累加;新增 `bfiExeStageRemark()`,A103-1/A103-3 分录备注携带履约计划id |
| `support/hcs/HcsPaymentSyncSupport.java` | `pushPayment()` 编排插入第②步 `pushExeStageUpdate()`:调 `HcsExeStageSyncSupport.syncExeStages` 重推 C103-9 款项金额累加行 |
| `support/hcs/HcsPerfRecordSyncSupport.java` | C108 计划行匹配收敛到 `IContractExeStageService.findPaymentStageRow`(原"取第一条付款行"升级为先按性质精确匹配) |
| `constant/HcsSyncConstant.java` | 新增 `FUND_NATURE_PREPAYMENT` 常量与 `fundNatureOfPayment()` 映射 |
| `pojo/entity/HcsContractExeStage.java`、`blade-hcs-api/HcsExeplanStageDTO.java`、`HcsExeStageSyncSupport.java` | `fundAmount/fdFundAmount` 注释语义修正为「款项金额」(照 HCS 接口文档) |
---
## 5. 配置与开关(全部复用既有配置,无新增)
| 配置 | 作用 | 默认 |
| --- | --- | --- |
| `thirdParty.hcs.push.payment.sync-enabled` / `.cron` | 付款链任务(含新增的履约计划重推)开关与调度 | 关 / 每 5 分钟 |
| `thirdParty.hcs.push.perf-record.sync-enabled`(hcsProperties.perfRecord) | C108 履约记录开关(关闭时付款链仅推特批+履约计划重推) | 关 |
| `bfi.auto-sync.payment`(BfiProperties) | 审批通过自动推金蝶付款申请开关 | — |
| `thirdParty.hcs.exe-stage.fund-type` | C103-9 金额类型兜底值 | byAmount |
---
## 6. 边界与联调注意事项
1. **预付款不误计**:预付款类型匹配不到预付款计划行时跳过累加(不落入进度款行);若业务要求预付款也强制累加,需另行确认落行规则。
2. **款项金额不含原计划金额**:累加基于 0 起算(默认承运商进度款计划行金额留空,天然吻合);业务自维护了计划金额的行,累加值不会叠加在计划金额上——语义为「累计申请付款金额」,如需「计划金额+累加」口径需再确认。
3. **重试自愈**:C103-9 履约计划重推失败 → 付款申请整体置 failed → 下轮付款链重试(C108 走 C108-2 幂等更新,无副作用);重试超限后页面「推送合规/重推」人工兜底。
4. **竞态窗口**:付款审批早于合同首次 C103-9 同步完成时(极少见),计划行仍为 add 未同步——本次只累加金额不置 update,由合同链路首推携带累加后金额,不会产生重复行。
5. **金蝶备注长度**:`e_remark` 为金蝶非必传文本字段,拼接「履约计划id + 原备注」后长度上限需联调确认(超长会被金蝶强校验拦截时,可改为只送履约计划id)。
6. **HCS 侧回显**:款项金额经 C103-9 行级 update 覆盖 HCS 履约计划行金额,HCS 页面展示口径变化需与合规侧确认(原计划金额被累计付款金额覆盖属本需求的预期行为)。
@@ -0,0 +1,72 @@
# 付款申请作废对履约计划款项金额与履约记录的影响分析与补齐方案
> 结论先行:**目前付款申请作废不会同步修改 HCS 侧数据**——履约计划行款项金额不回减、履约记录不删除不更新,且作废单因状态不在扫描范围,即使本地置了重推标记也没有推送驱动。本文梳理现状、影响与补齐方案(截至 2026-10-22,配套提交 dcda231c)。
> 关联文档:`2026-10-22-付款审批履约计划款项金额累加与金蝶付款申请携带履约计划id业务流程梳理.md`
---
## 1. 作废现有处理范围(PaymentApplicationServiceImpl.voidBill)
| 处理 | 说明 |
| --- | --- |
| ✅ 门禁 | 仅审批通过(approved)可作废;金蝶锁单中(lockBusiData 未解锁)禁止作废 |
| ✅ 本地回滚 | 汇票占用释放、联动作废预结算预付记录、删除项目预付核销行、刷新结算单已付汇总 |
| ✅ 状态 | approvalStatus=voided、currentNode=已作废 |
| ❌ 履约计划行款项金额 | 不回减、不重推 C103-9 |
| ❌ 履约记录(C108) | 不删除、不更新 |
| ❌ 金蝶付款申请单 | 不联动金蝶删单(既有行为,另行评估) |
---
## 2. 影响分析
| # | 数据 | 作废后状态 | 影响 |
| --- | --- | --- | --- |
| 1 | 履约计划行款项金额(本地 fund_amount) | 审批时累加的申请金额**永久留存** | 本地计划行金额虚高 |
| 2 | HCS 履约计划行(fdFundAmount) | 已推送的累加金额**不会被修正** | 合规侧计划金额虚高,且本地后续修正也无法触达(见 §3) |
| 3 | 履约记录(C108) | 已新建的记录**继续挂在 HCS 计划行下**,实付金额口径也不再更新 | 合规侧展示已作废付款的履约付款数据 |
---
## 3. 连锁问题:作废单没有重推驱动
付款链定时任务扫描条件为 `approvalStatus = approved`(不含 voided/returned)。作废后该单永远不再进入扫描:
- 本地计划行即使置 `row_op=update`,也不会被付款链推送;
- 只能等**同合同下一笔付款审批**(pushPayment 第②步会捎带重推该合同全部待同步计划行)、或合同页手动重推时捎带;
- 长期无付款的合同,错误金额将一直停留在 HCS。
---
## 4. 补齐方案
### 4.1 履约计划款项金额回减(建议实施)
1. **回减**:`voidBill` 事务内,按审批累加的同一匹配规则(合同 + performType=payment + 款项性质精确匹配)找到计划行,`fund_amount = coalesce(fund_amount,0) - applied_amount` 原子回减(防并发);已同步行置 `row_op=update` + 待同步。
2. **重推驱动**:作废单进不了付款链扫描,需新增轻量事件(如 `HcsPaymentVoidedEvent`)+ AFTER_COMMIT 监听器,事务提交后直接调 `HcsExeStageSyncSupport.syncExeStages(contract)` 重推 C103-9(复用既有行级 update 凭 fdId 更新链路,失败仅告警落库待补偿)。
3. **回减为负保护**:若计划行金额已不足回减(如同行金额被业务编辑过),按 0 兜底并在日志/同步信息中标注,避免推送负数金额。
### 4.2 履约记录(受接口限制,待外部确认)
C108 仅有新建(C108-1)/更新(C108-2)接口,**无删除/作废接口**:
- 程序上无法清除 HCS 侧已生成的履约记录;
- 可选过渡口径(需与合规侧确认):a) 合规侧人工删除;b) HCS 扩展作废接口后 TMS 对接;c) C108-2 更新金额为 0/备注作废标识(语义是否被合规侧接受待确认)。
---
## 5. 待确认事项
| # | 事项 | 确认方 |
| --- | --- | --- |
| 1 | 作废是否需要回减履约计划款项金额(还是保留作为"历史发生额") | 业务 |
| 2 | C108 履约记录作废/删除的合规侧处理方式(人工/接口扩展/更新为0) | 合规侧 |
| 3 | 金蝶付款申请单是否需要联动作废/删单(现状:不联动) | 业务+财务 |
| 4 | 已作废单若金蝶侧已实付(先付款后作废的极端场景),回减与实付口径冲突如何处理 | 业务 |
---
## 6. 临时兜底(方案实施前的人工处理)
1. 发现作废单导致计划行金额虚高:合同履约规则页编辑该行金额(自动置 update),随后同合同任一付款审批或合同手动重推捎带 C103-9 修正;
2. HCS 侧履约记录:联系合规管理员人工删除。