Merge pull request #639 from marzn/master

Fix invoice calculation if rounding amount is present
This commit is contained in:
Jochen Staerk
2025-01-02 13:48:40 +01:00
committed by GitHub
4 changed files with 345 additions and 11 deletions

View File

@@ -3,11 +3,14 @@ package org.mustangproject.Exceptions;
import java.text.ParseException;
/***
* will be thrown if a invoice cant be reproduced numerically
* will be thrown if an invoice cant be reproduced numerically
*/
public class ArithmetricException extends ParseException {
public ArithmetricException() {
super(
"Could not reproduce the invoice, this could mean that it could not be read properly", 0);
this("");
}
public ArithmetricException(String details) {
super("Could not reproduce the invoice. " + details, 0);
}
}

View File

@@ -32,6 +32,9 @@ import java.nio.file.StandardOpenOption;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ZUGFeRDInvoiceImporter {
@@ -472,9 +475,11 @@ public class ZUGFeRDInvoiceImporter {
xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"DuePayableAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"PayableAmount\"]");
NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
BigDecimal duePayableAmount = null;
if (lineDueNodes.getLength() > 0) {
duePayableAmount = new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0)));
if (zpp instanceof CalculatedInvoice) {
((CalculatedInvoice) zpp).setDuePayable(new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0))));
((CalculatedInvoice) zpp).setDuePayable(duePayableAmount);
}
}
@@ -938,8 +943,7 @@ public class ZUGFeRDInvoiceImporter {
TransactionCalculator tc = new TransactionCalculator(zpp);
String expectedStringTotalGross = tc.getGrandTotal()
.subtract(Objects.requireNonNullElse(zpp.getTotalPrepaidAmount(), BigDecimal.ZERO)).toPlainString();
String calculatedPayableTotal = tc.getDuePayable().toPlainString();
EStandard whichType;
try {
whichType = getStandard();
@@ -947,10 +951,21 @@ public class ZUGFeRDInvoiceImporter {
throw new StructureException("Could not find out if it's an invoice, order, or delivery advice", 0);
}
if ((whichType != EStandard.despatchadvice)
&& ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2)))
&& (!ignoreCalculationErrors))) {
throw new ArithmetricException();
if (whichType != EStandard.despatchadvice && !ignoreCalculationErrors) {
// Check calculation if document type allows it and calculation errors should not be ignored
String payableTotalFromXml = XMLTools.nDigitFormat(Objects.requireNonNullElse(duePayableAmount, expectedGrandTotal), 2);
if (!calculatedPayableTotal.equals(payableTotalFromXml)) {
String moreDetails = "";
try {
moreDetails = " with tax basis " + tc.getTaxBasis() + " and with positions " + tc.getTotal() + " = "
+ Stream.of(tc.trans.getZFItems())
.map(item -> new LineCalculator(item).getItemTotalNetAmount().toPlainString())
.collect(Collectors.joining(" + "));
} catch (Exception ignored) {
}
throw new ArithmetricException("Payable total in XML is " + payableTotalFromXml + ", but calculated total is " + calculatedPayableTotal + moreDetails);
}
}
}
return zpp;
@@ -1068,5 +1083,4 @@ public class ZUGFeRDInvoiceImporter {
LOGGER.error(e.getMessage(), e);
}
}
}

View File

@@ -466,4 +466,12 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
assertFalse(invoice.getZFItems()[0].getNotes() == null);
assertEquals(1, invoice.getZFItems()[0].getNotes().length);
}
@Test
public void testImportXRechnungWithoutCalculationErrors() throws FileNotFoundException, XPathExpressionException, ParseException {
File inputFile = getResourceAsFile("cii/02.03a-INVOICE_uncefact.xml");
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(inputFile));
assertEquals("0", zii.importedInvoice.getDuePayable().toPlainString());
}
}

File diff suppressed because one or more lines are too long