feat: 金蝶收票单A303-2按先票后款组装报文(分录取发票明细行/往来户销方供应商代码/币别CNY)
This commit is contained in:
+3
@@ -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收票登记";
|
||||
|
||||
|
||||
+177
-53
@@ -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 收票单查询)。
|
||||
* <p>收票登记「推送金蝶」由 {@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}。</p>
|
||||
*
|
||||
* @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<KingdeeApBillSyncSupport.BfiEntryGroup> 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<BfiReceivedInvoiceSaveEntryDTO> buildEntries(List<KingdeeApBillSyncSupport.BfiEntryGroup> entryGroups) {
|
||||
private List<BfiReceivedInvoiceSaveEntryDTO> buildEntries(InvoiceReceipt receipt, KingdeeInvoicePool mirror) {
|
||||
if (Func.isBlank(mirror.getItemsJson())) {
|
||||
throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]票池镜像无明细行,请先按票号补查票池明细");
|
||||
}
|
||||
List<BfiInvoicePoolItemVO> items = this.parseMirrorItems(receipt.getInvoiceNo(), mirror.getItemsJson());
|
||||
if (Func.isEmpty(items)) {
|
||||
throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]票池镜像明细行为空,请先按票号补查票池明细");
|
||||
}
|
||||
List<BfiReceivedInvoiceSaveEntryDTO> 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<BfiInvoicePoolItemVO> 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 收票指定应付 ====================
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user