!3 金蝶组织编码、收款流水多次认领、收款流水认领
Merge pull request !3 from mindless/feature/payment-apply-bfi-sync
This commit is contained in:
+6
@@ -307,6 +307,12 @@ public class BfiProperties {
|
||||
*/
|
||||
private String invoiceTypes = "";
|
||||
|
||||
/**
|
||||
* 客商单位过滤开关:开启后只同步开票单位(销方)与受票单位(购方)税号均已登记在
|
||||
* 客商发票信息(blade_customer_invoice_info)中的发票,前端票池展示查询同用此开关过滤,默认关闭
|
||||
*/
|
||||
private boolean customerUnitsOnly = false;
|
||||
|
||||
/**
|
||||
* 同步 single-flight 锁租约秒数,须大于最坏一轮耗时;进程异常退出时锁在租约到期后自动释放
|
||||
*/
|
||||
|
||||
+6
-4
@@ -44,11 +44,13 @@ public interface KingdeeInvoicePoolMapper extends BaseMapper<KingdeeInvoicePool>
|
||||
/**
|
||||
* 金蝶票据池镜像统一分页查询:仅出可用票(剩余可用金额>0 且未使用),认领/登记标记由 EXISTS 子查询直出
|
||||
*
|
||||
* @param page 分页参数
|
||||
* @param query 统一查询条件(已在上层完成 trim 与日期格式校验)
|
||||
* @param tenantId 当前租户
|
||||
* @param page 分页参数
|
||||
* @param query 统一查询条件(已在上层完成 trim 与日期格式校验)
|
||||
* @param tenantId 当前租户
|
||||
* @param customerUnitsOnly 客商单位过滤开关:开启时只出销/购方税号均登记在客商发票信息中的发票(与定时同步同开关)
|
||||
* @return 统一视图分页
|
||||
*/
|
||||
IPage<KingdeeInvoicePoolVO> selectInvoicePoolPage(IPage<KingdeeInvoicePoolVO> page,
|
||||
@Param("query") KingdeeInvoicePoolQueryDTO query, @Param("tenantId") String tenantId);
|
||||
@Param("query") KingdeeInvoicePoolQueryDTO query, @Param("tenantId") String tenantId,
|
||||
@Param("customerUnitsOnly") boolean customerUnitsOnly);
|
||||
}
|
||||
|
||||
+12
@@ -39,12 +39,24 @@
|
||||
AND p.available_amount > 0 AND p.amount_use_status = '0'
|
||||
</sql>
|
||||
|
||||
<!-- 客商单位过滤:开票单位(销方)与受票单位(购方)税号均须登记在有效客商发票信息中
|
||||
(与定时同步拉取过滤同开关同口径;自定义 SQL 无租户插件,手工带租户与逻辑删除) -->
|
||||
<sql id="customerUnitsFilter">
|
||||
AND EXISTS (SELECT 1 FROM blade_customer_invoice_info ci
|
||||
WHERE ci.is_deleted = 0 AND ci.status = 1 AND ci.tenant_id = p.tenant_id AND ci.tax_no = p.saler_tax_no)
|
||||
AND EXISTS (SELECT 1 FROM blade_customer_invoice_info cj
|
||||
WHERE cj.is_deleted = 0 AND cj.status = 1 AND cj.tenant_id = p.tenant_id AND cj.tax_no = p.buyer_tax_no)
|
||||
</sql>
|
||||
|
||||
<select id="selectInvoicePoolPage" resultType="org.springblade.transport.pojo.vo.KingdeeInvoicePoolVO">
|
||||
SELECT
|
||||
<include refid="invoicePoolVoColumns"/>
|
||||
FROM blade_kingdee_invoice_pool p
|
||||
WHERE p.is_deleted = 0 AND p.status = 1 AND p.tenant_id = #{tenantId}
|
||||
<include refid="usableFilter"/>
|
||||
<if test="customerUnitsOnly">
|
||||
<include refid="customerUnitsFilter"/>
|
||||
</if>
|
||||
<if test="query.invoiceNo != null and query.invoiceNo != ''">
|
||||
AND p.invoice_no LIKE CONCAT('%', #{query.invoiceNo}, '%')
|
||||
</if>
|
||||
|
||||
+9
-5
@@ -97,6 +97,7 @@ import org.springblade.transport.pojo.vo.InvoiceApplicationSheetVO;
|
||||
import org.springblade.transport.pojo.vo.InvoiceApplicationVO;
|
||||
import org.springblade.transport.service.IInvoiceApplicationService;
|
||||
import org.springblade.transport.service.IKingdeeBillLockService;
|
||||
import org.springblade.transport.service.KingdeeSyncContext;
|
||||
import org.springblade.transport.support.KingdeeApBillSyncSupport;
|
||||
import org.springblade.transport.support.KingdeeArBillSyncSupport;
|
||||
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
||||
@@ -806,6 +807,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
if (dept == null || Func.isBlank(dept.getKingdeeDeptCode())) {
|
||||
throw new ServiceException("推送金蝶开票申请失败:申请部门[" + Func.toStr(entity.getDeptId()) + "]未维护金蝶组织编码,请检查部门数据");
|
||||
}
|
||||
// 金蝶组织编码:项目业务部门反查唯一平台公司 → 平台公司金蝶组织编码
|
||||
String kingdeeOrgNumber = kingdeeApBillSyncSupport.kingdeeOrgNumber(entity.getProjectId(), KingdeeSyncContext.empty());
|
||||
CustomerArchive customer = entity.getReceiverCustomerId() == null ? null : customerArchiveMapper.selectById(entity.getReceiverCustomerId());
|
||||
if (customer == null || Func.isBlank(customer.getCustomerCode())) {
|
||||
throw new ServiceException("推送金蝶开票申请失败:受票方客户未在客商档案中维护客商编码,请补充后重试");
|
||||
@@ -836,7 +839,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
+ "]无商品行,无法组装分录");
|
||||
}
|
||||
BfiInvoiceApplySaveDTO param = this.buildInvoiceApplyParam(entity, sheetBillNo, seller, salerAddr, salerBank,
|
||||
dept, customer, firstArSrcCode, money(sheet.getInvoiceAmount()), items);
|
||||
dept, kingdeeOrgNumber, customer, firstArSrcCode, money(sheet.getInvoiceAmount()), items);
|
||||
log.info("金蝶开票申请推送:开票申请[{}]发票张[{}],billno=[{}],价税合计=[{}],分录{}条",
|
||||
entity.getApplicationNo(), sheet.getSheetNo(), sheetBillNo, sheet.getInvoiceAmount(), items.size());
|
||||
BfiBillSaveResultVO saveResult = bfiService.saveInvoiceApply(param);
|
||||
@@ -885,16 +888,17 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
* 组装 A303-1 单张开票申请单入参(字段映射见设计文档 6.3/6.4),
|
||||
* 购销方等主表公共字段随开票申请取数,价税合计与单据体分录取本张发票
|
||||
*
|
||||
* @param orgNumber 金蝶组织编码(项目业务部门反查平台公司)
|
||||
* @param firstArSrcCode 上游来源单据号:首张关联结算单的金蝶应收单防重键 awrp_src_code(多张结算单取第一张)
|
||||
*/
|
||||
private BfiInvoiceApplySaveDTO buildInvoiceApplyParam(InvoiceApplication entity, String billNo,
|
||||
CustomerArchive seller, String salerAddr, String salerBank, Dept dept, CustomerArchive customer,
|
||||
CustomerArchive seller, String salerAddr, String salerBank, Dept dept, String orgNumber, CustomerArchive customer,
|
||||
String firstArSrcCode, BigDecimal sheetAmount, List<BfiInvoiceApplySaveEntryDTO> items) {
|
||||
BfiInvoiceApplySaveDTO param = new BfiInvoiceApplySaveDTO();
|
||||
// ==================== 以下为【必填】字段(BfiInvoiceApplySaveDTO 标注),非必填字段先注释保留 ====================
|
||||
// 【必填】组织.编码:申请部门 deptId → 金蝶组织编码
|
||||
param.setOrgidNumber(dept.getKingdeeDeptCode());
|
||||
// 【必填】申请部门.编码:同上
|
||||
// 【必填】组织.编码:项目业务部门反查唯一平台公司 → 平台公司金蝶组织编码
|
||||
param.setOrgidNumber(orgNumber);
|
||||
// 【必填】申请部门.编码:申请部门 deptId → 金蝶部门编码
|
||||
param.setAwrpOrgfieldNumber(dept.getKingdeeDeptCode());
|
||||
// 【必填】业务单据编号(候选键):单张发票候选 billno(多张发票时开票申请单号-张次序号)
|
||||
param.setBillno(billNo);
|
||||
|
||||
+56
-15
@@ -63,6 +63,7 @@ import org.springblade.transport.mapper.FormalSettlementMapper;
|
||||
import org.springblade.transport.mapper.InvoiceReceiptMapper;
|
||||
import org.springblade.transport.mapper.InvoiceReceiptRecordMapper;
|
||||
import org.springblade.transport.mapper.InvoiceReceiptSettlementMapper;
|
||||
import org.springblade.transport.mapper.CustomerInvoiceInfoMapper;
|
||||
import org.springblade.transport.mapper.KingdeeInvoicePoolMapper;
|
||||
import org.springblade.transport.pojo.dto.InvoiceReceiptSaveRequest;
|
||||
import org.springblade.transport.pojo.dto.InvoiceReceiptStatusRequest;
|
||||
@@ -70,6 +71,7 @@ import org.springblade.transport.pojo.dto.KingdeeInvoicePoolQueryDTO;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.CustomerArchive;
|
||||
import org.springblade.transport.pojo.entity.CustomerContact;
|
||||
import org.springblade.transport.pojo.entity.CustomerInvoiceInfo;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlementDetail;
|
||||
import org.springblade.transport.pojo.entity.InvoiceReceipt;
|
||||
@@ -146,6 +148,7 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
private final FormalSettlementDetailMapper formalSettlementDetailMapper;
|
||||
private final CustomerArchiveMapper customerArchiveMapper;
|
||||
private final CustomerContactMapper customerContactMapper;
|
||||
private final CustomerInvoiceInfoMapper customerInvoiceInfoMapper;
|
||||
private final IBfiService bfiService;
|
||||
private final BfiProperties bfiProperties;
|
||||
private final KingdeeApBillSyncSupport kingdeeApBillSyncSupport;
|
||||
@@ -243,9 +246,10 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
return page;
|
||||
}
|
||||
|
||||
/** 本地镜像真分页(可用过滤/认领登记标记均在 SQL 内直出) */
|
||||
/** 本地镜像真分页(可用过滤/认领登记标记均在 SQL 内直出;客商单位过滤与定时同步同开关同口径) */
|
||||
private IPage<KingdeeInvoicePoolVO> selectLocalPoolPage(KingdeeInvoicePoolQueryDTO query, long current, long size) {
|
||||
return invoicePoolMapper.selectInvoicePoolPage(new Page<>(current, size), query, AuthUtil.getTenantId());
|
||||
return invoicePoolMapper.selectInvoicePoolPage(new Page<>(current, size), query, AuthUtil.getTenantId(),
|
||||
bfiProperties.getInvoiceReceipt().isCustomerUnitsOnly());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1248,20 +1252,25 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
LocalDateTime windowStart = LocalDateTime.now().minusDays(bfiProperties.getInvoiceReceipt().getIncrementDays());
|
||||
LocalDateTime windowEnd = LocalDateTime.now();
|
||||
List<String> invoiceTypes = this.poolInvoiceTypeCodes();
|
||||
// 客商单位过滤集:开关开启时一次性载入有效客商发票信息税号(销/购方税号都须命中才入镜像),整轮共享避免逐页查询
|
||||
Set<String> customerTaxNos = bfiProperties.getInvoiceReceipt().isCustomerUnitsOnly() ? this.customerInvoiceTaxNos() : null;
|
||||
SyncStats stats = new SyncStats();
|
||||
log.info("金蝶全票池同步开始,增量窗口 {} ~ {},发票种类:{},单页 {} 条最多 {} 页(触顶自动二分切片)",
|
||||
log.info("金蝶全票池同步开始,增量窗口 {} ~ {},发票种类:{},客商单位过滤:{},单页 {} 条最多 {} 页(触顶自动二分切片)",
|
||||
windowStart.format(POOL_TIME_FORMATTER), windowEnd.format(POOL_TIME_FORMATTER),
|
||||
invoiceTypes.isEmpty() ? "不限制" : invoiceTypes, POOL_PAGE_SIZE, MAX_POOL_PAGES);
|
||||
invoiceTypes.isEmpty() ? "不限制" : invoiceTypes, customerTaxNos != null ? "开启" : "关闭",
|
||||
POOL_PAGE_SIZE, MAX_POOL_PAGES);
|
||||
if (invoiceTypes.isEmpty()) {
|
||||
this.syncWindowSlice(windowStart, windowEnd, null, stats);
|
||||
this.syncWindowSlice(windowStart, windowEnd, null, customerTaxNos, stats);
|
||||
} else {
|
||||
// 单值参数拆轮:每个票种编码独立完整切片续扫,互不挤占翻页额度
|
||||
for (String invoiceType : invoiceTypes) {
|
||||
this.syncWindowSlice(windowStart, windowEnd, invoiceType, stats);
|
||||
this.syncWindowSlice(windowStart, windowEnd, invoiceType, customerTaxNos, stats);
|
||||
}
|
||||
}
|
||||
String summary = "金蝶全票池同步:翻页" + stats.pageCount + "页切片" + stats.sliceCount + "片拉取"
|
||||
+ stats.pulledCount + "张发票,镜像新增" + stats.created + "更新" + stats.updated + "无变化"
|
||||
+ stats.pulledCount + "张发票"
|
||||
+ (stats.customerFiltered > 0 ? "(客商单位过滤" + stats.customerFiltered + "张)" : "")
|
||||
+ ",镜像新增" + stats.created + "更新" + stats.updated + "无变化"
|
||||
+ stats.mirrorUnchanged + ",补绑" + stats.bound + ",登记无变化跳过" + stats.unchanged
|
||||
+ ",失败" + stats.failed + ",耗时" + (System.currentTimeMillis() - startMillis) + "ms";
|
||||
log.info("金蝶全票池同步结束,{}", summary);
|
||||
@@ -1284,6 +1293,8 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
private static final class SyncStats {
|
||||
/** A801-1 原始拉取张数 */
|
||||
int pulledCount;
|
||||
/** 客商单位过滤剔除张数(销/购方税号未同时登记在客商发票信息中,开关开启时统计) */
|
||||
int customerFiltered;
|
||||
/** 镜像新增条数(票池有、本地无) */
|
||||
int created;
|
||||
/** 镜像有变化并重写条数 */
|
||||
@@ -1307,11 +1318,13 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
* 触顶说明该时间片数据 >2.5 万,从中点二分为两片递归续扫(递归深度受时间粒度自然收敛,
|
||||
* 单片 500 条以内 1 页即短页终止)
|
||||
*
|
||||
* @param sliceStart 切片起点(含)
|
||||
* @param sliceEnd 切片终点(不含,避免与下一片边界重复拉取)
|
||||
* @param stats 整轮统计累加器
|
||||
* @param sliceStart 切片起点(含)
|
||||
* @param sliceEnd 切片终点(不含,避免与下一片边界重复拉取)
|
||||
* @param customerTaxNos 客商发票信息税号集,null 表示客商单位过滤关闭(整轮共享)
|
||||
* @param stats 整轮统计累加器
|
||||
*/
|
||||
private void syncWindowSlice(LocalDateTime sliceStart, LocalDateTime sliceEnd, String invoiceType, SyncStats stats) {
|
||||
private void syncWindowSlice(LocalDateTime sliceStart, LocalDateTime sliceEnd, String invoiceType,
|
||||
Set<String> customerTaxNos, SyncStats stats) {
|
||||
// 二分收敛保护:切片粒度细到 1 秒仍触顶说明远端异常(同秒 >2.5 万条不可能),不再二分直接记漏
|
||||
if (!sliceStart.isBefore(sliceEnd)) {
|
||||
log.error("金蝶全票池同步切片区间为空({} ~ {}),远端 updateTime 粒度异常,放弃该切片", sliceStart, sliceEnd);
|
||||
@@ -1325,6 +1338,13 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
}
|
||||
stats.pageCount++;
|
||||
stats.pulledCount += rows.size();
|
||||
// 客商单位过滤:销/购方税号未同时登记在客商发票信息中的发票不入镜像;
|
||||
// 短页判定用原始条数,过滤后的空页不能提前终止翻页
|
||||
int rawPageSize = rows.size();
|
||||
if (customerTaxNos != null) {
|
||||
rows = rows.stream().filter(row -> this.bothUnitsRegistered(row, customerTaxNos)).toList();
|
||||
stats.customerFiltered += rawPageSize - rows.size();
|
||||
}
|
||||
BatchUpsertResult upsertResult = this.batchUpsertInvoicePool(rows);
|
||||
stats.created += upsertResult.created();
|
||||
stats.updated += upsertResult.updated();
|
||||
@@ -1336,8 +1356,8 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
stats.unchanged += refreshResult.unchanged();
|
||||
stats.failed += refreshResult.failed();
|
||||
}
|
||||
// 远程短页:该片到底,正常终止
|
||||
if (rows.size() < POOL_PAGE_SIZE) {
|
||||
// 远程短页:该片到底,正常终止(按原始拉取条数判断,客商过滤不改变翻页节奏)
|
||||
if (rawPageSize < POOL_PAGE_SIZE) {
|
||||
stats.sliceCount++;
|
||||
return;
|
||||
}
|
||||
@@ -1355,13 +1375,34 @@ public class InvoiceReceiptServiceImpl extends BaseServiceImpl<InvoiceReceiptMap
|
||||
}
|
||||
log.warn("金蝶全票池同步切片 {} ~ {} 触达翻页保护 {} 页({} 条),二分续扫 [{} ~ {}) + [{} ~ {})",
|
||||
sliceStart, sliceEnd, MAX_POOL_PAGES, MAX_POOL_PAGES * POOL_PAGE_SIZE, sliceStart, mid, mid, sliceEnd);
|
||||
this.syncWindowSlice(sliceStart, mid, invoiceType, stats);
|
||||
this.syncWindowSlice(mid, sliceEnd, invoiceType, stats);
|
||||
this.syncWindowSlice(sliceStart, mid, invoiceType, customerTaxNos, stats);
|
||||
this.syncWindowSlice(mid, sliceEnd, invoiceType, customerTaxNos, stats);
|
||||
return;
|
||||
}
|
||||
stats.sliceCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 有效客商发票信息税号集(状态正常且未删除,MP 租户插件自动隔离租户):
|
||||
* 客商单位过滤开关开启时,同步拉取与票池展示查询共用的单位白名单
|
||||
*/
|
||||
private Set<String> customerInvoiceTaxNos() {
|
||||
return customerInvoiceInfoMapper.selectList(Wrappers.<CustomerInvoiceInfo>lambdaQuery()
|
||||
.eq(CustomerInvoiceInfo::getStatus, 1)
|
||||
.eq(CustomerInvoiceInfo::getIsDeleted, 0)).stream()
|
||||
.map(CustomerInvoiceInfo::getTaxNo)
|
||||
.filter(StringUtil::isNotBlank)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 开票单位(销方)与受票单位(购方)税号是否均已登记在客商发票信息中;税号缺失视为未登记(开关开启时不入镜像)
|
||||
*/
|
||||
private boolean bothUnitsRegistered(BfiInvoicePoolVO row, Set<String> customerTaxNos) {
|
||||
return StringUtil.isNotBlank(row.getSalerTaxNo()) && StringUtil.isNotBlank(row.getBuyerTaxNo())
|
||||
&& customerTaxNos.contains(row.getSalerTaxNo()) && customerTaxNos.contains(row.getBuyerTaxNo());
|
||||
}
|
||||
|
||||
/** 已登记票跟随刷新结果统计(绑定/刷新/无变化/失败) */
|
||||
private record RefreshResult(int bound, int unchanged, int failed) {
|
||||
}
|
||||
|
||||
+43
-9
@@ -435,6 +435,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
entity.setCurrentProcessor(AuthUtil.getUserName());
|
||||
entity.setRemark(request.getReason() == null ? entity.getRemark() : limit(request.getReason(), 200));
|
||||
updateById(entity);
|
||||
// 联动作废提前建档的预结算预付记录,避免残留记录阻断预结算作废
|
||||
voidPreSettlementAdvance(entity);
|
||||
refreshFormalSettlement(entity);
|
||||
}
|
||||
|
||||
@@ -590,8 +592,9 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
// 真实付款结果改由定时回写任务驱动;联调环境需要模拟时恢复以下注释即可
|
||||
// entity.setPaidAmount(money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP));
|
||||
// mockPaymentRecords(entity);
|
||||
// syncPreSettlementAdvance(entity);
|
||||
// syncFormalSettlementPayments(entity);
|
||||
// 金蝶付款申请单生成成功:提前联动预结算预付记录(approved/已付0)回显预付信息,付款完成回写时更新为已付款
|
||||
syncPreSettlementAdvance(entity, TransportBusiConstant.APPROVED, money(entity.getPaidAmount()));
|
||||
updateById(entity);
|
||||
log.info("付款申请[{}]同步金蝶完成:kingdeeStatus={},kingdeeBillId={},kingdeeBillNo={}",
|
||||
entity.getPaymentNo(), entity.getKingdeeStatus(), kingdeeBillId, entity.getKingdeeBillNo());
|
||||
@@ -660,6 +663,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
.set(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_FAILED)
|
||||
.set(PaymentApplication::getKingdeeBillId, null)
|
||||
.set(PaymentApplication::getKingdeeBillNo, null));
|
||||
// 金蝶单据已删除/解锁重推:联动作废提前建档的预付记录,重推生成新单后重新建档
|
||||
voidPreSettlementAdvance(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -809,9 +814,10 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
String orgNumber = payApply == null ? null : payApply.getPayorgNumber();
|
||||
if (Func.isBlank(orgNumber)) {
|
||||
try {
|
||||
// A103-4 付款人(公司)编码必填:优先取 A103-2 返回付款组织编码,兜底按合同所属组织解析
|
||||
// A103-4 付款人(公司)编码必填:优先取 A103-2 返回付款组织编码,兜底按合同关联项目解析
|
||||
KingdeeSyncContext context = KingdeeSyncContext.empty();
|
||||
orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(
|
||||
kingdeeApBillSyncSupport.requireContract(application.getContractId(), KingdeeSyncContext.empty()));
|
||||
kingdeeApBillSyncSupport.requireContract(application.getContractId(), context), context);
|
||||
} catch (Exception exception) {
|
||||
log.warn("付款申请[{}]付款处理查询跳过,付款组织编码获取失败:{}", application.getPaymentNo(), exception.getMessage());
|
||||
continue;
|
||||
@@ -877,10 +883,15 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
updateById(entity);
|
||||
if (KingdeeBfiConstant.KINGDEE_STATUS_PAID.equals(kingdeeStatus)) {
|
||||
// 付款完成:联动履约付款数据(预结算预付记录、正式结算付款记录)
|
||||
syncPreSettlementAdvance(entity);
|
||||
syncPreSettlementAdvance(entity, "paid", money(entity.getAppliedAmount()));
|
||||
syncFormalSettlementPayments(entity);
|
||||
return true;
|
||||
}
|
||||
// 生成失败单据勾稽确认金蝶已生成(已生成/付款中):与推送成功同语义,提前落预付记录回显预付信息
|
||||
if (KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(kingdeeStatus)
|
||||
|| KingdeeBfiConstant.KINGDEE_STATUS_PAYING.equals(kingdeeStatus)) {
|
||||
syncPreSettlementAdvance(entity, TransportBusiConstant.APPROVED, money(entity.getPaidAmount()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1135,7 +1146,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
*/
|
||||
private String pushBfiPayApply(PaymentApplication entity, KingdeeSyncContext context) {
|
||||
ContractManage contract = kingdeeApBillSyncSupport.requireContract(entity.getContractId(), context);
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract);
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract, context);
|
||||
String currency = kingdeeApBillSyncSupport.bfiCurrency(contract);
|
||||
// 申请日期:yyyy-MM-dd HH:mm:ss(与 A102-2/A103-1 统一,金蝶各接口日期格式要求一致)
|
||||
String applyDateText = bfiDateTimeText(entity);
|
||||
@@ -1241,7 +1252,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
*/
|
||||
private String pushBfiPayApplyPush(PaymentApplication entity, KingdeeSyncContext context) {
|
||||
ContractManage contract = kingdeeApBillSyncSupport.requireContract(entity.getContractId(), context);
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract);
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract, context);
|
||||
// 申请日期:yyyy-MM-dd HH:mm:ss(与 A102-2 单据日期格式统一)
|
||||
String applyDateText = bfiDateTimeText(entity);
|
||||
BfiPayApplyPushDTO param = new BfiPayApplyPushDTO();
|
||||
@@ -1555,7 +1566,14 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
}
|
||||
}
|
||||
|
||||
private void syncPreSettlementAdvance(PaymentApplication entity) {
|
||||
/**
|
||||
* 付款申请联动预结算预付记录:金蝶付款申请单生成成功(synced)即建档提前回显预付信息,
|
||||
* 付款完成回写(paid)更新为已付款;按付款申请单号幂等,不产生重复记录
|
||||
*
|
||||
* @param billStatus 预付记录状态:approved(已生成待付)/ paid(已付款)
|
||||
* @param paidAmount 已付金额:synced 阶段通常为0,paid 阶段取申请全额
|
||||
*/
|
||||
private void syncPreSettlementAdvance(PaymentApplication entity, String billStatus, BigDecimal paidAmount) {
|
||||
if (entity.getPreSettlementId() == null) return;
|
||||
if (!List.of("progress_advance", "settlement_payment").contains(entity.getPaymentType())) return;
|
||||
PreSettlementAdvance advance = preSettlementAdvanceMapper.selectList(
|
||||
@@ -1580,8 +1598,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
advance.setAdvanceNo(entity.getPaymentNo());
|
||||
advance.setAppliedAmount(money(entity.getAppliedAmount()));
|
||||
}
|
||||
advance.setPaidAmount(money(entity.getAppliedAmount()));
|
||||
advance.setBillStatus("paid");
|
||||
advance.setPaidAmount(paidAmount);
|
||||
advance.setBillStatus(billStatus);
|
||||
advance.setKingdeeAdvanceNo(entity.getKingdeeBillNo());
|
||||
if (advance.getId() == null) preSettlementAdvanceMapper.insert(advance);
|
||||
else preSettlementAdvanceMapper.updateById(advance);
|
||||
@@ -1596,6 +1614,22 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
formalSettlementService.refreshPaymentSummariesForPreSettlement(entity.getPreSettlementId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 联动作废付款申请关联的预结算预付记录(付款申请作废、金蝶反审核解锁场景):
|
||||
* 仅清理提前建档的未付记录,已付款记录不联动(付款完成的事实不回退),避免残留记录阻断预结算作废
|
||||
*/
|
||||
private void voidPreSettlementAdvance(PaymentApplication entity) {
|
||||
if (entity.getPreSettlementId() == null) return;
|
||||
if (!List.of("progress_advance", "settlement_payment").contains(entity.getPaymentType())) return;
|
||||
preSettlementAdvanceMapper.update(null, Wrappers.<PreSettlementAdvance>lambdaUpdate()
|
||||
.eq(PreSettlementAdvance::getPreSettlementId, entity.getPreSettlementId())
|
||||
.eq(PreSettlementAdvance::getAdvanceNo, entity.getPaymentNo())
|
||||
.eq(PreSettlementAdvance::getIsDeleted, 0)
|
||||
.ne(PreSettlementAdvance::getBillStatus, "paid")
|
||||
.set(PreSettlementAdvance::getBillStatus, TransportBusiConstant.VOIDED));
|
||||
refreshPreSettlementAdvanceSummary(entity.getPreSettlementId());
|
||||
}
|
||||
|
||||
private BigDecimal findPreSettlementPaid(Long preSettlementId) {
|
||||
return preSettlementAdvanceMapper.selectList(Wrappers.<PreSettlementAdvance>lambdaQuery()
|
||||
.eq(PreSettlementAdvance::getPreSettlementId, preSettlementId)
|
||||
|
||||
+40
-12
@@ -233,7 +233,7 @@ public class KingdeeApBillSyncSupport {
|
||||
if (Func.isBlank(billtypeNumber)) {
|
||||
throw new ServiceException("推送金蝶财务应付单失败:单据类型[" + KingdeeBfiConstant.BFI_AP_BILL_BILLTYPE_NAME + "]未在映射字典中维护");
|
||||
}
|
||||
String orgNumber = bfiOrgNumber(contract);
|
||||
String orgNumber = bfiOrgNumber(contract, context);
|
||||
// 币种取合同结算币种
|
||||
String currency = bfiCurrency(contract);
|
||||
// 单据日期:yyyy-MM-dd HH:mm:ss(各接口统一格式),流程拆分后以同步当日作为应付单据日期
|
||||
@@ -925,22 +925,50 @@ public class KingdeeApBillSyncSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 合同所属组织编码
|
||||
* 合同金蝶组织编码:合同关联项目 → 项目业务部门沿部门树向上反查唯一平台公司 → 平台公司金蝶组织编码
|
||||
*
|
||||
* @param contract 合同实体
|
||||
* @param context 金蝶同步上下文(缓存载体)
|
||||
* @return 组织编码
|
||||
*/
|
||||
public String bfiOrgNumber(ContractManage contract) {
|
||||
Dept dept = contract.getOrganizationId() == null ? null : SysCache.getDept(contract.getOrganizationId());
|
||||
// dept为空,直接报错
|
||||
if (dept == null) {
|
||||
throw new ServiceException("推送金蝶失败,合同所属组织不存在,请检查合同所属组织配置");
|
||||
public String bfiOrgNumber(ContractManage contract, KingdeeSyncContext context) {
|
||||
if (contract.getProjectId() == null) {
|
||||
throw new ServiceException("推送金蝶失败,合同[" + Func.toStr(contract.getContractNo()) + "]未关联项目,无法确定金蝶组织编码");
|
||||
}
|
||||
// kingdeeDeptCode为空,直接报错
|
||||
if (Func.isBlank(dept.getKingdeeDeptCode())) {
|
||||
throw new ServiceException("推送金蝶失败,合同所属组织编码不能为空,请检查组织[" + dept.getDeptName() + "]是否维护金蝶组织编码");
|
||||
return this.kingdeeOrgNumber(contract.getProjectId(), context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 金蝶组织编码:项目业务部门沿部门树向上反查唯一平台公司(是否平台公司=是),取该平台公司的金蝶组织编码;
|
||||
* 项目未关联/未维护业务部门/业务部门未归属平台公司/平台公司未维护金蝶组织编码时拦截
|
||||
*
|
||||
* @param projectId 项目id
|
||||
* @param context 金蝶同步上下文(缓存载体)
|
||||
* @return 组织编码
|
||||
*/
|
||||
public String kingdeeOrgNumber(Long projectId, KingdeeSyncContext context) {
|
||||
if (projectId == null) {
|
||||
throw new ServiceException("推送金蝶失败,单据未关联项目,无法确定金蝶组织编码");
|
||||
}
|
||||
return dept.getKingdeeDeptCode();
|
||||
ProjectApply project = context.projectCache().computeIfAbsent(projectId, projectApplyMapper::selectById);
|
||||
if (project == null) {
|
||||
throw new ServiceException("推送金蝶失败,项目[" + projectId + "]不存在,无法确定金蝶组织编码");
|
||||
}
|
||||
if (project.getBusinessDeptId() == null) {
|
||||
throw new ServiceException("推送金蝶失败,项目[" + Func.toStr(project.getProjectName()) + "]未维护业务部门,无法确定金蝶组织编码");
|
||||
}
|
||||
// 沿部门树向上反查平台公司,上限 20 级防脏数据成环死循环
|
||||
Dept dept = SysCache.getDept(project.getBusinessDeptId());
|
||||
for (int depth = 0; dept != null && depth < 20; depth++) {
|
||||
if (Objects.equals(dept.getIsPlatformCompany(), 1)) {
|
||||
if (Func.isBlank(dept.getKingdeeDeptCode())) {
|
||||
throw new ServiceException("推送金蝶失败,平台公司[" + dept.getDeptName() + "]未维护金蝶组织编码,请检查部门数据");
|
||||
}
|
||||
return dept.getKingdeeDeptCode();
|
||||
}
|
||||
dept = dept.getParentId() == null ? null : SysCache.getDept(dept.getParentId());
|
||||
}
|
||||
throw new ServiceException("推送金蝶失败,项目业务部门[" + Func.toStr(project.getBusinessDeptName()) + "]未归属平台公司,无法确定金蝶组织编码");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -955,7 +983,7 @@ public class KingdeeApBillSyncSupport {
|
||||
return "RMB".equalsIgnoreCase(currency) ? "CNY" : currency;
|
||||
}
|
||||
|
||||
/** 财务应付单往来类型:合同类别为“客户合同”时为 bd_customer(客户),否则 bd_supplier(供应商) */
|
||||
/** 财务应付单/付款申请/应收单/收票单统一往来类型:固定 bd_customer(按合同类别区分供应商的逻辑已注释停用,恢复时同步影响付款申请) */
|
||||
public String bfiAsstactType(ContractManage contract) {
|
||||
return KingdeeBfiConstant.BFI_CUSTOMER_ASSTACT_TYPE;
|
||||
/*return "客户合同".equals(contract.getContractCategory())
|
||||
|
||||
+3
-3
@@ -170,7 +170,7 @@ public class KingdeeArBillSyncSupport {
|
||||
if (Func.isBlank(billtypeNumber)) {
|
||||
throw new ServiceException("推送金蝶财务应收单失败:单据类型[" + KingdeeBfiConstant.BFI_AR_BILL_BILLTYPE_NAME + "]未在映射字典中维护");
|
||||
}
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract);
|
||||
String orgNumber = kingdeeApBillSyncSupport.bfiOrgNumber(contract, context);
|
||||
// 币种取合同结算币种
|
||||
String currency = kingdeeApBillSyncSupport.bfiCurrency(contract);
|
||||
// 单据日期:yyyy-MM-dd HH:mm:ss(各接口统一格式),以同步当日作为应收单据日期
|
||||
@@ -193,8 +193,8 @@ public class KingdeeArBillSyncSupport {
|
||||
param.setRecorgNumber(orgNumber);
|
||||
// 【必填】单据日期
|
||||
param.setBizdate(billDateText);
|
||||
// 【必填】往来类型:应收固定客户
|
||||
param.setAsstacttype(KingdeeBfiConstant.BFI_CUSTOMER_ASSTACT_TYPE);
|
||||
// 【必填】往来类型:统一固定客户(KingdeeApBillSyncSupport#bfiAsstactType,与应付单/付款申请同口径)
|
||||
param.setAsstacttype(kingdeeApBillSyncSupport.bfiAsstactType(contract));
|
||||
// 【必填】往来户.编码:结算单付款方(客户)
|
||||
param.setAsstactNumber(customerCode);
|
||||
// 【必填】付款方式(CASH:现销, CREDIT:赊销),现销由金蝶自动推收款
|
||||
|
||||
+4
-4
@@ -183,10 +183,10 @@ public class KingdeeReceivedInvoiceSyncSupport {
|
||||
param.setInvoicetypef7Number(mirror.getInvoiceTypeCode());
|
||||
// 【必填】付款方式:(与应付单一致,不允许金蝶自动推付款)
|
||||
param.setPaymenttype(BfiConstant.PAY_MODE_CASH);
|
||||
// 【必填】往来类型:进项收票固定供应商
|
||||
param.setAsstacttype(BfiConstant.ASSTACT_TYPE_SUPPLIER);
|
||||
// 【必填】结算组织.编码:合同所属组织(与应付单同源)
|
||||
param.setOrgNumber(kingdeeApBillSyncSupport.bfiOrgNumber(contract));
|
||||
// 【必填】往来类型:统一固定客户(KingdeeApBillSyncSupport#bfiAsstactType,与应付单/付款申请同口径)
|
||||
param.setAsstacttype(kingdeeApBillSyncSupport.bfiAsstactType(contract));
|
||||
// 【必填】结算组织.编码:合同关联项目业务部门反查平台公司(与应付单同源)
|
||||
param.setOrgNumber(kingdeeApBillSyncSupport.bfiOrgNumber(contract, context));
|
||||
// 【必填】结算币别:合同结算币种
|
||||
param.setCurrencyNumber(kingdeeApBillSyncSupport.bfiCurrency(contract));
|
||||
// 【必填】往来户.编码:合同乙方(开票供应商)客商编码
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
# 金蝶(星瀚)BFI 推送入参取值映射表:页面字段 → 数据库表字段 / 配置对照
|
||||
|
||||
> 整理日期:2026-10-15
|
||||
> 代码基线:`tms-api` 当前 master(分支 `feature/payment-apply-bfi-sync`,提交 `259407c3` 之后)。
|
||||
> 用途:给联调/实施/排查用——每个送金蝶的入参,**页面上是哪个字段、落库在哪张表哪个字段、或来自哪个配置/常量**。逐字段核对代码(文件+行号见各节备注)。
|
||||
> 配套文档:《2026-10-14-金蝶(星瀚)BFI全链路梳理(前端-后端-定时任务·业务域汇总与操作手册).md》。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [0. 公共取数与通用规则](#0-公共取数与通用规则)
|
||||
- [1. A100-1 获取令牌](#1-a100-1-获取令牌)
|
||||
- [2. A902-2 客户保存](#2-a902-2-客户保存)
|
||||
- [3. A801-5 黑名单查询](#3-a801-5-黑名单查询)
|
||||
- [4. A102-2 财务应付单保存](#4-a102-2-财务应付单保存)
|
||||
- [5. A302-2 财务应收单保存(与应付差异)](#5-a302-2-财务应收单保存与应付差异)
|
||||
- [6. A103-1 付款申请下推](#6-a103-1-付款申请下推)
|
||||
- [7. A103-3 预付款申请保存](#7-a103-3-预付款申请保存)
|
||||
- [8. A303-1 开票申请单保存](#8-a303-1-开票申请单保存)
|
||||
- [9. A303-2 收票单保存 + A302-6 指定应付](#9-a303-2-收票单保存--a302-6-指定应付)
|
||||
- [10. A301-3 收款认领结果推送](#10-a301-3-收款认领结果推送)
|
||||
- [11. lockBusiData 单据锁定](#11-lockbusidata-单据锁定)
|
||||
- [12. A902 附件上传](#12-a902-附件上传)
|
||||
- [13. 查询类接口入参简表](#13-查询类接口入参简表)
|
||||
- [14. 与总纲记载不一致的核对结论](#14-与总纲记载不一致的核对结论)
|
||||
|
||||
---
|
||||
|
||||
## 0. 公共取数与通用规则
|
||||
|
||||
### 0.1 配置前缀(两套并存,勿混)
|
||||
|
||||
| 前缀 | 归属 | 内容 |
|
||||
|---|---|---|
|
||||
| `third-party.bfi.*` | bfi-api 客户端(令牌/网关) | baseUrl、authorization、clientId、clientSecret、username、accountId、language |
|
||||
| `thirdparty.bfi.*` | blade-transport 业务 | srcSys、各业务开关/cron、payApply.*、invoiceApply.*、receivedInvoice.*、material.*、claimAnnounce.*、blacklist.*、billLock.*(宽松绑定兼容 camelCase) |
|
||||
|
||||
### 0.2 通用取数
|
||||
|
||||
| 项 | 规则 | 代码 |
|
||||
|---|---|---|
|
||||
| awrp_src_sys / systemsource / sourceSystem | Nacos `thirdparty.bfi.srcSys`,默认常量 `WLTMS` | BfiProperties(srcSys) |
|
||||
| 组织 org/结算/付款/收款/申请组织 | 合同/申请单所属组织 → `SysCache.getDept()` → **部门金蝶组织编码**,空拦截 | `blade_contract_manage.organization_id` / 申请单 dept_id → `blade_dept.kingdee_dept_code`(KingdeeApBillSyncSupport.bfiOrgNumber L933-944) |
|
||||
| 币种 | 合同结算币种,`RMB→CNY` | `blade_contract_manage.settlement_currency`(bfiCurrency L952-956) |
|
||||
| 往来户 | 名称 → `blade_customer_archive` 按 `full_name/short_name` 匹配(limit 1)取 `customer_code`,查不到报错 | queryCustomerCode L966-984 |
|
||||
| 日期文本 | 统一 `yyyy-MM-dd HH:mm:ss` | bfiDateTimeText L922-925 |
|
||||
| 防重键 awrp_src_code | `ExternalSrcCodeSupport.buildSrcCode`:下划线拼接、去空白、去重保序 | ExternalSrcCodeSupport L54-63 |
|
||||
|
||||
### 0.3 分录拆分规则(A102-2/A302-2/A303-2 共用 `resolveEntryGroups`)
|
||||
|
||||
- 拆分维度:**货物 → 费用大类(装卸/运输)→ 税率** 三级分组,各组成一条分录;手动合计费用项(manual_flag=1)每行一条。
|
||||
- 物料编码:装卸大类/手动非运费杂费 → `thirdparty.bfi.material.loading`(默认 XDWL11002004);项目业务模式或业务类型含「国际」→ `.international`(Item-00001081);国内按运单运输方式 → `.domestic.公路运输`=XDWL11010001 / `.铁路运输`=XDWL11010002 / `.水路运输`=XDWL11010003 / `.航空运输`=Item-00001082;运输方式未配置直接拦截。
|
||||
- 税率:货物费用行按合同计费方案规则分组(`blade_contract_manage.billing_plan_json` 的 `rules[].feeType/feeItem/taxRate`);税率未配置并入无税率组。
|
||||
- 数量:运输类=结算明细运输量(≤0 兜底 1,同税率拆多行每行都取运输量);装卸/合计费用项行=1;单价=金额÷数量反算(2 位 HALF_UP)。
|
||||
- 0 元费用项(signum==0)与 0 元货物行整行跳过。
|
||||
- 计量单位**当前不送**(DTO 有字段无赋值)。
|
||||
|
||||
---
|
||||
|
||||
## 1. A100-1 获取令牌
|
||||
|
||||
| 入参 | 含义 | 来源:配置键 / 生成方式 |
|
||||
|---|---|---|
|
||||
| client_id | 应用编码 | `third-party.bfi.clientId` |
|
||||
| client_secret | 认证密钥 | `third-party.bfi.clientSecret` |
|
||||
| username | 访问用户名 | `third-party.bfi.username` |
|
||||
| accountId | 数据中心 ID | `third-party.bfi.accountId` |
|
||||
| language | 语言 | `third-party.bfi.language`(默认 `zh_CN`) |
|
||||
| nonce | 随机数 | 每次生成 `UUID` |
|
||||
| timestamp | 时间戳(±5 分钟校验) | 每次生成 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
> 令牌缓存 Redis `blade:third:bfi:token`,TTL=expires_in−120s;`setIfAbsent` 写入。代码:BfiServiceImpl L136-156、BfiTokenDTO L44-65。
|
||||
|
||||
---
|
||||
|
||||
## 2. A902-2 客户保存
|
||||
|
||||
调用链:`CustomerArchiveServiceImpl.syncKingdee → pushCustomerToKingdee → CustomerBfiConverter.buildSaveDTO`
|
||||
|
||||
| 金蝶入参 | 含义 | 页面字段 | 表.字段 / 常量 |
|
||||
|---|---|---|---|
|
||||
| number | 客户编码(不传由星瀚生成) | 客商编号(提交时生成 KS+6 位) | blade_customer_archive.customer_code |
|
||||
| name【必】 | 名称 | 客商名称 | blade_customer_archive.full_name |
|
||||
| societycreditcode【必】 | 统一社会信用代码(金蝶候选键) | 统一社会信用代码 | blade_customer_archive.unified_credit_code |
|
||||
| tx_register_no | 纳税人识别号 | 同上(同一字段) | blade_customer_archive.unified_credit_code |
|
||||
| artificialperson | 法人代表 | 企业法人 | blade_customer_archive.legal_person |
|
||||
| type【必】 | 伙伴类型(1~17) | 客商性质字典 | blade_customer_archive.customer_nature + 字典 `nature_of_client` dictKey(文本值反查 dictKey,无效抛异常) |
|
||||
| bizpartner_address | 详细地址 | 注册地址 | blade_customer_archive.registered_address |
|
||||
| bizpartner_phone | 联系电话 | 联系电话 | blade_customer_archive.contact_phone |
|
||||
| businessterm | 营业期限 | 营业期限类型=长期→「长期」;否则「至 yyyy-MM-dd」 | blade_customer_archive.business_term_type / business_end_date |
|
||||
| businessscope | 经营范围 | 经营范围多选 → 字典 `scope_of_business` 文本「、」拼接 | blade_customer_archive.business_scope |
|
||||
| bizfunction | 业务职能 | 固定全选 | 常量 `"1,2,3,4"` |
|
||||
| fcusbeizhu | 备注 | 备注(空不传) | blade_customer_archive.remark |
|
||||
| awrp_datasourcesys | 数据来源 | 配置 | `thirdparty.bfi.srcSys` |
|
||||
| awrp_cusfortrade【必】 | 是否贸易客商 | 固定 | 常量 `"A"` |
|
||||
| entry_groupstandard[].standardid_number【必】 | 分类标准 | 固定 | 常量 `"JBFLBZ"` |
|
||||
| entry_groupstandard[].groupid_number【必】 | 客户分组 | 客户类型:internal→1101,其余→1102 | blade_customer_archive.customer_kind + 常量 1101/1102 |
|
||||
| entry_bank[].bankaccount | 银行账号(金蝶候选键) | 收款账号 | blade_customer_receipt_account.bank_account |
|
||||
| entry_bank[].accountname | 账户名称 | **传开户行名称** | blade_customer_receipt_account.bank_name |
|
||||
| entry_bank[].bank_number | 开户银行.编码(联行号) | 联行号 | blade_customer_receipt_account.cnaps_code |
|
||||
| entry_bank[].currency_number【必】 | 币种 | 固定 | 常量 `"CNY"` |
|
||||
| entry_bank[].isdefault_bank | 默认银行 | 固定 | `true` |
|
||||
| entry_linkman[].contactperson | 联系人 | 客商负责人(空则整组分录不传) | blade_customer_archive.principal |
|
||||
| entry_linkman[].phone | 电话 | 联系电话 | blade_customer_archive.contact_phone |
|
||||
| entry_linkman[].isdefault_linkman | 默认联系人 | 固定 | `true` |
|
||||
|
||||
不传字段:status、linkman、establishdate、postal_code、createorg_number(已注释)等。
|
||||
> 代码:CustomerBfiConverter L67-280、常量 CustomerBfiConstants / BfiConstant L288-313。
|
||||
|
||||
---
|
||||
|
||||
## 3. A801-5 黑名单查询
|
||||
|
||||
| 入参 | 含义 | 来源 |
|
||||
|---|---|---|
|
||||
| enable | 使用状态 | 固定 `"1"`(可用) |
|
||||
| number | 企业税号数组 | 客商发票信息所有纳税人识别号,`trim().toUpperCase()` 去重 → blade_customer_invoice_info.tax_no |
|
||||
| pageNo / pageSize | 分页 | 自动翻页(50 页保护)/ 固定 `2000` |
|
||||
| name | 企业名称数组 | 不传 |
|
||||
|
||||
> 命中判定按应答 awrpCombofield:A 硬拦截;B 默认放行告警(`thirdparty.bfi.blacklist.blockRiskWarning=true` 时拦截);查询异常上抛阻断提审。代码:CustomerArchiveServiceImpl L1043-1092、BfiConstant L90。
|
||||
|
||||
---
|
||||
|
||||
## 4. A102-2 财务应付单保存
|
||||
|
||||
### 4.1 单据头
|
||||
|
||||
| 金蝶入参 | 含义 | 页面字段 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| awrp_src_code【必·候选键】 | 来源单据号(防重键) | 结算单号+结算明细单据号 | blade_formal_settlement.formal_settlement_no + blade_formal_settlement_detail.document_no(document_no 空兜底明细行 id)下划线拼接 |
|
||||
| awrp_src_sys【必】 | 外部系统编号 | 配置 | `thirdparty.bfi.srcSys`(WLTMS) |
|
||||
| awrp_src_type【必】 | 外部单据类型 | — | 常量 `TMS财务应付单` |
|
||||
| billtypeid_number【必】 | 单据类型.编码 | — | 常量字典「服务采购应付」→ `ApFin_service_BT_S` |
|
||||
| org_number / payorg_number【必】 | 结算/付款组织.编码 | 合同所属组织 | blade_contract_manage.organization_id → blade_dept.kingdee_dept_code |
|
||||
| bizdate【必】 | 单据日期 | 同步当日 | LocalDate.now() |
|
||||
| asstacttype【必】 | 往来类型 | — | 常量 `bd_customer`(固定,供应商分支已注释) |
|
||||
| asstact_number【必】 | 往来户.编码 | 合同乙方 | blade_contract_manage.party_b → blade_customer_archive.customer_code |
|
||||
| purmode【必】 | 付款方式 | — | 常量 `CASH`(现购) |
|
||||
| currency_number【必】 | 结算币别 | 合同结算币种 | blade_contract_manage.settlement_currency(RMB→CNY) |
|
||||
| payproperty_number【必】 | 款项性质.编码 | — | 常量 `2001`(材料采购) |
|
||||
| isincludetax / ispricetotal | 含税价/总价录入 | — | `true` / `true` |
|
||||
| isintax | 按应付金额分配 | — | `false` |
|
||||
| exratedate / exchangerate | 汇率日期/汇率 | 结算单页面「汇率日期/结算汇率」,未维护不送 | blade_formal_settlement.exchange_rate_date / .exchange_rate |
|
||||
| settlementtype_number | 结算方式 | **不送**(仅 A103-1 分录需要) | — |
|
||||
| taxrateid_number / purorg_number / exratetable_number / planentity 等 | 可选 | **均不送**(planentity 由金蝶按分录自动汇总付款计划) | — |
|
||||
|
||||
### 4.2 明细分录 detailentry
|
||||
|
||||
| 金蝶入参 | 含义 | 来源 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| awrp_entry_code【必·候选键】 | 分录标识 | 拆分行=`明细单据号-货物序号-大类-税率`;合计费用项行=`结算单号-费用类型-费用项` | blade_formal_settlement_detail.document_no / blade_formal_settlement_summary_fee.fee_type+fee_item |
|
||||
| material_number【必】 | 物料编码.编码 | 按拆分规则映射(见 0.3) | 配置 `thirdparty.bfi.material.loading / international / domestic.公路运输 / .铁路运输 / .水路运输 / .航空运输`;判定字段 blade_project_apply.business_mode / business_type、blade_formal_settlement_detail.transport_type |
|
||||
| awrp_transport | 运输货物 | 货物名称(合计费用项行不传) | blade_formal_settlement_detail.cargo_name(一车多货取 blade_formal_settlement_detail_fee.cargo_name) |
|
||||
| awrp_lpn | 车号 | 车号(合计费用项行不传) | blade_formal_settlement_detail.vehicle_no |
|
||||
| quantity【必】 | 数量 | 运输量(≤0 兜底 1;装卸/合计行=1) | blade_formal_settlement_detail.transport_quantity(一车多货取 detail_fee.transport_quantity) |
|
||||
| price | 单价 | 金额÷数量反算 | 派生值 |
|
||||
| e_pricetaxtotal【必】 | 应付金额(含税) | 结算金额 | blade_formal_settlement_detail.settlement_amount_tax / blade_formal_settlement_summary_fee.settlement_amount |
|
||||
| e_amount | 金额(不含税) | 不含税结算金额(仅运输类携带;合计行不传) | blade_formal_settlement_detail.settlement_amount_no_tax |
|
||||
| project_number【必】 | 项目.编码 | 联调临时值 | 常量 `"999"` |
|
||||
| taxrateid_number | 税率.编码 | 不送(税率只作拆行依据) | blade_contract_manage.billing_plan_json |
|
||||
|
||||
---
|
||||
|
||||
## 5. A302-2 财务应收单保存(与应付差异)
|
||||
|
||||
分录与应付**同源同口径**(复用 resolveEntryGroups,分录字段名前缀 e_:e_material_number/e_quantity/e_unitprice/e_recamount(含税)/e_amount(不含税))。头字段差异:
|
||||
|
||||
| 金蝶入参 | 应收取值 | 来源 |
|
||||
|---|---|---|
|
||||
| awrp_src_type | `TMS财务应收单` | 常量 |
|
||||
| billtype_number | 「服务销售应收」→ `arfin_sersal_BT_S` | 常量映射 |
|
||||
| recorg_number | 收款组织=结算组织 | 同 org_number |
|
||||
| asstact_number | **结算单付款方**(非合同乙方) | blade_formal_settlement.payer_name → blade_customer_archive.customer_code |
|
||||
| paymode | 现销 `CASH` | 常量 |
|
||||
| payproperty_number | `1001`(一般销售) | 常量 |
|
||||
| bizdate | 同步当日 | LocalDate.now() |
|
||||
| settlementtype_number / duedate 等 | 不送(TODO 待金蝶确认) | — |
|
||||
|
||||
---
|
||||
|
||||
## 6. A103-1 付款申请下推
|
||||
|
||||
| 金蝶入参 | 含义 | 页面字段 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| awrp_src_code【必·候选键】 | 来源单据号 | 付款申请单号 | blade_payment_application.payment_no |
|
||||
| awrp_src_sys【必】 | 外部系统 | 配置 | `thirdparty.bfi.srcSys` |
|
||||
| applydate【必】 | 申请日期 | 付款申请页-申请日期(空取当天) | blade_payment_application.apply_date |
|
||||
| entry[].e_paymenttype_number【必】 | 付款类型 | 正式付款 | 常量 `201` |
|
||||
| entry[].e_settlementtype_number | 结算方式 | 付款方式含「汇票」→`012-02`(电子银行承兑汇票),其余→`02`(银行转账) | 字典 `pay_method` + blade_payment_application.payment_method |
|
||||
| entry[].awrp_finapbill_entryid【必】 | 勾稽应付单分录标识 | 主结算单应付单防重键(本地组装) | blade_formal_settlement.formal_settlement_no + blade_formal_settlement_detail.document_no(主结算单=blade_payment_application.settlement_id,空取关联明细首个结算单) |
|
||||
| entry[].awrp_entry_code【必】 | 行 id | 付款申请号-1 | payment_no + "-1" |
|
||||
| entry[].e_assacct【必】 | 往来账户(收款账号) | 收款账号 | blade_payment_application.bank_account |
|
||||
| entry[].e_bebank_number【必】 | 往来银行.编码(联行号) | 收款账户联行号(缺失拦截;兜底按合同乙方客商+收款账号匹配) | blade_customer_receipt_account.cnaps_code(按 blade_payment_application.receipt_account_id 直查) |
|
||||
| entry[].e_applyamount【必】 | 申请金额 | 申请付款金额(2 位 HALF_UP) | blade_payment_application.applied_amount |
|
||||
| entry[].project_number【必】 | 项目.编码 | 联调临时值 | 常量 `"999"` |
|
||||
| entry[].awrp_src_type【必】 | 外部单据类型 | 配置 | `thirdparty.bfi.payApply.srcType`(默认 `0KzXs`) |
|
||||
| exratetable_number / awrp_textfield1 | 汇率表/付款渠道 | **实际不送**(仅 A103-3 送;配置 exratetable=01 在此接口不生效) | — |
|
||||
|
||||
---
|
||||
|
||||
## 7. A103-3 预付款申请保存
|
||||
|
||||
| 金蝶入参 | 含义 | 来源 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| billtype_number【必】 | 单据类型 | 配置 | `thirdparty.bfi.payApply.billtype`(默认 `ap_payapply_oth_BT_S`) |
|
||||
| settleorg/awrp_department/applyorg/payorg_number【必】 | 组织/部门 | 合同所属组织 | blade_contract_manage.organization_id → blade_dept.kingdee_dept_code |
|
||||
| paycurrency/settlecurrency_number【必】 | 币种 | 合同结算币种(RMB→CNY) | blade_contract_manage.settlement_currency |
|
||||
| applycause | 请款事由 | 按付款类型映射文案:项目垫资→「项目预付款申请」、进度垫资→「进度预付款申请」、结算付款→「结算付款申请」 | blade_payment_application.payment_type |
|
||||
| exratetable_number【必】 | 汇率表 | 配置(**此接口实际送**) | `thirdparty.bfi.payApply.exratetable`(默认 `01`) |
|
||||
| exratedate【必】 | 汇率日期 | 关联结算单维护值,兜底申请日期 | blade_formal_settlement.exchange_rate_date → blade_pre_settlement.exchange_rate_date → blade_payment_application.apply_date |
|
||||
| awrp_textfield1 | 付款渠道 | 付款方式字典标签(**此接口实际送**) | 字典 `pay_method` 翻译 blade_payment_application.payment_method |
|
||||
| entry[].e_paymenttype_number【必】 | 付款类型 | 结算付款→`201`;项目垫资/进度垫资→`202` | 常量 BFI_PAYMENT_TYPE_FORMAL / BFI_PAYMENT_TYPE_PREPAY |
|
||||
| entry[].e_asstacttype / e_asstactnumber【必】 | 往来类型/户 | 固定客户 / 合同乙方→客商编码 | `bd_customer`;blade_contract_manage.party_b → customer_code |
|
||||
| entry[].e_assacct / e_bebank_number【必】 | 账号/联行号 | 同 A103-1 | blade_payment_application.bank_account / blade_customer_receipt_account.cnaps_code |
|
||||
| entry[].e_applyamount【必】 | 申请金额 | 申请付款金额 | blade_payment_application.applied_amount |
|
||||
| entry[].project_number【必】 | 项目.编码 | **实际固定 999**(取项目档案真实编码的逻辑已注释停用) | 常量 `"999"`(备用:blade_project_apply.project_code) |
|
||||
| entry[].e_remark | 备注 | 付款申请备注(空不传) | blade_payment_application.remark |
|
||||
| entry[].awrp_entry_code / awrp_src_type【必】 | 行 id / 单据类型 | payment_no-1 / 配置 | `thirdparty.bfi.payApply.srcType` |
|
||||
|
||||
---
|
||||
|
||||
## 8. A303-1 开票申请单保存(每张发票张次一单)
|
||||
|
||||
### 8.1 主表
|
||||
|
||||
| 金蝶入参 | 含义 | 页面字段 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| billno【必·候选键】 | 单据编号 | 开票申请单号(多张加张次序号) | blade_invoice_application.application_no(多张 + "-" + blade_invoice_application_sheet.sheet_no) |
|
||||
| orgid_number / awrp_orgfield_number【必】 | 组织/申请部门 | 申请部门 | blade_invoice_application.dept_id → blade_dept.kingdee_dept_code |
|
||||
| billdate【必】 | 单据日期 | 申请日期(空取当天) | blade_invoice_application.application_date |
|
||||
| billproperties / taxationstyle / buyerproperty | 单据性质/征税方式/购方类型 | 固定 | `"1"` / `"0"` / `"0"` |
|
||||
| invoicetype【必】 | 发票种类 | 发票类型:数电专票→`08xdp`,其余→`10xdp` | blade_invoice_application.invoice_type |
|
||||
| buyeremail【必】 | 推送邮箱 | 部门邮箱(空拦截) | blade_invoice_application.department_emails |
|
||||
| buyertaxno / buyername【必】 | 购方税号/名称 | 受票方快照 | blade_invoice_application.taxpayer_no / receiver_name |
|
||||
| buyeraddr / buyerbank | 购方地址电话/开户行账号 | 受票方快照 | blade_invoice_application.registered_address+contact_phone / bank_name+bank_account |
|
||||
| salername【必】 | 销方名称 | 开票单位(空拦截) | blade_invoice_application.issuer_name |
|
||||
| salertaxno【必】 | 销方税号 | 开票单位→客商档案 | blade_customer_archive.unified_credit_code(按 issuer_name 匹配) |
|
||||
| saleraddr / salerbank | 销方地址电话/开户行账号(数电专票强制) | 客商档案(启用开票信息优先) | blade_customer_archive.registered_address+contact_phone;blade_customer_invoice_info.bank_name+bank_account |
|
||||
| totalamount【必】 | 申请价税合计 | 本张开票金额 | blade_invoice_application_sheet.invoice_amount |
|
||||
| hsbz | 是否含税 | 固定不含税 | 常量 `"0"` |
|
||||
| specialtype | 特殊票种 | 本张商品行**全部**为货物运输类→`E04`,否则→`00`(名称/分类含「运费/货物运输」关键字) | 常量 BFI_INVOICE_SPECIAL_TYPE_FREIGHT / NONE + BFI_INVOICE_FREIGHT_KEYWORDS |
|
||||
| freights[] | 货物运输单据体(E04 报送) | 结算明细快照,见 8.2 | blade_invoice_application_detail |
|
||||
| customnameid_number【必】 | 客户.编码 | 受票方客户 | blade_invoice_application.receiver_customer_id → blade_customer_archive.customer_code |
|
||||
| awrp_project_number【必】 | 项目.编码 | 联调临时值 | 常量 `"999"` |
|
||||
| awrp_src_bt | 上游单据类型 | 固定 | `ar_finarbill` |
|
||||
| awrp_src_con_no | 上游单据号 | 首张关联结算单金蝶应收防重键 | blade_formal_settlement.formal_settlement_no + detail.document_no(经 blade_invoice_application_settlement 关联) |
|
||||
| systemsource | 来源系统 | 配置 | `thirdparty.bfi.srcSys` |
|
||||
| showbuyeraddresstel / showbuyerbank | 票面展示开关 | 固定 | `true` |
|
||||
|
||||
### 8.2 freights[](按「车牌×起运×到达×货物×种类」去重归并,全空行剔除)
|
||||
|
||||
| 入参 | 来源:blade_invoice_application_detail 字段 |
|
||||
|---|---|
|
||||
| transporttype 运输工具种类 | transport_type(字典 `transport_type` 翻译,不在合法枚举兜底「其它运输工具」) |
|
||||
| licenseplate 牌号 | vehicle_no |
|
||||
| transportgoods 货物名称 | cargo_name |
|
||||
| startplace 起运地 | departure_address |
|
||||
| endplace 到达地 | arrival_address |
|
||||
|
||||
### 8.3 sim_original_bill_item[] 分录(blade_invoice_application_line 商品行,line_no 升序)
|
||||
|
||||
| 入参 | 来源:blade_invoice_application_line 字段 |
|
||||
|---|---|
|
||||
| awrp_entry_code【必】 | id(雪花 id) |
|
||||
| rowtype / benchmark【必】 | 常量 `"2"`(商品行)/ `"0"`(数量基准) |
|
||||
| goodsname【必】 | goods_name(货物或服务简称) |
|
||||
| goodssimplename【必】 | goods_category(商品和服务分类) |
|
||||
| taxratecodeid_number / goodscode【必】 | tax_classification_code(税收分类编码,空拦截) |
|
||||
| num / unit | quantity(0 兜底 1)/ unit(空兜底「个」) |
|
||||
| unitprice / amount / tax | unit_price_no_tax / amount_no_tax / tax_amount |
|
||||
| taxrate | tax_rate(百分数÷100 转小数,0 送 "0.00") |
|
||||
| remark | remark |
|
||||
| awrp_src_type【必】 | 配置 `thirdparty.bfi.invoiceApply.srcType`(默认 `0KzXs`) |
|
||||
|
||||
(specification 规格型号、含税金额等本期不传。)
|
||||
|
||||
---
|
||||
|
||||
## 9. A303-2 收票单保存 + A302-6 指定应付
|
||||
|
||||
### A303-2 收票单批量保存
|
||||
|
||||
| 金蝶入参 | 含义 | 来源 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| awrp_src_code【必·候选键】 | 来源单据号 | 收票登记 id(一票一登记) | blade_invoice_receipt.id |
|
||||
| awrp_src_sys / awrp_src_type【必】 | 系统/单据类型 | 配置 / 常量 | `thirdparty.bfi.srcSys`;`TMS收票登记` |
|
||||
| invoiceno | 发票号码 | 收票登记发票号 | blade_invoice_receipt.invoice_no |
|
||||
| receivedate【必】 | 收票日期 | 推送当日 | LocalDate.now() |
|
||||
| issuedate【必】 | 开票日期 | 票池镜像票面 | blade_kingdee_invoice_pool.invoice_date |
|
||||
| biztype【必】 | 业务类型 | 固定采购 | `PUR` |
|
||||
| invoicetypef7_number【必】 | 发票类型.编码 | 票易通原始编码(缺失拦截并按票号回源 A801-1 补填) | blade_kingdee_invoice_pool.invoice_type_code |
|
||||
| paymenttype【必】 | 付款方式 | 固定现购 | `CASH` |
|
||||
| asstacttype【必】 | 往来类型 | 统一固定客户(与应付单/付款申请同口径) | `bd_customer` |
|
||||
| org_number / currency_number【必】 | 组织/币种 | 分摊应付结算单关联合同 | blade_contract_manage.organization_id / settlement_currency |
|
||||
| receivablessupp_number【必】 | 往来户.编码 | 合同乙方→客商编码 | blade_contract_manage.party_b → customer_code |
|
||||
| exratetable_number【必】 | 汇率表 | 固定 | `"01"` |
|
||||
| asstactname / buyertin / sellertin【必】 | 销方名称/购销方税号 | 票池镜像票面 | blade_kingdee_invoice_pool.issuer_name / buyer_tax_no / saler_tax_no |
|
||||
| buyername【必】 | 购方名称 | 镜像受票单位 | blade_kingdee_invoice_pool.receiver_name |
|
||||
| sellertel / sellerbank / selleracct | 销方电话/开户行/账号 | 镜像 | blade_kingdee_invoice_pool.phone / bank_name / bank_account |
|
||||
| isincludetax | 含税录入 | 固定 | `true` |
|
||||
| entry[] | 分录 | 与 A102-2 同源同口径(复用 resolveEntryGroups) | 同 4.2 |
|
||||
|
||||
### A302-6 收票指定应付
|
||||
|
||||
| 入参 | 来源 |
|
||||
|---|---|
|
||||
| op | 常量 `"assign"` |
|
||||
| ap_invoice[0].awrp_src_code / awrp_src_sys | blade_invoice_receipt.id / `thirdparty.bfi.srcSys` |
|
||||
| ap_finapbill[0].awrp_src_code / awrp_src_sys | 应付防重键(结算单号_明细单据号)/ 同上 |
|
||||
|
||||
---
|
||||
|
||||
## 10. A301-3 收款认领结果推送
|
||||
|
||||
| 金蝶入参 | 含义 | 来源 | 表.字段 / 配置 / 常量 |
|
||||
|---|---|---|---|
|
||||
| billid【必】 | 认领公告 id | 流水同步落库(缺失拦截「请先同步金蝶收款流水」) | blade_kingdee_receipt_flow.bill_id |
|
||||
| awrp_src_sys【必】 | 来源系统 | 配置 | `thirdparty.bfi.srcSys` |
|
||||
| awrp_src_code【必】 | 来源单据编码 | 该流水全部分摊结算单号去重下划线拼接(推送时生成落库,分摊不变重试复用) | blade_receipt_claim_settlement.formal_settlement_no(status=1,结算单 id 升序)→ 回写 blade_kingdee_receipt_flow.awrp_src_code |
|
||||
| recpaytype_number【必】 | 收款类型 | **常量当前值 `"999"`**(注释「示例 101 待金蝶确认」) | KingdeeBfiConstant.BFI_CLAIM_REC_PAY_TYPE |
|
||||
| paymenttype【必】 | 付款人类型 | 固定 | `bd_customer` |
|
||||
| recviewpayer / recpayer【必】 | 付款人名称/id | 流水付款方 | blade_kingdee_receipt_flow.payer_name / payer_id |
|
||||
| entryentity[0].e_receivableamt【必】 | 应收金额(单条分录) | 全部有效认领分摊金额合计 | SUM(blade_receipt_claim_settlement.allocated_receipt_amount) |
|
||||
| entryentity[0].seq / e_corebillno【必】 | 行序/核心单据编号 | 第一张分摊结算单 id / 其金蝶星瀚内码(缺失拦截「未同步金蝶」) | blade_receipt_claim_settlement.formal_settlement_id / blade_formal_settlement.kingdee_bill_id |
|
||||
| entryentity[0].e_corebilltype | 核心单据类型 | **注释不传** | — |
|
||||
|
||||
---
|
||||
|
||||
## 11. lockBusiData 单据锁定
|
||||
|
||||
| 入参 | 含义 | 来源 |
|
||||
|---|---|---|
|
||||
| lockAction【必】 | 操作 | 常量 `LOCK` |
|
||||
| sourceSystem【必】 | 源系统 | `thirdparty.bfi.srcSys`(默认 WLTMS) |
|
||||
| targetSystem【必】 | 目标系统 | 常量 `XH` |
|
||||
| operationType【必】 | 操作类型 | 常量 `SAVE` |
|
||||
| sourceBillType【必】 | 源单据类型 | 见下表常量 |
|
||||
| sourceBillNo【必】 | 本地单据号 | 见下表 |
|
||||
| targetBillType【必】 | 金蝶单据类型 | 见下表 |
|
||||
| targetBillNo【必】 | 金蝶单号(逐张) | 见下表 |
|
||||
| operator | 操作人 | 当前用户名(定时/补偿兜底 `system`;补偿取 blade_kingdee_bill_lock.lock_operator) |
|
||||
| time | 操作时间 | 当前时间 `yyyy-MM-dd HH:mm:ss` |
|
||||
|
||||
| sourceBillType | sourceBillNo | targetBillType | targetBillNo |
|
||||
|---|---|---|---|
|
||||
| `SETTLEMENT` | blade_formal_settlement.formal_settlement_no | receivable→`arfin_other_BT_S`;否则 `ApFin_other_BT_S` | 推送返回 billNo(多张逗号拆分逐张锁) |
|
||||
| `PAY_APPLY` | blade_payment_application.payment_no | `ap_payapply_oth_BT_S` | blade_payment_application.kingdee_bill_no |
|
||||
| `INVOICE_APPLY` | blade_invoice_application.application_no | `cdm_paybill_bank_elect_BT_S` | 推送返回 billNo(多张逗号拆分) |
|
||||
| `RECEIVED_INVOICE` | — | `cdm_recbill_bank_BT_S` | 预留,**本期不锁** |
|
||||
|
||||
> 锁记录表 blade_kingdee_bill_lock(lock_status: failed/locked/unlocked,try_count≤5);开关 `thirdparty.bfi.billLock.enabled`(默认 true)。代码:KingdeeBillLockServiceImpl L331-344、KingdeeLockConstant L24-87。
|
||||
|
||||
---
|
||||
|
||||
## 12. A902 附件上传
|
||||
|
||||
multipart 上传:`file`=文件流,`args`=JSON `{entityNumber, billPkId, controlKey}`。
|
||||
|
||||
| 入参 | 来源 |
|
||||
|---|---|
|
||||
| entityNumber(星止单据类型) | biz_type 映射:`pay_application`→ap_payapply;`formal_settlement_ap`→ap_finapbill;`formal_settlement_ar`→ar_finarbill;`invoice_application`→sim_original_bill;`invoice_receipt`→ap_invoice |
|
||||
| billPkId(星瀚内码) | 推送落库的金蝶内码:blade_payment_application.kingdee_bill_id / blade_invoice_application.kingdee_bill_id / 结算单与收票推送返回内码(为空整单跳过待重推补传) |
|
||||
| controlKey | 常量 `attachmentpanel` |
|
||||
| 文件名 | 业务表 attachments_json 行内 originalName/name/fileName(空兜底 attachment;票池镜像兜底「发票文件」) |
|
||||
| 文件流 | 业务表 attachments_json 的 url 下载;收票票池镜像按 path 经 A801-2 下载 |
|
||||
|
||||
- 附件存储:各业务单据 `attachments_json` 列(非 blade_attachment 表);同步流水落 `blade_kingdee_attachment_sync`(biz_type/biz_id/attachment_key/kingdee_path/sync_status/fail_reason)。
|
||||
- 单文件软限制 20MB,超限拦截(重推时跳过该件继续其余)。
|
||||
- 调用时机:推送成功事务提交后联带上传,失败不污染主单状态;重推补传。
|
||||
> 代码:KingdeeAttachmentConstant L45-108、KingdeeAttachmentSyncSupport L125-264、BfiServiceImpl L613-638。
|
||||
|
||||
---
|
||||
|
||||
## 13. 查询类接口入参简表
|
||||
|
||||
| 接口 | 关键入参 | 取值来源 / 常量 / 配置 |
|
||||
|---|---|---|
|
||||
| A102-4 应付单查询 | awrp_src_code 多值(50/批)、awrp_src_sys、pageSize 200 | 应付防重键批次;`thirdparty.bfi.srcSys` |
|
||||
| A302-4 应收单查询 | 同上(50/批、200) | 应收防重键批次 |
|
||||
| A103-2 付款申请单查询 | awrp_src_code=paymentNo(50/批)、pageSize 200 | blade_payment_application.payment_no |
|
||||
| A103-4 付款处理查询 | org_number(优先取 A103-2 返回 payorg_number,兜底合同组织)、业务日期今天回溯 1 个月、200/页 | A103-2 返回值 → blade_dept.kingdee_dept_code |
|
||||
| A303-4 开票申请查询 | billno 多值(application_no、-张次序号)、pageSize=max(张数,10) | blade_invoice_application(.sheet) |
|
||||
| A303-5 开票发票查询(openApi) | serialNo=金蝶单号逗号拆分(≤50/批)、sellerTaxpayerId=开票单位税号、请求体 Base64 | blade_invoice_application.kingdee_bill_no;blade_customer_archive.unified_credit_code |
|
||||
| A801-3 收票单查询 | buyername(按镜像受票单位分组)、modifytimeS/E=回看 30 天、200/页;本地按 srcCode=登记 id 匹配 | blade_kingdee_invoice_pool.receiver_name;`thirdparty.bfi.receivedInvoice.queryWindowDays/resultWindowDays`(默认 30);blade_invoice_receipt.id |
|
||||
| A801-1 票池查询 | invoiceStatus=`0`(正常票);invoiceType 单值(白名单逐轮);updateTime 窗口=incrementDays 增量、2.5 万触顶二分;500/页。**expenseStatus 不下推**(可用性本地过滤 amount_use_status=0 且 available_amount>0) | `thirdparty.bfi.invoiceReceipt.invoiceTypes / incrementDays`(30);本地表 blade_kingdee_invoice_pool |
|
||||
| A801-2 附件下载 | path=文件地址(镜像优先、实时回源兜底;优先级 pdfurl→taxPdfUrl→downloadUrl,fileType=4 判 OFD) | blade_kingdee_invoice_pool.attachments_json |
|
||||
| A301-1 收款单查询 | 业务日期今天回溯 30 天、200/页;勾稽 sourcebillnumber==awrp_src_code | blade_kingdee_receipt_flow.awrp_src_code |
|
||||
| A301-2 认领公告查询 | claimusers=**Nacos 配置值**(登录人入参已废弃)、modifytimeS/E=回溯 months 个月、200/页;billstatus 字段存在但不传 | `thirdParty.bfi.claimAnnounce.claimusers`(必配)、`.months`(6) |
|
||||
|
||||
---
|
||||
|
||||
## 14. 与总纲记载不一致的核对结论(已按代码核实)
|
||||
|
||||
| # | 事项 | 代码事实 |
|
||||
|---|---|---|
|
||||
| 1 | A103-1 的 exratetable_number / awrp_textfield1 | **实际不送**(L1245/L1249 注释);配置 `payApply.exratetable=01` 只在 A103-3 生效 |
|
||||
| 2 | A103-3 项目编码 | **实际固定 999**,「取项目档案 projectCode 真实编码」逻辑已注释停用(bfiProjectNumber L1508-1513)——总纲 12.2-#2「仅 A103-3 用真实编码」的说法作废,全部链路均为 999 |
|
||||
| 3 | A301-3 recpaytype_number | 常量当前值 **"999"**(非旧文档写的 101;注释「示例 101 待金蝶确认」) |
|
||||
| 4 | A301-2 claimusers | 只读 Nacos 配置,方法入参(当前登录人姓名)已不参与取值;billstatus 字段存在但不传 |
|
||||
| 5 | A801-1 expenseStatus | 同步**不下推**(常量已定义未使用),「在用/已用票随窗口刷新」,可选票过滤在本地 SQL |
|
||||
| 6 | 应付 asstact_number | 取**合同乙方 party_b**(payer_name 仅应收侧使用) |
|
||||
|
||||
> 校验提醒:以上配置键的**环境实际值**以 Nacos 为准(本文只列键名与代码默认值);blade_* 表列名按 Entity 驼峰↔下划线约定标注,个别快照列以建表 SQL 为准。
|
||||
@@ -0,0 +1,53 @@
|
||||
# 付款申请金蝶生成即回显预结算预付信息:synced 提前建档 + 作废联动清理
|
||||
|
||||
> 日期:2026-10-16
|
||||
> 关联文档:
|
||||
> - `doc/docs/业务流程/2026-09-29-预付款申请A103-3金蝶链路补齐方案(e_bebank_number统一-消失解锁-回写默认开启).md`(A103-3 推送与 A103-2 消失解锁)
|
||||
> - `doc/docs/业务流程/2026-09-17-付款管理金蝶付款结果定时回写(A103-2付款申请单查询+A103-4付款处理查询)技术文档.md`(回写任务与 `applyPayApplyResult`)
|
||||
> - `doc/docs/业务流程/2026-09-20-应付申请与付款申请同步处理全链路业务流程(A102-2下推-A102-4查询-A103-1生成-A103-2回写).md`
|
||||
|
||||
## 1. 背景与问题
|
||||
|
||||
排查预结算单 YJ2026101500001(已完成预结算审批、付款申请 FKD2026101500001 审批、金蝶付款申请单生成 FKSQ-00000229):预结算单详情页"预付信息"卡片始终空白。
|
||||
|
||||
根因:`blade_pre_settlement_advance`(预付记录表,详情接口 `advances` 字段的数据源)此前**只在金蝶回写"付款完成"(`kingdeeStatus=paid`)时才写入**(`applyPayApplyResult` → `syncPreSettlementAdvance`)。付款申请推进到"已生成"(synced)阶段不落记录,页面预付信息必须等付款完成才可见;且基本信息区的预付申请/已付金额来自付款申请实时汇总(`fillPaymentApplicationAmounts`),金额有值、明细卡片空白,容易误判。
|
||||
|
||||
## 2. 改造内容(`PaymentApplicationServiceImpl`)
|
||||
|
||||
| # | 变更点 | 变更前 | 变更后 |
|
||||
|---|---|---|---|
|
||||
| 1 | `syncPreSettlementAdvance` | 无参方法,写死 `billStatus="paid"`、已付=申请全额 | 参数化 `(entity, billStatus, paidAmount)`,建档与付款完成共用;按付款申请单号查重幂等 |
|
||||
| 2 | `doSyncKingdee` synced 落库处 | 仅更新付款申请状态 | **生成成功即建档**:`approved` / 已付 0,同事务落库,预付信息提前回显 |
|
||||
| 3 | `applyPayApplyResult` paid 分支 | 调原方法 | `paid` / 申请全额(语义不变),已建档时更新为已付款 |
|
||||
| 4 | `applyPayApplyResult` 勾稽分支(新增) | failed 单据经 A103-2 勾稽确认 synced/paying 时不落预付记录 | 与推送成功同语义,`approved` / 已付 0 建档 |
|
||||
| 5 | 新增 `voidPreSettlementAdvance` | 无 | 按付款申请单号将未付预付记录置 `voided`(`paid` 记录不联动,付款完成事实不回退),并刷新预结算汇总 |
|
||||
| 6 | `voidBill`(付款申请作废) | 不清理预付记录 | 联动作废,避免残留记录阻断预结算作废(`PreSettlementServiceImpl#voidBill` 的"存在关联预付申请"拦截) |
|
||||
| 7 | `unlockBill`(金蝶反审核解锁,含 A103-2 连续两轮消失判定) | 不清理预付记录 | 联动作废,重推生成新单后重新建档 |
|
||||
|
||||
建档/作废后的联动刷新(`refreshPreSettlementAdvanceSummary` 预结算 advance_no/预付金额列、`formal_settlement_source` 预付金额、正式结算付款汇总)复用原有尾部逻辑,无需额外改动。转正式结算时来源表预付金额不再恒为 0。
|
||||
|
||||
## 3. 状态与联动时序
|
||||
|
||||
| 付款申请阶段 | 预付记录(blade_pre_settlement_advance) | 页面预付信息 |
|
||||
|---|---|---|
|
||||
| 审批通过、未推送 | 不存在 | 空白 |
|
||||
| 金蝶付款申请单生成成功(synced) | `approved` / 已付 0(提前建档) | **回显**(状态"审批通过") |
|
||||
| 付款中(paying)/ 勾稽确认已生成 | `approved`(补建或维持) | 回显 |
|
||||
| 付款完成(paid 回写) | `paid` / 已付全额 | 回显(状态"已付款") |
|
||||
| 付款申请作废 / 金蝶反审核解锁 | 未付记录置 `voided`(`paid` 不动) | 消失(汇总同步扣减) |
|
||||
|
||||
前端零改动:`pre-settlement-editor.vue` 的 `advanceStatusName` 已含 `approved→审批通过` 映射。
|
||||
|
||||
## 4. 定时任务影响
|
||||
|
||||
- **付款结果回写任务(`syncKingdeePaymentResult`)**:零改动。轮询条件基于付款申请自身状态,不依赖预付记录表;paid 回写时命中已有记录更新,不产生重复。
|
||||
- **失败重推(`retryKingdeeFailed`)**:failed 阶段未建档,重推成功经 synced 建档一次,幂等查重兜底。
|
||||
- **A103-4 付款记录回填、金蝶单据锁补偿**:不涉及预付记录表,无影响。
|
||||
|
||||
## 5. 行为变化与存量数据
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 预结算作废拦截提前 | "存在关联预付申请,请先作废预付申请"从付款完成后提前到金蝶生成即生效——作废预结算前需先作废付款申请(预期收紧) |
|
||||
| 存量单据不自动补 | 改造上线前已 synced 的付款申请(如 FKD2026101500001)不会重推(状态门禁跳过 synced),预付记录要等 paid 回写补建;需立即回显的,手工补一条 `blade_pre_settlement_advance`(`advance_no=付款申请单号`、`bill_status='approved'`、已付 0) |
|
||||
| 金额模糊匹配触发提前 | synced 建档时同额未付记录的兜底匹配与 paid 阶段同一逻辑,仅触发时点提前;如出现误绑定,可将模糊匹配收敛到 paid 分支 |
|
||||
@@ -0,0 +1,130 @@
|
||||
# 收款流水(认领通知单)多次认领与作废口径梳理
|
||||
|
||||
> 日期:2026-10-16
|
||||
> 范围:`tms-api` blade-transport 收款流水认领、认领记录模块;`tms-erp-web` 认领记录页面
|
||||
|
||||
---
|
||||
|
||||
## 一、结论速览
|
||||
|
||||
| 问题 | 结论 |
|
||||
| --- | --- |
|
||||
| 一个通知单能被多次认领,每次认领生成一条还是多条认领记录? | **每次认领操作生成一条认领主单**(`blade_receipt_claim`),多次认领即多条主单;每次认领同时按所选拆分生成 N 条结算单分摊明细(`blade_receipt_claim_settlement`) |
|
||||
| 认领记录页面是一条一条显示还是只显示一条? | **一条认领主单一行,逐条显示**,不按通知单合并;同一通知单认领 3 次就显示 3 行,每行"认领金额"只是该次认领的金额 |
|
||||
| 作废是逐条作废归还金额,还是整个通知单全部作废? | **逐条作废、按条归还金额**:作废入口在单条认领记录上,回退金额 = 该条认领主单的 `claim_amount`,流水已认领金额只减这一条;其余认领记录不受影响 |
|
||||
|
||||
---
|
||||
|
||||
## 二、数据模型
|
||||
|
||||
```
|
||||
blade_kingdee_receipt_flow(收款流水 / 认领通知单,1)
|
||||
│ receipt_flow_id
|
||||
├─< blade_receipt_claim(认领主单,N) ← 每次认领一条
|
||||
│ claim_amount 本次认领总金额
|
||||
│ claimer_id 认领人(记录页面按本人过滤)
|
||||
│ claim_status claimed / voided
|
||||
│ voided_by / voided_time 作废人 / 作废时间
|
||||
│
|
||||
└─< blade_receipt_claim_settlement(分摊明细,N × M) ← 每次认领按结算单拆行
|
||||
receipt_claim_id 归属哪次认领
|
||||
formal_settlement_id/no 关联应收正式结算单
|
||||
allocated_receipt_amount 本次分摊金额
|
||||
claimed_receipt_amount 认领前该结算单已认领金额
|
||||
status 1 有效 / 0 无效(作废置 0,不物理删除)
|
||||
```
|
||||
|
||||
关键点:**认领主单的粒度是"一次认领操作"**,不是通知单。通知单维度只有累计金额(`claimed_amount`)和状态(`claim_status`:unclaimed / partial / claimed),由每次认领/作废累加回退得出。
|
||||
|
||||
---
|
||||
|
||||
## 三、认领:一次操作生成一条主单 + N 条分摊
|
||||
|
||||
代码:`ReceiptFlowServiceImpl#claim(ReceiptClaimRequest)`
|
||||
|
||||
1. 锁定流水(通知单)与所选结算单。
|
||||
2. 校验两条累计上限:
|
||||
- 每张结算单:已有累计认领 + 本次分摊 ≤ 结算总应收含税金额;
|
||||
- 流水:已有累计认领(`flowClaimedAmount`,只算未作废记录)+ 本次总额 ≤ 公告未认领金额(`unclaimed_amount`,A301-2 口径)。
|
||||
3. **插入一条** `ReceiptClaim` 主单:`claim_amount` = 本次分摊总额,`claim_status = claimed`。
|
||||
4. 按所选结算单逐张**插入 N 条** `ReceiptClaimSettlement` 分摊明细。
|
||||
5. 累加流水 `claimed_amount`,按累计值重算流水状态(< 未认领金额 → partial,= → claimed)。
|
||||
6. 只登记本地,不推送金蝶;推送由定时任务/手动按钮触发。
|
||||
|
||||
所以:**同一通知单第二次认领是再插一条新主单**,不存在"找已有主单合并金额"的逻辑;校验全部按"未作废记录累计"实时计算,天然支持多次认领。
|
||||
|
||||
### 认领按钮的显示条件(为什么有些行没有"认领")
|
||||
|
||||
收款流水列表操作列(`receipt-flow.vue` → `canClaim(row)`):
|
||||
|
||||
```js
|
||||
canClaim(row) {
|
||||
return this.hasPermission('receipt_flow_claim') && Number(row.remainingAmount || 0) > 0;
|
||||
}
|
||||
```
|
||||
|
||||
两个条件同时满足才显示"认领"按钮:
|
||||
|
||||
1. **按钮权限**:当前用户拥有 `receipt_flow_claim` 按钮权限(admin 直接放行,普通用户取决于菜单按钮配置);
|
||||
2. **剩余可认领金额 > 0**:`remainingAmount` 由后端 `ReceiptFlowWrapper` 统一计算:
|
||||
|
||||
```
|
||||
remainingAmount = max(公告未认领金额 unclaimed_amount − 累计认领金额 claimed_amount, 0)
|
||||
```
|
||||
|
||||
按认领状态换算:
|
||||
|
||||
| 流水认领状态 | remainingAmount | 认领按钮 |
|
||||
| --- | --- | --- |
|
||||
| unclaimed 未认领(公告金额 > 0) | = 公告未认领金额 > 0 | 显示 |
|
||||
| partial 部分认领 | > 0 | 显示 |
|
||||
| claimed 认领完成 | = 0(累计 ≥ 公告金额) | **不显示** |
|
||||
| 公告未认领金额为 0 的流水 | = 0 | **不显示** |
|
||||
| 无 `receipt_flow_claim` 权限 | — | **不显示** |
|
||||
|
||||
即:**按钮显隐只看金额余额与权限,不看认领状态字段本身**;`claimed` 状态的行因为剩余额度为 0 自然不显示按钮。按钮不显示且不满足"推送金蝶"条件时,操作列显示"-"。
|
||||
|
||||
注意:顶部"批量认领"按钮只判断权限与是否勾选行(`receipt_flow_claim` + `selectedRows.length`),**不按行剩余金额过滤**;对无剩余额度的流水发起批量认领时,进入批量页面后由提交校验/后端 `claim()` 兜底拦截。
|
||||
|
||||
---
|
||||
|
||||
## 四、认领记录页面:逐条显示
|
||||
|
||||
后端:`ReceiptClaimMapper.xml#selectClaimRecordPage` → `FROM blade_receipt_claim c JOIN blade_kingdee_receipt_flow f`,固定条件 `c.claimer_id = 当前用户`。
|
||||
|
||||
- **列表一行 = 一条认领主单**,无按流水/通知单的 GROUP BY 合并;
|
||||
- 同一通知单多次认领 → 多行,各行的认领金额、认领日期、金蝶单号都是当次认领自己的;
|
||||
- 关联结算单列用 `GROUP_CONCAT(分摊明细.结算单号)` 拼成一串展示(只展示,不参与合并);
|
||||
- 详情页(`selectClaimRecordDetail` + 分摊明细列表)展示的也是**该次认领**的分摊明细,明细里的"认领前已认领金额"记录的是认领那一刻结算单的累计值,可还原多次认领的先后关系;
|
||||
- 前端 `receipt-claim-record.vue` 列表 + 行级"作废"按钮(`api.voidClaim(row.id)`),确认文案为"作废后将回退本次认领金额"——与逐条口径一致。
|
||||
|
||||
另:收款流水列表的详情弹窗里有一个"认领记录"区块,展示的是该流水的**操作日志**(`blade_receipt_flow_record`,action_type = claim/push),按时间线展示每次认领/推送动作,同样不是合并视图。
|
||||
|
||||
---
|
||||
|
||||
## 五、作废:逐条作废、按条归还金额
|
||||
|
||||
代码:`ReceiptClaimRecordServiceImpl#voidClaim(Long id)`,入参就是**单条认领主单 id**。
|
||||
|
||||
流程(单事务):
|
||||
|
||||
1. 锁定该条认领主单,校验:本人记录、`claim_status = claimed`(已作废不可再作废)。
|
||||
2. 锁定关联流水,校验:**未推送金蝶**——已推送的不允许作废,提示"请在金蝶侧人工处置"。
|
||||
3. 取该主单名下有效分摊明细(status=1),全部置 `status = 0`(保留审计,不删行)。
|
||||
4. 批量刷新相关结算单收款汇总(`refreshPaymentSummaries`)。
|
||||
5. **流水 `claimed_amount` 只减去本条主单的 `claim_amount`**(下限 0),并按减后累计重算流水认领状态(0 → unclaimed,< 未认领金额 → partial,≥ → claimed)。
|
||||
6. 主单置 `voided`,记录作废人、作废时间;写 `void_claim` 操作日志(金额 = 本次回退金额)。
|
||||
7. **不生成金蝶认领冲单**:金蝶单号/状态保留原值,金蝶侧冲销/红红由人工处置;冲单逻辑代码已注释保留(`buildKingdeeBillNo`),后续迭代可放开。
|
||||
|
||||
边界与影响:
|
||||
|
||||
- 作废某一条,只归还这一条的金额,**同一通知单的其他认领记录不受影响**,仍正常显示、仍可单独作废;
|
||||
- 作废后该条记录仍在列表中(状态"已作废"),不允许再改附件、不可重复作废;
|
||||
- 归还后的金额空间立即可被再次认领(认领校验按未作废累计实时计算);
|
||||
- 附件/操作日志/分摊明细均保留,金额回退可追溯。
|
||||
|
||||
---
|
||||
|
||||
## 六、一句话总结
|
||||
|
||||
通知单(收款流水)是"累计额度"维度,认领记录是"每次操作"维度:**认领一次记一条、页面逐条展示、作废按条回退金额**;整单性质的只有流水上的累计已认领金额与认领状态,由各条记录的认领/作废实时推导,没有任何"按通知单整体作废"的入口。
|
||||
@@ -57,6 +57,7 @@ thirdParty:
|
||||
# 国内运输:运单运输方式 -> 金蝶物料编码(公路运输/铁路运输/水路运输/航空运输四种)
|
||||
domestic:
|
||||
"[公路运输]": ${BFI_MATERIAL_DOMESTIC_ROAD:XDWL11010001}
|
||||
"[road]": ${BFI_MATERIAL_DOMESTIC_ROAD:XDWL11010001}
|
||||
"[铁路运输]": ${BFI_MATERIAL_DOMESTIC_RAIL:XDWL11010002}
|
||||
"[水路运输]": ${BFI_MATERIAL_DOMESTIC_WATER:XDWL11010003}
|
||||
"[航空运输]": ${BFI_MATERIAL_DOMESTIC_AIR:Item-00001082}
|
||||
@@ -118,6 +119,8 @@ thirdParty:
|
||||
incrementDays: ${BFI_INVOICE_RECEIPT_INCREMENT_DAYS:30}
|
||||
# 同步发票种类白名单(逗号分隔票易通编码,如 26,27 数电普票/专票),为空不限制
|
||||
invoiceTypes: ${BFI_INVOICE_RECEIPT_INVOICE_TYPES:26,27}
|
||||
# 客商单位过滤开关:开启后只同步开票单位(销方)与受票单位(购方)税号均已登记在客商发票信息中的发票,前端票池展示查询同用此开关过滤
|
||||
customerUnitsOnly: ${BFI_INVOICE_RECEIPT_CUSTOMER_UNITS_ONLY:false}
|
||||
# 补偿绑定单轮限量(未绑定登记按发票号码精确补查条数上限)
|
||||
compensateLimit: ${BFI_INVOICE_RECEIPT_COMPENSATE_LIMIT:200}
|
||||
# single-flight 分布式锁租约秒数,须大于最坏一轮耗时;进程异常退出时锁在租约到期后自动释放
|
||||
|
||||
Reference in New Issue
Block a user