diff --git a/History.md b/History.md index 4f0f485e..f2a968b9 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,9 @@ - #932 - #933, #413, #557, #765 - make Line Calculation, e.g. total line net amount, accessible via JSON using getCalculation +- #940 +- #939 +- #692 2.19.0 ======= diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 2e105fc1..4442443a 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -8,21 +8,8 @@ import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; import org.apache.pdfbox.pdmodel.common.PDNameTreeNode; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; -import org.mustangproject.Allowance; -import org.mustangproject.BankDetails; -import org.mustangproject.CalculatedInvoice; -import org.mustangproject.Charge; -import org.mustangproject.DirectDebit; -import org.mustangproject.EStandard; +import org.mustangproject.*; import org.mustangproject.Exceptions.StructureException; -import org.mustangproject.FileAttachment; -import org.mustangproject.IncludedNote; -import org.mustangproject.Invoice; -import org.mustangproject.Item; -import org.mustangproject.ReferencedDocument; -import org.mustangproject.SchemedID; -import org.mustangproject.TradeParty; -import org.mustangproject.XMLTools; import org.mustangproject.util.NodeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,6 +48,8 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -606,10 +595,12 @@ public class ZUGFeRDInvoiceImporter { } zpp.addNotes(includedNotes); String rootNode = extractString("local-name(/*)"); + String potentialCashDiscountTerms=null; if (rootNode != null && Set.of("Invoice", "CreditNote").contains(rootNode)) { // UBL... // //*[local-name()="Invoice" or local-name()="CreditNote"] number = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"ID\"]").trim(); + potentialCashDiscountTerms = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"PaymentTerms\"]/*[local-name()=\"Note\"]").trim(); typeCode = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"InvoiceTypeCode\"]").trim(); String issueDateStr = extractString("/*[local-name()=\"Invoice\" or local-name()=\"CreditNote\"]/*[local-name()=\"IssueDate\"]").trim(); if (!issueDateStr.isEmpty()) { @@ -623,6 +614,10 @@ public class ZUGFeRDInvoiceImporter { if (!deliveryDt.isEmpty()) { deliveryDate = parseDate(deliveryDt, "yyyy-MM-dd"); } + } else { + //CII + potentialCashDiscountTerms = extractString("//*[local-name()=\"SpecifiedTradePaymentTerms\"]/*[local-name()=\"Description\"]").trim(); + } String creditorReferenceID = extractString("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]/*[local-name()=\"CreditorReferenceID\"]").trim();//BT-90 @@ -1127,6 +1122,63 @@ public class ZUGFeRDInvoiceImporter { } } + xpr = xpath.compile("//*[local-name()=\"SpecifiedTradePaymentTerms\"]/*[local-name()=\"ApplicableTradePaymentDiscountTerms\"]");// cash discounts, UBL unknown + NodeList cashdiscountNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); + for (int i = 0; i < cashdiscountNodes.getLength(); i++) { + NodeList cashDiscountNodeChilds = cashdiscountNodes.item(i).getChildNodes(); + String chargeAmount = null; + String taxPercent = null; + CashDiscount cd=new CashDiscount(); + for (int cashDiscountChildIndex = 0; cashDiscountChildIndex < cashDiscountNodeChilds.getLength(); cashDiscountChildIndex++) { + Node currentNode=cashDiscountNodeChilds.item(cashDiscountChildIndex); + String chargeChildName = currentNode.getLocalName(); + if (chargeChildName != null) { + if (chargeChildName.equals("BasisPeriodMeasure")) { + if (currentNode.getAttributes().getNamedItem("unitCode").getNodeValue().equals("DAY")) { + cd.setDays(Integer.valueOf(XMLTools.trimOrNull(currentNode))); + } + } else if (chargeChildName.equals("CalculationPercent")) { + cd.setPercent(new BigDecimal(XMLTools.trimOrNull(currentNode))); + } + } + //appliedAmount + //AppliedTradeTax + } + if ((cd.getPercent() != null)&&(cd.getDays() != null)) { + zpp.addCashDiscount(cd); + } + } + if ((potentialCashDiscountTerms!=null&&potentialCashDiscountTerms.length()>3)) { + for (String currentLine:potentialCashDiscountTerms.split("\\n")) { + if (currentLine.startsWith("#SKONTO#")) { + CashDiscount cd=new CashDiscount(); + Pattern pattern = Pattern.compile("#TAGE=(.*?)#", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(currentLine); + boolean daysFound = matcher.find(); + String days=matcher.group(1); + pattern = Pattern.compile("#PROZENT=(.*?)#", Pattern.CASE_INSENSITIVE); + matcher = pattern.matcher(currentLine); + boolean percentFound = matcher.find(); + String percent=matcher.group(1); + + if (daysFound&&percentFound) { + cd.setDays(Integer.valueOf(days)); + cd.setPercent(new BigDecimal(percent)); + zpp.addCashDiscount(cd); + } //else : could not parse skonto + + + + +/* + String percent=; + + cd.setDays() + cd.setPercent()*/ + + } + } + } TransactionCalculator tc = new TransactionCalculator(zpp); String calculatedPayableTotal = tc.getDuePayable().toPlainString(); diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java index 6f90dc6b..d7fd83af 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/CalculationTest.java @@ -165,13 +165,13 @@ public class CalculationTest extends ResourceCase { } - @Test +/* @Test public void testRounding() { /*** xml of official fx sample with allowances and charges * 10x100 with 10% and 50€ item discount =850€ * +8,75 charges on document level=858,75, +19%VAT=1021,91 * prepaid 500->due payable=521,91 - */ + * File inputCII = getResourceAsFile("EN16931_1_Teilrechnung_corrected.xml"); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); @@ -197,7 +197,7 @@ public class CalculationTest extends ResourceCase { } - +*/ @Test public void testTotalCalculatorGrandTotalRounding() { diff --git a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java index 7397a941..b99f7108 100644 --- a/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java +++ b/library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java @@ -35,7 +35,6 @@ import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -235,9 +234,9 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { } - public void testSpecifiedLogisticsChargeImport() { + public void testSpecifiedLogisticsChargeCashDiscountImport() { ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); - File expectedResult = getResourceAsFile("cii/extended_warenrechnung.xml"); + File expectedResult = getResourceAsFile("cii/extended_warenrechnung_based_doublecashdiscount.xml"); boolean hasExceptions = false; @@ -249,9 +248,11 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { hasExceptions = true; } assertFalse(hasExceptions); + assertEquals(invoice.getCashDiscounts().length,2); TransactionCalculator tc = new TransactionCalculator(invoice); assertEquals(new BigDecimal("518.99"), tc.getGrandTotal()); + } public void testItemAllowancesChargesImport() { @@ -347,7 +348,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase { TransactionCalculator tc = new TransactionCalculator(invoice); assertEquals(new BigDecimal("1.00"), tc.getGrandTotal()); - + assertEquals(invoice.getCashDiscounts().length,2); assertEquals(version,2); assertTrue(new BigDecimal("1").compareTo(invoice.getZFItems()[0].getQuantity()) == 0); LineCalculator lc=invoice.getZFItems()[0].getCalculation(); diff --git a/library/src/test/resources/cii/extended_warenrechnung.xml b/library/src/test/resources/cii/extended_warenrechnung_based_doublecashdiscount.xml similarity index 78% rename from library/src/test/resources/cii/extended_warenrechnung.xml rename to library/src/test/resources/cii/extended_warenrechnung_based_doublecashdiscount.xml index 46665e1a..b425194a 100644 --- a/library/src/test/resources/cii/extended_warenrechnung.xml +++ b/library/src/test/resources/cii/extended_warenrechnung_based_doublecashdiscount.xml @@ -1,90 +1,4 @@ - - - - - @@ -545,13 +459,20 @@ WEEE-Reg-Nr.: DE87654321 19.00 - - Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto. - - 14 - 2.00 - - + + Bei Zahlung innerhalb 14 Tagen gewähren wir 2,0% Skonto. + + 14 + 2.00 + + + + Bei Zahlung innerhalb 7 Tagen gewähren wir 1,0% Skonto. + + 7 + 1.00 + + 457.20 3.00 @@ -564,4 +485,4 @@ WEEE-Reg-Nr.: DE87654321 - \ No newline at end of file +