refactor: 规范金蝶应付单同步日志英文标点并补充预付信息关键日志

This commit is contained in:
刘泉佳
2026-11-04 16:17:00 +08:00
parent 01cc9f7f37
commit d69b293dd3
@@ -100,6 +100,10 @@ import java.util.stream.Collectors;
* + 结算合计费用项(手动添加的每行一条); * + 结算合计费用项(手动添加的每行一条);
* 物料编码按装卸大类/项目业务模式/运单运输方式映射,编码维护在 Nacos 配置 thirdparty.bfi.material.*(见 {@link #resolveMaterialNumber}); * 物料编码按装卸大类/项目业务模式/运单运输方式映射,编码维护在 Nacos 配置 thirdparty.bfi.material.*(见 {@link #resolveMaterialNumber});
* 运输类分录数量取运输量(按税率拆行时每行都取运输量)、装卸类默认 1;单价按结算金额/数量反算。</p> * 运输类分录数量取运输量(按税率拆行时每行都取运输量)、装卸类默认 1;单价按结算金额/数量反算。</p>
* <p>预付信息分录(preentry,见 {@link #resolvePreEntries}):存在预付款抵扣(进度预付/项目预付核销)时,
* 按金蝶付款处理单(cas_paybill)逐笔送出 y_billtype/y_billid/y_settleamt,同 y_billid 合并求和;
* 任一笔应抵扣预付无法定位金蝶付款处理单内码(未付款/内码未回写/实付不足)时抛异常拦截应付单推送,
* 失败落 kingdee_fail_reason,付款结果回写后由重推任务自动续推。</p>
* *
* @author Chill * @author Chill
*/ */
@@ -283,7 +287,9 @@ public class KingdeeApBillSyncSupport {
* 幂等跳过保存并返回已有单据编号(说明上次同步已保存成功、在后续环节中断,应续推而非拦截报错)。 * 幂等跳过保存并返回已有单据编号(说明上次同步已保存成功、在后续环节中断,应续推而非拦截报错)。
* 明细分录取页面结算明细(每行按 货物→费用大类→税率 拆分)+ 结算合计费用项(每行一条),结算金额逐行直送; * 明细分录取页面结算明细(每行按 货物→费用大类→税率 拆分)+ 结算合计费用项(每行一条),结算金额逐行直送;
* 汇率日期/汇率取结算单页面维护值;付款计划 planentity 暂不传(启用方案见方法内注释), * 汇率日期/汇率取结算单页面维护值;付款计划 planentity 暂不传(启用方案见方法内注释),
* 当前由金蝶按明细自动汇总生成 * 当前由金蝶按明细自动汇总生成;
* 预付信息分录 preentry 仅在真正新建应付单时解析(幂等跳过路径不做预付校验),
* 每条对应一张金蝶付款处理单(cas_paybill),取数口径见 {@link #resolvePreEntries}
* *
* @param apBillSrcCode 财务应付单防重键(正式结算单号_结算明细单据号拼接) * @param apBillSrcCode 财务应付单防重键(正式结算单号_结算明细单据号拼接)
* @param settlement 正式结算单(合同/汇率日期/汇率取数来源) * @param settlement 正式结算单(合同/汇率日期/汇率取数来源)
@@ -299,7 +305,7 @@ public class KingdeeApBillSyncSupport {
String existBillNo = context.apBillNoCache().computeIfAbsent(apBillSrcCode, code -> String existBillNo = context.apBillNoCache().computeIfAbsent(apBillSrcCode, code ->
queryApBillExistNos(List.of(code)).getOrDefault(code, "")); queryApBillExistNos(List.of(code)).getOrDefault(code, ""));
if (Func.isNotEmpty(existBillNo)) { if (Func.isNotEmpty(existBillNo)) {
log.info("正式结算单[{}]金蝶财务应付单已生成(单据编号[{}]),幂等跳过 A102-2 保存", subject, existBillNo); log.info("正式结算单[{}]金蝶财务应付单已生成(单据编号[{}]), 幂等跳过 A102-2 保存", subject, existBillNo);
return new BfiApBillPushOutcome(existBillNo, null, true); return new BfiApBillPushOutcome(existBillNo, null, true);
} }
ContractManage contract = requireContract(settlement.getContractId(), context); ContractManage contract = requireContract(settlement.getContractId(), context);
@@ -410,14 +416,14 @@ public class KingdeeApBillSyncSupport {
} }
// 推送入参摘要(联调排查:防重键、分录条数、页面维护的汇率口径) // 推送入参摘要(联调排查:防重键、分录条数、页面维护的汇率口径)
log.info("推送金蝶财务应付单:正式结算单[{}],防重键[{}],明细分录{}条,预付信息{}条,汇率日期[{}],汇率[{}]", log.info("推送金蝶财务应付单: 正式结算单[{}], 防重键[{}], 明细分录{}条, 预付信息{}条, 汇率日期[{}], 汇率[{}]",
subject, apBillSrcCode, detailentry.size(), preEntries.size(), param.getExratedate(), param.getExchangerate()); subject, apBillSrcCode, detailentry.size(), preEntries.size(), param.getExratedate(), param.getExchangerate());
BfiBillSaveResultVO saveResult = bfiService.saveApBill(param); BfiBillSaveResultVO saveResult = bfiService.saveApBill(param);
// 应付单id不落库,后续如需可经 A102-4 按防重键(awrp_src_sys + awrp_src_code)查询 // 应付单id不落库,后续如需可经 A102-4 按防重键(awrp_src_sys + awrp_src_code)查询
requireBfiSaveSuccess(saveResult, "推送金蝶财务应付单"); requireBfiSaveSuccess(saveResult, "推送金蝶财务应付单");
String billNo = saveResult.getResult().get(0).getNumber(); String billNo = saveResult.getResult().get(0).getNumber();
log.info("金蝶财务应付单保存成功:正式结算单[{}],单据编号[{}],明细分录{}条", subject, billNo, detailentry.size()); log.info("金蝶财务应付单保存成功: 正式结算单[{}], 单据编号[{}], 明细分录{}条", subject, billNo, detailentry.size());
// 星瀚应付单内码随结果返回(A902 附件上传定位用) // 星瀚应付单内码随结果返回(A902 附件上传定位用)
BfiBillSaveResultItemVO savedItem = saveResult.getResult().get(0); BfiBillSaveResultItemVO savedItem = saveResult.getResult().get(0);
return new BfiApBillPushOutcome(savedItem.getNumber(), savedItem.getId() == null ? null : String.valueOf(savedItem.getId()), false); return new BfiApBillPushOutcome(savedItem.getNumber(), savedItem.getId() == null ? null : String.valueOf(savedItem.getId()), false);
@@ -487,8 +493,14 @@ public class KingdeeApBillSyncSupport {
} }
} }
if (demandAmounts.isEmpty()) { if (demandAmounts.isEmpty()) {
// 无预付抵扣(无核销行且预付记录已付金额均为0):不送 preentry,保持报文字段缺省
log.info("应付单预付信息归集: 正式结算单[{}], 无预付抵扣, 不送预付信息分录", settlement.getFormalSettlementNo());
return List.of(); return List.of();
} }
// 预付抵扣归集摘要(联调排查: 项目预付按核销行、进度预付按预付记录已付金额归集到底层付款申请)
log.info("应付单预付信息归集: 正式结算单[{}], 应抵扣付款申请{}笔, 合计金额{}",
settlement.getFormalSettlementNo(), demandAmounts.size(),
demandAmounts.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add));
// 批量取付款申请与付款处理记录(仅保留带回金蝶付款处理单内码的记录),逐笔校验并分摊 // 批量取付款申请与付款处理记录(仅保留带回金蝶付款处理单内码的记录),逐笔校验并分摊
Map<Long, PaymentApplication> applications = this.paymentApplicationMapper.selectBatchIds(demandAmounts.keySet()).stream() Map<Long, PaymentApplication> applications = this.paymentApplicationMapper.selectBatchIds(demandAmounts.keySet()).stream()
.collect(Collectors.toMap(PaymentApplication::getId, Function.identity(), (first, second) -> first)); .collect(Collectors.toMap(PaymentApplication::getId, Function.identity(), (first, second) -> first));
@@ -542,6 +554,9 @@ public class KingdeeApBillSyncSupport {
String summary = blockReasons.size() > PREPAY_BLOCK_REASON_LIMIT String summary = blockReasons.size() > PREPAY_BLOCK_REASON_LIMIT
? String.join(";", blockReasons.subList(0, PREPAY_BLOCK_REASON_LIMIT)) + ";等" + blockReasons.size() + "笔预付无法抵扣" ? String.join(";", blockReasons.subList(0, PREPAY_BLOCK_REASON_LIMIT)) + ";等" + blockReasons.size() + "笔预付无法抵扣"
: String.join(";", blockReasons); : String.join(";", blockReasons);
// 拦截关键日志:原因落盘供排查(异常文案同步落 kingdee_fail_reason,付款结果回写后由重推任务自动续推)
log.warn("应付单预付信息校验未通过, 拦截应付单推送: 正式结算单[{}], 应抵扣付款申请{}笔, 原因: {}",
settlement.getFormalSettlementNo(), demandAmounts.size(), summary);
throw new ServiceException("推送金蝶财务应付单失败:" + summary); throw new ServiceException("推送金蝶财务应付单失败:" + summary);
} }
List<BfiApBillSavePreEntryDTO> preEntries = new ArrayList<>(); List<BfiApBillSavePreEntryDTO> preEntries = new ArrayList<>();
@@ -554,6 +569,11 @@ public class KingdeeApBillSyncSupport {
log.info("应付单预付信息分录: 正式结算单[{}], 来源[{}], y_billid={}, y_settleamt={}", log.info("应付单预付信息分录: 正式结算单[{}], 来源[{}], y_billid={}, y_settleamt={}",
settlement.getFormalSettlementNo(), sourcesByBillId.get(billId), billId, settleAmount); settlement.getFormalSettlementNo(), sourcesByBillId.get(billId), billId, settleAmount);
}); });
// 分录解析摘要:同一付款申请跨多预结算分摊后落在同一付款处理单的金额已按 y_billid 合并
log.info("应付单预付信息解析完成: 正式结算单[{}], 应抵扣付款申请{}笔, 生成分录{}条, 合计金额{}",
settlement.getFormalSettlementNo(), demandAmounts.size(), preEntries.size(),
preEntries.stream().map(BfiApBillSavePreEntryDTO::getYSettleamt)
.reduce(BigDecimal.ZERO, BigDecimal::add));
return preEntries; return preEntries;
} }
@@ -635,7 +655,7 @@ public class KingdeeApBillSyncSupport {
+ "]无结算明细与结算合计费用项,无法解析分录"); + "]无结算明细与结算合计费用项,无法解析分录");
} }
// 分录解析摘要(联调排查:分录数=结算明细拆分行+手动添加的结算合计费用项,物料/分录标识逐条见下方分录生成日志) // 分录解析摘要(联调排查:分录数=结算明细拆分行+手动添加的结算合计费用项,物料/分录标识逐条见下方分录生成日志)
log.info("金蝶应付单分录解析完成:正式结算单[{}],结算明细{}条 + 手工结算合计{}条 = 共{}条分录", log.info("金蝶应付单分录解析完成: 正式结算单[{}], 结算明细{}条 + 手工结算合计{}条 = 共{}条分录",
settlement.getFormalSettlementNo(), settlementDetails.size(), manualSummaryFees.size(), entryGroups.size()); settlement.getFormalSettlementNo(), settlementDetails.size(), manualSummaryFees.size(), entryGroups.size());
return entryGroups; return entryGroups;
} }
@@ -680,7 +700,7 @@ public class KingdeeApBillSyncSupport {
context.taxRateByRule(), rowAmount); context.taxRateByRule(), rowAmount);
if (splits.isEmpty()) { if (splits.isEmpty()) {
// 0元货物行(费用项与结算金额均为0)跳过不生成0元分录 // 0元货物行(费用项与结算金额均为0)跳过不生成0元分录
log.info("金蝶应付单分录生成:来源[{}],费用项与结算金额均为0,跳过该货物行", sourceDesc); log.info("金蝶应付单分录生成: 来源[{}], 费用项与结算金额均为0, 跳过该货物行", sourceDesc);
return; return;
} }
// 运输类数量:取运单运输量(未维护兜底 1) // 运输类数量:取运单运输量(未维护兜底 1)
@@ -707,7 +727,7 @@ public class KingdeeApBillSyncSupport {
String entryCode = entryCodePrefix + "-" + split.getCategory().getLabel() + split.taxRateSuffix(); String entryCode = entryCodePrefix + "-" + split.getCategory().getLabel() + split.taxRateSuffix();
entryGroups.add(new BfiEntryGroup(materialNumber, entryCode, quantity, price, amount, entryGroups.add(new BfiEntryGroup(materialNumber, entryCode, quantity, price, amount,
amountNoTax, cargoName, vehicleNo, context.settlement().getId())); amountNoTax, cargoName, vehicleNo, context.settlement().getId()));
log.info("金蝶应付单分录生成:来源[{}],分录标识[{}],物料编码[{}],数量[{}],单价[{}],含税金额[{}],不含税金额[{}]", log.info("金蝶应付单分录生成: 来源[{}], 分录标识[{}], 物料编码[{}], 数量[{}], 单价[{}], 含税金额[{}], 不含税金额[{}]",
sourceDesc, entryCode, materialNumber, quantity, price, amount, amountNoTax); sourceDesc, entryCode, materialNumber, quantity, price, amount, amountNoTax);
} }
} }
@@ -741,7 +761,7 @@ public class KingdeeApBillSyncSupport {
+ (Func.isBlank(feeItem) ? feeType : feeType + "-" + feeItem); + (Func.isBlank(feeItem) ? feeType : feeType + "-" + feeItem);
entryGroups.add(new BfiEntryGroup(materialNumber, entryCode, quantity, price, amount, entryGroups.add(new BfiEntryGroup(materialNumber, entryCode, quantity, price, amount,
null, null, null, settlement.getId())); null, null, null, settlement.getId()));
log.info("金蝶应付单分录生成:来源[{}],分录标识[{}],物料编码[{}],数量[{}],单价[{}],含税金额[{}]", log.info("金蝶应付单分录生成: 来源[{}], 分录标识[{}], 物料编码[{}], 数量[{}], 单价[{}], 含税金额[{}]",
sourceDesc, entryCode, materialNumber, quantity, price, amount); sourceDesc, entryCode, materialNumber, quantity, price, amount);
} }
@@ -840,7 +860,7 @@ public class KingdeeApBillSyncSupport {
} catch (RuntimeException ignored) { } catch (RuntimeException ignored) {
// 计费方案 JSON 兼容历史数据,解析失败按未配置处理 // 计费方案 JSON 兼容历史数据,解析失败按未配置处理
} }
log.info("合同[{}]计费方案解析:{}项费用类型/费用项配置税率{}", contract.getContractNo(), taxRateByRule.size(), taxRateByRule); log.info("合同[{}]计费方案解析: {}项费用类型/费用项配置税率{}", contract.getContractNo(), taxRateByRule.size(), taxRateByRule);
return taxRateByRule; return taxRateByRule;
} }
@@ -1090,7 +1110,7 @@ public class KingdeeApBillSyncSupport {
} }
loaded.putIfAbsent(feeItem.getName().trim(), feeItem.getFeeCategory().trim()); loaded.putIfAbsent(feeItem.getName().trim(), feeItem.getFeeCategory().trim());
} }
log.info("费用项档案加载:{}项(费用项名称 -> 费用大类编码)", loaded.size()); log.info("费用项档案加载: {}项(费用项名称 -> 费用大类编码)", loaded.size());
cache.putAll(loaded); cache.putAll(loaded);
return cache; return cache;
} }