⚡ 调整金蝶付款同步:分录标识改用结算明细单据号,付款计划送审核通过的付款信息
This commit is contained in:
+8
@@ -26,12 +26,20 @@ import java.math.BigDecimal;
|
||||
@TableName("blade_formal_settlement_payment")
|
||||
public class FormalSettlementPayment extends TenantEntity {
|
||||
@Serial private static final long serialVersionUID = 1L;
|
||||
/** 正式结算单id */
|
||||
private Long formalSettlementId;
|
||||
/** 付款单号(租户内唯一) */
|
||||
private String paymentNo;
|
||||
/** 付款类型(final:结算尾款) */
|
||||
private String paymentType;
|
||||
/** 申请付款金额 */
|
||||
private BigDecimal appliedAmount;
|
||||
/** 已付金额 */
|
||||
private BigDecimal paidAmount;
|
||||
/** 单据状态(reviewing:审核中,approved:审核通过,returned:已退回,voided:已作废,paid:已付款) */
|
||||
private String billStatus;
|
||||
/** 金蝶(星瀚)单据编号 */
|
||||
private String kingdeeBillNo;
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
|
||||
+2
@@ -52,6 +52,8 @@ public final class KingdeeBfiConstant {
|
||||
|
||||
/** 联调默认计量单位编码:金蝶测试档案值,档案维护计量单位编码后替换。 */
|
||||
public static final String BFI_AP_BILL_MEASUREUNIT_NUMBER_TEST = "TON";
|
||||
/** 联调默认银行行号:金蝶测试档案值,档案维护银行行号后替换。 */
|
||||
public static final String BFI_AP_BILL_BANKACCOUNT_NUMBER_TEST = "001110012009";
|
||||
|
||||
/** 联调默认项目编码:金蝶测试档案值,项目档案与金蝶对齐后替换。 */
|
||||
public static final String BFI_AP_BILL_PROJECT_NUMBER_TEST = "999";
|
||||
|
||||
+3
-2
@@ -41,10 +41,11 @@ import java.util.Map;
|
||||
public record KingdeeSyncContext(Map<String, String> cargoCodeByName, Map<Long, ContractManage> contractCache,
|
||||
Map<String, String> customerCodeCache, Map<Long, ProjectApply> projectCache,
|
||||
Map<String, String> apBillNoCache,
|
||||
Map<Long, List<FormalSettlementDetail>> settlementDetailCache) {
|
||||
Map<Long, List<FormalSettlementDetail>> settlementDetailCache,
|
||||
Map<String, String> apBillPlanCodeCache) {
|
||||
|
||||
/** 空上下文:缓存可写但初始为空,首条记录命中后全批复用 */
|
||||
public static KingdeeSyncContext empty() {
|
||||
return new KingdeeSyncContext(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
|
||||
return new KingdeeSyncContext(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
+177
-42
@@ -41,12 +41,14 @@ import org.springblade.system.pojo.entity.DictBiz;
|
||||
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.dto.BfiApBillSavePlanDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushEntryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyQueryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveEntryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayBillQueryDTO;
|
||||
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;
|
||||
@@ -110,6 +112,7 @@ import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -422,7 +425,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
kingdeeBillId = reconcileExistPayApply(entity);
|
||||
}
|
||||
if (Func.isEmpty(kingdeeBillId)) {
|
||||
kingdeeBillId = pushBfiPayApplyPush(entity, entryGroupResult.entryGroups(), context);
|
||||
kingdeeBillId = pushBfiPayApplyPush(entity, entryGroupResult.entryGroups(), apBillOutcome.planEntryCodes(), context);
|
||||
log.info("付款申请[{}]金蝶付款申请单(A103-1 应付下推)推送成功:kingdeeBillId={}", entity.getPaymentNo(), kingdeeBillId);
|
||||
}
|
||||
} else {
|
||||
@@ -789,6 +792,62 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
return existBillNos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询正式结算单下绑定、单据状态为审核通过(approved)的付款信息(blade_formal_settlement_payment)。
|
||||
* 付款申请可能关联多张正式结算单,按结算明细归属的结算单汇总去重后查询,创建时间升序
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
* @param context 金蝶同步上下文(缓存载体)
|
||||
* @return 审核通过的付款信息列表
|
||||
*/
|
||||
private List<FormalSettlementPayment> approvedSettlementPayments(PaymentApplication entity, KingdeeSyncContext context) {
|
||||
List<Long> settlementIds = settlementDetails(entity, context).stream()
|
||||
.map(FormalSettlementDetail::getFormalSettlementId)
|
||||
.filter(Objects::nonNull).distinct().toList();
|
||||
if (Func.isEmpty(settlementIds)) {
|
||||
return List.of();
|
||||
}
|
||||
return formalSettlementPaymentMapper.selectList(Wrappers.<FormalSettlementPayment>lambdaQuery()
|
||||
.in(FormalSettlementPayment::getFormalSettlementId, settlementIds)
|
||||
.eq(FormalSettlementPayment::getIsDeleted, 0)
|
||||
.eq(FormalSettlementPayment::getBillStatus, APPROVED)
|
||||
.orderByAsc(FormalSettlementPayment::getCreateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* A102-4 反查财务应付单付款计划分录的外部系统来源分录标识(awrp_entry_code_p)。
|
||||
* A102-2 保存未传 planentity,由金蝶按明细自动汇总生成(awrp_entry_code_p 不传时自动取外部系统单据号),
|
||||
* A103-1 应付下推的 awrp_finapbill_entryid 需填该值勾稽应付单;
|
||||
* 结果按防重键(awrp_src_code)缓存,恢复场景避免重复远程查询
|
||||
*
|
||||
* @param entity 付款申请实体(异常提示用)
|
||||
* @param apBillSrcCode 财务应付单防重键(外部系统来源单据号)
|
||||
* @param context 金蝶同步上下文(缓存载体)
|
||||
* @return 付款计划分录 awrp_entry_code_p
|
||||
*/
|
||||
private String apBillPlanEntryCode(PaymentApplication entity, String apBillSrcCode, KingdeeSyncContext context) {
|
||||
return context.apBillPlanCodeCache().computeIfAbsent(apBillSrcCode, code -> {
|
||||
BfiApBillQueryDTO param = new BfiApBillQueryDTO();
|
||||
param.setAwrpSrcCode(List.of(code));
|
||||
param.setAwrpSrcSys(bfiPayApplySrcSys);
|
||||
List<BfiApBillVO> 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("推送金蝶付款申请(应付下推)失败:财务应付单[" + entity.getPaymentNo() + "]付款计划分录 awrp_entry_code_p 未查询到,无法勾稽关联");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A103-4 分页查询:按付款人(公司)编码 + 最近1个月业务日期窗口(接口限制不超过1个月),自动翻页
|
||||
*
|
||||
@@ -948,12 +1007,14 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
* 推送前先经 A102-4 按防重键(awrp_src_sys + awrp_src_code)查询:金蝶已存在财务应付单时
|
||||
* 幂等跳过保存并返回已有单据编号(说明上次同步已在后续下推/落库环节中断,应续推而非拦截报错),
|
||||
* 由调用方先经 A103-2 预检付款申请单是否已下推成功,再决定继续下推或直接回填。
|
||||
* 明细分录按结算明细货物编码+计量单位分组推送,金额取各组结算金额(含税/不含税)。
|
||||
* 明细分录按结算明细货物编码+计量单位分组推送,金额取各组结算金额(含税/不含税);
|
||||
* 付款计划送正式结算单下绑定、单据状态为审核通过(approved)的付款信息(awrp_entry_code_p 取付款单号),
|
||||
* 无审核通过付款信息时不传、由金蝶自动汇总生成
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
* @param entryGroups 结算明细聚合出的应付单分录组
|
||||
* @param documentNos 结算明细单据号(去重),用于拼接外部系统来源单据号
|
||||
* @return 财务应付单推送结果(单据编号 + 是否金蝶已存在幂等跳过)
|
||||
* @return 财务应付单推送结果(单据编号 + 是否金蝶已存在幂等跳过 + 付款计划勾稽标识集合)
|
||||
*/
|
||||
private BfiApBillPushOutcome pushBfiApBill(PaymentApplication entity, List<BfiEntryGroup> entryGroups, List<String> documentNos, KingdeeSyncContext context) {
|
||||
// A102-4 幂等预检:按防重键查询金蝶是否已生成财务应付单(批量同步时整批已预热缓存,此处仅懒加载未命中编码),
|
||||
@@ -963,7 +1024,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
queryApBillExistNos(List.of(code)).getOrDefault(code, ""));
|
||||
if (Func.isNotEmpty(existBillNo)) {
|
||||
log.info("付款申请[{}]金蝶财务应付单已生成(单据编号[{}]),幂等跳过 A102-2 保存,继续后续下推流程", entity.getPaymentNo(), existBillNo);
|
||||
return new BfiApBillPushOutcome(existBillNo, true);
|
||||
return new BfiApBillPushOutcome(existBillNo, true, List.of());
|
||||
}
|
||||
ContractManage contract = requireContract(entity, context);
|
||||
// 单据类型按映射字典转换:默认服务采购应付,金蝶确认其他类型后修改 BFI_AP_BILL_BILLTYPE_NAME 即可
|
||||
@@ -1041,12 +1102,12 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
entry.setEAmount(entryGroup.amountNoTax());
|
||||
// 【必填】项目.项目编码(联调期临时传金蝶测试档案值)
|
||||
entry.setProjectNumber(KingdeeBfiConstant.BFI_AP_BILL_PROJECT_NUMBER_TEST);
|
||||
// 【必填】外部系统来源分录标识(候选键),A103-1 下推时以 awrp_finapbill_entryid 关联此行
|
||||
entry.setAwrpEntryCode(bfiEntryCode(entity.getPaymentNo(), index + 1));
|
||||
// 【必填】外部系统来源分录标识(候选键):结算明细单据号(多行并入同组时下划线拼接),A103-1 下推时以 awrp_finapbill_entryid 关联此行
|
||||
entry.setAwrpEntryCode(entryGroup.documentNo());
|
||||
|
||||
// 调试
|
||||
|
||||
if (true){
|
||||
if (true) {
|
||||
// 【必填】物料编码.编码:结算明细货物类型匹配货物类型档案货物编码
|
||||
entry.setMaterialNumber(KingdeeBfiConstant.BFI_AP_BILL_MATERIAL_NUMBER_TEST);
|
||||
// 【必填】计量单位.编码:货物费用行运费计算单位(快照 price_unit,档案暂无编码字段暂传单位名称)
|
||||
@@ -1059,62 +1120,108 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
}
|
||||
param.setDetailentry(detailentry);
|
||||
|
||||
// 付款计划 planentity 不传,由金蝶按明细自动汇总生成
|
||||
// 付款计划 planentity:送正式结算单下绑定、单据状态为审核通过(approved)的付款信息,每条付款信息一行;
|
||||
// awrp_entry_code_p 取付款单号(A103-1 下推 awrp_finapbill_entryid 的勾稽值);
|
||||
// 无审核通过的付款信息时不传,由金蝶按明细自动汇总生成,A103-1 推送前再经 A102-4 反查 awrp_entry_code_p
|
||||
List<FormalSettlementPayment> approvedPayments = approvedSettlementPayments(entity, context);
|
||||
List<String> planEntryCodes = new ArrayList<>();
|
||||
if (Func.isNotEmpty(approvedPayments)) {
|
||||
List<BfiApBillSavePlanDTO> planentity = new ArrayList<>();
|
||||
for (FormalSettlementPayment payment : approvedPayments) {
|
||||
BfiApBillSavePlanDTO plan = new BfiApBillSavePlanDTO();
|
||||
// 外部系统来源分录标识(候选键):付款单号
|
||||
plan.setAwrpEntryCodeP(payment.getPaymentNo());
|
||||
// 应付金额:付款信息申请付款金额
|
||||
plan.setPlanpricetax(money(payment.getAppliedAmount()));
|
||||
// 【必填】项目.项目编码(联调期临时传金蝶测试档案值)
|
||||
plan.setPlanprojectNumber(KingdeeBfiConstant.BFI_AP_BILL_PROJECT_NUMBER_TEST);
|
||||
// 到期日:付款信息创建日期(yyyy-MM-dd)
|
||||
plan.setPlanduedate(payment.getCreateTime() == null ? null
|
||||
: payment.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().toString());
|
||||
planentity.add(plan);
|
||||
planEntryCodes.add(payment.getPaymentNo());
|
||||
}
|
||||
param.setPlanentity(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, "推送金蝶财务应付单");
|
||||
return new BfiApBillPushOutcome(saveResult.getResult().get(0).getNumber(), false);
|
||||
return new BfiApBillPushOutcome(saveResult.getResult().get(0).getNumber(), false, planEntryCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送金蝶付款申请(A103-1 应付下推付款申请,正式结算付款第二步),返回星瀚付款申请单id。
|
||||
* 付款申请分录与财务应付单明细分录一一对应,申请金额取各组结算金额(含税)。
|
||||
* 付款申请分录与财务应付单明细分录一一对应,申请金额取各组结算金额(含税);
|
||||
* awrp_finapbill_entryid 勾稽 A102-2 付款计划分录 awrp_entry_code_p(审核通过的付款信息单据号,
|
||||
* 无审核通过付款信息时经 A102-4 反查金蝶自动生成的值);awrp_entry_code 填结算明细单据号
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
* @param entryGroups 结算明细聚合出的应付单分录组
|
||||
* @param entity 付款申请实体
|
||||
* @param entryGroups 结算明细聚合出的应付单分录组
|
||||
* @param planEntryCodes A102-2 付款计划分录 awrp_entry_code_p 集合(付款信息单据号),空表示未传付款计划
|
||||
* @param context 金蝶同步上下文(缓存载体)
|
||||
* @return 星瀚付款申请单id
|
||||
*/
|
||||
private String pushBfiPayApplyPush(PaymentApplication entity, List<BfiEntryGroup> entryGroups, KingdeeSyncContext context) {
|
||||
private String pushBfiPayApplyPush(PaymentApplication entity, List<BfiEntryGroup> entryGroups, List<String> planEntryCodes, KingdeeSyncContext context) {
|
||||
ContractManage contract = requireContract(entity, context);
|
||||
String orgNumber = bfiOrgNumber(contract);
|
||||
// 申请日期:yyyy-MM-dd HH:mm:ss(与 A102-2 单据日期格式统一)
|
||||
String applyDateText = bfiDateTimeText(entity);
|
||||
// 【必填】结算单分录id勾稽值(awrp_finapbill_entryid):优先取审核通过的付款信息单据号(多条下划线拼接),
|
||||
// 无审核通过付款信息时经 A102-4 反查金蝶自动生成的付款计划 awrp_entry_code_p
|
||||
String finapbillEntryid;
|
||||
if (Func.isNotEmpty(planEntryCodes)) {
|
||||
finapbillEntryid = planEntryCodes.size() == 1 ? planEntryCodes.get(0) : String.join("_", planEntryCodes);
|
||||
} else {
|
||||
finapbillEntryid = apBillPlanEntryCode(entity, apBillSrcCode(entity, context), context);
|
||||
}
|
||||
BfiPayApplyPushDTO param = new BfiPayApplyPushDTO();
|
||||
// 【必填】外部系统来源单据号(候选键):付款申请号
|
||||
param.setAwrpSrcCode(entity.getPaymentNo());
|
||||
// 【必填】外部系统编号
|
||||
param.setAwrpSrcSys(bfiPayApplySrcSys);
|
||||
// 【必填】申请日期:yyyy-MM-dd HH:mm:ss(各接口统一格式)
|
||||
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());
|
||||
// 付款组织.编码(非必传,不传时根据应付单自动带出)
|
||||
// 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());
|
||||
|
||||
// 付款申请分录与财务应付单明细分录一一对应(awrp_finapbill_entryid 关联应付单明细 awrp_entry_code)
|
||||
List<BfiPayApplyPushEntryDTO> entries = new ArrayList<>();
|
||||
for (int index = 0; index < entryGroups.size(); index++) {
|
||||
BfiEntryGroup entryGroup = entryGroups.get(index);
|
||||
BfiPayApplyPushEntryDTO entry = new BfiPayApplyPushEntryDTO();
|
||||
// 正式付款固定 201
|
||||
// 【必填】付款类型.编码:正式付款固定 201
|
||||
entry.setEPaymenttypeNumber(KingdeeBfiConstant.BFI_PAYMENT_TYPE_FORMAL);
|
||||
// 关联 A102-2 财务应付单分录,对应应付单明细的 awrp_entry_code
|
||||
entry.setAwrpFinapbillEntryid(bfiEntryCode(entity.getPaymentNo(), index + 1));
|
||||
entry.setAwrpEntryCode(bfiEntryCode(entity.getPaymentNo(), index + 1));
|
||||
// 【必填】结算单分录id:A102-2 财务应付单付款计划分录的外部系统来源分录标识(awrp_entry_code_p)
|
||||
entry.setAwrpFinapbillEntryid(finapbillEntryid);
|
||||
// 【必填】对方系统唯一行id(不能重复):结算明细单据号
|
||||
entry.setAwrpEntryCode(entryGroup.documentNo());
|
||||
// 【必填】往来账户:乙方(收款方)银行账号
|
||||
entry.setEAssacct(entity.getBankAccount());
|
||||
// TODO 待确认:往来银行为必填编码(e_bebank_number),当前暂传银行名称,待建立银行档案编码映射后调整
|
||||
entry.setEBebankNumber(entity.getBankName());
|
||||
// 申请金额:与对应应付单分录结算金额(含税)保持一致
|
||||
// 【必填】往来银行.编码(联调期临时传金蝶测试档案值,待建立银行档案编码映射后调整)todo
|
||||
entry.setEBebankNumber(KingdeeBfiConstant.BFI_AP_BILL_BANKACCOUNT_NUMBER_TEST);
|
||||
// 【必填】申请金额:与对应应付单分录结算金额(含税)保持一致
|
||||
entry.setEApplyamount(entryGroup.amount());
|
||||
entry.setEExpaydate(applyDateText);
|
||||
// entry.setProjectNumber(bfiProjectNumber(entity));
|
||||
// 【必填】项目.项目编码(联调期临时传金蝶测试档案值)
|
||||
entry.setProjectNumber(KingdeeBfiConstant.BFI_AP_BILL_PROJECT_NUMBER_TEST);
|
||||
entry.setERemark(entity.getRemark());
|
||||
// 期望付款日(非必传,不传时根据应付单自动带出)
|
||||
// entry.setEExpaydate(applyDateText);
|
||||
// 备注(非必传)
|
||||
// entry.setERemark(entity.getRemark());
|
||||
// 【必填】外部单据类型 todo
|
||||
entry.setAwrpSrcType(bfiPayApplySrcType);
|
||||
entries.add(entry);
|
||||
}
|
||||
@@ -1129,12 +1236,13 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
*
|
||||
* @param materialNumber 物料编码(货物类型档案货物编码)
|
||||
* @param measureunitNumber 计量单位编码(运费计算单位,档案暂无编码字段暂传单位名称)
|
||||
* @param documentNo 结算明细单据号,作为分录唯一标识(awrp_entry_code),多行并入同组时下划线拼接
|
||||
* @param quantity 组内运输数量合计
|
||||
* @param amount 组内结算金额(含税)合计,对应明细应付金额 e_pricetaxtotal
|
||||
* @param amountNoTax 组内结算金额(不含税)合计,对应明细金额 e_amount
|
||||
*/
|
||||
private record BfiEntryGroup(String materialNumber, String measureunitNumber, BigDecimal quantity,
|
||||
BigDecimal amount, BigDecimal amountNoTax) {
|
||||
private record BfiEntryGroup(String materialNumber, String measureunitNumber, String documentNo,
|
||||
BigDecimal quantity, BigDecimal amount, BigDecimal amountNoTax) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1149,16 +1257,19 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
/**
|
||||
* 财务应付单(A102-2)推送结果
|
||||
*
|
||||
* @param billNo 财务应付单单据编号(金蝶已存在时返回已有单据编号)
|
||||
* @param alreadyExist 金蝶是否已存在该防重键的财务应付单,true 表示本次幂等跳过保存(上次同步已在后续环节中断)
|
||||
* @param billNo 财务应付单单据编号(金蝶已存在时返回已有单据编号)
|
||||
* @param alreadyExist 金蝶是否已存在该防重键的财务应付单,true 表示本次幂等跳过保存(上次同步已在后续环节中断)
|
||||
* @param planEntryCodes 付款计划分录外部系统来源分录标识(awrp_entry_code_p,取审核通过的付款信息单据号),
|
||||
* 空表示 A102-2 未传付款计划(A103-1 推送前需经 A102-4 反查)
|
||||
*/
|
||||
private record BfiApBillPushOutcome(String billNo, boolean alreadyExist) {
|
||||
private record BfiApBillPushOutcome(String billNo, boolean alreadyExist, List<String> planEntryCodes) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析付款申请关联的正式结算明细,按货物编码+计量单位聚合为应付单分录组。
|
||||
* <p>物料编码:货物费用行/结算明细货物类型(支持“、”分隔多类型)匹配货物类型档案货物编码;
|
||||
* 计量单位:快照运费计算单位(历史快照为空时兜底数量单位),档案暂无编码字段暂以单位名称作为编码。
|
||||
* 计量单位:快照运费计算单位(历史快照为空时兜底数量单位),档案暂无编码字段暂以单位名称作为编码;
|
||||
* 分录唯一标识(awrp_entry_code)取所属结算明细单据号,A103-1 下推时以 awrp_finapbill_entryid 关联此行;
|
||||
* 分录金额直接取结算金额(含税/不含税),不按申请付款金额分摊。</p>
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
@@ -1176,9 +1287,15 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
// 优先按货物费用行逐条取数(blade_formal_settlement_detail_fee 每货物一条,金额为结算调整后的精确值)
|
||||
List<FormalSettlementDetailFee> detailFees = formalSettlementDetailFees(settlementDetails);
|
||||
if (Func.isNotEmpty(detailFees)) {
|
||||
// 结算明细id->单据号映射:货物费用行经此回溯所属结算明细单据号(作为分录唯一标识)
|
||||
Map<Long, String> detailDocumentNoMap = new LinkedHashMap<>();
|
||||
for (FormalSettlementDetail settlementDetail : settlementDetails) {
|
||||
detailDocumentNoMap.putIfAbsent(settlementDetail.getId(), settlementDetail.getDocumentNo());
|
||||
}
|
||||
for (FormalSettlementDetailFee detailFee : detailFees) {
|
||||
collectEntryGroups(groupMap, cargoCodeByName, detailFee.getCargoType(),
|
||||
resolveMeasureunitNumber(null, detailFee.getQuantityUnit()),
|
||||
detailDocumentNoMap.get(detailFee.getFormalSettlementDetailId()),
|
||||
money(detailFee.getSettlementAmountTax()), money(detailFee.getSettlementAmountNoTax()),
|
||||
money(detailFee.getTransportQuantity()),
|
||||
"货物费用[" + Func.toStr(detailFee.getCargoName()) + " 第" + Func.toStr(detailFee.getLineNo()) + "行]");
|
||||
@@ -1187,6 +1304,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
for (FormalSettlementDetail settlementDetail : settlementDetails) {
|
||||
collectEntryGroups(groupMap, cargoCodeByName, settlementDetail.getCargoType(),
|
||||
resolveMeasureunitNumber(null, settlementDetail.getQuantityUnit()),
|
||||
settlementDetail.getDocumentNo(),
|
||||
money(settlementDetail.getSettlementAmountTax()), money(settlementDetail.getSettlementAmountNoTax()),
|
||||
money(settlementDetail.getTransportQuantity()),
|
||||
"结算明细[" + Func.toStr(settlementDetail.getDocumentNo()) + " 第" + Func.toStr(settlementDetail.getLineNo()) + "行]");
|
||||
@@ -1220,8 +1338,9 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
|
||||
/**
|
||||
* 金蝶分录标识:付款申请号-分录序号(1 起)。
|
||||
* A102-2 明细 awrp_entry_code 与 A103-1 awrp_finapbill_entryid/awrp_entry_code 必须由本方法统一生成,
|
||||
* 保证应付下推勾稽关联一致,禁止各接口自行拼接
|
||||
* 现仅 A103-3 预付款直接保存路径使用(序号固定 1);正式结算链路 A102-2 明细 awrp_entry_code 与
|
||||
* A103-1 awrp_entry_code 已改为结算明细单据号(resolveBfiEntryGroups 统一解析),
|
||||
* A103-1 awrp_finapbill_entryid 改为财务应付单付款计划 awrp_entry_code_p(A102-4 反查),禁止各接口自行拼接
|
||||
*
|
||||
* @param paymentNo 付款申请号
|
||||
* @param seq 分录序号(1 起)
|
||||
@@ -1275,18 +1394,22 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
* @param cargoCodeByName 货物类型名称->货物编码映射
|
||||
* @param cargoType 货物类型(支持“、”分隔多类型)
|
||||
* @param measureunitNumber 计量单位编码(运费计算单位,档案暂无编码字段暂传单位名称)
|
||||
* @param documentNo 结算明细单据号,作为分录唯一标识(awrp_entry_code)
|
||||
* @param amount 结算金额(含税)
|
||||
* @param amountNoTax 结算金额(不含税)
|
||||
* @param quantity 运输数量
|
||||
* @param sourceDesc 来源行描述,用于异常提示
|
||||
*/
|
||||
private void collectEntryGroups(LinkedHashMap<String, BfiEntryGroup> groupMap, Map<String, String> cargoCodeByName,
|
||||
String cargoType, String measureunitNumber, BigDecimal amount, BigDecimal amountNoTax,
|
||||
String cargoType, String measureunitNumber, String documentNo, BigDecimal amount, BigDecimal amountNoTax,
|
||||
BigDecimal quantity, String sourceDesc) {
|
||||
List<String> cargoNames = splitCargoTypes(cargoType);
|
||||
if (Func.isBlank(measureunitNumber)) {
|
||||
throw new ServiceException("推送金蝶财务应付单失败:" + sourceDesc + "计量单位为空,请先维护运费计算单位");
|
||||
}
|
||||
if (Func.isBlank(documentNo)) {
|
||||
throw new ServiceException("推送金蝶财务应付单失败:" + sourceDesc + "单据号为空,无法生成金蝶分录标识(awrp_entry_code)");
|
||||
}
|
||||
List<BigDecimal> amountShares = splitEqually(amount, cargoNames.size());
|
||||
List<BigDecimal> amountNoTaxShares = splitEqually(amountNoTax, cargoNames.size());
|
||||
List<BigDecimal> quantityShares = splitEqually(quantity, cargoNames.size());
|
||||
@@ -1297,12 +1420,24 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
throw new ServiceException("推送金蝶财务应付单失败:" + sourceDesc + "货物类型[" + cargoName + "]未在货物类型档案中维护编码,请补充后重试");
|
||||
}
|
||||
groupMap.merge(materialNumber + "/" + measureunitNumber,
|
||||
new BfiEntryGroup(materialNumber, measureunitNumber, quantityShares.get(index), amountShares.get(index), amountNoTaxShares.get(index)),
|
||||
new BfiEntryGroup(materialNumber, measureunitNumber, documentNo, quantityShares.get(index), amountShares.get(index), amountNoTaxShares.get(index)),
|
||||
(left, right) -> new BfiEntryGroup(left.materialNumber(), left.measureunitNumber(),
|
||||
joinEntryDocumentNos(left.documentNo(), right.documentNo()),
|
||||
left.quantity().add(right.quantity()), left.amount().add(right.amount()), left.amountNoTax().add(right.amountNoTax())));
|
||||
}
|
||||
}
|
||||
|
||||
/** 分录组合并时结算明细单据号去重下划线拼接(保持先后顺序),与外部系统来源单据号拼接规则一致 */
|
||||
private String joinEntryDocumentNos(String left, String right) {
|
||||
if (Func.isBlank(left)) {
|
||||
return right;
|
||||
}
|
||||
if (Func.isBlank(right) || left.equals(right)) {
|
||||
return left;
|
||||
}
|
||||
return left.contains(right) ? left : right.contains(left) ? right : left + "_" + right;
|
||||
}
|
||||
|
||||
/** 付款申请关联的正式结算货物费用行(blade_formal_settlement_detail_fee,每货物一条,金额为结算调整后精确值) */
|
||||
private List<FormalSettlementDetailFee> formalSettlementDetailFees(List<FormalSettlementDetail> settlementDetails) {
|
||||
List<Long> settlementDetailIds = settlementDetails.stream().map(FormalSettlementDetail::getId)
|
||||
|
||||
Reference in New Issue
Block a user