feat: 金蝶收票单A303-2分录不送税收分类编码/税率/税额并去物料编码尾部0

This commit is contained in:
刘泉佳
2026-11-02 14:52:57 +08:00
parent f6dcca9d6b
commit d0043cd21c
@@ -261,8 +261,8 @@ public class KingdeeReceivedInvoiceSyncSupport {
/**
* 分录组装:逐行取发票明细行(镜像 items_json 反序列化为 {@link BfiInvoicePoolItemVO}),一条明细行一条分录:
* 价税合计=明细金额+税额(合计为 0 拦截);数量取 num(≤0/空/不可解析兜底 1);单价取 unitPrice(空按价税合计/数量反算);
* 物料编码与税收分类编码取行商品编码 goodsCode(缺失拦截);税率表编码="V"+整数税率(13/13%/0.13→V13,
* 缺失、0/免税行拦截提示人工);不含税金额/税额取明细金额/税额(空不送);分录标识=发票号码_seq(重推幂等候选键)
* 物料编码取行商品编码 goodsCode(缺失拦截,去尾部 0);不含税金额取明细金额(空不送);
* 税收分类编码/税率表编码/税额不送;分录标识=发票号码_seq(重推幂等候选键)
*
* @param receipt 收票登记(发票号码,分录标识前缀)
* @param mirror 票池镜像(明细行 items_json)
@@ -291,7 +291,7 @@ public class KingdeeReceivedInvoiceSyncSupport {
throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]分录[" + seq + "]价税合计为 0,无法推送");
}
if (Func.isBlank(item.getGoodsCode())) {
throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]分录[" + seq + "]缺少行商品编码,无法确定物料与税收分类编码");
throw new ServiceException("推送金蝶收票单失败:发票[" + receipt.getInvoiceNo() + "]分录[" + seq + "]缺少行商品编码,无法确定物料编码");
}
BfiReceivedInvoiceSaveEntryDTO entry = new BfiReceivedInvoiceSaveEntryDTO();
entry.setEPricetaxtotal(priceTaxTotal);
@@ -299,19 +299,13 @@ public class KingdeeReceivedInvoiceSyncSupport {
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()));
// 金额(不含税)/税额:明细金额/税额(空不送)
// 【必填】物料编码.编码:行商品编码(金蝶档案不含末尾补零,去掉尾部 0)
String goodsCode = this.stripTrailingZeros(item.getGoodsCode().trim());
entry.setMaterialNumber(goodsCode);
// 金额(不含税):明细金额(空不送);税收分类编码/税率表编码/税额不送,税额仅参与价税合计
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);
@@ -319,6 +313,12 @@ public class KingdeeReceivedInvoiceSyncSupport {
return entries;
}
/** 商品编码去尾部 0:金蝶物料编码档案不含末尾补零(如 3010102020100000000→30101020201);全 0 兜底保留原值 */
private String stripTrailingZeros(String code) {
String stripped = code.replaceAll("0+$", "");
return stripped.isEmpty() ? code : stripped;
}
/** 镜像明细行反序列化:items_json 解析异常按脏数据拦截(A801-1 原样落库,理论上可解析) */
private List<BfiInvoicePoolItemVO> parseMirrorItems(String invoiceNo, String itemsJson) {
try {
@@ -347,27 +347,6 @@ public class KingdeeReceivedInvoiceSyncSupport {
return result.setScale(4, 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)) {