✨ 付款管理正式结算付款同步金蝶改为财务应付单保存+应付下推付款申请

This commit is contained in:
刘泉佳
2026-09-17 10:38:44 +08:00
parent d1833a7ac1
commit d7c2cadd95
@@ -30,10 +30,15 @@ import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.transport.constant.KingdeeBfiConstant;
import org.springblade.system.cache.DictBizCache;
import org.springblade.system.cache.SysCache;
import org.springblade.system.pojo.entity.DictBiz;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveEntryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushEntryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveEntryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO;
@@ -103,11 +108,6 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
private static final String APPROVED = "approved";
private static final String RETURNED = "returned";
private static final String VOIDED = "voided";
private static final String BFI_SUPPLIER_ASSTACT_TYPE = "bd_supplier";
private static final String BFI_CONTRACT_SRC_BT = "awrp_contract_info";
private static final String BFI_PROJECT_NUMBER_NONE = "999";
private static final String BFI_PAYMENT_TYPE_FORMAL = "201";
private static final String BFI_PAYMENT_TYPE_PREPAY = "202";
private final PaymentApplicationInvoiceMapper invoiceMapper;
private final PaymentApplicationRecordMapper recordMapper;
private final PaymentApplicationSettlementMapper settlementRelationMapper;
@@ -333,7 +333,17 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
if ("synced".equals(entity.getKingdeeStatus())) return entity.getKingdeeBillNo();
String kingdeeBillId;
try {
kingdeeBillId = pushBfiPayApply(entity);
if ("settlement_payment".equals(entity.getPaymentType())) {
// 单据类型路由:按正式结算单结算类型——应付对应财务应付单(A102-2),应收对应财务应收单(A302-2,推送规则待业务确认)
requirePayableSettlement(entity);
// 正式结算付款:先推财务应付单(A102-2,仅校验保存成功,应付单id不落库,
// 后续如需可经 A102-4 按 awrp_src_sys + awrp_src_code 查询),再由应付下推付款申请(A103-1)
pushBfiApBill(entity);
kingdeeBillId = pushBfiPayApplyPush(entity);
} else {
// 预付款:直接推付款申请单(A103-3),不绑应付单
kingdeeBillId = pushBfiPayApply(entity);
}
} catch (ServiceException exception) {
markSyncFailed(entity);
throw exception;
@@ -370,7 +380,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
}
/**
* 推送金蝶付款申请单(A103-3 付款申请保存),返回金蝶单据id
* 推送金蝶付款申请单(A103-3 付款申请保存,预付款路径),返回金蝶单据id
*
* @param entity 付款申请实体
* @return 金蝶付款申请单id
@@ -399,15 +409,15 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
param.setAwrpTextfield1(bfiPaymentMethodLabel(entity.getPaymentMethod()));
// 来源单据
param.setAwrpSrcBt(BFI_CONTRACT_SRC_BT);
param.setAwrpSrcBt(KingdeeBfiConstant.BFI_CONTRACT_SRC_BT);
// 有上游合同才传 awrpSrcConNo
// if (entity.getContractId() != null) {
// param.setAwrpSrcConNo(entity.getContractNo());
// }
BfiPayApplySaveEntryDTO entry = new BfiPayApplySaveEntryDTO();
entry.setEPaymenttypeNumber("settlement_payment".equals(entity.getPaymentType()) ? BFI_PAYMENT_TYPE_FORMAL : BFI_PAYMENT_TYPE_PREPAY);
entry.setEAsstacttype(BFI_SUPPLIER_ASSTACT_TYPE);
entry.setEPaymenttypeNumber("settlement_payment".equals(entity.getPaymentType()) ? KingdeeBfiConstant.BFI_PAYMENT_TYPE_FORMAL : KingdeeBfiConstant.BFI_PAYMENT_TYPE_PREPAY);
entry.setEAsstacttype(KingdeeBfiConstant.BFI_SUPPLIER_ASSTACT_TYPE);
// 供应商编码
entry.setEAsstactNumber(bfiSupplierCode(entity));
entry.setEAssacct(entity.getBankAccount());
@@ -423,17 +433,169 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
param.setEntry(List.of(entry));
BfiBillSaveResultVO saveResult = bfiService.savePrePayApply(param);
// BfiBillSaveResultVO saveResult = null;
if (saveResult == null || Func.isEmpty(saveResult.getResult())) {
throw new ServiceException("推送金蝶付款申请失败,金蝶未返回单据结果");
}
return requireBfiBillId(saveResult, "推送金蝶付款申请");
}
BfiBillSaveResultItemVO item = saveResult.getResult().get(0);
if (!Boolean.TRUE.equals(item.getBillStatus()) || item.getId() == null) {
String errorText = Func.isEmpty(item.getErrors()) ? "未知错误" : String.join(";", item.getErrors());
throw new ServiceException("推送金蝶付款申请失败:" + errorText);
/**
* 校验正式结算付款对应的结算单结算类型:应付(财务应付单 A102-2)直接放行,
* 应收(对应财务应收单 A302-2)推送规则待业务确认,暂拦截报错。
*
* @param entity 付款申请实体
*/
private void requirePayableSettlement(PaymentApplication entity) {
if (entity.getSettlementId() == null) {
// 聚合多结算单场景暂按应付处理,TODO 待业务确认后细化
return;
}
return String.valueOf(item.getId());
FormalSettlement settlement = formalSettlementMapper.selectById(entity.getSettlementId());
if (settlement != null && "receivable".equals(settlement.getSettlementType())) {
throw new ServiceException("推送金蝶失败:结算单[" + entity.getSettlementNo() + "]结算类型为应收,对应财务应收单(A302-2),推送规则待业务确认后启用");
}
}
/**
* 推送金蝶财务应付单(A102-2 财务应付单保存,正式结算付款第一步),仅校验保存成功
*
* @param entity 付款申请实体
*/
private void pushBfiApBill(PaymentApplication entity) {
if (Func.isBlank(KingdeeBfiConstant.BFI_AP_BILL_MATERIAL)) {
throw new ServiceException("推送金蝶财务应付单失败:明细物料编码待金蝶侧确认后补充(常量 KingdeeBfiConstant.BFI_AP_BILL_MATERIAL)");
}
if (Func.isBlank(KingdeeBfiConstant.BFI_AP_BILL_MEASURE_UNIT)) {
throw new ServiceException("推送金蝶财务应付单失败:明细计量单位待金蝶侧确认后补充(常量 KingdeeBfiConstant.BFI_AP_BILL_MEASURE_UNIT)");
}
ContractManage contract = requireContract(entity);
// 单据类型按映射字典转换:默认服务采购应付,金蝶确认其他类型后修改 BFI_AP_BILL_BILLTYPE_NAME 即可
String billtypeNumber = KingdeeBfiConstant.billtypeBy(KingdeeBfiConstant.AP_BILL_BILLTYPE_MAP, KingdeeBfiConstant.BFI_AP_BILL_BILLTYPE_NAME);
if (Func.isBlank(billtypeNumber)) {
throw new ServiceException("推送金蝶财务应付单失败:单据类型[" + KingdeeBfiConstant.BFI_AP_BILL_BILLTYPE_NAME + "]未在映射字典中维护");
}
String orgNumber = bfiOrgNumber(contract);
// 币种取合同结算币种
String currency = bfiCurrency(contract);
// TODO 待确认:日期格式沿用现有 A103-3 的 yyyyMMdd,若金蝶校验要求 yyyy-MM-dd 再统一调整
String billDateText = (entity.getApplyDate() == null ? LocalDate.now() : entity.getApplyDate()).format(DateTimeFormatter.BASIC_ISO_DATE);
BigDecimal applyAmount = money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP);
BfiApBillSaveDTO param = new BfiApBillSaveDTO();
param.setBilltypeidNumber(billtypeNumber);
param.setOrgNumber(orgNumber);
param.setBizdate(billDateText);
param.setAsstacttype(KingdeeBfiConstant.BFI_SUPPLIER_ASSTACT_TYPE);
param.setAsstactNumber(bfiSupplierCode(entity));
param.setPurmode(KingdeeBfiConstant.BFI_AP_BILL_PUR_MODE);
param.setCurrencyNumber(currency);
param.setPayorgNumber(orgNumber);
param.setPurorgNumber(orgNumber);
param.setPaypropertyNumber(KingdeeBfiConstant.BFI_AP_BILL_PAY_PROPERTY);
// TODO 待确认:结算方式编码(settlementtype_number)与税率编码(taxrateid_number)需金蝶侧确认后补充,当前不传
param.setIsincludetax(true);
param.setIspricetotal(true);
param.setExratetableNumber(bfiPayApplyExratetable);
param.setExratedate(billDateText);
param.setAsstactname(entity.getPayeeName());
param.setRemark(entity.getPaymentNo() + (Func.isEmpty(entity.getSettlementNo()) ? "" : " " + entity.getSettlementNo()));
param.setAwrpSrcSys(bfiPayApplySrcSys);
param.setAwrpSrcCode(entity.getPaymentNo());
param.setAwrpSrcBt(KingdeeBfiConstant.BFI_CONTRACT_SRC_BT);
if (Func.isNotEmpty(entity.getContractNo())) {
param.setAwrpSrcConNo(entity.getContractNo());
}
param.setAwrpSrcType(KingdeeBfiConstant.BFI_AP_BILL_SRC_TYPE);
BfiApBillSaveEntryDTO entry = new BfiApBillSaveEntryDTO();
entry.setMaterialNumber(KingdeeBfiConstant.BFI_AP_BILL_MATERIAL);
entry.setMeasureunitNumber(KingdeeBfiConstant.BFI_AP_BILL_MEASURE_UNIT);
// 无吨位数据:数量固定 1,配合 ispricetotal=true 录入总价(含税应付金额)
entry.setQuantity(BigDecimal.ONE);
entry.setEPricetaxtotal(applyAmount);
entry.setProjectNumber(bfiProjectNumber(entity));
// 应付单分录唯一标识,A103-1 下推时以 awrp_finapbill_entryid 关联此行
entry.setAwrpEntryCode(entity.getPaymentNo() + "-1");
entry.setERemark(entity.getRemark());
param.setDetailentry(List.of(entry));
// 付款计划 planentity 不传,由金蝶按明细自动汇总生成
// TODO 待业务确认:存在预付款抵扣时是否需传预付信息 preentry(y_billtype/y_billid/y_settleamt),本期不传、后续迭代
BfiBillSaveResultVO saveResult = bfiService.saveApBill(param);
// 应付单id不落库,后续如需可经 A102-4 按防重键(awrp_src_sys + awrp_src_code)查询
requireBfiSaveSuccess(saveResult, "推送金蝶财务应付单");
}
/**
* 推送金蝶付款申请(A103-1 应付下推付款申请,正式结算付款第二步),返回星瀚付款申请单id
*
* @param entity 付款申请实体
* @return 星瀚付款申请单id
*/
private String pushBfiPayApplyPush(PaymentApplication entity) {
ContractManage contract = requireContract(entity);
String orgNumber = bfiOrgNumber(contract);
// TODO 待确认:日期格式沿用现有 A103-3 的 yyyyMMdd,若金蝶校验要求 yyyy-MM-dd 再统一调整
String applyDateText = (entity.getApplyDate() == null ? LocalDate.now() : entity.getApplyDate()).format(DateTimeFormatter.BASIC_ISO_DATE);
BigDecimal applyAmount = money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP);
BfiPayApplyPushDTO param = new BfiPayApplyPushDTO();
param.setAwrpSrcCode(entity.getPaymentNo());
param.setAwrpSrcSys(bfiPayApplySrcSys);
param.setApplydate(applyDateText);
param.setPayorgNumber(orgNumber);
param.setPurorgNumber(orgNumber);
param.setApplycause(bfiApplyCause(entity.getPaymentType()));
param.setExratetableNumber(bfiPayApplyExratetable);
param.setExratedate(applyDateText);
param.setAwrpTextfield1(bfiPaymentMethodLabel(entity.getPaymentMethod()));
param.setCreatorName(entity.getApplicantName());
BfiPayApplyPushEntryDTO entry = new BfiPayApplyPushEntryDTO();
// 正式付款固定 201
entry.setEPaymenttypeNumber(KingdeeBfiConstant.BFI_PAYMENT_TYPE_FORMAL);
// 关联 A102-2 财务应付单分录,对应应付单明细的 awrp_entry_code
entry.setAwrpFinapbillEntryid(entity.getPaymentNo() + "-1");
entry.setAwrpEntryCode(entity.getPaymentNo() + "-1");
entry.setEAssacct(entity.getBankAccount());
// TODO 待确认:往来银行为必填编码(e_bebank_number),当前暂传银行名称,待建立银行档案编码映射后调整
entry.setEBebankNumber(entity.getBankName());
entry.setEApplyamount(applyAmount);
entry.setEExpaydate(applyDateText);
entry.setProjectNumber(bfiProjectNumber(entity));
entry.setERemark(entity.getRemark());
entry.setAwrpSrcType(bfiPayApplySrcType);
param.setEntry(List.of(entry));
BfiBillSaveResultVO saveResult = bfiService.pushPayApply(param);
return requireBfiBillId(saveResult, "推送金蝶付款申请(应付下推)");
}
/**
* 校验金蝶保存/下推返回结果(单据级失败以 errors 明细抛出)
*
* @param saveResult 金蝶返回结果
* @param action 业务动作描述,用于异常提示
*/
private void requireBfiSaveSuccess(BfiBillSaveResultVO saveResult, String action) {
if (saveResult == null || Func.isEmpty(saveResult.getResult())) {
throw new ServiceException(action + "失败,金蝶未返回单据结果");
}
BfiBillSaveResultItemVO item = saveResult.getResult().get(0);
if (!Boolean.TRUE.equals(item.getBillStatus())) {
String errorText = Func.isEmpty(item.getErrors()) ? "未知错误" : String.join(";", item.getErrors());
throw new ServiceException(action + "失败:" + errorText);
}
}
/**
* 校验金蝶保存/下推返回结果并取星瀚单据id(单据级失败以 errors 明细抛出)
*
* @param saveResult 金蝶返回结果
* @param action 业务动作描述,用于异常提示
* @return 星瀚单据id
*/
private String requireBfiBillId(BfiBillSaveResultVO saveResult, String action) {
requireBfiSaveSuccess(saveResult, action);
Object billId = saveResult.getResult().get(0).getId();
if (billId == null) throw new ServiceException(action + "失败,金蝶未返回单据id");
return String.valueOf(billId);
}
private void markSyncFailed(PaymentApplication entity) {
@@ -485,9 +647,9 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
}
private String bfiProjectNumber(PaymentApplication entity) {
if (entity.getProjectId() == null) return BFI_PROJECT_NUMBER_NONE;
if (entity.getProjectId() == null) return KingdeeBfiConstant.BFI_PROJECT_NUMBER_NONE;
ProjectApply project = projectApplyMapper.selectById(entity.getProjectId());
return project == null || Func.isEmpty(project.getProjectCode()) ? BFI_PROJECT_NUMBER_NONE : project.getProjectCode();
return project == null || Func.isEmpty(project.getProjectCode()) ? KingdeeBfiConstant.BFI_PROJECT_NUMBER_NONE : project.getProjectCode();
}
private void mockPaymentRecords(PaymentApplication entity) {