diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java index be0a5cc5..fddbe20c 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java @@ -96,4 +96,12 @@ public interface IPaymentApplicationService extends BaseService cargoCodeByName, Map customerCodeCache, Map projectCache, Map apBillNoCache, Map> settlementDetailCache, - Map apBillPlanCodeCache, Map settlementCache) { /** 空上下文:缓存可写但初始为空,首条记录命中后全批复用 */ public static KingdeeSyncContext empty() { return new KingdeeSyncContext(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), - new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()); + new HashMap<>(), new HashMap<>(), new HashMap<>()); } } 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 c94bc1e7..cdb00917 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 @@ -33,6 +33,7 @@ import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.SpringUtil; import org.springblade.system.cache.DictBizCache; import org.springblade.system.pojo.entity.DictBiz; +import org.springblade.thirdparty.bfi.constant.BfiConstant; import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushEntryDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyQueryDTO; @@ -955,14 +956,18 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl entries = new ArrayList<>(); + // 结算方式.编码:按本单付款方式映射(汇票付款→金蝶应付票据电子银行承兑汇票 012-02,其余→银行转账 02),仅 A103-1 下推需要 + String settlementtypeNumber = isBillPayment(entity.getPaymentMethod()) + ? BfiConstant.SETTLE_TYPE_PAYABLE_BANK_ELECTRONIC : BfiConstant.SETTLE_TYPE_BANK; for (KingdeeApBillSyncSupport.BfiEntryGroup entryGroup : entryGroups) { - // 【必填】结算单分录id勾稽值(awrp_finapbill_entryid):按分录所属正式结算单的财务应付单, - // 经 A102-4 反查其付款计划分录 awrp_entry_code_p(按防重键缓存,同一结算单分录只反查一次) - String finapbillEntryid = kingdeeApBillSyncSupport.apBillPlanEntryCode( - settlementApBillSrcCode(entity, entryGroup.formalSettlementId(), context), entity.getPaymentNo(), context); + // 【必填】结算单分录id勾稽值(awrp_finapbill_entryid):A102-2 保存未传 planentity,金蝶自动生成的付款计划分录 + // awrp_entry_code_p 即外部系统单据号(应付单防重键:正式结算单号_结算明细单据号拼接,本地按数据库数据组装,不远程反查) + String finapbillEntryid = settlementApBillSrcCode(entity, entryGroup.formalSettlementId(), context); BfiPayApplyPushEntryDTO entry = new BfiPayApplyPushEntryDTO(); // 【必填】付款类型.编码:正式付款固定 201 entry.setEPaymenttypeNumber(KingdeeBfiConstant.BFI_PAYMENT_TYPE_FORMAL); + // 结算方式.编码:不传时金蝶按应付单自动带出,此处按付款方式映射显式送值 + entry.setESettlementtypeNumber(settlementtypeNumber); // 【必填】结算单分录id:A102-2 财务应付单付款计划分录的外部系统来源分录标识(awrp_entry_code_p) entry.setAwrpFinapbillEntryid(finapbillEntryid); // 【必填】对方系统唯一行id(不能重复):费用明细行id(与应付单的勾稽走 awrp_finapbill_entryid,与本标识无关) @@ -1599,7 +1604,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl paymentMethods = DictBizCache.getList("pay_method"); if (Func.isEmpty(paymentMethods)) return false; diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeApBillSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeApBillSyncSupport.java index b41f647b..36c5fc40 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeApBillSyncSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeApBillSyncSupport.java @@ -38,12 +38,12 @@ import org.springblade.system.pojo.entity.Dept; import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillQueryDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveDTO; import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveEntryDTO; -import org.springblade.thirdparty.bfi.pojo.vo.BfiApBillPlanVO; import org.springblade.thirdparty.bfi.pojo.vo.BfiApBillVO; import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO; import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO; import org.springblade.thirdparty.bfi.service.IBfiService; import org.springblade.thirdparty.bfi.util.BfiUtil; +import org.springblade.transport.config.BfiProperties; import org.springblade.transport.constant.KingdeeBfiConstant; import org.springblade.transport.mapper.ContractManageMapper; import org.springblade.transport.mapper.CustomerArchiveMapper; @@ -53,7 +53,6 @@ import org.springblade.transport.mapper.ReceivablePayableCargoFeeMapper; import org.springblade.transport.pojo.entity.ContractManage; import org.springblade.transport.pojo.entity.CustomerArchive; import org.springblade.transport.pojo.entity.FormalSettlement; -import org.springblade.transport.config.BfiProperties; import org.springblade.transport.pojo.entity.FormalSettlementDetail; import org.springblade.transport.pojo.entity.FormalSettlementDetailFee; import org.springblade.transport.pojo.entity.PreSettlementDetailFee; @@ -163,40 +162,6 @@ public class KingdeeApBillSyncSupport { return existBillNos; } - /** - * A102-4 反查财务应付单付款计划分录的外部系统来源分录标识(awrp_entry_code_p)。 - * A102-2 保存未传 planentity,由金蝶按明细自动汇总生成(awrp_entry_code_p 不传时自动取外部系统单据号), - * A103-1 应付下推的 awrp_finapbill_entryid 需填该值勾稽应付单; - * 结果按防重键(awrp_src_code)缓存,恢复场景避免重复远程查询 - * - * @param apBillSrcCode 财务应付单防重键(外部系统来源单据号) - * @param subject 业务单据号(付款申请号,异常提示用) - * @param context 金蝶同步上下文(缓存载体) - * @return 付款计划分录 awrp_entry_code_p - */ - public String apBillPlanEntryCode(String apBillSrcCode, String subject, KingdeeSyncContext context) { - return context.apBillPlanCodeCache().computeIfAbsent(apBillSrcCode, code -> { - BfiApBillQueryDTO param = new BfiApBillQueryDTO(); - param.setAwrpSrcCode(List.of(code)); - param.setAwrpSrcSys(bfiProperties.getSrcSys()); - List bills = BfiUtil.queryAllPages(pageNo -> { - param.setPageNo(pageNo); - param.setPageSize(PAY_RESULT_PAGE_SIZE); - return bfiService.queryApBill(param); - }); - for (BfiApBillVO apBill : bills) { - if (Func.isNotEmpty(apBill.getPlanentity())) { - for (BfiApBillPlanVO plan : apBill.getPlanentity()) { - if (Func.isNotBlank(plan.getAwrpEntryCodeP())) { - return plan.getAwrpEntryCodeP(); - } - } - } - } - throw new ServiceException("推送金蝶付款申请(应付下推)失败:财务应付单[" + subject + "]付款计划分录 awrp_entry_code_p 未查询到,无法勾稽关联"); - }); - } - // ==================== A102-2 财务应付单保存 ==================== /** @@ -255,7 +220,8 @@ public class KingdeeApBillSyncSupport { // param.setPurorgNumber(orgNumber); // 【必填】款项性质.编码 param.setPaypropertyNumber(KingdeeBfiConstant.BFI_AP_BILL_PAY_PROPERTY); - // TODO 待确认:结算方式编码(settlementtype_number)与税率编码(taxrateid_number)需金蝶侧确认后补充,当前不传 + // 结算方式.编码(settlementtype_number)不在应付单送:按付款方式映射的结算方式仅 A103-1 付款申请(应付下推)分录需要; + // 税率编码(taxrateid_number)仍待金蝶侧确认后补充 // 录入含税价(非必传) param.setIsincludetax(true); // 录入总价,为 true 后可不录入单价(非必传) @@ -335,7 +301,8 @@ public class KingdeeApBillSyncSupport { * 解析正式结算明细,逐行生成应付单分录(不聚合,每条来源费用行一条分录),分录携带所属正式结算单id * (多结算单付款申请 A103-1 下推时按结算单分组勾稽各自应付单付款计划分录)。 *

