From aa22ad98d84f722ed6f400f35e16029986381fbd 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, 18 Sep 2026 14:15:07 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E4=BF=AE=E5=A4=8D=E4=BB=98=E6=AC=BE?= =?UTF-8?q?=E7=94=B3=E8=AF=B7=E5=90=8C=E6=AD=A5=E9=87=91=E8=9D=B6=E6=96=AD?= =?UTF-8?q?=E9=93=BE=E5=90=8E=E9=87=8D=E6=8E=A8=E8=A2=AB=E6=8B=A6=E6=88=AA?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E6=8E=A8=E9=80=81=E4=B8=8E=E5=9B=9E?= =?UTF-8?q?=E5=86=99=E5=85=A8=E9=93=BE=E8=B7=AF=E6=94=AF=E6=8C=81=E5=B9=82?= =?UTF-8?q?=E7=AD=89=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - A102-2 应付单已生成时幂等跳过保存并续推 A103-1,A103-2 勾稽已下推成功则直接回填 - 预付款 A103-3 恢复推送前增加 A103-2 预检,避免重复推送 - 定时回写任务将生成失败单据纳入 A103-2 轮询,自动纠偏远端已生成单据 - 抽取公共方法:A103-2 取最近修改、分录标识生成、批量分批、统一日期格式(yyyy-MM-dd HH:mm:ss) - 新增全链路逻辑梳理文档(推送链路与回写链路连贯性) --- .../impl/PaymentApplicationServiceImpl.java | 210 ++++++++++++++---- ...蝶同步全链路逻辑梳理(推送链路与回写链路连贯性).md | 145 ++++++++++++ 2 files changed, 307 insertions(+), 48 deletions(-) create mode 100644 doc/docs/业务流程/2026-09-18-付款管理金蝶同步全链路逻辑梳理(推送链路与回写链路连贯性).md diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java index 637ed3f4..38fb5d01 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java @@ -118,6 +118,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Consumer; import java.util.stream.Collectors; /** 付款申请服务实现。 @author Chill */ @@ -395,7 +396,9 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl applications = list(Wrappers.lambdaQuery() .eq(PaymentApplication::getApprovalStatus, APPROVED) - .in(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED, KingdeeBfiConstant.KINGDEE_STATUS_PAYING) + .in(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED, + KingdeeBfiConstant.KINGDEE_STATUS_PAYING, KingdeeBfiConstant.KINGDEE_STATUS_FAILED) .orderByAsc(PaymentApplication::getId)); IPaymentApplicationService self = SpringUtil.getBean(IPaymentApplicationService.class); int updated = 0; @@ -489,18 +511,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl paymentNos = applications.stream() .map(PaymentApplication::getPaymentNo).filter(Func::isNotEmpty).distinct().toList(); - for (int from = 0; from < paymentNos.size(); from += PAY_RESULT_QUERY_BATCH) { - List batch = paymentNos.subList(from, Math.min(from + PAY_RESULT_QUERY_BATCH, paymentNos.size())); - for (BfiPayApplyVO payApply : queryPayApplyPages(batch)) { - if (Func.isEmpty(payApply.getAwrpSrcCode())) { - continue; - } - BfiPayApplyVO existing = payApplyMap.get(payApply.getAwrpSrcCode()); - if (existing == null || strCompare(payApply.getModifytime(), existing.getModifytime()) > 0) { - payApplyMap.put(payApply.getAwrpSrcCode(), payApply); - } - } - } + batchForeach(paymentNos, PAY_RESULT_QUERY_BATCH, batch -> + payApplyMap.putAll(latestPayApplyBySrcCode(queryPayApplyPages(batch)))); for (PaymentApplication application : applications) { BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo()); if (payApply == null) { @@ -681,6 +693,75 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl 最近修改的付款申请单 + */ + private Map latestPayApplyBySrcCode(List payApplies) { + Map latestBySrcCode = new HashMap<>(); + for (BfiPayApplyVO payApply : payApplies) { + if (Func.isEmpty(payApply.getAwrpSrcCode())) { + continue; + } + BfiPayApplyVO existing = latestBySrcCode.get(payApply.getAwrpSrcCode()); + if (existing == null || strCompare(payApply.getModifytime(), existing.getModifytime()) > 0) { + latestBySrcCode.put(payApply.getAwrpSrcCode(), payApply); + } + } + return latestBySrcCode; + } + + private BfiPayApplyVO queryLatestPayApply(String paymentNo) { + return latestPayApplyBySrcCode(queryPayApplyPages(List.of(paymentNo))).get(paymentNo); + } + + /** + * A103-2 恢复勾稽回填:按付款申请号查询金蝶付款申请单是否已生成, + * 已生成则回填单据id与编号(编号覆盖 kingdeeBillNo,与定时回写任务语义一致)并返回星瀚单据id; + * 未生成返回 null,由调用方继续推送(推送接口真正报错才算同步失败) + * + * @param entity 付款申请实体 + * @return 星瀚付款申请单id,金蝶未生成返回 null + */ + private String reconcileExistPayApply(PaymentApplication entity) { + BfiPayApplyVO existPayApply = queryLatestPayApply(entity.getPaymentNo()); + if (existPayApply == null || existPayApply.getId() == null) { + return null; + } + String kingdeeBillId = String.valueOf(existPayApply.getId()); + if (Func.isNotEmpty(existPayApply.getBillno())) { + entity.setKingdeeBillNo(existPayApply.getBillno()); + } + log.info("付款申请[{}]经 A103-2 查得金蝶付款申请单已生成(单据编号[{}]),跳过重复推送直接回填", + entity.getPaymentNo(), existPayApply.getBillno()); + return kingdeeBillId; + } + + /** + * 列表按批大小切分逐批处理(末批可能不足批大小),远程接口批量入参分批用,避免单次过滤条件过长 + * + * @param items 待处理列表 + * @param batchSize 批大小 + * @param batchConsumer 批处理逻辑 + * @param 元素类型 + */ + private void batchForeach(List items, int batchSize, Consumer> batchConsumer) { + for (int from = 0; from < items.size(); from += batchSize) { + batchConsumer.accept(items.subList(from, Math.min(from + batchSize, items.size()))); + } + } + /** * A102-4 批量查询已生成财务应付单:来源单据号批量 + 外部系统编号(防重键),自动翻页, * 用于推送前幂等预检(awrp_src_code 支持多值,整批一次查询消除逐单远程查询 N+1) @@ -691,11 +772,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl queryApBillExistNos(List awrpSrcCodes) { Map existBillNos = new LinkedHashMap<>(); // 防重键批量分批查询,避免单次过滤条件过长 - for (int from = 0; from < awrpSrcCodes.size(); from += PAY_RESULT_QUERY_BATCH) { - List batch = awrpSrcCodes.subList(from, Math.min(from + PAY_RESULT_QUERY_BATCH, awrpSrcCodes.size())); - if (batch.isEmpty()) { - continue; - } + batchForeach(awrpSrcCodes, PAY_RESULT_QUERY_BATCH, batch -> { BfiApBillQueryDTO param = new BfiApBillQueryDTO(); param.setAwrpSrcCode(batch); param.setAwrpSrcSys(bfiPayApplySrcSys); @@ -708,7 +785,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl entryGroups, List documentNos, KingdeeSyncContext context) { - // A102-4 幂等预检:按防重键查询金蝶是否已生成财务应付单(批量同步时整批已预热缓存,此处仅懒加载未命中编码),已处理则终止后续推送流程并提示用户 + private BfiApBillPushOutcome pushBfiApBill(PaymentApplication entity, List entryGroups, List documentNos, KingdeeSyncContext context) { + // A102-4 幂等预检:按防重键查询金蝶是否已生成财务应付单(批量同步时整批已预热缓存,此处仅懒加载未命中编码), + // 已存在则幂等跳过保存,由调用方经 A103-2 预检后续推付款申请(A103-1)或直接回填已有付款申请单 String apBillSrcCode = ExternalSrcCodeSupport.buildSrcCode(entity.getPaymentNo(), documentNos); String existBillNo = context.apBillNoCache().computeIfAbsent(apBillSrcCode, code -> queryApBillExistNos(List.of(code)).getOrDefault(code, "")); if (Func.isNotEmpty(existBillNo)) { - throw new ServiceException("推送金蝶失败:付款申请[" + entity.getPaymentNo() + "]的财务应付单已生成" - + (Func.isBlank(existBillNo) ? "" : "(单据编号" + existBillNo + ")") + ",请勿重复推送"); + log.info("付款申请[{}]金蝶财务应付单已生成(单据编号[{}]),幂等跳过 A102-2 保存,继续后续下推流程", entity.getPaymentNo(), existBillNo); + return new BfiApBillPushOutcome(existBillNo, true); } ContractManage contract = requireContract(entity, context); // 单据类型按映射字典转换:默认服务采购应付,金蝶确认其他类型后修改 BFI_AP_BILL_BILLTYPE_NAME 即可 @@ -882,8 +974,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl entryGroups, KingdeeSyncContext context) { ContractManage contract = requireContract(entity, context); String orgNumber = bfiOrgNumber(contract); - // 申请日期:yyyy-MM-dd 00:00:00(与 A102-2 单据日期格式统一) - String applyDateText = (entity.getApplyDate() == null ? LocalDate.now() : entity.getApplyDate()).atStartOfDay().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + // 申请日期:yyyy-MM-dd HH:mm:ss(与 A102-2 单据日期格式统一) + String applyDateText = bfiDateTimeText(entity); BfiPayApplyPushDTO param = new BfiPayApplyPushDTO(); param.setAwrpSrcCode(entity.getPaymentNo()); param.setAwrpSrcSys(bfiPayApplySrcSys); @@ -1011,8 +1103,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl entryGroups, List documentNos) { } + /** + * 财务应付单(A102-2)推送结果 + * + * @param billNo 财务应付单单据编号(金蝶已存在时返回已有单据编号) + * @param alreadyExist 金蝶是否已存在该防重键的财务应付单,true 表示本次幂等跳过保存(上次同步已在后续环节中断) + */ + private record BfiApBillPushOutcome(String billNo, boolean alreadyExist) { + } + /** * 解析付款申请关联的正式结算明细,按货物编码+计量单位聚合为应付单分录组。 *

物料编码:货物费用行/结算明细货物类型(支持“、”分隔多类型)匹配货物类型档案货物编码; @@ -1117,6 +1218,19 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl 关联文档: +> - `2026-09-16-付款管理正式付款同步金蝶(财务应付单+付款申请下推)技术文档.md` +> - `2026-09-17-付款管理金蝶付款结果定时回写(A103-2付款申请单查询+A103-4付款处理查询)技术文档.md` +> - `../TMS业财融合-集成流程图文字版与业务流程梳理.md`(§4.3 流程步骤、§4.4 关键规则) +> +> 本文为 2026-09-18 幂等恢复改造后的全链路现状梳理,代码落点 `PaymentApplicationServiceImpl`。 + +## 1. 背景与范围 + +付款申请审批通过后与金蝶(星瀚)的双向同步: + +- **推送链路(TMS → 金蝶)**:正式结算付款两步链路 A102-2 财务应付单保存 → A103-1 应付下推付款申请;预付款一步链路 A103-3 预付款保存。 +- **回写链路(金蝶 → TMS)**:定时任务经 A103-2 付款申请单查询回写单据与付款状态,经 A103-4 付款处理查询回填付款记录。 + +原实现两步推送同处一个本地事务,A102-2 成功后 A103-1 失败会导致"金蝶已生成、本地回滚为生成失败"的断链,重试被"请勿重复推送"拦截而死锁。本次改造确立全链路恢复原则: + +> **以金蝶侧实际生成为准:已生成则幂等跳过/回填勾稽,未生成才续推;推送接口真正报错才算同步失败。** + +## 2. 接口与角色清单 + +| 接口 | 名称 | 方向 | 方法(TMS 侧) | 用途 | +| --- | --- | --- | --- | --- | +| A102-2 | 财务应付单保存 | 推送 | `pushBfiApBill` | 正式结算付款第一步 | +| A102-4 | 财务应付单查询 | 查询 | `queryApBillExistNos` | 推送前幂等预检(防重键) | +| A103-1 | 付款申请保存(应付下推) | 推送 | `pushBfiPayApplyPush` | 正式结算付款第二步 | +| A103-2 | 付款申请单查询 | 查询 | `queryPayApplyPages` / `queryLatestPayApply` | 恢复勾稽 + 定时回写 | +| A103-3 | 付款申请保存(预付款) | 推送 | `pushBfiPayApply` | 预付款一步链路 | +| A103-4 | 付款处理查询 | 查询 | `queryPayBillPages` | 回填付款记录 | + +**防重键体系**(`awrp_src_sys = WLTMS`,常量统一在 `KingdeeBfiConstant`): + +| 键 | 取值 | 使用接口 | +| --- | --- | --- | +| 付款申请单来源单号 | `awrp_src_code = paymentNo` | A103-1 / A103-2 / A103-3 | +| 财务应付单来源单号 | `awrp_src_code = paymentNo_结算明细单据号拼接`(`ExternalSrcCodeSupport.buildSrcCode`) | A102-2 / A102-4 | +| 分录标识 | `bfiEntryCode = paymentNo-分录序号(1 起)` | A102-2 `awrp_entry_code`、A103-1 `awrp_finapbill_entryid` / `awrp_entry_code`、A103-3(序号固定 1) | + +公共约定:各接口日期文本统一 `yyyy-MM-dd HH:mm:ss`(`bfiDateTimeText`,申请日期为空取当天);分页查询统一 `BfiUtil.queryAllPages` 自动翻页(页大小 200、最大 50 页保护);批量入参按 50 单/批(`batchForeach`);A103-2 结果按来源单号取最近修改一条(`latestPayApplyBySrcCode`)。 + +## 3. 状态机 + +本地 `kingdeeStatus` 流转与 A103-2 状态映射(`kingdeeResultStatus`): + +``` +unsynced ──推送成功──> synced ──paystatus=Inpayment──> paying ──paystatus=Alreadypay──> paid(终态) + │ │ │ + │ └──billstatus=D──> closed(终态) └──billstatus=D──> closed(终态) + └──推送异常(事务回滚 + 独立事务落状态)──> failed ──重推成功或回写勾稽纠偏──> synced/paying/paid +``` + +| A103-2 返回 | 本地状态 | 是否终态 | +| --- | --- | --- | +| paystatus=Alreadypay | paid | 是(不再轮询) | +| billstatus=D(已关闭) | closed | 是(不再轮询) | +| paystatus=Inpayment | paying | 否(继续轮询) | +| 其余(A暂存/B已提交/C已审核 + Unpaid) | synced | 否(继续轮询) | +| 无返回(金蝶未生成) | 状态不变 | — | + +门禁:仅 `approvalStatus = approved` 允许同步;已 synced/paying/paid/closed 在 `doSyncKingdee` 入口幂等跳过并返回既有单据 id。 + +## 4. 推送链路完整逻辑(TMS → 金蝶) + +### 4.1 入口与事务边界 + +- 单条:`POST /sync-kingdee` → `syncKingdee(id)`;批量:`POST /sync-kingdee-batch` → `syncKingdeeBatch(ids)`。 +- 经自代理逐单调用 `doSyncKingdee`:**单据级事务**,单条失败只回滚自身,批量不中断。 +- 失败时在独立事务 `markKingdeeSyncFailed` 落 `failed`(仅 approved 单据生效)。 +- 批量同步共享 `KingdeeSyncContext`:档案缓存(合同/客商/项目/货物类型/结算明细)+ 应付单防重键缓存,批量 A102-4 预热一次(`preloadApBillExistNos`,仅正式结算单据),消除逐单远程查询 N+1。 + +### 4.2 正式结算付款链路(settlement_payment) + +| 步骤 | 动作 | 说明 | +| --- | --- | --- | +| 1 | `requirePayableSettlement` | 结算类型路由:应付放行,应收(A302-2)待业务确认暂拦截 | +| 2 | `resolveBfiEntryGroups` | 结算明细按货物编码+计量单位聚合分录组(金额取结算金额含税/不含税) | +| 3 | A102-4 幂等预检 | 按防重键查应付单是否已生成(上下文缓存优先,懒加载兜底) | +| 3a | **已生成 → 幂等跳过保存**,返回已有单据编号并回填 `kingdeeBillNo` | 说明上次同步已在后续环节中断,应续推而非拦截报错 | +| 3b | 未生成 → A102-2 保存 | 仅校验保存成功,应付单 id 不落库;单据编号回填 `kingdeeBillNo` | +| 4 | **已存在时先 A103-2 恢复勾稽**(`reconcileExistPayApply`) | 付款申请单已生成 → 回填 `kingdeeBillId/kingdeeBillNo`,直接视为同步成功,跳过下推 | +| 5 | 勾稽未命中 → A103-1 应付下推 | 分录经 `bfiEntryCode` 与 A102-2 明细一一关联;返回星瀚单据 id | +| 6 | 落库 | `kingdeeBillId = 付款申请单 id`,`kingdeeStatus = synced` | + +### 4.3 预付款链路(project_advance / progress_advance) + +| 步骤 | 动作 | 说明 | +| --- | --- | --- | +| 1 | 同步前状态为 `failed` 时先 A103-2 恢复勾稽 | 金蝶已生成 → 回填并视为成功,避免重推产生重复单据;首推(状态非 failed)不多一次远程查询 | +| 2 | 勾稽未命中 → A103-3 保存 | 不绑应付单;返回星瀚单据 id | +| 3 | 落库 | 同 4.2 第 6 步 | + +## 5. 回写链路完整逻辑(金蝶 → TMS) + +触发:`PaymentKingdeeResultTask` 定时任务(默认每 10 分钟,`thirdParty.bfi.payResult.syncEnabled/cron`),也可手动 `POST /sync-kingdee-result`;逻辑收敛在 `syncKingdeePaymentResult()`。 + +### 5.1 ① A103-2 单据与付款状态回写 + +- 轮询范围:`approved` 且 `kingdeeStatus IN (synced, paying, failed)`;paid/closed 终态不再轮询。 +- **failed 纳入勾稽**:金蝶已生成说明上次推送实际成功(如下推请求超时但远端已落单、本地事务回滚),查到即经 `applyPayApplyResult` 回填 `kingdeeBillNo/billno`、`kingdeeBillId/id`、状态、已付金额(分录 `e_paidamt` 汇总),自动纠偏,防止人工重推重复;未查到保持 failed 等待异常处理后重新推送。**任务只勾稽回写,不主动推送**。 +- 付款完成(paid)联动:清理 MOCK 付款记录、履约付款数据(预结算预付记录、正式结算付款记录)。 + +### 5.2 ② A103-4 付款记录回填 + +- 候选:付款中/已付款单据(本轮 A103-2 查到 `paystatus ≠ Unpaid`)+ 已付款但无真实付款记录的单据(补偿轮询)。 +- 按付款人(公司)编码分组查询(编码优先取 A103-2 `payorg_number`,兜底合同组织;业务日期窗口最近 1 个月,接口限制)。 +- 勾稽:付款处理单源单 id(= `kingdeeBillId`)优先,源单编号(= `kingdeeBillNo`)兜底(`matchPayBill`)。 +- 回填(`applyPayBillRecord`):按付款处理单号 upsert 付款记录(金额、日期、凭证留档 JSON)。 + +## 6. 两边逻辑连贯性对照 + +### 6.1 正常时序(正式结算付款) + +``` +TMS 审批通过 → A102-2 保存应付单(回填 kingdeeBillNo)→ A103-1 下推付款申请(落 kingdeeBillId,置 synced) +→ 任务 A103-2 回写(billno 覆盖 kingdeeBillNo,状态 synced→paying→paid)→ 任务 A103-4 回填付款记录 → 终态 +``` + +### 6.2 异常场景矩阵(两边行为与闭环路径) + +| # | 断点场景 | 金蝶侧实际 | 本地状态 | 恢复路径 | 闭环 | +| --- | --- | --- | --- | --- | --- | +| 1 | 推送前校验/档案缺失失败(A102-2 之前) | 无任何单据 | failed | 重新推送走全链路首推 | ✅ | +| 2 | A102-2 成功、A103-1 报错(如分录校验失败) | 应付单已生成 | failed(事务回滚,编号回填一并回滚) | 重推:A102-4 预检命中 → 幂等跳过保存 → A103-2 勾稽未命中 → A103-1 续推;下推再报错才真实失败 | ✅ 本次改造 | +| 3 | A103-1 远端已落单但响应超时/本地回滚 | 应付单 + 付款申请单均已生成 | failed | 重推:A102-4 命中跳过 → A103-2 勾稽命中 → 回填置 synced;或定时任务自动纠偏 | ✅ 本次改造 | +| 4 | 预付款 A103-3 远端成功但本地失败 | 付款申请单已生成 | failed | 重推前 A103-2 预检命中 → 回填跳过;或定时任务纠偏 | ✅ 本次改造 | +| 5 | 推送成功但 kingdeeBillNo 尚为应付单编号 | 付款申请单已生成 | synced | 定时任务 A103-2 回写覆盖为付款申请单编号(字段语义随阶段演进,设计如此) | ✅ | +| 6 | 手工重推与定时任务并发纠偏 | 单据唯一(防重键唯一) | — | 两侧均为"先查后动":勾稽只读远端按 paymentNo 唯一归并,重复执行幂等无冲突 | ✅ | +| 7 | 金蝶反审核删除后重推 | 无单据 | failed | 预检查无 → 重新推送生成新单据(符合"金蝶反审核+删除后 ERP 重推"规则) | ✅ | +| 8 | failed 且金蝶确无单据(如档案长期缺失) | 无单据 | failed | 每轮任务空查一次 A103-2(量级 = failed 单据数),状态保持,等待人工处理 | ⚠️ 已知边界 | + +### 6.3 连贯性结论 + +1. **推送侧自洽**:三条件分支(应付单已存在 / 付款申请单已存在 / 均不存在)覆盖全部断点组合,任一断点重推均从断点续传而非从头重推,且以远端实际为准。 +2. **回写侧衔接**:恢复成功后本地状态为 synced(或直接 paid/paying),落入任务轮询范围,付款状态与付款记录由任务持续驱动至终态,无需再次人工干预。 +3. **双通道兜底**:手工重推(勾稽预检)与定时任务(勾稽纠偏)对同一断点均可闭环,先后到达不冲突。 +4. **语义一致性**:防重键、分录标识、日期格式、A103-2 归并规则均已收敛为统一生成点(`apBillSrcCode`/`bfiEntryCode`/`bfiDateTimeText`/`latestPayApplyBySrcCode`/`batchForeach`),两侧关联字段不存在漂移可能。 + +## 7. 已知边界与注意事项 + +1. **failed 空查**:确无金蝶单据的 failed 单据会被任务持续空查(每轮 A103-2 一次),如量大可考虑按申请日期窗口截断或引入重试次数上限(当前不加,保持简单)。 +2. **`kingdeeBillNo` 阶段语义**:推送瞬间为应付单编号,勾稽/回写后为付款申请单编号;依赖该字段展示时以回写后的值为准。 +3. **应收结算(A302-2)未接入**:结算类型为应收的单据在 `requirePayableSettlement` 拦截,推送规则待业务确认。 +4. **A102-2/A103-1 联调临时取值**:物料/计量单位/项目编码仍传金蝶测试档案值(调试块),档案对齐后需拆除,见 2026-09-16 技术文档遗留项。 +5. **A103-2 勾稽不校验金额**:恢复勾稽以"来源单号存在"为准,不比对金额与分录;若远端单据被人工修改,以远端为准回写(与回写任务原则一致)。