attachments
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
- Enhance Charges/Allowances with reasonCode. #432
|
- Enhance Charges/Allowances with reasonCode. #432
|
||||||
- Fix build warnings from editing and building. #415
|
- Fix build warnings from editing and building. #415
|
||||||
- ZUGFeRDVisualizer.toPDF(): generate PDF/A-3b. #400
|
- ZUGFeRDVisualizer.toPDF(): generate PDF/A-3b. #400
|
||||||
|
- allow access to invoice attachments via ZUGFeRDInvoiceImporter zii.getEmbeddedFilenames()/zii.getEmbeddedFile(filename)
|
||||||
|
and XML (zii.getFileAttachments)
|
||||||
|
|
||||||
|
|
||||||
2.12.0
|
2.12.0
|
||||||
=======
|
=======
|
||||||
|
|||||||
@@ -21,12 +21,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
@@ -57,7 +52,7 @@ import org.w3c.dom.NodeList;
|
|||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class ZUGFeRDImporter {
|
public class ZUGFeRDImporter {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger (ZUGFeRDImporter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDImporter.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if metadata has been found
|
* if metadata has been found
|
||||||
@@ -67,6 +62,10 @@ public class ZUGFeRDImporter {
|
|||||||
* map filenames of additional XML files to their contents
|
* map filenames of additional XML files to their contents
|
||||||
*/
|
*/
|
||||||
private final HashMap<String, byte[]> additionalXMLs = new HashMap<>();
|
private final HashMap<String, byte[]> additionalXMLs = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* map filenames of all embedded files in the respective PDF
|
||||||
|
*/
|
||||||
|
private final HashMap<String, byte[]> PDFAttachments = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* Raw XML form of the extracted data - may be directly obtained.
|
* Raw XML form of the extracted data - may be directly obtained.
|
||||||
*/
|
*/
|
||||||
@@ -90,7 +89,7 @@ public class ZUGFeRDImporter {
|
|||||||
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
|
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
|
||||||
extractLowLevel(bis);
|
extractLowLevel(bis);
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
LOGGER.error ("Failed to extract ZUGFeRD data", e);
|
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
||||||
throw new ZUGFeRDExportException(e);
|
throw new ZUGFeRDExportException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,12 +99,35 @@ public class ZUGFeRDImporter {
|
|||||||
try {
|
try {
|
||||||
extractLowLevel(pdfStream);
|
extractLowLevel(pdfStream);
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
LOGGER.error ("Failed to extract ZUGFeRD data", e);
|
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
||||||
throw new ZUGFeRDExportException(e);
|
throw new ZUGFeRDExportException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* return the file names of all files embedded into the PDF
|
||||||
|
* @see for XML embedded files please use ZUGFeRDInvoiceImporter.getFileAttachments
|
||||||
|
* @return a Stringset
|
||||||
|
*/
|
||||||
|
public Set<String> getEmbeddedFilenames() {
|
||||||
|
return PDFAttachments.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* returns the file contents of the specified filename embedded into the PDF
|
||||||
|
* @param filename String
|
||||||
|
* @return a bytearray, or null if the filename has not been fond
|
||||||
|
*/
|
||||||
|
public byte[] getEmbeddedFile(String filename) {
|
||||||
|
|
||||||
|
if (PDFAttachments.containsKey(filename)) {
|
||||||
|
return PDFAttachments.get(filename);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
|
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
|
||||||
*
|
*
|
||||||
@@ -121,7 +143,7 @@ public class ZUGFeRDImporter {
|
|||||||
if (Arrays.equals(pad, pdfSignature)) { // we have a pdf
|
if (Arrays.equals(pad, pdfSignature)) { // we have a pdf
|
||||||
|
|
||||||
|
|
||||||
try (PDDocument doc = Loader.loadPDF(IOUtils.toByteArray (pdfStream))) {
|
try (PDDocument doc = Loader.loadPDF(IOUtils.toByteArray(pdfStream))) {
|
||||||
// PDDocumentInformation info = doc.getDocumentInformation();
|
// PDDocumentInformation info = doc.getDocumentInformation();
|
||||||
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
|
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
|
||||||
//start
|
//start
|
||||||
@@ -174,10 +196,10 @@ public class ZUGFeRDImporter {
|
|||||||
/**
|
/**
|
||||||
* filenames for invoice data (ZUGFeRD v1 and v2, Factur-X)
|
* filenames for invoice data (ZUGFeRD v1 and v2, Factur-X)
|
||||||
*/
|
*/
|
||||||
|
final PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
|
||||||
if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml")) || filename.equals("xrechnung.xml") || filename.equals("order-x.xml") || filename.equals("cida.xml")) {
|
if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml")) || filename.equals("xrechnung.xml") || filename.equals("order-x.xml") || filename.equals("cida.xml")) {
|
||||||
containsMeta = true;
|
containsMeta = true;
|
||||||
|
|
||||||
final PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
|
|
||||||
// String embeddedFilename = filePath + filename;
|
// String embeddedFilename = filePath + filename;
|
||||||
// File file = new File(filePath + filename);
|
// File file = new File(filePath + filename);
|
||||||
// System.out.println("Writing " + embeddedFilename);
|
// System.out.println("Writing " + embeddedFilename);
|
||||||
@@ -191,9 +213,9 @@ public class ZUGFeRDImporter {
|
|||||||
// fos.close();
|
// fos.close();
|
||||||
}
|
}
|
||||||
if (filename.startsWith("additional_data")) {
|
if (filename.startsWith("additional_data")) {
|
||||||
final PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
|
|
||||||
additionalXMLs.put(filename, embeddedFile.toByteArray());
|
additionalXMLs.put(filename, embeddedFile.toByteArray());
|
||||||
}
|
}
|
||||||
|
PDFAttachments.put(filename, embeddedFile.toByteArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,12 +236,13 @@ public class ZUGFeRDImporter {
|
|||||||
|
|
||||||
|
|
||||||
public void setRawXML(byte[] rawXML) throws IOException {
|
public void setRawXML(byte[] rawXML) throws IOException {
|
||||||
|
this.containsMeta = true;
|
||||||
this.rawXML = rawXML;
|
this.rawXML = rawXML;
|
||||||
this.version = null;
|
this.version = null;
|
||||||
try {
|
try {
|
||||||
setDocument();
|
setDocument();
|
||||||
} catch (ParserConfigurationException | SAXException e) {
|
} catch (ParserConfigurationException | SAXException e) {
|
||||||
LOGGER.error ("Failed to parse XML", e);
|
LOGGER.error("Failed to parse XML", e);
|
||||||
throw new ZUGFeRDExportException(e);
|
throw new ZUGFeRDExportException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +259,7 @@ public class ZUGFeRDImporter {
|
|||||||
final XPath xpath = xpathFact.newXPath();
|
final XPath xpath = xpathFact.newXPath();
|
||||||
result = xpath.evaluate(xpathStr, document);
|
result = xpath.evaluate(xpathStr, document);
|
||||||
} catch (final XPathExpressionException e) {
|
} catch (final XPathExpressionException e) {
|
||||||
LOGGER.error ("Failed to evaluate XPath", e);
|
LOGGER.error("Failed to evaluate XPath", e);
|
||||||
throw new ZUGFeRDExportException(e);
|
throw new ZUGFeRDExportException(e);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -268,7 +291,7 @@ public class ZUGFeRDImporter {
|
|||||||
*/
|
*/
|
||||||
public String getZUGFeRDProfil() {
|
public String getZUGFeRDProfil() {
|
||||||
String guideline = extractString("//*[local-name() = 'GuidelineSpecifiedDocumentContextParameter']//*[local-name() = 'ID']");
|
String guideline = extractString("//*[local-name() = 'GuidelineSpecifiedDocumentContextParameter']//*[local-name() = 'ID']");
|
||||||
if(guideline.contains("xrechnung")) {
|
if (guideline.contains("xrechnung")) {
|
||||||
return "XRECHNUNG";
|
return "XRECHNUNG";
|
||||||
}
|
}
|
||||||
switch (guideline) {
|
switch (guideline) {
|
||||||
@@ -438,7 +461,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TotalPrepaidAmount']");
|
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TotalPrepaidAmount']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,7 +499,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IncludedNote']");
|
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IncludedNote']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,7 +530,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'LineTotalAmount']");
|
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'LineTotalAmount']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -530,7 +553,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'ActualDeliverySupplyChainEvent']//*[local-name() = 'OccurrenceDateTime']//*[local-name() = 'DateTimeString']");
|
return extractString("//*[local-name() = 'ActualDeliverySupplyChainEvent']//*[local-name() = 'OccurrenceDateTime']//*[local-name() = 'DateTimeString']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -546,7 +569,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'ID']");
|
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'ID']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -563,7 +586,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'ExchangedDocument']/*[local-name() = 'TypeCode']");
|
return extractString("//*[local-name() = 'ExchangedDocument']/*[local-name() = 'TypeCode']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,7 +603,7 @@ public class ZUGFeRDImporter {
|
|||||||
return extractString("//*[local-name() = 'ApplicableHeaderTradeAgreement']/*[local-name() = 'BuyerReference']");
|
return extractString("//*[local-name() = 'ApplicableHeaderTradeAgreement']/*[local-name() = 'BuyerReference']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -784,7 +807,7 @@ public class ZUGFeRDImporter {
|
|||||||
static String convertStreamToString(java.io.InputStream is) {
|
static String convertStreamToString(java.io.InputStream is) {
|
||||||
try {
|
try {
|
||||||
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UncheckedIOException(e);
|
throw new UncheckedIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -805,7 +828,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -827,7 +850,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'SellerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'SellerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,7 +872,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeDelivery']//*[local-name() = 'ShipToTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeDelivery']//*[local-name() = 'ShipToTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
||||||
}
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,7 +1085,7 @@ public class ZUGFeRDImporter {
|
|||||||
nl = getNodeListByPath("//*[local-name() = 'IncludedSupplyChainTradeLineItem']");
|
nl = getNodeListByPath("//*[local-name() = 'IncludedSupplyChainTradeLineItem']");
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
// Exception was already logged
|
// Exception was already logged
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < nl.getLength(); i++) {
|
for (int i = 0; i < nl.getLength(); i++) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -16,14 +17,7 @@ import javax.xml.xpath.XPathExpression;
|
|||||||
import javax.xml.xpath.XPathExpressionException;
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
import javax.xml.xpath.XPathFactory;
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
import org.mustangproject.Allowance;
|
import org.mustangproject.*;
|
||||||
import org.mustangproject.BankDetails;
|
|
||||||
import org.mustangproject.Charge;
|
|
||||||
import org.mustangproject.EStandard;
|
|
||||||
import org.mustangproject.Invoice;
|
|
||||||
import org.mustangproject.Item;
|
|
||||||
import org.mustangproject.TradeParty;
|
|
||||||
import org.mustangproject.XMLTools;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
@@ -33,6 +27,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDInvoiceImporter.class.getCanonicalName()); // log
|
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDInvoiceImporter.class.getCanonicalName()); // log
|
||||||
private boolean recalcPrice = false;
|
private boolean recalcPrice = false;
|
||||||
private boolean ignoreCalculationErrors = false;
|
private boolean ignoreCalculationErrors = false;
|
||||||
|
private ArrayList<FileAttachment> fileAttachments=new ArrayList<>();
|
||||||
|
|
||||||
public ZUGFeRDInvoiceImporter() {
|
public ZUGFeRDInvoiceImporter() {
|
||||||
super();
|
super();
|
||||||
@@ -346,6 +341,16 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now handling base64 encoded attachments AttachmentBinaryObject=CII, EmbeddedDocumentBinaryObject=UBL
|
||||||
|
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(),"",Base64.getDecoder().decode(attachmentNodes.item(i).getTextContent()));
|
||||||
|
fileAttachments.add(fa);
|
||||||
|
// filename = "Aufmass.png" mimeCode = "image/png"
|
||||||
|
//EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png"
|
||||||
|
}
|
||||||
|
|
||||||
// item level charges+allowances are not yet handled but a lower item price will
|
// item level charges+allowances are not yet handled but a lower item price will
|
||||||
// be read,
|
// be read,
|
||||||
// so the invoice remains arithmetically correct
|
// so the invoice remains arithmetically correct
|
||||||
@@ -442,6 +447,15 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
*
|
||||||
|
* @return the file attachments embedded in XML using base64,
|
||||||
|
* @see for PDF embedded files use getEmbeddedFilenames()/getEmbeddedFile()
|
||||||
|
*/
|
||||||
|
public List<FileAttachment> getFileAttachments() {
|
||||||
|
return fileAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* This will parse a XML into a invoice object
|
* This will parse a XML into a invoice object
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ import org.junit.FixMethodOrder;
|
|||||||
import org.junit.runners.MethodSorters;
|
import org.junit.runners.MethodSorters;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.xml.xpath.XPathExpressionException;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
import static org.xmlunit.assertj.XmlAssert.assertThat;
|
||||||
|
|
||||||
@@ -41,6 +45,7 @@ import static org.xmlunit.assertj.XmlAssert.assertThat;
|
|||||||
public class XRTest extends TestCase {
|
public class XRTest extends TestCase {
|
||||||
final String TARGET_XML = "./target/testout-XR.xml";
|
final String TARGET_XML = "./target/testout-XR.xml";
|
||||||
final String TARGET_EDGE_XML = "./target/testout-XR-Edge.xml";
|
final String TARGET_EDGE_XML = "./target/testout-XR-Edge.xml";
|
||||||
|
|
||||||
public void testXRExport() {
|
public void testXRExport() {
|
||||||
|
|
||||||
// the writing part
|
// the writing part
|
||||||
@@ -54,13 +59,13 @@ public class XRTest extends TestCase {
|
|||||||
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
|
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
|
||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
||||||
.asInt()
|
.asInt()
|
||||||
.isEqualTo(1); //2 errors are OK because there is a known bug
|
.isEqualTo(1); //2 errors are OK because there is a known bug
|
||||||
|
|
||||||
|
|
||||||
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
||||||
.asDouble()
|
.asDouble()
|
||||||
.isEqualTo(1);
|
.isEqualTo(1);
|
||||||
try {
|
try {
|
||||||
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_XML));
|
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_XML));
|
||||||
writer.write(theXML);
|
writer.write(theXML);
|
||||||
@@ -80,17 +85,17 @@ public class XRTest extends TestCase {
|
|||||||
String amountStr = "1.00";
|
String amountStr = "1.00";
|
||||||
BigDecimal amount = new BigDecimal(amountStr);
|
BigDecimal amount = new BigDecimal(amountStr);
|
||||||
byte[] b = {12, 13};
|
byte[] b = {12, 13};
|
||||||
|
|
||||||
FileAttachment fe1=new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
|
FileAttachment fe1 = new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
|
||||||
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
|
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
|
||||||
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").setEmail("sender@example.com").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
|
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").setEmail("sender@example.com").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test", "+49123456789", "test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890", "COBADEFXXX")))
|
||||||
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("recipient@sample.org"))
|
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("recipient@sample.org"))
|
||||||
.addCashDiscount(new CashDiscount(new BigDecimal(2),7))
|
.addCashDiscount(new CashDiscount(new BigDecimal(2), 7))
|
||||||
.addCashDiscount(new CashDiscount(new BigDecimal(3),14))
|
.addCashDiscount(new CashDiscount(new BigDecimal(3), 14))
|
||||||
.setReferenceNumber("991-01484-64")//leitweg-id
|
.setReferenceNumber("991-01484-64")//leitweg-id
|
||||||
// not using any VAT, this is also a test of zero-rated goods:
|
// not using any VAT, this is also a test of zero-rated goods:
|
||||||
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
|
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
|
||||||
.embedFileInXML(fe1);
|
.embedFileInXML(fe1);
|
||||||
|
|
||||||
|
|
||||||
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
|
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
|
||||||
@@ -101,13 +106,13 @@ public class XRTest extends TestCase {
|
|||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
assertTrue(theXML.contains("#SKONTO#"));
|
assertTrue(theXML.contains("#SKONTO#"));
|
||||||
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
||||||
.asInt()
|
.asInt()
|
||||||
.isEqualTo(1); //2 errors are OK because there is a known bug
|
.isEqualTo(1); //2 errors are OK because there is a known bug
|
||||||
|
|
||||||
|
|
||||||
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
||||||
.asDouble()
|
.asDouble()
|
||||||
.isEqualTo(1);
|
.isEqualTo(1);
|
||||||
try {
|
try {
|
||||||
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_EDGE_XML));
|
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_EDGE_XML));
|
||||||
writer.write(theXML);
|
writer.write(theXML);
|
||||||
@@ -116,6 +121,25 @@ public class XRTest extends TestCase {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Invoice readInvoice = new Invoice();
|
||||||
|
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
|
||||||
|
try {
|
||||||
|
|
||||||
|
zii.setRawXML(zf2p.getXML());
|
||||||
|
zii.extractInto(readInvoice);
|
||||||
|
} catch (ParseException | XPathExpressionException xp) {
|
||||||
|
fail("Exception not expected");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
List<FileAttachment> attachedFiles=zii.getFileAttachments();
|
||||||
|
assertNotNull(attachedFiles);
|
||||||
|
assertEquals(attachedFiles.size(), 1);
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(attachedFiles.get(0).getData(), b));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testXRExportWithoutStreet() {
|
public void testXRExportWithoutStreet() {
|
||||||
@@ -130,13 +154,13 @@ public class XRTest extends TestCase {
|
|||||||
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
|
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
|
||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
|
||||||
.asInt()
|
.asInt()
|
||||||
.isEqualTo(1); //2 errors are OK because there is a known bug
|
.isEqualTo(1); //2 errors are OK because there is a known bug
|
||||||
|
|
||||||
|
|
||||||
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
|
||||||
.asDouble()
|
.asDouble()
|
||||||
.isEqualTo(1);
|
.isEqualTo(1);
|
||||||
try {
|
try {
|
||||||
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_XML));
|
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_XML));
|
||||||
writer.write(theXML);
|
writer.write(theXML);
|
||||||
@@ -153,11 +177,11 @@ public class XRTest extends TestCase {
|
|||||||
String amountStr = "1.00";
|
String amountStr = "1.00";
|
||||||
BigDecimal amount = new BigDecimal(amountStr);
|
BigDecimal amount = new BigDecimal(amountStr);
|
||||||
return new Invoice().setDueDate(new java.util.Date()).setIssueDate(new java.util.Date()).setDeliveryDate(new java.util.Date())
|
return new Invoice().setDueDate(new java.util.Date()).setIssueDate(new java.util.Date()).setDeliveryDate(new java.util.Date())
|
||||||
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setEmail("info@example.org").setContact(new org.mustangproject.Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890","COBADEFXXX")))
|
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("DE4711").addVATID("DE0815").setEmail("info@example.org").setContact(new org.mustangproject.Contact("Hans Test", "+49123456789", "test@example.org")).addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890", "COBADEFXXX")))
|
||||||
.setRecipient(recipient)
|
.setRecipient(recipient)
|
||||||
.setReferenceNumber("991-01484-64")//leitweg-id
|
.setReferenceNumber("991-01484-64")//leitweg-id
|
||||||
// not using any VAT, this is also a test of zero-rated goods:
|
// not using any VAT, this is also a test of zero-rated goods:
|
||||||
.setNumber(number).addItem(new org.mustangproject.Item(new org.mustangproject.Product("Testprodukt", "", "C62", java.math.BigDecimal.ZERO), amount, new java.math.BigDecimal(1.0)));
|
.setNumber(number).addItem(new org.mustangproject.Item(new org.mustangproject.Product("Testprodukt", "", "C62", java.math.BigDecimal.ZERO), amount, new java.math.BigDecimal(1.0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@@ -285,5 +286,33 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* testing if other files embedded in pdf additionally to the invoice can be read correctly
|
||||||
|
* */
|
||||||
|
public void testDetach() {
|
||||||
|
boolean hasExceptions = false;
|
||||||
|
|
||||||
|
byte[] fileA=null;
|
||||||
|
byte[] fileB=null;
|
||||||
|
|
||||||
|
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2PushAttachments.pdf");
|
||||||
|
for (String filename:zii.getEmbeddedFilenames()
|
||||||
|
) {
|
||||||
|
if (filename.equals("one.pdf")) {
|
||||||
|
fileA=zii.getEmbeddedFile(filename);
|
||||||
|
} else if (filename.equals("two.pdf")) {
|
||||||
|
fileB=zii.getEmbeddedFile(filename);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
byte[] b = {12, 13}; // the sample data that was used to write the files
|
||||||
|
|
||||||
|
assertTrue(Arrays.equals(fileA, b));
|
||||||
|
assertEquals(fileA.length, 2);
|
||||||
|
assertTrue(Arrays.equals(fileB, b));
|
||||||
|
assertEquals(fileB.length, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user