取数来源:应付明细货物费用行(blade_receivable_payable_cargo_fee,经结算明细 sourceDetailId 关联应付明细, - * 每货物一条)——货物类型/计量单位/运输数量/含税金额直接取应付费用行(含税金额取调整后总金额 after_amount); + * 每货物一条)——货物类型/计量单位/运输数量/含税金额直接取应付费用行(含税金额取数链:调整后总金额 after_amount → + * 原总金额 → 单价×数量兜底,见 {@link #resolveFeeAmount}); * 应付费用行无不含税字段,不含税金额经结算货物费用行 sourceFeeId 回链(直接选明细路径 sourceFeeId=应付费用行id, * 预结算合并路径 sourceFeeId=预结算费用行id,经预结算费用行 sourceFeeId 回溯应付费用行id); * 结算明细对应应付明细无货物费用行时直接报错拦截(取数必须来自应付费用明细,无兜底)。物料编码:货物类型(支持“、”分隔多类型) @@ -365,14 +332,15 @@ public class KingdeeApBillSyncSupport { + "]对应应付明细无货物费用行,分录取数必须来自应付费用明细,请先维护费用数据"); } for (ReceivablePayableCargoFee payableFee : payableFees) { + String sourceDesc = "应付货物费用[" + Func.toStr(payableFee.getCargoName()) + " 第" + Func.toStr(payableFee.getLineNo()) + "行]"; appendEntryGroups(entryGroups, cargoCodeByName, settlementDetail.getFormalSettlementId(), payableFee.getCargoType(), resolveMeasureunitNumber(payableFee.getPriceUnit(), payableFee.getQuantityUnit()), payableFee.getId(), money(payableFee.getUnitPrice()), - money(payableFee.getAfterAmount()), + resolveFeeAmount(payableFee, sourceDesc), noTaxByPayableFeeId.getOrDefault(payableFee.getId(), BigDecimal.ZERO), money(payableFee.getTransportQuantity()), - "应付货物费用[" + Func.toStr(payableFee.getCargoName()) + " 第" + Func.toStr(payableFee.getLineNo()) + "行]"); + sourceDesc); } } return entryGroups; @@ -661,6 +629,31 @@ public class KingdeeApBillSyncSupport { return (value == null ? BigDecimal.ZERO : value).setScale(2, RoundingMode.HALF_UP); } + /** + * 费用行含税金额取数链:优先调整后总金额(after_amount),未维护(null/0)回退原总金额(original_amount), + * 仍取不到按运输单价×运输量兜底(单据头 isincludetax=true 录入含税价,单价按含税口径相乘); + * 全部无法取值时拦截报错,避免分录价税合计送 0 被金蝶校验拦截("整单'价税合计'不能为空"), + * 应付/应收/收票单分录共用(应收单同步组件复用) + * + * @param payableFee 应收应付货物费用行 + * @param sourceDesc 来源行描述,用于异常提示 + * @return 该行含税金额(保留 2 位小数) + */ + public BigDecimal resolveFeeAmount(ReceivablePayableCargoFee payableFee, String sourceDesc) { + BigDecimal amount = payableFee.getAfterAmount(); + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) { + amount = payableFee.getOriginalAmount(); + } + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) { + amount = money(payableFee.getUnitPrice()).multiply(money(payableFee.getTransportQuantity())); + } + if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("推送金蝶失败:" + sourceDesc + + "未维护费用金额(调整后总金额/原总金额/单价×数量均无法取值),无法组装分录价税合计,请先维护费用数据"); + } + return money(amount); + } + /** * 财务应付单/付款申请分录:每条来源费用行一条(不聚合) * diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeArBillSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeArBillSyncSupport.java index 47d240a3..86179f3e 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeArBillSyncSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeArBillSyncSupport.java @@ -239,7 +239,8 @@ public class KingdeeArBillSyncSupport { /** * 解析正式结算明细,逐行生成应收单分录(不聚合,每条来源费用行一条分录),分录携带所属正式结算单id。 *

取数来源与应付单分录同源:结算明细 sourceDetailId 关联应收应付货物费用行(blade_receivable_payable_cargo_fee, - * 每货物一条)——货物类型/计量单位/运输数量/含税金额(调整后总金额 after_amount)直接取费用行;不含税金额经结算 + * 每货物一条)——货物类型/计量单位/运输数量/含税金额直接取费用行(含税金额取数链:调整后总金额 after_amount → + * 原总金额 → 单价×数量兜底,见 {@link KingdeeApBillSyncSupport#resolveFeeAmount});不含税金额经结算 * 货物费用行 sourceFeeId 回链;无货物费用行时直接报错拦截。物料编码取第一种货物类型匹配货物类型档案货物编码; * 分录唯一标识(awrp_entry_code)取费用明细行id。

* @@ -265,14 +266,15 @@ public class KingdeeArBillSyncSupport { + "]对应应收明细无货物费用行,分录取数必须来自应收费用明细,请先维护费用数据"); } for (ReceivablePayableCargoFee fee : fees) { + String sourceDesc = "应收货物费用[" + Func.toStr(fee.getCargoName()) + " 第" + Func.toStr(fee.getLineNo()) + "行]"; appendArEntryGroups(entryGroups, cargoCodeByName, settlementDetail.getFormalSettlementId(), fee.getCargoType(), kingdeeApBillSyncSupport.resolveMeasureunitNumber(fee.getPriceUnit(), fee.getQuantityUnit()), fee.getId(), kingdeeApBillSyncSupport.money(fee.getUnitPrice()), - kingdeeApBillSyncSupport.money(fee.getAfterAmount()), + kingdeeApBillSyncSupport.resolveFeeAmount(fee, sourceDesc), noTaxByFeeId.getOrDefault(fee.getId(), BigDecimal.ZERO), kingdeeApBillSyncSupport.money(fee.getTransportQuantity()), - "应收货物费用[" + Func.toStr(fee.getCargoName()) + " 第" + Func.toStr(fee.getLineNo()) + "行]"); + sourceDesc); } } return entryGroups;