Fix invoice calculation if rounding amount is present
This commit is contained in:
@@ -3,11 +3,14 @@ package org.mustangproject.Exceptions;
|
|||||||
import java.text.ParseException;
|
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 class ArithmetricException extends ParseException {
|
||||||
public ArithmetricException() {
|
public ArithmetricException() {
|
||||||
super(
|
this("");
|
||||||
"Could not reproduce the invoice, this could mean that it could not be read properly", 0);
|
}
|
||||||
|
|
||||||
|
public ArithmetricException(String details) {
|
||||||
|
super("Could not reproduce the invoice. " + details, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ import java.nio.file.StandardOpenOption;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
|
||||||
public class ZUGFeRDInvoiceImporter {
|
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\"]");
|
xpr = xpath.compile("//*[local-name()=\"SpecifiedTradeSettlementHeaderMonetarySummation\"]/*[local-name()=\"DuePayableAmount\"]|//*[local-name()=\"LegalMonetaryTotal\"]/*[local-name()=\"PayableAmount\"]");
|
||||||
NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList lineDueNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
|
BigDecimal duePayableAmount = null;
|
||||||
if (lineDueNodes.getLength() > 0) {
|
if (lineDueNodes.getLength() > 0) {
|
||||||
|
duePayableAmount = new BigDecimal(XMLTools.trimOrNull(lineDueNodes.item(0)));
|
||||||
if (zpp instanceof CalculatedInvoice) {
|
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);
|
TransactionCalculator tc = new TransactionCalculator(zpp);
|
||||||
String expectedStringTotalGross = tc.getGrandTotal()
|
String calculatedPayableTotal = tc.getDuePayable().toPlainString();
|
||||||
.subtract(Objects.requireNonNullElse(zpp.getTotalPrepaidAmount(), BigDecimal.ZERO)).toPlainString();
|
|
||||||
EStandard whichType;
|
EStandard whichType;
|
||||||
try {
|
try {
|
||||||
whichType = getStandard();
|
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);
|
throw new StructureException("Could not find out if it's an invoice, order, or delivery advice", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((whichType != EStandard.despatchadvice)
|
if (whichType != EStandard.despatchadvice && !ignoreCalculationErrors) {
|
||||||
&& ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2)))
|
// Check calculation if document type allows it and calculation errors should not be ignored
|
||||||
&& (!ignoreCalculationErrors))) {
|
|
||||||
throw new ArithmetricException();
|
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;
|
return zpp;
|
||||||
@@ -1068,5 +1083,4 @@ public class ZUGFeRDInvoiceImporter {
|
|||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,4 +466,12 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
|
|||||||
assertFalse(invoice.getZFItems()[0].getNotes() == null);
|
assertFalse(invoice.getZFItems()[0].getNotes() == null);
|
||||||
assertEquals(1, invoice.getZFItems()[0].getNotes().length);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
309
library/src/test/resources/cii/02.03a-INVOICE_uncefact.xml
Normal file
309
library/src/test/resources/cii/02.03a-INVOICE_uncefact.xml
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user