diff --git a/History.md b/History.md index 4d85d62b..b8be1ce9 100644 --- a/History.md +++ b/History.md @@ -3,8 +3,16 @@ 2024- - 435 use invoiceimporter as common technical basis also for zugferdimporter - also import delivery address -- 527 +- 527 metrics raises errors +- 517 read product GlobalID +- 380 Added test for input stream validation +- 518 corrently validate more XRechnung versions - make document charges and allowances serializable +- 523 +- 530 +- 532 support validation warnings! +- 534 new signature +- 2.14.2 diff --git a/library/src/main/java/org/mustangproject/XMLTools.java b/library/src/main/java/org/mustangproject/XMLTools.java index ad3bf4c2..dbccd4b0 100644 --- a/library/src/main/java/org/mustangproject/XMLTools.java +++ b/library/src/main/java/org/mustangproject/XMLTools.java @@ -214,4 +214,16 @@ public class XMLTools extends XMLWriter { return IOUtils.toByteArray (fileinput); } + + public static String trimOrNull(Node node) { + if (node != null) { + String textContent = node.getTextContent(); + if (textContent != null) { + return textContent.trim(); + } + } + return null; + } + + } diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java index 72718b58..03ed3099 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java @@ -334,7 +334,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { this.trans = trans; this.calc = new TransactionCalculator(trans); - boolean hasDueDate = false; + boolean hasDueDate = trans.getDueDate()!=null; final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); String exemptionReason = ""; @@ -818,7 +818,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } } - if ((trans.getPaymentTerms() == null) && (getProfile() != Profiles.getByName("Minimum")) && ((paymentTermsDescription != null) || (trans.getTradeSettlement() != null) || (hasDueDate))) { xml += ""; @@ -834,7 +833,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider { } } - if (hasDueDate && (trans.getDueDate() != null)) { + if (trans.getDueDate() != null) { xml += "" // $NON-NLS-2$ + DATE.udtFormat(trans.getDueDate()) + "";// 20130704 diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java index cad7e624..e9c5f712 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java @@ -14,55 +14,19 @@ package org.mustangproject.ZUGFeRD; * @author jstaerk */ import java.io.*; -import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.apache.commons.io.IOUtils; -import org.apache.fop.util.XMLUtil; - -import org.apache.pdfbox.Loader; -import org.apache.pdfbox.io.IOUtils; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; -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.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.xpath.*; -import java.io.*; -import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.text.SimpleDateFormat; -import java.util.*; public class ZUGFeRDImporter extends ZUGFeRDInvoiceImporter { private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDImporter.class); @@ -753,7 +717,7 @@ public class ZUGFeRDImporter extends ZUGFeRDInvoiceImporter { SchemedID globalId = new SchemedID() .setScheme(node.getAttributes() .getNamedItem("schemeID").getNodeValue()) - .setId(getNodeValue(node)); + .setId(XMLTools.getNodeValue(node)); lineItem.getProduct().addGlobalID(globalId); } node = getNodeByName(nn.getChildNodes(), "SellerAssignedID"); diff --git a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java index 85f289d0..901f1b96 100644 --- a/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java +++ b/library/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDInvoiceImporter.java @@ -79,24 +79,6 @@ public class ZUGFeRDInvoiceImporter { setInputStream(pdfStream); } - private static String trimOrNull(Node node) { - if (node != null) { - String textContent = node.getTextContent(); - if (textContent != null) { - return textContent.trim(); - } - } - return null; - } - - private static String convertStreamToString(java.io.InputStream is) { - try { - return IOUtils.toString(is, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - public void setPDFFilename(String pdfFilename) { try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) { extractLowLevel(bis); @@ -151,7 +133,8 @@ public class ZUGFeRDInvoiceImporter { } final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata(); - xmpString = convertStreamToString(XMP); + + xmpString = new String(XMLTools.getBytesFromStream(XMP), StandardCharsets.UTF_8); final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles(); if (etn == null) { @@ -330,7 +313,7 @@ public class ZUGFeRDInvoiceImporter { BigDecimal expectedGrandTotal = null; NodeList totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); if (totalNodes.getLength() > 0) { - expectedGrandTotal = new BigDecimal(trimOrNull(totalNodes.item(0))); + expectedGrandTotal = new BigDecimal(XMLTools.trimOrNull(totalNodes.item(0))); if (zpp instanceof CalculatedInvoice) { // usually we would re-calculate the invoice to get expectedGrandTotal // however, for "minimal" invoices or other invoices without lines @@ -342,7 +325,7 @@ public class ZUGFeRDInvoiceImporter { xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]"); NodeList prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); if (prepaidNodes.getLength() > 0) { - zpp.setTotalPrepaidAmount(new BigDecimal(trimOrNull(prepaidNodes.item(0)))); + zpp.setTotalPrepaidAmount(new BigDecimal(XMLTools.trimOrNull(prepaidNodes.item(0)))); } Date issueDate = null; @@ -355,17 +338,17 @@ public class ZUGFeRDInvoiceImporter { for (int documentChildIndex = 0; documentChildIndex < exchangedDocumentChilds.getLength(); documentChildIndex++) { Node item = exchangedDocumentChilds.item(documentChildIndex); if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) { - number = trimOrNull(item); + number = XMLTools.trimOrNull(item); } if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) { - typeCode = trimOrNull(item); + typeCode = XMLTools.trimOrNull(item); } if ((item.getLocalName() != null) && (item.getLocalName().equals("IssueDateTime"))) { NodeList issueDateTimeChilds = item.getChildNodes(); for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) { if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null) && (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) { - issueDate = new SimpleDateFormat("yyyyMMdd").parse(trimOrNull(issueDateTimeChilds.item(issueDateChildIndex))); + issueDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(issueDateTimeChilds.item(issueDateChildIndex))); } } } @@ -375,6 +358,7 @@ public class ZUGFeRDInvoiceImporter { if (rootNode.equals("Invoice")) { // UBL... number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim(); + typeCode = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"InvoiceTypeCode\"]").trim(); issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim()); String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim(); if (dueDt.length() > 0) { @@ -402,7 +386,7 @@ public class ZUGFeRDInvoiceImporter { for (int occurenceChildIndex = 0; occurenceChildIndex < occurenceChilds.getLength(); occurenceChildIndex++) { if ((occurenceChilds.item(occurenceChildIndex).getLocalName() != null) && (occurenceChilds.item(occurenceChildIndex).getLocalName().equals("DateTimeString"))) { - deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(trimOrNull(occurenceChilds.item(occurenceChildIndex))); + deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(occurenceChilds.item(occurenceChildIndex))); } } } @@ -414,7 +398,7 @@ public class ZUGFeRDInvoiceImporter { for (int despatchAdviceChildIndex = 0; despatchAdviceChildIndex < despatchAdviceChilds.getLength(); despatchAdviceChildIndex++) { if (despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName() != null && despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName().equals("IssuerAssignedID")) { - despatchAdviceReferencedDocument = trimOrNull(despatchAdviceChilds.item(despatchAdviceChildIndex)); + despatchAdviceReferencedDocument = XMLTools.trimOrNull(despatchAdviceChilds.item(despatchAdviceChildIndex)); } } } @@ -427,7 +411,7 @@ public class ZUGFeRDInvoiceImporter { String buyerOrderIssuerAssignedID = null; String sellerOrderIssuerAssignedID = null; for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) { - // trimOrNull(nodes.item(i)))) { + // XMLTools.trimOrNull(nodes.item(i)))) { Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i); NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes(); for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) { @@ -437,7 +421,7 @@ public class ZUGFeRDInvoiceImporter { for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) { if ((buyerOrderChilds.item(buyerOrderChildIndex).getLocalName() != null) && (buyerOrderChilds.item(buyerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { - buyerOrderIssuerAssignedID = trimOrNull(buyerOrderChilds.item(buyerOrderChildIndex)); + buyerOrderIssuerAssignedID = XMLTools.trimOrNull(buyerOrderChilds.item(buyerOrderChildIndex)); } } } @@ -447,7 +431,7 @@ public class ZUGFeRDInvoiceImporter { for (int sellerOrderChildIndex = 0; sellerOrderChildIndex < sellerOrderChilds.getLength(); sellerOrderChildIndex++) { if ((sellerOrderChilds.item(sellerOrderChildIndex).getLocalName() != null) && (sellerOrderChilds.item(sellerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { - sellerOrderIssuerAssignedID = trimOrNull(sellerOrderChilds.item(sellerOrderChildIndex)); + sellerOrderIssuerAssignedID = XMLTools.trimOrNull(sellerOrderChilds.item(sellerOrderChildIndex)); } } } @@ -466,7 +450,7 @@ public class ZUGFeRDInvoiceImporter { String IBAN = null, BIC = null; for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) { - // trimOrNull(nodes.item(i)))) { + // XMLTools.trimOrNull(nodes.item(i)))) { Node headerTradeSettlementNode = headerTradeSettlementNodes.item(i); NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes(); @@ -479,7 +463,7 @@ public class ZUGFeRDInvoiceImporter { NodeList dueDateChilds = paymentTermChilds.item(paymentTermChildIndex).getChildNodes(); for (int dueDateChildIndex = 0; dueDateChildIndex < dueDateChilds.getLength(); dueDateChildIndex++) { if ((dueDateChilds.item(dueDateChildIndex).getLocalName() != null) && (dueDateChilds.item(dueDateChildIndex).getLocalName().equals("DateTimeString"))) { - dueDate = new SimpleDateFormat("yyyyMMdd").parse(trimOrNull(dueDateChilds.item(dueDateChildIndex))); + dueDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(dueDateChilds.item(dueDateChildIndex))); } } } @@ -500,7 +484,7 @@ public class ZUGFeRDInvoiceImporter { NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII - IBAN = trimOrNull(accountChilds.item(accountChildIndex)); + IBAN = XMLTools.trimOrNull(accountChilds.item(accountChildIndex)); } } } @@ -508,7 +492,7 @@ public class ZUGFeRDInvoiceImporter { NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII - BIC = trimOrNull(accountChilds.item(accountChildIndex)); + BIC = XMLTools.trimOrNull(accountChilds.item(accountChildIndex)); } } } @@ -531,7 +515,7 @@ public class ZUGFeRDInvoiceImporter { NodeList startPeriodChilds = periodChilds.item(periodChildIndex).getChildNodes(); for (int startPeriodIndex = 0; startPeriodIndex < startPeriodChilds.getLength(); startPeriodIndex++) { if ((startPeriodChilds.item(startPeriodIndex).getLocalName() != null) && (startPeriodChilds.item(startPeriodIndex).getLocalName().equals("DateTimeString"))) {//CII - deliveryPeriodStart = trimOrNull(startPeriodChilds.item(startPeriodIndex)); + deliveryPeriodStart = XMLTools.trimOrNull(startPeriodChilds.item(startPeriodIndex)); } } } @@ -539,7 +523,7 @@ public class ZUGFeRDInvoiceImporter { NodeList endPeriodChilds = periodChilds.item(periodChildIndex).getChildNodes(); for (int endPeriodIndex = 0; endPeriodIndex < endPeriodChilds.getLength(); endPeriodIndex++) { if ((endPeriodChilds.item(endPeriodIndex).getLocalName() != null) && (endPeriodChilds.item(endPeriodIndex).getLocalName().equals("DateTimeString"))) {//CII - deliveryPeriodEnd = trimOrNull(endPeriodChilds.item(endPeriodIndex)); + deliveryPeriodEnd = XMLTools.trimOrNull(endPeriodChilds.item(endPeriodIndex)); } } } @@ -558,7 +542,7 @@ public class ZUGFeRDInvoiceImporter { NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); for (int i = 0; i < paymentMeansNodes.getLength(); i++) { - // trimOrNull(nodes.item(i)))) { + // XMLTools.trimOrNull(nodes.item(i)))) { Node paymentMeansNode = paymentMeansNodes.item(i); NodeList paymentMeansChilds = paymentMeansNode.getChildNodes(); for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds.getLength(); meansChildIndex++) { @@ -567,7 +551,7 @@ public class ZUGFeRDInvoiceImporter { NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes(); for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("ID"))) { - IBAN = trimOrNull(paymentTermChilds.item(paymentTermChildIndex)); + IBAN = XMLTools.trimOrNull(paymentTermChilds.item(paymentTermChildIndex)); if (IBAN != null) { BankDetails bd = new BankDetails(IBAN); bankDetails.add(bd); @@ -607,7 +591,7 @@ public class ZUGFeRDInvoiceImporter { String buyerReference = null; prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); if (prepaidNodes.getLength() > 0) { - buyerReference = trimOrNull(prepaidNodes.item(0)); + buyerReference = XMLTools.trimOrNull(prepaidNodes.item(0)); } if (buyerReference != null) { zpp.setReferenceNumber(buyerReference); @@ -629,7 +613,7 @@ public class ZUGFeRDInvoiceImporter { xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]"); NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); for (int i = 0; i < attachmentNodes.getLength(); i++) { - FileAttachment fa = new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(), attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(), "Data", Base64.getDecoder().decode(trimOrNull(attachmentNodes.item(i)))); + FileAttachment fa = new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(), attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(), "Data", Base64.getDecoder().decode(XMLTools.trimOrNull(attachmentNodes.item(i)))); fileAttachments.add(fa); // filename = "Aufmass.png" mimeCode = "image/png" //EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png" @@ -657,21 +641,21 @@ public class ZUGFeRDInvoiceImporter { for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) { if ((indicatorChilds.item(indicatorChildIndex).getLocalName() != null) && (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator"))) { - isCharge = trimOrNull(indicatorChilds.item(indicatorChildIndex)).equalsIgnoreCase("true"); + isCharge = XMLTools.trimOrNull(indicatorChilds.item(indicatorChildIndex)).equalsIgnoreCase("true"); } } } else if (chargeChildName.equals("ActualAmount")) { - chargeAmount = trimOrNull(chargeNodeChilds.item(chargeChildIndex)); + chargeAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex)); } else if (chargeChildName.equals("Reason")) { - reason = trimOrNull(chargeNodeChilds.item(chargeChildIndex)); + reason = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex)); } else if (chargeChildName.equals("ReasonCode")) { - reasonCode = trimOrNull(chargeNodeChilds.item(chargeChildIndex)); + reasonCode = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex)); } else if (chargeChildName.equals("CategoryTradeTax")) { NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { String taxItemName = taxChilds.item(taxChildIndex).getLocalName(); if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent") || taxItemName.equals("ApplicablePercent"))) { - taxPercent = trimOrNull(taxChilds.item(taxChildIndex)); + taxPercent = XMLTools.trimOrNull(taxChilds.item(taxChildIndex)); } } } @@ -721,7 +705,7 @@ public class ZUGFeRDInvoiceImporter { && ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) && (!ignoreCalculationErrors))) { throw new ParseException( - "Could not reproduce the invoice, this could mean that it could not be read properly exp "+expectedStringTotalGross+" is "+XMLTools.nDigitFormat(expectedGrandTotal, 2), 0); + "Could not reproduce the invoice, this could mean that it could not be read properly", 0); } } return zpp; diff --git a/library/src/main/resources/stylesheets/xr-pdf.xsl b/library/src/main/resources/stylesheets/xr-pdf.xsl index 153dba41..406e4044 100644 --- a/library/src/main/resources/stylesheets/xr-pdf.xsl +++ b/library/src/main/resources/stylesheets/xr-pdf.xsl @@ -64,7 +64,7 @@ - + - Seller trading name - - - Seller contact point - - +41 345 654455 - - - seller@contact.de - - - - 12345 - Seller address line 1 - Seller address line 2 - Seller address line 3 - Seller city - DE - Seller country subdivision - - - Seller electronic address - - - DE12345677 - - - DE49294093 - - - - Buyer identifier - Buyer name - - Buyer legal registration identifier - Buyer trading name - - - Buyer contact point - - +353 2948584 - - - buyer@contact.ie - - - - 34562 - Buyer address line 1 - Buyer address line 2 - Buyer address line 3 - Buyer city - IE - Buyer country subdivision - - - Buyer electronic address - - - IE394838894 - - - - Tax representative name - - 23455 - Tax representative address line 1 - Tax representative address line 2 - Tax representative address line 3 - Tax representative city - DE - Tax representative country subdivision - - - DE3949053 - - - - def - - - abc - - - 789 - - - Supporting document ref - External document location - 916 - Supporting document descr - ZGVmYXVsdA== - - - rst - 130 - AAA - - - 456 - Project reference - - - - - deliver location identifier - Deliver to party name - - 98765 - Deliver to address line 1 - Deliver to address line 2 - Deliver to address line 3 - Deliver to city - IE - Deliver to country subdivision - - - - - 20181204 - - - - lmn - - - ghi - - - - Bank assigned creditor identifier - Remittance information - NOK - EUR - - Payee identifier - Payee name - - Payee legal registration identifier - - - - 4 - SEPA - - 1234 - Payment card holder name - - - Debited account identifier - - - IT1212341234123412 - Payment account name - - - - - - - 50.00 - VAT - 1000.00 - S - - 5.00 - - - 0.00 - VAT - Exemtion reason text - 1000.00 - E - VATEX-EU-O - 29 - 0.00 - - - - 20181112 - - - 20181130 - - - - - false - - 1.00 - 1000.00 - 10.00 - 95 - Doc allowance reason text - - VAT - S - 5.00 - - - - - true - - 1.00 - 1000.00 - 10.00 - AAA - Doc charge reason text - - VAT - S - 5.00 - - - - total amount - - 20181130 - - Mandate reference identifier - - - 2000.00 - 10.00 - 10.00 - 2000.00 - 50.00 - 46.00 - 0.00 - 2050.00 - 0.00 - 2050.00 - - - abc123 - - 20181004 - - - - uvz - - - - \ No newline at end of file + + + invoice note text + AAA + + + invoice note text 2 + AAA + + + + + + 1a + + Invoice line note + + + + Item standar identifier + + + Item seller's identifier + Item buyer's identifier + Item name + Item description + + Color + Red + + + Size + L + + + Item classification identifier0 + + + IT + + + + + 12345 + + + 11.00 + 1.00 + + + false + + 1.00 + + + + 10.00 + 1.00 + + + + 10.00 + + + + VAT + S + 5.00 + + + + 20181112 + + + 20181130 + + + + + false + + 1.00 + 100.00 + 10.00 + 95 + Invoice line allowance reason + + + + true + + 1.00 + 100.00 + 10.00 + AAA + Invoice line charge reason + + + 100.00 + + + Line object identifier + 130 + + + 6789 + + + + + + 1b + + + Item name 2 + + + + 10.00 + + + + 10.00 + + + + VAT + E + 0.00 + + + 100.00 + + + + + 123 + + Seller identifier 1 + Seller identifier 2 + Seller name + Seller additional legal information + + + Seller trading name + + + Seller contact point + + +41 345 654455 + + + seller@contact.de + + + + 12345 + Seller address line 1 + Seller address line 2 + Seller address line 3 + Seller city + DE + Seller country subdivision + + + Seller electronic address + + + DE12345677 + + + DE49294093 + + + + Buyer identifier + Buyer name + + Buyer legal registration identifier + Buyer trading name + + + Buyer contact point + + +353 2948584 + + + buyer@contact.ie + + + + 34562 + Buyer address line 1 + Buyer address line 2 + Buyer address line 3 + Buyer city + IE + Buyer country subdivision + + + Buyer electronic address + + + IE394838894 + + + + Tax representative name + + 23455 + Tax representative address line 1 + Tax representative address line 2 + Tax representative address line 3 + Tax representative city + DE + Tax representative country subdivision + + + DE3949053 + + + + def + + + abc + + + 789 + + + Supporting document ref + External document location + 916 + Supporting document descr + ZGVmYXVsdA== + + + rst + 130 + AAA + + + 456 + Project reference + + + + + deliver location identifier + Deliver to party name + + 98765 + Deliver to address line 1 + Deliver to address line 2 + Deliver to address line 3 + Deliver to city + IE + Deliver to country subdivision + + + + + 20181204 + + + + lmn + + + ghi + + + + Bank assigned creditor identifier + Remittance information + NOK + EUR + + Payee identifier + Payee name + + Payee legal registration identifier + + + + 4 + SEPA + + 1234 + Payment card holder name + + + Debited account identifier + + + IT1212341234123412 + Payment account name + + + + + + + 5.00 + VAT + 100.00 + S + + 5.00 + + + 0.00 + VAT + Exemtion reason text + 100.00 + E + VATEX-EU-O + 29 + 0.00 + + + + 20181112 + + + 20181130 + + + + + false + + 1.00 + 100.00 + 10.00 + 95 + Doc allowance reason text + + VAT + S + 5.00 + + + + + true + + 1.00 + 100.00 + 10.00 + AAA + Doc charge reason text + + VAT + S + 5.00 + + + + total amount + + 20181130 + + Mandate reference identifier + + + 200.00 + 10.00 + 10.00 + 200.00 + 5.00 + 4.60 + 0.00 + 205.00 + 0.00 + 205.00 + + + abc123 + + 20181004 + + + + uvz + + + + diff --git a/library/src/test/resources/not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.ubl.xml b/library/src/test/resources/not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.ubl.xml index a314773a..6aec88d9 100644 --- a/library/src/test/resources/not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.ubl.xml +++ b/library/src/test/resources/not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.ubl.xml @@ -1,404 +1,365 @@ - - urn:cen.eu:en16931:2017 - BT-23 Business Process Type - Test_EeISI_100 - 2018-11-12 - 2018-11-30 - 380 - ##AAA##invoice note text - ##AAA##invoice note text 2 - EUR - NOK - uvz - 123 - - 2018-11-12 - 2018-11-30 - 35 - - - abc - def - - - - abc123 - 2018-10-04 - - - - lmn - - - ghi - - - opq - - - 789 - - - rst - 130 - - - Supporting document ref - Supporting document descr - - ZGVmYXVsdA== - - External document location - - - - - 456 - - - - Seller electronic address + + urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0 + urn:fdc:peppol.eu:2017:poacc:billing:01:1.0 + Test_EeISI_100 + 2018-11-12 + 2018-11-30 + 380 + #AAA#invoice note text + #AAA#invoice note text 2 + EUR + NOK + uvz + 123 + + 2018-11-12 + 2018-11-30 + + + abc + def + + + + abc123 + 2018-10-04 + + + + lmn + + + ghi + + + 789 + + + Supporting document ref + Supporting document descr + + ZGVmYXVsdA== + + External document location + + + + + rst + 130 + + + 456 + + + + Seller electronic address + + Seller identifier 1 + + + Seller identifier 2 + + + Seller trading name + + + Seller address line 1 + Seller address line 2 + Seller city + 12345 + Seller country subdivision + + Seller address line 3 + + + DE + + + + DE12345677 + + VAT + + + + DE49294093 + + FC + + + + Seller name + Seller additional legal information + + + Seller contact point + +41 345 654455 + seller@contact.de + + + + + + Buyer electronic address + + Buyer identifier + + + Buyer trading name + + + Buyer address line 1 + Buyer address line 2 + Buyer city + 34562 + Buyer country subdivision + + Buyer address line 3 + + + IE + + + + IE394838894 + + VAT + + + + Buyer name + Buyer legal registration identifier + + + Buyer contact point + +353 2948584 + buyer@contact.ie + + + + - Seller identifier 1 - - - Seller identifier 2 - - - Bank assigned creditor identifier + Payee identifier - Seller trading name + Payee name + + + + + Tax representative name - Seller address line 1 - Seller address line 2 - Seller city - 12345 - Seller country subdivision + Tax representative address line 1 + Tax representative address line 2 + Tax representative city + 23455 + Tax representative country subdivision - Seller address line 3 + Tax representative address line 3 DE - DE12345677 + DE3949053 VAT - - DE49294093 - - NOVAT - - - - Seller name - Seller legal identifier - Seller additional legal information - - - Seller contact point - +41 345 654455 - seller@contact.de - - - - - - Buyer electronic address - - 0190:Buyer identifier - - - Buyer trading name - - - Buyer address line 1 - Buyer address line 2 - Buyer city - 34562 - Buyer country subdivision - - Buyer address line 3 - - - IE - - - - IE394838894 - - VAT - - - - Buyer name - Buyer legal registration identifier - - - Buyer contact point - +353 2948584 - buyer@contact.ie - - - - - - Payee identifier - - - Payee name - - - Payee legal registration identifier - - - - - Tax representative name - - - Tax representative address line 1 - Tax representative address line 2 - Tax representative city - 23455 - Tax representative country subdivision - - Tax representative address line 3 - - - DE - - - - DE3949053 - - VAT - - - - - 2018-12-04 - - deliver location identifier - - Deliver to address line 1 - Deliver to address line 2 - Deliver to city - 98765 - Deliver to country subdivision - - Deliver to address line 3 - - - IE - - - - - - Deliver to party name - - - - - 4 - Remittance information - - 1234 - mandatory network id - Payment card holder name - - - IT1212341234123412 - Payment account name - - BSCTCH22 - - - - IT1212341234123413 - Payment account name 2 - - BSCTCH22 - - - - Mandate reference identifier - - Debited account identifier - - - - - total amount - - - false - 55 - Doc allowance reason text - 1.0000 - 10.00 - 1000.00 - - S - 5.00 - - VAT - - - - - true - AAA - Doc charge reason text - 1.0000 - 10.00 - 1000.00 - - S - 5.00 - - VAT - - - - - 46.00 - - - 50.00 - - 1000.00 - 50.00 - - S - 5.00 - - VAT - - - - - 1000.00 - 0.00 - - E - 0.00 - Exemption reason code - Exemtion reason text - - VAT - - - - - - 2000.00 - 2000.00 - 2050.00 - 10.00 - 10.00 - 2050.00 - - - 1a - Invoice line note - 10.00000000 - 1000.00 - 6789 - - 2018-11-12 - 2018-11-30 - - - 12345 - - - Line object identifier - 130 - + + + 2018-12-04 + + deliver location identifier + + Deliver to address line 1 + Deliver to address line 2 + Deliver to city + 98765 + Deliver to country subdivision + + Deliver to address line 3 + + + IE + + + + + + Deliver to party name + + + + + total amount + false - 55 - Invoice line allowance reason - 1 + 95 + Doc allowance reason text + 1.00 10 1000 + + S + 5 + + VAT + + true AAA - Invoice line charge reason - 1 + Doc charge reason text + 1.00 10 1000 - - - Item description - Item name - - Item buyer's identifier - - - Item seller's identifier - - - Item standar identifier - - - IT - - - Item classification identifier0 - - + S - 5.00 + 5 VAT - - - Color - Red - - - Size - L - - - - 10.00 - 1.00 + + + + 50 + + 1000 + 50 + + S + 5 + + VAT + + + + + 1000 + 0 + + E + 0 + VATEX-EU-O + Exemtion reason text + + VAT + + + + + + 46 + + + 200 + 200 + 205 + 10 + 10 + 0 + 205 + + + 1a + Invoice line note + 10 + 1000 + 6789 + + 2018-11-12 + 2018-11-30 + + + 12345 + + + Line object identifier + 130 + false - 1 - 11 + 95 + Invoice line allowance reason + 1.00 + 10 + 1000 - - - - 1b - 10.00000000 - 1000.00 - - Item name 2 - - E - 0.00 - - VAT - - - - - 10.00 - - - + + true + AAA + Invoice line charge reason + 1.00 + 10 + 1000 + + + Item description + Item name + + Item buyer's identifier + + + Item seller's identifier + + + Item standar identifier + + + IT + + + Item classification identifier0 + + + S + 5 + + VAT + + + + Color + Red + + + Size + L + + + + 10 + 1 + + false + 1 + 11 + + + + + 1b + 10 + 1000 + + Item name 2 + + E + 0 + + VAT + + + + + 10 + + + diff --git a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java index 7c988425..c42caa9e 100644 --- a/validator/src/main/java/org/mustangproject/validator/PDFValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/PDFValidator.java @@ -52,7 +52,7 @@ public class PDFValidator extends Validator { } private static final Logger LOGGER = LoggerFactory.getLogger(PDFValidator.class.getCanonicalName()); // log output - private static final PDFAFlavour[] PDF_A_3_FLAVOURS = {PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_A}; + private static final PDFAFlavour[] PDF_A_3_FLAVOURS = {PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_B, PDFAFlavour.PDFA_3_U}; private String pdfFilename; @@ -264,6 +264,7 @@ public class PDFValidator extends Validator { final byte[] konikSignature = "Konik".getBytes(StandardCharsets.UTF_8); final byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes(StandardCharsets.UTF_8); final byte[] ghostscriptSignature = "%%Invocation:".getBytes(StandardCharsets.UTF_8); + final byte[] cibpdfbrewerSignature = "CIB pdf brewer".getBytes(StandardCharsets.UTF_8); if (ByteArraySearcher.contains(fileContents, symtraxSignature)) { Signature = "Symtrax"; @@ -279,6 +280,8 @@ public class PDFValidator extends Validator { Signature = "pdfMachine"; } else if (ByteArraySearcher.contains(fileContents, ghostscriptSignature)) { Signature = "Ghostscript"; + } else if (ByteArraySearcher.contains(fileContents, cibpdfbrewerSignature)) { + Signature = "CIB pdf brewer"; } context.setSignature(Signature); @@ -298,8 +301,10 @@ public class PDFValidator extends Validator { if (!processorResult.getValidationResult().isCompliant()) { context.setInvalid(); } + + PDFAFlavour pdfaFlavourFromValidationResult = processorResult.getValidationResult().getPDFAFlavour(); if (Arrays.stream(PDF_A_3_FLAVOURS) - .anyMatch(pdfaFlavour -> processorResult.getValidationResult().getPDFAFlavour().equals(pdfaFlavour))) { + .noneMatch(pdfaFlavourFromValidationResult::equals)) { context.addResultItem( new ValidationResultItem(ESeverity.error, "Not a PDF/A-3").setSection(23).setPart(EPart.pdf)); diff --git a/validator/src/main/java/org/mustangproject/validator/XMLValidator.java b/validator/src/main/java/org/mustangproject/validator/XMLValidator.java index abe94216..4392448e 100644 --- a/validator/src/main/java/org/mustangproject/validator/XMLValidator.java +++ b/validator/src/main/java/org/mustangproject/validator/XMLValidator.java @@ -419,11 +419,16 @@ public class XMLValidator extends Validator { * @param xml the xml to be checked * @param xsltFilename the filename of the intermediate XSLT file * @param section the error type code, if one arises - * @param severity how serious a error should be treated - may only be notice + * @param defaultSeverity how serious a error should be treated - may only be notice * @throws IrrecoverableValidationError if anything happened that prevents further checks */ - public void validateSchematron(String xml, String xsltFilename, int section, ESeverity severity) throws IrrecoverableValidationError { + public void validateSchematron(String xml, String xsltFilename, int section, ESeverity defaultSeverity) throws IrrecoverableValidationError { ISchematronResource aResSCH = null; + ESeverity severity=defaultSeverity; + if (defaultSeverity!=ESeverity.notice) { + severity=ESeverity.error; + } + aResSCH = SchematronResourceXSLT.fromClassPath(xsltFilename); if (aResSCH != null) { @@ -465,6 +470,16 @@ public class XMLValidator extends Validator { thisFailLocation = currentFailNode.getAttributes().getNamedItem("location").getNodeValue(); } + if (currentFailNode.getAttributes().getNamedItem("flag") != null) { + // the XR issues warnings with flag=warning + if (currentFailNode.getAttributes().getNamedItem("flag").getNodeValue().equals("warning")) { + if (defaultSeverity!=ESeverity.notice) { + severity=ESeverity.warning; + } + } + + } + NodeList failChilds = currentFailNode.getChildNodes(); for (int failChildIndex = 0; failChildIndex < failChilds.getLength(); failChildIndex++) { if (failChilds.item(failChildIndex).getLocalName() != null) { diff --git a/validator/src/test/java/org/mustangproject/validator/ZUGFeRDValidatorTest.java b/validator/src/test/java/org/mustangproject/validator/ZUGFeRDValidatorTest.java index 4a4638b6..695e51da 100644 --- a/validator/src/test/java/org/mustangproject/validator/ZUGFeRDValidatorTest.java +++ b/validator/src/test/java/org/mustangproject/validator/ZUGFeRDValidatorTest.java @@ -2,6 +2,15 @@ package org.mustangproject.validator; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.xmlunit.builder.Input; +import org.xmlunit.xpath.JAXPXPathEngine; +import org.xmlunit.xpath.XPathEngine; import static org.xmlunit.assertj.XmlAssert.assertThat; @@ -132,6 +141,17 @@ public class ZUGFeRDValidatorTest extends ResourceCase { } + public void testPDFA3AValidation() { + File tempFile = getResourceAsFile("zugferd_2p1_EXTENDED_PDFA-3A.pdf"); + + ZUGFeRDValidator zfv = new ZUGFeRDValidator(); + + String res = zfv.validate(tempFile.getAbsolutePath()); + + assertThat(res).valueByXPath("/validation/pdf/summary/@status") + .isEqualTo("valid"); + } + /*** * the XMLValidatorTests only cover the part, this one includes the root element and * the global part as well @@ -205,6 +225,9 @@ public class ZUGFeRDValidatorTest extends ResourceCase { assertThat(res).valueByXPath("count(//error)") .asInt() .isEqualTo(3); + assertThat(res).valueByXPath("count(//warning)") + .asInt() + .isEqualTo(1); assertThat(res).valueByXPath("count(//notice)") .asInt() diff --git a/validator/src/test/resources/invalidXRV30.xml b/validator/src/test/resources/invalidXRV30.xml index 6b62123d..0aa3b42c 100644 --- a/validator/src/test/resources/invalidXRV30.xml +++ b/validator/src/test/resources/invalidXRV30.xml @@ -115,9 +115,6 @@ [Seller city] DE - - seller@email.de - DE 123456789 diff --git a/validator/src/test/resources/zugferd_2p1_EXTENDED_PDFA-3A.pdf b/validator/src/test/resources/zugferd_2p1_EXTENDED_PDFA-3A.pdf new file mode 100644 index 00000000..a4610175 Binary files /dev/null and b/validator/src/test/resources/zugferd_2p1_EXTENDED_PDFA-3A.pdf differ