From bbf819d1b308a9a6114547d32445512ae5eb0da7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com>
Date: Sat, 31 Oct 2026 16:06:32 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=91=E8=9D=B6=E6=94=B6=E7=A5=A8?=
=?UTF-8?q?=E5=8D=95A303-2=E6=8C=89=E5=85=88=E7=A5=A8=E5=90=8E=E6=AC=BE?=
=?UTF-8?q?=E7=BB=84=E8=A3=85=E6=8A=A5=E6=96=87=EF=BC=88=E5=88=86=E5=BD=95?=
=?UTF-8?q?=E5=8F=96=E5=8F=91=E7=A5=A8=E6=98=8E=E7=BB=86=E8=A1=8C/?=
=?UTF-8?q?=E5=BE=80=E6=9D=A5=E6=88=B7=E9=94=80=E6=96=B9=E4=BE=9B=E5=BA=94?=
=?UTF-8?q?=E5=95=86=E4=BB=A3=E7=A0=81/=E5=B8=81=E5=88=ABCNY=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../constant/KingdeeBfiConstant.java | 3 +
.../KingdeeReceivedInvoiceSyncSupport.java | 230 ++++++++++++++----
2 files changed, 180 insertions(+), 53 deletions(-)
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java
index b1c45f44..055be3f3 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/KingdeeBfiConstant.java
@@ -186,6 +186,9 @@ public final class KingdeeBfiConstant {
/** A303-2 业务类型(PUR:采购 / FEE:费用,TMS 服务费进项票暂按采购)。待确认:取值需金蝶侧最终确认。 */
public static final String BFI_RECEIVED_BIZ_TYPE = "PUR";
+ /** A303-2 结算币别:收票登记不挂合同(先票后款),数电票固定人民币。 */
+ public static final String BFI_RECEIVED_CURRENCY_NUMBER = "CNY";
+
/** A303-2 必填:外部单据类型(收票单来源标识)。待确认:取值需金蝶侧确认,确认后如有调整直接改此常量。 */
public static final String BFI_RECEIVED_INVOICE_SRC_TYPE = "TMS收票登记";
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeReceivedInvoiceSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeReceivedInvoiceSyncSupport.java
index 48a74c14..72e6cb7b 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeReceivedInvoiceSyncSupport.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/KingdeeReceivedInvoiceSyncSupport.java
@@ -28,6 +28,7 @@ package org.springblade.transport.support;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceAssignItemDTO;
@@ -37,22 +38,24 @@ import org.springblade.thirdparty.bfi.pojo.dto.BfiReceivedInvoiceSaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiReceivedInvoiceSaveEntryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolItemVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiReceivedInvoiceVO;
import org.springblade.thirdparty.bfi.service.IBfiService;
import org.springblade.thirdparty.bfi.util.BfiUtil;
import org.springblade.transport.config.BfiProperties;
import org.springblade.transport.constant.KingdeeBfiConstant;
-import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.entity.InvoiceReceipt;
import org.springblade.transport.pojo.entity.KingdeeInvoicePool;
import org.springblade.transport.service.KingdeeSyncContext;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@@ -61,12 +64,11 @@ import java.util.Map;
/**
* 金蝶收票单同步支撑组件(图05 环节②③:A303-2 收票单批量保存 + A302-6 收票指定应付 + A801-3 收票单查询)。
*
收票登记「推送金蝶」由 {@link org.springblade.transport.service.IInvoiceReceiptService} 编排前置校验
- * (审批通过、已绑定镜像、一票一应付、A102-4 应付单已生成预检、A801-3 幂等预检)后调用本组件;
- * 报文组装取数:主表组织/币种/往来户复用应付支撑组件的合同派生方法,票面字段取自票池镜像
- * (invoice_type_code 等,A801-1 落库),明细分录与财务应付单 A102-2 同源同口径
- * (结算明细行+结算合计费用项各一条,{@link KingdeeApBillSyncSupport.BfiEntryGroup},分录标识同键)。
- * 防重键:收票单 awrp_src_code=收票登记id,awrp_entry_code=结算明细行单据号/结算合计费用项拼接
- * (与应付单分录同键,便于金蝶侧逐行勾稽);A302-6 按「收票单防重键 ↔ 财务应付单防重键」一票一应付指定。
+ * (审批通过、已绑定镜像、A801-3 幂等预检)后调用本组件;先票后款链路:A303-2 推送即终态(pushed),
+ * A302-6 指定应付移交付款结算单侧(应付单 A102-2 生成后按认领关系逐单指定)。
+ * 报文组装取数:往来户=发票销售方金蝶供应商代码(A901-1),结算组织=页面手选部门沿部门树上溯平台公司,
+ * 币别固定 CNY;票面字段取自票池镜像(A801-1 落库),明细分录逐行取发票明细(镜像 items_json),
+ * 分录标识 awrp_entry_code=发票号码_seq(重推幂等候选键)。
* 财务应付单侧(A102-2/A102-4)取数与校验复用 {@link KingdeeApBillSyncSupport}。
*
* @author Chill
@@ -148,21 +150,19 @@ public class KingdeeReceivedInvoiceSyncSupport {
/**
* 推送金蝶收票单(A303-2 批量保存,调用方已完成 A801-3 幂等预检)。
- * 主表:组织/币种/往来户编码按分摊结算单关联合同派生(与应付单同源),销方=开票单位、购方=受票单位;
- * 明细分录与财务应付单 A102-2 同源同口径:结算明细按 货物→费用大类→税率 拆分行+手动添加结算合计费用项每行一条,物料/数量/单价/金额取自
- * 与应付单分录同一解析结果({@link KingdeeApBillSyncSupport.BfiEntryGroup}),分录标识同键。
+ * 先票后款链路:收票登记不挂结算单/合同——主表往来户=发票销售方金蝶供应商代码(receipt.supplierCode,
+ * 缺失实时查 A901-1 一次,仍缺失报错阻断),结算组织=页面手选部门(receipt.deptId)沿部门树反查平台公司,
+ * 币别固定 CNY,往来类型取固定常量;销方=开票单位、购方=受票单位。
+ * 明细分录逐行取发票明细行(镜像 items_json,{@link BfiInvoicePoolItemVO}),不与结算明细同源。
* 联调口径沿用应付侧:先只送 DTO 标注【必填】字段与票面核心字段(发票号码),
* 非必填的税号/地址/电话/开户行等 setter 注释保留,联调确认后放开
*
- * @param receipt 收票登记(审批通过、已绑定镜像)
- * @param mirror 票池镜像(票面主表字段取数来源)
- * @param contract 分摊应付结算单关联合同(组织/币种/往来户派生)
- * @param entryGroups 分摊结算单解析出的分录(与应付单 A102-2 同一解析结果)
- * @param context 金蝶同步上下文(缓存载体)
+ * @param receipt 收票登记(审批通过、已绑定镜像)
+ * @param mirror 票池镜像(票面主表字段+明细行取数来源)
+ * @param context 金蝶同步上下文(缓存载体)
* @return 推送结果(单据编号 + 星瀚收票单内码,A902 附件上传定位用)
*/
- public ReceivedInvoicePushOutcome pushReceivedInvoice(InvoiceReceipt receipt, KingdeeInvoicePool mirror, ContractManage contract,
- List entryGroups, KingdeeSyncContext context) {
+ public ReceivedInvoicePushOutcome pushReceivedInvoice(InvoiceReceipt receipt, KingdeeInvoicePool mirror, KingdeeSyncContext context) {
if (Func.isBlank(mirror.getInvoiceTypeCode())) {
throw new ServiceException("推送金蝶收票单失败:镜像缺少票易通发票类型编码,请先重新绑定票池发票");
}
@@ -183,14 +183,17 @@ public class KingdeeReceivedInvoiceSyncSupport {
param.setInvoicetypef7Number(mirror.getInvoiceTypeCode());
// 【必填】付款方式:(与应付单一致,不允许金蝶自动推付款)
param.setPaymenttype(BfiConstant.PAY_MODE_CASH);
- // 【必填】往来类型:统一固定客户(KingdeeApBillSyncSupport#bfiAsstactType,与应付单/付款申请同口径)
- param.setAsstacttype(kingdeeApBillSyncSupport.bfiAsstactType(contract));
- // 【必填】结算组织.编码:合同关联项目业务部门反查平台公司(与应付单同源)
- param.setOrgNumber(kingdeeApBillSyncSupport.bfiOrgNumber(contract, context));
- // 【必填】结算币别:合同结算币种
- param.setCurrencyNumber(kingdeeApBillSyncSupport.bfiCurrency(contract));
- // 【必填】往来户.编码:合同乙方(开票供应商)客商编码
- param.setReceivablessuppNumber(kingdeeApBillSyncSupport.bfiAsstactNumber(contract, context));
+ // 【必填】往来类型:固定常量(收票登记不挂合同,直接取常量)
+ param.setAsstacttype(KingdeeBfiConstant.BFI_CUSTOMER_ASSTACT_TYPE);
+ // 【必填】结算组织.编码:页面手选部门沿部门树反查平台公司(收票登记 deptId)
+ param.setOrgNumber(kingdeeApBillSyncSupport.kingdeeOrgNumberByDept(receipt.getDeptId(), context));
+ // 【必填】结算币别:固定 CNY(收票登记不挂合同,数电票人民币)
+ param.setCurrencyNumber(KingdeeBfiConstant.BFI_RECEIVED_CURRENCY_NUMBER);
+ // 【必填】往来户.编码:发票销售方金蝶供应商代码(缺失实时查 A901-1,仍缺失报错阻断)
+ String supplierNumber = this.requireSupplierNumber(receipt, mirror);
+ param.setReceivablessuppNumber(supplierNumber);
+ // 销售方名称.编码(非必填):同往来户供应商代码
+ param.setAsstactNumber(supplierNumber);
// 【必填】汇率表.编码
param.setExratetableNumber(BfiConstant.EXRATE_TABLE_DEFAULT);
// 【必填】销方名称:镜像开票单位
@@ -212,8 +215,8 @@ public class KingdeeReceivedInvoiceSyncSupport {
// param.setRemark(receipt.getRemark());
// 【必填】外部单据类型
param.setAwrpSrcType(KingdeeBfiConstant.BFI_RECEIVED_INVOICE_SRC_TYPE);
- // 【必填】明细分录:与应付单 A102-2 同源同口径,结算明细行+结算合计费用项各一条(不聚合)
- param.setEntry(buildEntries(entryGroups));
+ // 【必填】明细分录:逐行取发票明细行(镜像 items_json),不与结算明细同源
+ param.setEntry(this.buildEntries(receipt, mirror));
BfiBillSaveResultVO saveResult = bfiService.saveReceivedInvoice(param);
kingdeeApBillSyncSupport.requireBfiSaveSuccess(saveResult, "推送金蝶收票单");
@@ -222,6 +225,30 @@ public class KingdeeReceivedInvoiceSyncSupport {
savedItem.getId() == null ? null : String.valueOf(savedItem.getId()));
}
+ /**
+ * 往来户供应商编码:优先取登记上已回填的 supplierCode;缺失时按销方名称实时查一次 A901-1 并回填实体
+ * (调用方推送成功后随推送结果落库),仍缺失报错阻断
+ *
+ * @param receipt 收票登记
+ * @param mirror 票池镜像(销方名称=开票单位)
+ * @return 金蝶供应商编码
+ */
+ private String requireSupplierNumber(InvoiceReceipt receipt, KingdeeInvoicePool mirror) {
+ String supplierCode = receipt.getSupplierCode();
+ if (Func.isBlank(supplierCode)) {
+ log.info("收票登记[{}]往来户供应商代码缺失,按销方[{}]实时查询 A901-1", receipt.getInvoiceNo(), mirror.getIssuerName());
+ supplierCode = kingdeeApBillSyncSupport.queryCustomerCode(mirror.getIssuerName());
+ if (Func.isNotBlank(supplierCode)) {
+ receipt.setSupplierCode(supplierCode);
+ }
+ }
+ if (Func.isBlank(supplierCode)) {
+ throw new ServiceException("推送金蝶收票单失败:发票销售方[" + Func.toStr(mirror.getIssuerName())
+ + "]未建档金蝶供应商编码,请先维护客商档案后再推送");
+ }
+ return supplierCode;
+ }
+
/**
* A303-2 收票单推送结果
*
@@ -232,42 +259,139 @@ public class KingdeeReceivedInvoiceSyncSupport {
}
/**
- * 分录组装与应付单 A102-2 明细同源同口径:结算明细按 货物→费用大类→税率 拆分行 + 手动添加的结算合计费用项每行一条,
- * 物料/数量/单价/含税/不含税金额
- * 取自 {@link KingdeeApBillSyncSupport.BfiEntryGroup}(与应付单分录同一解析结果),分录标识同键,
- * 便于金蝶侧收票单明细与应付单明细逐行勾稽;数量无效时兜底 1;
- * 票面明细行(items_json)不再作为分录取数来源,税收分类/税率/税额随应付口径暂不送(联调强校验时再补)
+ * 分录组装:逐行取发票明细行(镜像 items_json 反序列化为 {@link BfiInvoicePoolItemVO}),一条明细行一条分录:
+ * 价税合计=明细金额+税额(合计为 0 拦截);数量取 num(≤0/空/不可解析兜底 1);单价取 unitPrice(空按价税合计/数量反算);
+ * 物料编码与税收分类编码取行商品编码 goodsCode(缺失拦截);税率表编码="V"+整数税率(13/13%/0.13→V13,
+ * 缺失、0/免税行拦截提示人工);不含税金额/税额取明细金额/税额(空不送);分录标识=发票号码_seq(重推幂等候选键)
*
- * @param entryGroups 分摊结算单解析出的分录(与应付单 A102-2 同一解析结果)
+ * @param receipt 收票登记(发票号码,分录标识前缀)
+ * @param mirror 票池镜像(明细行 items_json)
* @return A303-2 明细分录
*/
- private List buildEntries(List entryGroups) {
+ private List buildEntries(InvoiceReceipt receipt, KingdeeInvoicePool mirror) {
+ if (Func.isBlank(mirror.getItemsJson())) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]票池镜像无明细行,请先按票号补查票池明细");
+ }
+ List items = this.parseMirrorItems(receipt.getInvoiceNo(), mirror.getItemsJson());
+ if (Func.isEmpty(items)) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]票池镜像明细行为空,请先按票号补查票池明细");
+ }
List entries = new ArrayList<>();
- for (KingdeeApBillSyncSupport.BfiEntryGroup entryGroup : entryGroups) {
- BfiReceivedInvoiceSaveEntryDTO entry = new BfiReceivedInvoiceSaveEntryDTO();
- // 【必填】价税合计:该行含税金额(与应付单 e_pricetaxtotal 同源取数)
- entry.setEPricetaxtotal(kingdeeApBillSyncSupport.money(entryGroup.amount()));
- // 【必填】数量:该行运输数量,无吨位数据时兜底 1(与应付单口径一致)
- entry.setQuantity(entryGroup.quantity().compareTo(BigDecimal.ZERO) > 0 ? entryGroup.quantity() : BigDecimal.ONE);
- // 【必填】单价:该行运输单价(与应付单同源;单据头 isincludetax=true 录入价税合计,单价仅作参考)
- entry.setPrice(entryGroup.price());
- // 【必填】物料编码.编码:按装卸大类/项目业务模式/运单运输方式映射(thirdparty.bfi.material.* 配置,与应付单同源)
- entry.setMaterialNumber(entryGroup.materialNumber());
- // 金额(不含税):该行结算金额(不含税)(与应付单 e_amount 同源取数;结算合计费用项行无不含税金额不传)
- if (entryGroup.amountNoTax() != null) {
- entry.setEAmount(kingdeeApBillSyncSupport.money(entryGroup.amountNoTax()));
+ int rowNo = 0;
+ for (BfiInvoicePoolItemVO item : items) {
+ rowNo++;
+ // 分录序号:优先取明细行 seq,缺失按行号兜底
+ String seq = Func.isBlank(item.getSeq()) ? String.valueOf(rowNo) : item.getSeq().trim();
+ BigDecimal detailAmount = this.parseMirrorAmount(receipt.getInvoiceNo(), seq, "明细金额", item.getDetailAmount());
+ BigDecimal taxAmount = this.parseMirrorAmount(receipt.getInvoiceNo(), seq, "税额", item.getTaxAmount());
+ // 【必填】价税合计:明细金额+税额(合计为 0 拦截)
+ BigDecimal priceTaxTotal = kingdeeApBillSyncSupport.money(detailAmount == null ? BigDecimal.ZERO : detailAmount)
+ .add(kingdeeApBillSyncSupport.money(taxAmount == null ? BigDecimal.ZERO : taxAmount));
+ if (priceTaxTotal.compareTo(BigDecimal.ZERO) == 0) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]分录[" + seq + "]价税合计为 0,无法推送");
}
- // 【必填】外部系统来源分录标识(候选键):与应付单分录同键(结算明细行单据号/结算合计费用项拼接),便于逐行勾稽
- entry.setAwrpEntryCode(entryGroup.entryCode());
-
+ if (Func.isBlank(item.getGoodsCode())) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]分录[" + seq + "]缺少行商品编码,无法确定物料与税收分类编码");
+ }
+ BfiReceivedInvoiceSaveEntryDTO entry = new BfiReceivedInvoiceSaveEntryDTO();
+ entry.setEPricetaxtotal(priceTaxTotal);
+ // 【必填】数量:明细行数量(≤0/空/不可解析兜底 1)
+ entry.setQuantity(this.mirrorQuantity(item.getNum()));
+ // 【必填】单价:明细行单价(空按价税合计/数量反算;单据头 isincludetax=true,单价仅作参考)
+ entry.setPrice(this.mirrorPrice(item.getUnitPrice(), priceTaxTotal, entry.getQuantity()));
+ // 【必填】物料编码.编码:行商品编码
+ entry.setMaterialNumber(item.getGoodsCode().trim());
+ // 税收分类编码.编码:数电票行商品编码即税收分类编码
+ entry.setETaxclassNumber(item.getGoodsCode().trim());
+ // 【必填】税率表.编码:"V"+整数税率(缺失、0/免税行拦截提示人工)
+ entry.setTaxrateidNumber(this.taxRateCode(receipt.getInvoiceNo(), seq, item.getTaxRate()));
+ // 金额(不含税)/税额:明细金额/税额(空不送)
+ if (detailAmount != null) {
+ entry.setEAmount(kingdeeApBillSyncSupport.money(detailAmount));
+ }
+ if (taxAmount != null) {
+ entry.setETax(kingdeeApBillSyncSupport.money(taxAmount));
+ }
+ // 【必填】外部系统来源分录标识(候选键):发票号码_seq,重推同键幂等
+ entry.setAwrpEntryCode(receipt.getInvoiceNo() + "_" + seq);
entries.add(entry);
}
- if (entries.isEmpty()) {
- throw new ServiceException("推送金蝶收票单失败:分摊结算单无结算明细与结算合计费用项,无法组装收票单明细");
- }
return entries;
}
+ /** 镜像明细行反序列化:items_json 解析异常按脏数据拦截(A801-1 原样落库,理论上可解析) */
+ private List parseMirrorItems(String invoiceNo, String itemsJson) {
+ try {
+ return Arrays.asList(JsonUtil.parse(itemsJson, BfiInvoicePoolItemVO[].class));
+ } catch (RuntimeException exception) {
+ log.warn("收票登记[{}]票池镜像明细行反序列化失败:{}", invoiceNo, exception.getMessage());
+ throw new ServiceException("推送金蝶收票单失败:发票[" + invoiceNo + "]票池镜像明细行数据异常,请先按票号补查票池明细");
+ }
+ }
+
+ /** 明细行数量:num ≤0/空/不可解析兜底 1 */
+ private BigDecimal mirrorQuantity(String num) {
+ BigDecimal quantity = this.parseMirrorAmountQuietly(num);
+ if (quantity == null || quantity.compareTo(BigDecimal.ZERO) <= 0) {
+ return BigDecimal.ONE;
+ }
+ return quantity;
+ }
+
+ /** 明细行单价:unitPrice 空/不可解析按 价税合计/数量 反算(2 位小数) */
+ private BigDecimal mirrorPrice(String unitPrice, BigDecimal priceTaxTotal, BigDecimal quantity) {
+ BigDecimal price = this.parseMirrorAmountQuietly(unitPrice);
+ if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
+ return price;
+ }
+ return priceTaxTotal.divide(quantity, 2, RoundingMode.HALF_UP);
+ }
+
+ /** 镜像税率转税率表编码:"V"+整数税率(13/13%/0.13→V13);缺失、零/免税行拦截提示人工 */
+ private String taxRateCode(String invoiceNo, String seq, String taxRateText) {
+ if (Func.isBlank(taxRateText)) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + invoiceNo + "]分录[" + seq + "]税率缺失,无法映射税率表,请人工核实");
+ }
+ BigDecimal rate;
+ try {
+ rate = new BigDecimal(taxRateText.trim().replace("%", ""));
+ } catch (NumberFormatException exception) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + invoiceNo + "]分录[" + seq + "]税率[" + taxRateText.trim() + "]无法识别,请人工核实");
+ }
+ if (rate.compareTo(BigDecimal.ZERO) <= 0) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + invoiceNo + "]分录[" + seq + "]为免税/零税率行,无法映射税率表,请人工核实");
+ }
+ // 小于 1 的小数按比率换算百分比(0.13→13),"1" 按百分比(1%→V1)
+ if (rate.compareTo(BigDecimal.ONE) < 0) {
+ rate = rate.multiply(BigDecimal.valueOf(100));
+ }
+ return "V" + rate.stripTrailingZeros().toPlainString();
+ }
+
+ /** 镜像明细数值解析:空返回 null,不可解析返回 null(数量/单价有兜底,金额空不送) */
+ private BigDecimal parseMirrorAmountQuietly(String value) {
+ if (Func.isBlank(value)) {
+ return null;
+ }
+ try {
+ return new BigDecimal(value.trim());
+ } catch (NumberFormatException exception) {
+ return null;
+ }
+ }
+
+ /** 镜像明细金额解析:空返回 null,不可解析按脏数据拦截(金额直接进分录,不能静默丢弃) */
+ private BigDecimal parseMirrorAmount(String invoiceNo, String seq, String fieldLabel, String value) {
+ if (Func.isBlank(value)) {
+ return null;
+ }
+ try {
+ return new BigDecimal(value.trim());
+ } catch (NumberFormatException exception) {
+ throw new ServiceException("推送金蝶收票单失败:发票[" + invoiceNo + "]分录[" + seq + "]" + fieldLabel + "[" + value.trim() + "]无法解析,请先按票号补查票池明细");
+ }
+ }
+
// ==================== A302-6 收票指定应付 ====================
/**