Mustangproject生成XRechnung时价格校验失败问题求助
错误信息
当提供Gross price时,Item net price必须等于(Gross price - Allowance amount),错误路径为/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction[1]/ram:IncludedSupplyChainTradeLineItem[1]/ram:SpecifiedLineTradeAgreement[1]/ram:GrossPriceProductTradePrice[1]
当前代码实现
for (jsonMapper.InvoiceLine invoiceLine : invoiceData.invoiceLines) { String description = invoiceLine.itemInformation.itemDescription.getContent(); String name = invoiceLine.itemInformation.itemName.getContent(); String unit = invoiceLine.invoicedQuantityUnitOfMeasureCode.getContent(); BigDecimal vATPercent = new BigDecimal(invoiceLine.lineVatInformation.invoicedItemVatRate.getContent()); Double netPrice = invoiceLine.priceDetails.itemNetPrice.getContent(); Double quantity = invoiceLine.invoicedQuantity.getContent(); invoice.addItem(new Item(new BigDecimal(netPrice), new BigDecimal(quantity), new Product(description, name, unit, vATPercent))).addAllowance(new IZUGFeRDAllowanceCharge() { @Override public BigDecimal getTotalAmount(IAbsoluteValueProvider trans) { return new BigDecimal( invoiceLine.invoiceLineAllowances.invoiceLineAllowanceAmount.getContent()); } @Override public BigDecimal getTaxPercent() { return new BigDecimal(invoiceLine.invoiceLineAllowances.invoiceLineAllowancePercentage.getContent()); } @Override public boolean isCharge() { return false; } @Override public String getReason() { return invoiceLine.invoiceLineAllowances.invoiceLineAllowanceReason.getContent(); } }); }
生成的XML片段
行项目价格部分
<ram:SpecifiedLineTradeAgreement> <ram:GrossPriceProductTradePrice> <ram:ChargeAmount>40.0000</ram:ChargeAmount> <ram:BasisQuantity unitCode="EA">1.0000</ram:BasisQuantity> </ram:GrossPriceProductTradePrice> <ram:NetPriceProductTradePrice> <ram:ChargeAmount>40.0000</ram:ChargeAmount> <ram:BasisQuantity unitCode="EA">1.0000</ram:BasisQuantity> </ram:NetPriceProductTradePrice> </ram:SpecifiedLineTradeAgreement>
结算汇总部分
<ram:SpecifiedTradeSettlementHeaderMonetarySummation> <ram:LineTotalAmount>400.00</ram:LineTotalAmount> <ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount> <ram:AllowanceTotalAmount>50.00</ram:AllowanceTotalAmount> <ram:TaxBasisTotalAmount>350.00</ram:TaxBasisTotalAmount> <ram:TaxTotalAmount currencyID="EUR">71.00</ram:TaxTotalAmount> <ram:GrandTotalAmount>421.00</ram:GrandTotalAmount> <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount> <ram:DuePayableAmount>421.00</ram:DuePayableAmount> </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
问题分析与解决方案
问题根源
从XML可以看出,单条行项目的GrossPrice和NetPrice都是40.00,但结算汇总里总Allowance是50.00(对应10个单位的话,单单位Allowance是5.00),这就导致GrossPrice - AllowanceAmount != NetPrice,违反了XRechnung的强制校验规则。
核心规则说明
XRechnung要求:当同时提供行项目的GrossPrice和Allowance时,必须满足单单位NetPrice = 单单位GrossPrice - 单单位AllowanceAmount。
Mustangproject框架中,Item构造器的第一个参数是单单位GrossPrice,添加Allowance后,框架会自动计算并生成正确的NetPrice。你之前的错误是直接传入了NetPrice作为GrossPrice,导致价格逻辑不匹配。
修正代码
假设JSON中的itemNetPrice是折扣后的净价,先计算单单位的Allowance,再推导GrossPrice,代码调整如下:
for (jsonMapper.InvoiceLine invoiceLine : invoiceData.invoiceLines) { String description = invoiceLine.itemInformation.itemDescription.getContent(); String name = invoiceLine.itemInformation.itemName.getContent(); String unit = invoiceLine.invoicedQuantityUnitOfMeasureCode.getContent(); BigDecimal vATPercent = new BigDecimal(invoiceLine.lineVatInformation.invoicedItemVatRate.getContent()); Double netPriceDouble = invoiceLine.priceDetails.itemNetPrice.getContent(); Double quantityDouble = invoiceLine.invoicedQuantity.getContent(); BigDecimal quantity = new BigDecimal(quantityDouble); // 获取当前行项目的总折扣金额 BigDecimal lineAllowanceTotal = new BigDecimal( invoiceLine.invoiceLineAllowances.invoiceLineAllowanceAmount.getContent()); // 计算单单位折扣金额(保留4位小数,避免精度问题) BigDecimal unitAllowance = lineAllowanceTotal.divide(quantity, 4, RoundingMode.HALF_UP); // 推导单单位GrossPrice:净价 + 单单位折扣 BigDecimal grossPrice = new BigDecimal(netPriceDouble).add(unitAllowance); // 创建Item时传入GrossPrice,框架会自动计算NetPrice invoice.addItem(new Item(grossPrice, quantity, new Product(description, name, unit, vATPercent))).addAllowance(new IZUGFeRDAllowanceCharge() { @Override public BigDecimal getTotalAmount(IAbsoluteValueProvider trans) { return lineAllowanceTotal; } @Override public BigDecimal getTaxPercent() { return new BigDecimal(invoiceLine.invoiceLineAllowances.invoiceLineAllowancePercentage.getContent()); } @Override public boolean isCharge() { return false; } @Override public String getReason() { return invoiceLine.invoiceLineAllowances.invoiceLineAllowanceReason.getContent(); } }); }
验证结果
修正后生成的行项目XML会变为:
<ram:SpecifiedLineTradeAgreement> <ram:GrossPriceProductTradePrice> <ram:ChargeAmount>45.0000</ram:ChargeAmount> <ram:BasisQuantity unitCode="EA">1.0000</ram:BasisQuantity> </ram:GrossPriceProductTradePrice> <ram:NetPriceProductTradePrice> <ram:ChargeAmount>40.0000</ram:ChargeAmount> <ram:BasisQuantity unitCode="EA">1.0000</ram:BasisQuantity> </ram:NetPriceProductTradePrice> </ram:SpecifiedLineTradeAgreement>
此时45.00 - 5.00 = 40.00,完全符合校验规则,同时结算汇总的数值也会保持逻辑一致。
内容的提问来源于stack exchange,提问作者Jonas Stengel

