From 3055aced246e5a27acfd9b5a6def29685f4b0676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Fri, 16 Oct 2026 19:46:52 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E7=BB=93=E7=AE=97?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8D=95=E6=8E=A5=E5=85=A5=E9=87=91=E8=9D=B6?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Ž¥入金蝶设计(未推送自动推-已推送冲销后重推新单).md | 290 ++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 doc/docs/业务流程/2026-10-16-结算调整单接入金蝶设计(未推送自动推-已推送冲销后重推新单).md diff --git a/doc/docs/业务流程/2026-10-16-结算调整单接入金蝶设计(未推送自动推-已推送冲销后重推新单).md b/doc/docs/业务流程/2026-10-16-结算调整单接入金蝶设计(未推送自动推-已推送冲销后重推新单).md new file mode 100644 index 00000000..d671f903 --- /dev/null +++ b/doc/docs/业务流程/2026-10-16-结算调整单接入金蝶设计(未推送自动推-已推送冲销后重推新单).md @@ -0,0 +1,290 @@ +# 结算调整单接入金蝶设计(未推送自动推 · 已推送手工冲销后重推新单) + +> 整理日期:2026-10-16 +> 目标:把「结算调整单」正式接入金蝶链路。核心约束——**原正式结算单一旦推送过金蝶,其本地数据一律冻结不动**;调整后的数据落在重推时新建的一张正式结算单上,用它向金蝶生成**全新**的财务应付/应收单。 +> 依据:`2026-09-20-应付单与付款申请同步金蝶全链路业务流程(A102-2保存-A102-4查询-A103-1下推-A103-2回写).md`、`2026-09-21-应收结算与开票申请同步金蝶全链路业务流程(A302-2保存-A302-4勾稽-A303-1新增-A303-4预检-A303-5回写).md`、`2026-10-05-审批通过自动同步金蝶设计(事件触发+定时重推+手动兜底).md`、`2026-10-09-金蝶单据锁定与解锁业务流程(...).md`、当前代码(探索核实)。 + +--- + +## 1. 背景与目标 + +结算调整单(`blade_settlement_adjustment`)用于在正式结算单审批通过后调整其费用项目与金额。目前它只有本地审批与一个假的 `repush`(拼 `K3AP+时间戳`),并未真正接入金蝶。 + +需要落地的三段操作: + +| 操作 | 内容 | +|---|---| +| 操作 1 新增调整单 | 只允许选**审核通过**且**未下推付款申请**的正式结算单;自动回带客商、调整类型、合同等 | +| 操作 2 审批通过 | 回写/记录调整 → 按原结算单金蝶状态分流(未推送→推原单;已推送→不动金蝶,提示财务手工冲销) | +| 操作 3 重新推送(高危) | 旧应付/应收单已在金蝶冲销后,把**调整后的数据**以**全新来源单据号**推成一张新的财务应付/应收单 | + +> 全程交互使用中文。 + +## 2. 名词与接口速查 + +| 代号 | 含义 | +|---|---| +| A102-2 | 财务应付单**保存**(推送) | +| A102-4 | 财务应付单**查询**(幂等预检;返回 `BfiApBillVO`,含 `iswrittenoff` / `hadwrittenoff`) | +| A302-2 | 财务应收单**保存**(推送) | +| A302-4 | 财务应收单**查询**(幂等预检,同上) | +| 防重键 | 金蝶来源单据号 `awrp_src_code`,由 `ExternalSrcCodeSupport.buildSrcCode(mainCode, partCodes)` 下划线拼接,规则 `结算单号_明细单据号` | +| 正式结算单 | `blade_formal_settlement`,单号 `FJS`(应付)/`SJS`(应收) + `yyyyMMdd` + 4 位流水(`nextNo`) | +| 结算调整单 | `blade_settlement_adjustment` | + +## 3. 现状与差距(代码核实) + +| # | 位置 | 现状 | 差距 | +|---|---|---|---| +| 1 | `SettlementAdjustmentServiceImpl.approve()`(约 221-261) | 仅改本地:回写原结算单费用、写变更记录、置 `approved`;`setKingdeeSyncStatus(formal.getKingdeeSyncStatus())`(推送前快照) | 未触发金蝶推送;未回填推送后的真实状态,导致前端「重新推送」按钮(条件 `approved && kingdeeSyncStatus='synced'`)不出现 | +| 2 | `SettlementAdjustmentServiceImpl.repush()`(约 207-217) | 假实现:拼 `K3AP+时间戳` 直接置 synced | 未校验冲销、未生成新单、未真正推送 | +| 3 | `FormalSettlementServiceImpl.syncKingdee/doSyncKingdee`(640/805) | 真实推送 + 落锁 + 失败重推链路;`doSyncKingdee` 在 `synced` 时**幂等早退** | 重推前须重置状态,否则被早退 | +| 4 | `KingdeeApBillSyncSupport.pushApBill`(220)/`queryApBillExistNos`(157) | A102-4 预检**不区分是否已冲销** | 冲销后同键重推会被预检判为"已存在"而跳过 | +| 5 | `BfiApBillVO` | 已含 `iswrittenoff`(154)/`hadwrittenoff`(158) | 尚未用于冲销判定 | +| 6 | `SettlementAdjustment` 实体 / `SettlementAdjustmentVO` | 实体无 `kingdeeBillNo`;VO 有(20) | 金蝶单号无处落库 | +| 7 | `IKingdeeBillLockService` | `lockAfterPush` / `checkEditable`;**冲销不触发解锁回调** | 原单锁记录会一直停在 `locked` | +| 8 | `IKingdeeBillLockService.checkEditable` | 仅开票申请、付款申请调用;**正式结算单未调用** | 正式结算单的编辑保护主要在前端 + `voidBill` 的 `synced` 拦截 | + +## 4. 关键决策(已确认) + +| 决策 | 结论 | +|---|---| +| **A 原单去向** | 已推送场景下**原单业务数据零改动**(费用/金额/状态全不动),只加 1 条变更记录 + 标记 + 解锁;新单是**全新独立单**,原单的下游关联(开票申请/收款认领/对账)**不迁移**,留在原单 | +| **B 展示标记** | 原单标「已调整」;新单标「调整后单」并显示原单号 | +| **B1 建单时机** | 「重新推送」时新建,审批阶段不动 | +| **B2 新单编号** | 走 `nextNo` 正式流水(`FJS`/`SJS` + 日期 + 4 位),**不**加后缀 | +| **B3 原单处理** | 保持原状,不做作废/删除 | +| **B4 调整单挂载** | 重推后调整单**改挂新单** | +| **B5 来源预结算** | 新单**重建** `FormalSettlementSource` 行(接续原单来源) | +| **B6 范围** | 应付、**应收都要做**(应付 A102-2/A102-4,应收 A302-2/A302-4) | +| **B7 列表** | 新旧两张单并存,带标识展示 | +| **C 冲销判定** | 只认 `hadwrittenoff=true`;部分冲销也放行 | +| **D 原单旧锁** | 重推时把原单旧锁标记为 `unlocked`,不改其它 | +| **E 未推送场景** | 审批通过后**自动推**(复用既有 `KingdeeBillApprovedEvent` 机制) | +| 新单状态 | 克隆后直接置 `approvalStatus=approved`,不再走审批 | +| 原单变更记录/unlock 时机 | 变更记录审批时写;`unlock` + `已调整`标记在**重推时**执行(审批时财务可能还没冲销) | +| **选单预检** | 操作 1 选定结算单后**预检**其金蝶冲销状态(软提示,不硬拦截);金蝶不可用时降级放行 | +| **重推冲销闸门** | 真正的**硬拦截**保留在操作 3 重推;预检不替代闸门 | + +## 5. 总体方案 + +### 5.1 流程图 + +> 操作 1 选单后会先做一次金蝶冲销**预检(软提示,不阻断)**,见第 6.2 节。 + +``` + ┌── 原结算单 kingdeeSyncStatus ──┐ +操作2 调整单审批通过 ──────────┤ │ + ▼ ▼ + 未推送(unsynced) 已推送(synced) + │ │ + ① 回写原单费用/金额 ① 原单业务数据【零改动】 + ② 原单写变更记录 ② 原单仅写 1 条变更记录 + ③ 发布 KingdeeBillApprovedEvent ③ 调整单置 approved + (formal-settlement, 原单id) ④ toast:原结算单已推送金蝶, + ④ AFTER_COMMIT → syncKingdee(原单) 请财务在金蝶手工完成旧应付单冲销处理 + ⑤ 回填调整单 synced + kingdee_bill_no + │ │ + │ 【财务在金蝶手工冲销旧应付/应收单】 + │ │ + │ 操作3 重新推送(高危) + │ │ + │ ① 校验调整单 approved + │ ② 旧键查 A102-4/A302-4 → 要求 hadwrittenoff=true + │ ③ 克隆原单 → 新正式结算单(+应用调整明细、重建 source) + │ ④ 新单号组新键推 A102-2/A302-2 + │ ⑤ 新单落锁 + 附件 + │ ⑥ 调整单改挂新单 + 回填 kingdee_bill_no/synced + │ ⑦ 原单:标记「已调整」+ 旧锁 unlocked + │ ⑧ 写变更记录;失败置 failed 可重试 + ▼ ▼ + 金蝶应付/应收单 金蝶【全新】应付/应收单 + (同原单号来源) (新单号来源,旧单已冲销) +``` + +### 5.2 三条不变式 + +1. **原单一经推送金蝶即冻结**:已推送场景下原单除「变更记录 / 标记 / 解锁」外,任何业务字段与状态均不改。 +2. **调整后的金额只体现在新单**:未推送场景作用于原单;已推送场景作用于重推时克隆出的新单。两者共用同一段「应用调整到结算单」的逻辑。 +3. **重推必须锚定金蝶侧已冲销**:以 A102-4/A302-4 的 `hadwrittenoff=true` 为唯一放行条件,避免重复生成应付/应收单。 + +## 6. 操作 1|新增调整单 + +### 6.1 选单来源(保持现状) + +选单来源 `candidateFormalSettlements`(`SettlementAdjustmentServiceImpl` 约 98)已符合要求,不改: + +- 仅 `approvalStatus = approved` 的正式结算单; +- 排除已下推付款申请的单(`tailPaymentSettlementIds`,约 324); +- 排除收付完成/尾款等不可调整(`adjustableFormalSettlement`,约 295); +- 保存时自动回带客商、调整类型、合同、原结算金额等。 + +### 6.2 选单后金蝶冲销预检(新增,软提示) + +选定关联结算单号、回带信息后,前端调后端**预检接口**: + +1. 若结算单**未推送金蝶**(`kingdeeSyncStatus != synced`)→ 返回"未推送",正常继续,后续按设计文档走审批自动推分支; +2. 若**已推送**→ 按防重键(`正式结算单号_结算明细单据号`)批量调 A102-4(应付)/ A302-4(应收),读 `hadwrittenoff`: + - `hadwrittenoff = true` → 返回"已推送·旧单已冲销",提示"旧应付/应收单已冲销,审批后可重新推送新单"; + - `hadwrittenoff != true`(含查询不到)→ 返回"已推送·旧单未冲销",给出**醒目提示**:"该结算单已推送金蝶且旧单尚未冲销,审批通过后需先由财务在金蝶完成冲销,才能重新推送"。 + +**口径**: + +- 预检为**软提示**,不阻断建单(默认建议);是否升级为硬拦截由业务另行确认。 +- **降级**:金蝶查询超时/不可用/接口异常时,预检返回"未校验",**不阻断**建单与回带,由后续审批分流与重推闸门兜底。 +- 预检结果**只用于提示**,不作为后续环节的结论缓存:审批(操作 2)、重推(操作 3)各自按**当下**状态重新判定。 + +> 为什么不把硬拦截放在选单:冲销是金蝶侧**不可逆/需人工撤销**的对外操作,而审批只是 TMS 内部决策。若选单即要求已冲销,会出现"旧单已冲销、调整单却被驳回/删除"的**应付单空档**(详见第 13 节)。 + +## 7. 操作 2|审批通过 + +### 7.1 未推送场景(原单 `kingdeeSyncStatus != synced`) + +沿用现有 `approve()` 逻辑并补推送: + +1. `applyAdjustmentToSettlement(原单, 调整单)`:回写费用项目、明细、合计、重算结算金额(**抽成公共方法**); +2. 原单追加变更记录、挂载调整单; +3. 调整单置 `approved`; +4. 事务提交后发布 `KingdeeBillApprovedEvent("formal-settlement", 原单id)`,由既有 `KingdeeApprovalSyncListener`(AFTER_COMMIT + 域开关 + 全量 try/catch)调 `formalSettlementService.syncKingdee(原单id)` 完成 A102-2/A302-2 推送 + 落锁; +5. 推送成功回填调整单 `kingdeeSyncStatus=synced` + `kingdeeBillNo`;失败置 `failed`(不改「已审批」结果,进入重推循环)。 + +> 复用 `2026-10-05-审批通过自动同步金蝶设计` 的事件机制,不另写推送路径;自动推送失败不影响审批结果。 + +### 7.2 已推送场景(原单 `kingdeeSyncStatus = synced`)★ 关键约束 + +**原单业务数据一律不动**,只做: + +1. 原单追加 1 条变更记录:`结算调整单XX审批通过,调整金额XX,待重推金蝶`; +2. 调整单置 `approved`(`kingdeeSyncStatus` 保持 `synced` 快照,前端据状态显示「重新推送」入口); +3. 返回前端 toast 提示:**「原结算单已推送金蝶,请财务人员在金蝶手工完成旧应付单冲销处理」**。 + +> 不做:不回写原单费用/金额、不修改旧应付、不自动推送新单、不作废原单。 + +## 8. 操作 3|重新推送(高危) + +`repush(调整单id)` 重写为: + +1. **前置校验**:调整单 `approvalStatus = approved`;原单存在。 +2. **冲销校验(硬闸门)**:取调整单号 + 原单号,按旧键(`原结算单号_明细单据号`)查 A102-4(应付)/ A302-4(应收);要求存在且 `hadwrittenoff = true`,否则抛出「原应付/应收单尚未在金蝶冲销,禁止重推」。 +3. **克隆新正式结算单**:`cloneFormalSettlement(原单, 调整单)` + - 新号由 `nextNo` 取正式流水(`FJS`/`SJS`+日期+4 位); + - 克隆明细、明细费项、汇总费项、发票、汇率等; + - 调 `applyAdjustmentToSettlement(新单, 调整单)` 应用调整(金额 = 原单 + 调整); + - **重建** `FormalSettlementSource`(接续原单来源预结算); + - `approvalStatus = approved`、`kingdeeSyncStatus = unsynced`、`originFormalSettlementNo = 原单号`; + - 附件复制/挂载。 +4. **推送新单**:新单号天然组成新防重键(`新结算单号_明细单据号`),调 `syncKingdee(新单id)`(内部 A102-2/A302-2),**推前把新单状态置 `unsynced`** 以免被 `doSyncKingdee` 的 `synced` 早退。 +5. **落锁**:新单 `lockAfterPush`(`TARGET_AP_BILL`/`TARGET_AR_BILL`)+ 附件上传。 +6. **改挂 + 回填**:调整单 `formalSettlementId/No` 改指新单,回填 `kingdeeBillNo` + `kingdeeSyncStatus=synced`。 +7. **原单收尾**:标记 `adjustStatus='已调整'` + 旧锁记录置 `unlocked`;追加重推变更记录。业务字段不动。 +8. **失败处理**:新单 `kingdeeSyncStatus=failed` + 失败原因,支持复用 `retryKingdeeFailed` 重试(重试即从第 4 步继续,克隆已存在则跳过,注意幂等)。 + +## 9. 关键机制 + +### 9.1 防重键与新单号 + +- 金蝶 `awrp_src_code = 结算单号_明细单据号`。因为重推**新建了真实结算单记录**(新 `FJS/SJS` 号),新键天然与原键不同,**无需再额外拼调整单号**——早期「拼调整单号改防重键」的方案已被「新建结算单」覆盖。 +- 旧键(原结算单号)用于查冲销;新键(新结算单号)用于生成全新应付/应收单。 + +### 9.2 冲销判定 + +- 只认 `hadwrittenoff = true`(`BfiApBillVO` 158 / `BfiArBillVO` 138)。部分冲销同样放行。 +- `iswrittenoff` 不单独作为放行依据。落地前需用 Apifox 实测 A102-4/A302-4 确实回传 `hadwrittenoff`。 +- **两处判定,用途不同**:操作 1 选单预检 = 软提示(第 6.2 节);操作 3 重推 = **硬闸门**(第 8 节)。预检不替代闸门——冲销在金蝶可被撤销,且建单到重推期间状态可能变化。 + +### 9.3 克隆新正式结算单 + +- 复用现有「保存/组装」逻辑而非另写:明细、费项、汇总、来源、附件、汇率、收付款单位等按原单快照复制。 +- 新单不再走审批流(直接 `approved`),因此不写审批变更记录以外的流程痕迹;是否补一条「由调整单XX生成」变更记录可选。 + +### 9.4 金蝶锁 + +- 新单推送成功 → `lockAfterPush` 落锁,状态与既有一致。 +- 原单旧锁:重推时置 `unlocked`(决策 D)。因正式结算单未调用 `checkEditable`,解锁主要影响前端标签,不构成后端编辑漏洞。 + +### 9.5 失败与重试 + +- 复用 `markKingdeeSyncFailed` / `retryKingdeeFailed` 模式:失败落 `failed` + 原因,可被定时重推任务扫描。 +- 克隆步骤需幂等:以 `originFormalSettlementNo + 调整单` 作为唯一判断,避免重试时重复建单。 + +### 9.6 展示标记 + +| 对象 | 标记 | 来源 | +|---|---|---| +| 新正式结算单 | 「调整后单」(并显示原单号) | `origin_formal_settlement_no` 非空 | +| 原正式结算单 | 「已调整」 | `adjust_status = 'adjusted'` | +| 结算调整单 | 金蝶状态 / 金蝶单据号 | `kingdee_sync_status` / `kingdee_bill_no` | + +## 10. 数据模型改动(DDL) + +新增脚本 `doc/sql/transport/blade_settlement_adjustment_kingdee_20261016.sql`(多数据库脚本同步,排序规则 `utf8mb4_general_ci`): + +```sql +-- 结算调整单:落金蝶单据号 +ALTER TABLE blade_settlement_adjustment ADD COLUMN kingdee_bill_no VARCHAR(64) NULL COMMENT '金蝶单据号'; + +-- 正式结算单:来源原单号(新单) + 调整标记(原单) +ALTER TABLE blade_formal_settlement ADD COLUMN origin_formal_settlement_no VARCHAR(64) NULL COMMENT '来源原正式结算单号(结算调整重推生成)'; +ALTER TABLE blade_formal_settlement ADD COLUMN adjust_status VARCHAR(32) NULL COMMENT '调整状态:adjusted=已被结算调整'; +``` + +同时生成新增按钮(「推送金蝶」「重新推送」)的菜单插入 SQL。 + +## 11. 改动清单(文件级) + +| # | 文件 | 改动 | +|---|---|---| +| 1 | `blade_service_api/.../pojo/entity/SettlementAdjustment.java` | 加 `kingdeeBillNo` | +| 2 | `blade_service_api/.../pojo/entity/FormalSettlement.java` | 加 `originFormalSettlementNo`、`adjustStatus` | +| 3 | `blade_service_api/.../pojo/vo/SettlementAdjustmentVO.java`、`FormalSettlementVO.java` | 补展示字段/翻译 | +| 4 | `SettlementAdjustmentServiceImpl.approve()` | 未推送→发 `KingdeeBillApprovedEvent` + 回填;已推送→仅变更记录 | +| 5 | `SettlementAdjustmentServiceImpl.repush()` | 重写:冲销校验 → 克隆新单 → 推 → 落锁 → 改挂 → 回填 → 原单标记/解锁 → 失败置 failed | +| 6 | `SettlementAdjustmentServiceImpl` | 抽公共方法 `applyAdjustmentToSettlement(结算单, 调整单)` | +| 7 | `FormalSettlementServiceImpl`(或新 support) | 新增 `cloneFormalSettlement(...)`(含重建 `FormalSettlementSource`) | +| 8 | `FormalSettlementServiceImpl` / `KingdeeApBillSyncSupport` | 冲销查询(A102-4/A302-4 读 `hadwrittenoff`)封装 | +| 9 | `FormalSettlementWrapper` / 列表 Mapper | 回填 `adjustStatus` / `originFormalSettlementNo` | +| 10 | 前端 `settlement-adjustment.vue`、`settlementAdjustment.js` | 金蝶状态/单号列、「推送金蝶」「重新推送」按钮(重推前冲销预检);选单后调预检接口并展示软提示 | +| 11 | 前端 `formal-settlement.vue` / `formal-settlement-table-panel.vue` / 详情 | 「调整后单 / 已调整」标识、查看弹窗补重推入口 | +| 12 | `doc/sql/...` | DDL + 菜单 SQL | +| 13 | `SettlementAdjustmentServiceImpl` + `SettlementAdjustmentController` | 新增选单预检接口(返回 未推送 / 已推送已冲销 / 已推送未冲销;金蝶异常降级返回未校验) | + +## 12. 边界场景推演 + +| 场景 | 行为 | +|---|---| +| 调整单未审批就重推 | 前置校验拒绝(要求 approved) | +| 旧应付单未冲销 | A102-4 返回 `hadwrittenoff=false`/不存在 → 拒绝,提示先冲销 | +| 部分冲销 | `hadwrittenoff=true` → 放行 | +| 旧应付单被删除(非冲销) | 查询不到 → 拒绝(不满足 `hadwrittenoff=true`) | +| 已推送场景审批后财务未冲销 | 重推被拦;原单与新单均未产生重复应付单 | +| 重推推送失败 | 新单置 `failed` + 原因;原单已标「已调整」+ 解锁;重试从推送步骤续上 | +| 重推成功但落库前崩溃 | 重试时幂等判断:已存在同一来源新单则跳过克隆,仅补推/补回填 | +| 应收结算单(A302-2/A302-4) | 走同一套逻辑,应收侧下游关联不迁移(决策 A) | +| 原单锁残留 | 重推时统一置 `unlocked` | +| 未推送场景自动推失败 | 审批仍成功;调整单 `failed`,走重推循环 | +| 选单时结算单已推送且旧单未冲销 | 预检返回"未冲销"并**软提示**(不阻断建单);审批后需先冲销再重推 | +| 选单时金蝶超时/不可用 | 预检**降级为"未校验"**,不阻断建单;由审批分流与重推闸门判定 | +| 建单时未推送、审批时已被推送到金蝶 | 审批按**当下**状态走"已推送"分支:不自动推、提示财务冲销;预检结论不沿用 | +| 选单时已冲销、重推前被金蝶撤销冲销 | 重推硬闸门重新查询 `hadwrittenoff` → 拦截,提示先冲销 | + +## 13. 风险与待验证 + +1. **A102-4/A302-4 的 `hadwrittenoff` 实际回传**:必须先用 Apifox 实测,否则整套冲销判定可能推翻。 +2. **克隆新单**:`FormalSettlementSource` 重建会牵动预结算占用(`PreSettlement.formalSettlementNo`),是最易产生脏数据的一步,需单独小步验证。 +3. **原单下游关联不迁移**(开票申请/收款认领/对账):新单为全新独立单,应收场景下新单将显示「未开票/未收款」,属已确认口径,但需业务知悉。 +4. **重推幂等**:克隆需以来源唯一键保证不重复建单,验证重试与并发(同一调整单重复点击)。 +5. **正式结算单无 `checkEditable`**:解锁不构成后端漏洞,但需确认前端在 `approved` 下仍不暴露编辑入口,避免原单被改。 +6. **冲销前置到选单的风险(故不做硬拦截)**:冲销是金蝶侧不可逆/需人工撤销的对外操作,审批是 TMS 内部决策。若选单即硬拦截(要求已冲销),会出现"旧单已冲销、调整单却被驳回/删除"的**应付单空档**,需人工在金蝶恢复冲销。故预检仅作软提示,硬闸门留在重推。 +7. **预检接口不可成为单点**:金蝶不可用时必须降级放行,否则远程依赖会阻断调整单的建单与回带。 + +--- + +## 14. 落地实现备注(2026-10-16) + +1. **未推送场景回填**:推送成功/失败分别由 `FormalSettlementServiceImpl.doSyncKingdee` / `markKingdeeSyncFailed` 按 `formal_settlement_id + approval_status=approved` 回填调整单 `kingdee_sync_status`(synced/failed)与 `kingdee_bill_no`,复用 §7.1 的事件自动推送链路,未新增推送路径。 +2. **重推为「非事务编排 + 段内事务」**:`repush` 依次执行 —— 冲销硬闸门(旧键查 A102-4/A302-4,`hadwrittenoff=true`)→ 自代理事务 `cloneAndApplyForRepush`(克隆新单 + 应用调整,原子)→ `syncKingdee(新单)` 推送 → 成功后调整单改挂新单 + 原单标记「已调整」+ 旧锁解锁。推送失败**不回滚已克隆新单**,重试按 `origin_formal_settlement_no = 调整单当前关联单号` 幂等复用,从推送步骤续上(§8.8、§9.5)。 +3. **改挂时机**:调整单改挂新单在**推送成功之后**,故成功后再点重推会被冲销闸门拦截(无副作用);如需彻底隐藏,前端按「关联单是否克隆单」判断。 +4. **新单不克隆发票**:遵循决策 A「下游关联不迁移」,新单 `invoiceStatus=unreceived`、`paymentStatus=unpaid`,`invoiceAmount/paidAmount/appliedPaymentAmount` 归零,发票不复制。 +5. **菜单**:新增按钮 `settlement_adjustment_sync`(推送金蝶);`settlement_adjustment_repush`(重新推送)复用既有菜单,不重复插入。 +6. **待实测**:A102-4/A302-4 的 `hadwrittenoff` 实际回传(§13.1);克隆新单对 `PreSettlement` 占用的影响(§13.2)。 +