Merge branch 'issues/435-importer-ubl'
# Conflicts: # History.md
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
|
2.15.0
|
||||||
|
=======
|
||||||
|
2024-
|
||||||
|
- 435 use invoiceimporter as common technical basis also for zugferdimporter
|
||||||
|
- also import delivery address
|
||||||
|
|
||||||
|
|
||||||
2.14.2
|
2.14.2
|
||||||
=======
|
=======
|
||||||
2024-10-14
|
2024-10-14
|
||||||
@@ -6,7 +13,6 @@
|
|||||||
- #509 CLI currently does not write a logfile
|
- #509 CLI currently does not write a logfile
|
||||||
- #505 crash after invoking ZUGFeRD2PullProvider
|
- #505 crash after invoking ZUGFeRD2PullProvider
|
||||||
- #506 Fix POM missing dependencies
|
- #506 Fix POM missing dependencies
|
||||||
|
|
||||||
|
|
||||||
2.14.1
|
2.14.1
|
||||||
=======
|
=======
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2023 Jochen Stärk, see LICENSE file
|
||||||
|
package org.mustangproject;
|
||||||
|
|
||||||
|
|
||||||
|
import org.mustangproject.ZUGFeRD.TransactionCalculator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
public class CalculatedInvoice extends Invoice implements Serializable {
|
||||||
|
|
||||||
|
protected BigDecimal grandTotal=null;
|
||||||
|
|
||||||
|
public void calculate() {
|
||||||
|
TransactionCalculator tc=new TransactionCalculator(this);
|
||||||
|
grandTotal=tc.getGrandTotal();
|
||||||
|
}
|
||||||
|
public BigDecimal getGrandTotal() {
|
||||||
|
if (grandTotal==null) {
|
||||||
|
calculate();
|
||||||
|
}
|
||||||
|
return grandTotal;
|
||||||
|
}
|
||||||
|
public CalculatedInvoice setGrandTotal(BigDecimal grand) {
|
||||||
|
grandTotal=grand;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -314,7 +314,7 @@ public class Item implements IZUGFeRDExportableItem {
|
|||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* adds item level references along with their typecodes and issuerassignedIDs (contract ID, cost centre, ...)
|
* adds item level references along with their typecodes and issuerassignedIDs (contract ID, cost centre, ...)
|
||||||
* @param doc the ReferencedDocument to add
|
* @param doc the ReferencedDocument to add
|
||||||
* @return fluent setter
|
* @return fluent setter
|
||||||
*/
|
*/
|
||||||
@@ -333,8 +333,8 @@ public class Item implements IZUGFeRDExportableItem {
|
|||||||
}
|
}
|
||||||
return additionalReference.toArray(new IReferencedDocument[0]);
|
return additionalReference.toArray(new IReferencedDocument[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* specify a item level delivery period
|
* specify a item level delivery period
|
||||||
* (apart from the document level delivery period, and the document level
|
* (apart from the document level delivery period, and the document level
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.dom4j.io.XMLWriter;
|
import org.dom4j.io.XMLWriter;
|
||||||
|
import org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
public class XMLTools extends XMLWriter {
|
public class XMLTools extends XMLWriter {
|
||||||
@Override
|
@Override
|
||||||
@@ -39,6 +43,52 @@ public class XMLTools extends XMLWriter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the value of an node
|
||||||
|
*
|
||||||
|
* @param node the Node to get the value from
|
||||||
|
* @return A String or empty String, if no value was found
|
||||||
|
*/
|
||||||
|
public static String getNodeValue(Node node) {
|
||||||
|
if (node != null && node.getFirstChild() != null) {
|
||||||
|
return node.getFirstChild().getNodeValue();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tries to convert a String to BigDecimal.
|
||||||
|
*
|
||||||
|
* @param nodeValue The value as String
|
||||||
|
* @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs
|
||||||
|
*/
|
||||||
|
public static BigDecimal tryBigDecimal(String nodeValue) {
|
||||||
|
try {
|
||||||
|
return new BigDecimal(nodeValue);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
try {
|
||||||
|
return BigDecimal.valueOf(Float.valueOf(nodeValue));
|
||||||
|
} catch (final Exception ex) {
|
||||||
|
return new BigDecimal("0.00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tries to convert a Node to a BigDecimal.
|
||||||
|
*
|
||||||
|
* @param node The value as String
|
||||||
|
* @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs
|
||||||
|
*/
|
||||||
|
public static BigDecimal tryBigDecimal(Node node) {
|
||||||
|
final String nodeValue = XMLTools.getNodeValue(node);
|
||||||
|
if (nodeValue.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return XMLTools.tryBigDecimal(nodeValue);
|
||||||
|
}
|
||||||
/***
|
/***
|
||||||
* formats a number so that at least minDecimals are displayed but at the maximum maxDecimals are there, i.e.
|
* formats a number so that at least minDecimals are displayed but at the maximum maxDecimals are there, i.e.
|
||||||
* cuts potential 0s off the end until minDecimals
|
* cuts potential 0s off the end until minDecimals
|
||||||
@@ -60,7 +110,39 @@ public class XMLTools extends XMLWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String encodeXML(CharSequence s) {
|
/***
|
||||||
|
* returns a util.Date from a 102 String yyyymmdd in a node
|
||||||
|
* @param node the node
|
||||||
|
* @return a util.Date, or null, if not parseable
|
||||||
|
*/
|
||||||
|
public static Date tryDate(Node node) {
|
||||||
|
final String nodeValue = XMLTools.getNodeValue(node);
|
||||||
|
if (nodeValue.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return tryDate(nodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* returns a util.Date from a 102 String yyyymmdd
|
||||||
|
* @param toParse the string
|
||||||
|
* @return a util.Date, or null, if not parseable
|
||||||
|
*/
|
||||||
|
public static Date tryDate(String toParse) {
|
||||||
|
final SimpleDateFormat formatter = ZUGFeRDDateFormat.DATE.getFormatter();
|
||||||
|
try {
|
||||||
|
return formatter.parse(toParse);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* relplaces some entities like < , > and & with their escaped pendant like <
|
||||||
|
* @param s the string
|
||||||
|
* @return the "safe" string
|
||||||
|
*/
|
||||||
|
public static String encodeXML(CharSequence s) {
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.nio.file.Files;
|
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.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ import javax.xml.xpath.XPathExpressionException;
|
|||||||
import javax.xml.xpath.XPathFactory;
|
import javax.xml.xpath.XPathFactory;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.fop.util.XMLUtil;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
|
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
|
||||||
@@ -48,57 +50,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class ZUGFeRDImporter {
|
public class ZUGFeRDImporter extends ZUGFeRDInvoiceImporter {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDImporter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDImporter.class);
|
||||||
|
|
||||||
/**
|
public ZUGFeRDImporter() {
|
||||||
* if metadata has been found
|
super();
|
||||||
*/
|
|
||||||
protected boolean containsMeta = false;
|
|
||||||
/**
|
|
||||||
* map filenames of additional XML files to their contents
|
|
||||||
*/
|
|
||||||
private final HashMap<String, byte[]> additionalXMLs = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* map filenames of all embedded files in the respective PDF
|
|
||||||
*/
|
|
||||||
private final ArrayList<FileAttachment> PDFAttachments = new ArrayList<>();
|
|
||||||
/**
|
|
||||||
* Raw XML form of the extracted data - may be directly obtained.
|
|
||||||
*/
|
|
||||||
private byte[] rawXML = null;
|
|
||||||
/**
|
|
||||||
* XMP metadata
|
|
||||||
*/
|
|
||||||
private String xmpString = null; // XMP metadata
|
|
||||||
/**
|
|
||||||
* parsed Document
|
|
||||||
*/
|
|
||||||
private Document document;
|
|
||||||
private Integer version;
|
|
||||||
|
|
||||||
|
|
||||||
protected ZUGFeRDImporter() {
|
|
||||||
//constructor for extending classes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZUGFeRDImporter(String pdfFilename) {
|
public ZUGFeRDImporter(String filename) {
|
||||||
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
|
super(filename);
|
||||||
extractLowLevel(bis);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
|
||||||
throw new ZUGFeRDExportException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ZUGFeRDImporter(InputStream stream) {
|
||||||
public ZUGFeRDImporter(InputStream pdfStream) {
|
super(stream);
|
||||||
try {
|
|
||||||
extractLowLevel(pdfStream);
|
|
||||||
} catch (final IOException e) {
|
|
||||||
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
|
||||||
throw new ZUGFeRDExportException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -112,145 +76,6 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
|
|
||||||
*
|
|
||||||
* @param inStream a inputstream of a pdf file
|
|
||||||
*/
|
|
||||||
private void extractLowLevel(InputStream inStream) throws IOException {
|
|
||||||
BufferedInputStream pdfStream = new BufferedInputStream(inStream);
|
|
||||||
byte[] pad = new byte[4];
|
|
||||||
pdfStream.mark(0);
|
|
||||||
pdfStream.read(pad);
|
|
||||||
pdfStream.reset();
|
|
||||||
byte[] pdfSignature = {'%', 'P', 'D', 'F'};
|
|
||||||
if (Arrays.equals(pad, pdfSignature)) { // we have a pdf
|
|
||||||
|
|
||||||
|
|
||||||
try (PDDocument doc = Loader.loadPDF(IOUtils.toByteArray(pdfStream))) {
|
|
||||||
// PDDocumentInformation info = doc.getDocumentInformation();
|
|
||||||
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
|
|
||||||
//start
|
|
||||||
|
|
||||||
if (doc.getDocumentCatalog() == null || doc.getDocumentCatalog().getMetadata() == null) {
|
|
||||||
LOGGER.info("no-xmlpart");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata();
|
|
||||||
xmpString = convertStreamToString(XMP);
|
|
||||||
|
|
||||||
final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles();
|
|
||||||
if (etn == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, PDComplexFileSpecification> efMap = etn.getNames();
|
|
||||||
// String filePath = "/tmp/";
|
|
||||||
|
|
||||||
if (efMap != null) {
|
|
||||||
extractFiles(efMap); // see
|
|
||||||
// https://memorynotfound.com/apache-pdfbox-extract-embedded-file-pdf-document/
|
|
||||||
} else {
|
|
||||||
|
|
||||||
final List<PDNameTreeNode<PDComplexFileSpecification>> kids = etn.getKids();
|
|
||||||
if (kids == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (final PDNameTreeNode<PDComplexFileSpecification> node : kids) {
|
|
||||||
final Map<String, PDComplexFileSpecification> namesL = node.getNames();
|
|
||||||
extractFiles(namesL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no PDF probably XML
|
|
||||||
containsMeta = true;
|
|
||||||
setRawXML(XMLTools.getBytesFromStream(pdfStream));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException {
|
|
||||||
for (final String alias : names.keySet()) {
|
|
||||||
|
|
||||||
final PDComplexFileSpecification fileSpec = names.get(alias);
|
|
||||||
final String filename = fileSpec.getFilename();
|
|
||||||
/**
|
|
||||||
* 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")) {
|
|
||||||
containsMeta = true;
|
|
||||||
|
|
||||||
// String embeddedFilename = filePath + filename;
|
|
||||||
// File file = new File(filePath + filename);
|
|
||||||
// System.out.println("Writing " + embeddedFilename);
|
|
||||||
// ByteArrayOutputStream fileBytes=new
|
|
||||||
// ByteArrayOutputStream();
|
|
||||||
// FileOutputStream fos = new FileOutputStream(file);
|
|
||||||
|
|
||||||
setRawXML(embeddedFile.toByteArray());
|
|
||||||
|
|
||||||
// fos.write(embeddedFile.getByteArray());
|
|
||||||
// fos.close();
|
|
||||||
}
|
|
||||||
if (filename.startsWith("additional_data")) {
|
|
||||||
additionalXMLs.put(filename, embeddedFile.toByteArray());
|
|
||||||
}
|
|
||||||
PDFAttachments.add(new FileAttachment(filename, embeddedFile.getSubtype(), "Data", embeddedFile.toByteArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected Document getDocument() {
|
|
||||||
return document;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void setDocument() throws ParserConfigurationException, IOException, SAXException {
|
|
||||||
final DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
|
|
||||||
xmlFact.setNamespaceAware(true);
|
|
||||||
final DocumentBuilder builder = xmlFact.newDocumentBuilder();
|
|
||||||
final ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
|
|
||||||
/// is.skip(guessBOMSize(is));
|
|
||||||
document = builder.parse(is);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setRawXML(byte[] rawXML) throws IOException {
|
|
||||||
this.containsMeta = true;
|
|
||||||
this.rawXML = rawXML;
|
|
||||||
this.version = null;
|
|
||||||
try {
|
|
||||||
setDocument();
|
|
||||||
} catch (ParserConfigurationException | SAXException e) {
|
|
||||||
LOGGER.error("Failed to parse XML", e);
|
|
||||||
throw new ZUGFeRDExportException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected String extractString(String xpathStr) {
|
|
||||||
if (!containsMeta) {
|
|
||||||
throw new ZUGFeRDExportException("No suitable data/ZUGFeRD file could be found.");
|
|
||||||
}
|
|
||||||
final String result;
|
|
||||||
try {
|
|
||||||
final Document document = getDocument();
|
|
||||||
final XPathFactory xpathFact = XPathFactory.newInstance();
|
|
||||||
final XPath xpath = xpathFact.newXPath();
|
|
||||||
result = xpath.evaluate(xpathStr, document);
|
|
||||||
} catch (final XPathExpressionException e) {
|
|
||||||
LOGGER.error("Failed to evaluate XPath", e);
|
|
||||||
throw new ZUGFeRDExportException(e);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Wrapper for protected method extractString
|
* Wrapper for protected method extractString
|
||||||
* @param xpathStr the xpath expression to be evaluated
|
* @param xpathStr the xpath expression to be evaluated
|
||||||
@@ -261,21 +86,21 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the reference (purpose) the sender specified for this invoice
|
* @return the reference (purpose) the sender specified for this invoice
|
||||||
*/
|
*/
|
||||||
public String getForeignReference() {
|
public String getForeignReference() {
|
||||||
String result = extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']/*[local-name() = 'PaymentReference']");
|
|
||||||
if (result == null || result.isEmpty()) {
|
return importedInvoice.getNumber();
|
||||||
result = extractString("//*[local-name() = 'ApplicableSupplyChainTradeSettlement']/*[local-name() = 'PaymentReference']");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the ZUGFeRD Profile
|
* @return the ZUGFeRD Profile
|
||||||
*/
|
*/
|
||||||
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";
|
||||||
@@ -299,21 +124,6 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the Invoice Currency Code
|
|
||||||
*/
|
|
||||||
public String getInvoiceCurrencyCode() {
|
|
||||||
try {
|
|
||||||
if (getVersion() == 1) {
|
|
||||||
return extractString("//*[local-name() = 'ApplicableSupplyChainTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']");
|
|
||||||
} else {
|
|
||||||
return extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']");
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
// Exception was already logged
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the IssuerAssigned ID
|
* @return the IssuerAssigned ID
|
||||||
@@ -336,57 +146,6 @@ public class ZUGFeRDImporter {
|
|||||||
return extractIssuerAssignedID("ContractReferencedDocument");
|
return extractIssuerAssignedID("ContractReferencedDocument");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String extractIssuerAssignedID(String propertyName) {
|
|
||||||
try {
|
|
||||||
if (getVersion() == 1) {
|
|
||||||
return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'ID']");
|
|
||||||
} else {
|
|
||||||
return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']");
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
// Exception was already logged
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the BuyerTradeParty ID
|
|
||||||
*/
|
|
||||||
public String getBuyerTradePartyID() {
|
|
||||||
return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'ID']");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the Issue Date()
|
|
||||||
*/
|
|
||||||
public String getIssueDate() {
|
|
||||||
try {
|
|
||||||
if (getVersion() == 1) {
|
|
||||||
return extractString("//*[local-name() = 'HeaderExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']");
|
|
||||||
} else {
|
|
||||||
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']");
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
// Exception was already logged
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDetailedDeliveryPeriodFrom() {
|
|
||||||
final String toParse = extractString(
|
|
||||||
"//*[local-name() = 'ApplicableHeaderTradeSettlement']" +
|
|
||||||
"//*[local-name() = 'BillingSpecifiedPeriod']" +
|
|
||||||
"//*[local-name() = 'StartDateTime']//*[local-name() = 'DateTimeString']");
|
|
||||||
return tryDate(toParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDetailedDeliveryPeriodTo() {
|
|
||||||
final String toParse = extractString(
|
|
||||||
"//*[local-name() = 'ApplicableHeaderTradeSettlement']" +
|
|
||||||
"//*[local-name() = 'BillingSpecifiedPeriod']" +
|
|
||||||
"//*[local-name() = 'EndDateTime']//*[local-name() = 'DateTimeString']");
|
|
||||||
return tryDate(toParse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the TaxBasisTotalAmount
|
* @return the TaxBasisTotalAmount
|
||||||
@@ -470,7 +229,16 @@ public class ZUGFeRDImporter {
|
|||||||
* @return the BuyerTradeParty SpecifiedTaxRegistration ID
|
* @return the BuyerTradeParty SpecifiedTaxRegistration ID
|
||||||
*/
|
*/
|
||||||
public String getBuyertradePartySpecifiedTaxRegistrationID() {
|
public String getBuyertradePartySpecifiedTaxRegistrationID() {
|
||||||
return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'SpecifiedTaxRegistration']//*[local-name() = 'ID']");
|
String id = null;
|
||||||
|
if ((importedInvoice.getRecipient()!=null) && (importedInvoice.getRecipient().getLegalOrganisation()!=null)) {
|
||||||
|
// this *should* be the official result
|
||||||
|
id = importedInvoice.getRecipient().getLegalOrganisation().getSchemedID().getID();
|
||||||
|
}
|
||||||
|
// but also provide some fallback
|
||||||
|
if (id == null) {
|
||||||
|
id = getBuyerTradePartyID();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -494,14 +262,14 @@ public class ZUGFeRDImporter {
|
|||||||
* @return the BuyerTradeParty Name
|
* @return the BuyerTradeParty Name
|
||||||
*/
|
*/
|
||||||
public String getBuyerTradePartyName() {
|
public String getBuyerTradePartyName() {
|
||||||
return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'Name']");
|
return importedInvoice.getRecipient().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the BuyerTradeParty Name
|
* @return the BuyerTradeParty Name
|
||||||
*/
|
*/
|
||||||
public String getDeliveryTradePartyName() {
|
public String getDeliveryTradePartyName() {
|
||||||
return extractString("//*[local-name() = 'ShipToTradeParty']//*[local-name() = 'Name']");
|
return importedInvoice.getDeliveryAddress().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -548,16 +316,7 @@ public class ZUGFeRDImporter {
|
|||||||
* @return the Invoice ID
|
* @return the Invoice ID
|
||||||
*/
|
*/
|
||||||
public String getInvoiceID() {
|
public String getInvoiceID() {
|
||||||
try {
|
return importedInvoice.getNumber();
|
||||||
if (getVersion() == 1) {
|
|
||||||
return extractString("//*[local-name() = 'HeaderExchangedDocument']//*[local-name() = 'ID']");
|
|
||||||
} else {
|
|
||||||
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'ID']");
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
// Exception was already logged
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -615,11 +374,21 @@ public class ZUGFeRDImporter {
|
|||||||
* @return the sender's account IBAN code
|
* @return the sender's account IBAN code
|
||||||
*/
|
*/
|
||||||
public String getIBAN() {
|
public String getIBAN() {
|
||||||
return extractString("//*[local-name() = 'PayeePartyCreditorFinancialAccount']/*[local-name() = 'IBANID']");
|
for (IZUGFeRDTradeSettlement settlement : importedInvoice.getTradeSettlement()) {
|
||||||
|
if (settlement instanceof IZUGFeRDTradeSettlementDebit) {
|
||||||
|
return ((IZUGFeRDTradeSettlementDebit) settlement).getIBAN();
|
||||||
|
}
|
||||||
|
if (settlement instanceof IZUGFeRDTradeSettlementPayment) {
|
||||||
|
return ((IZUGFeRDTradeSettlementPayment) settlement).getOwnIBAN();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getHolder() {
|
public String getHolder() {
|
||||||
|
|
||||||
|
|
||||||
return extractString("//*[local-name() = 'SellerTradeParty']/*[local-name() = 'Name']");
|
return extractString("//*[local-name() = 'SellerTradeParty']/*[local-name() = 'Name']");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,14 +397,8 @@ public class ZUGFeRDImporter {
|
|||||||
* @return the total payable amount
|
* @return the total payable amount
|
||||||
*/
|
*/
|
||||||
public String getAmount() {
|
public String getAmount() {
|
||||||
String result = extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']/*[local-name() = 'DuePayableAmount']");
|
|
||||||
if (result == null || result.isEmpty()) {
|
|
||||||
|
|
||||||
/* fx/zf would be SpecifiedTradeSettlementMonetarySummation
|
return importedInvoice.getGrandTotal().toPlainString();
|
||||||
* but ox is SpecifiedTradeSettlementHeaderMonetarySummation...*/
|
|
||||||
result = extractString("//*[local-name() = 'GrandTotalAmount']");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -643,7 +406,60 @@ public class ZUGFeRDImporter {
|
|||||||
* @return when the payment is due
|
* @return when the payment is due
|
||||||
*/
|
*/
|
||||||
public String getDueDate() {
|
public String getDueDate() {
|
||||||
return extractString("//*[local-name() = 'SpecifiedTradePaymentTerms']/*[local-name() = 'DueDateDateTime']/*[local-name() = 'DateTimeString']");
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
return sdf.format(importedInvoice.getDueDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the Invoice Currency Code
|
||||||
|
*/
|
||||||
|
public String getInvoiceCurrencyCode() {
|
||||||
|
return importedInvoice.getCurrency();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String extractIssuerAssignedID(String propertyName) {
|
||||||
|
try {
|
||||||
|
if (getVersion() == 1) {
|
||||||
|
return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'ID']");
|
||||||
|
} else {
|
||||||
|
return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']");
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// Exception was already logged
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the BuyerTradeParty ID
|
||||||
|
*/
|
||||||
|
public String getBuyerTradePartyID() {
|
||||||
|
String id = importedInvoice.getRecipient().getID();
|
||||||
|
if (id == null) {
|
||||||
|
// provide some fallback
|
||||||
|
id = importedInvoice.getRecipient().getVATID();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the Issue Date()
|
||||||
|
*/
|
||||||
|
public String getIssueDate() {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
return sdf.format(importedInvoice.getIssueDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDetailedDeliveryPeriodFrom() {
|
||||||
|
return importedInvoice.getDetailedDeliveryPeriodFrom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDetailedDeliveryPeriodTo() {
|
||||||
|
return importedInvoice.getDetailedDeliveryPeriodTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -691,28 +507,6 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public EStandard getStandard() throws Exception {
|
|
||||||
if (!containsMeta) {
|
|
||||||
throw new Exception("Not yet parsed");
|
|
||||||
}
|
|
||||||
final String head = getUTF8();
|
|
||||||
String rootNode = extractString("local-name(/*)");
|
|
||||||
if (rootNode.equals("CrossIndustryDocument")) {
|
|
||||||
return EStandard.zugferd;
|
|
||||||
} else if (rootNode.equals("Invoice")) {
|
|
||||||
return EStandard.ubl;
|
|
||||||
} else if (rootNode.equals("CrossIndustryInvoice")) {
|
|
||||||
return EStandard.facturx;
|
|
||||||
} else if (rootNode.equals("SCRDMCCBDACIDAMessageStructure")) {
|
|
||||||
return EStandard.despatchadvice;
|
|
||||||
} else if (head.contains("<rsm:SCRDMCCBDACIOMessageStructure")) {
|
|
||||||
return EStandard.orderx;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("ZUGFeRD version could not be determined");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVersion() throws Exception {
|
public int getVersion() throws Exception {
|
||||||
if (!containsMeta) {
|
if (!containsMeta) {
|
||||||
throw new Exception("Not yet parsed");
|
throw new Exception("Not yet parsed");
|
||||||
@@ -736,35 +530,6 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return return UTF8 XML (without BOM) of the invoice
|
|
||||||
*/
|
|
||||||
public String getUTF8() {
|
|
||||||
if (rawXML == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (rawXML.length < 3) {
|
|
||||||
return new String(rawXML);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final byte[] bomlessData;
|
|
||||||
|
|
||||||
if ((rawXML[0] == (byte) 0xEF)
|
|
||||||
&& (rawXML[1] == (byte) 0xBB)
|
|
||||||
&& (rawXML[2] == (byte) 0xBF)) {
|
|
||||||
// I don't like BOMs, lets remove it
|
|
||||||
bomlessData = new byte[rawXML.length - 3];
|
|
||||||
System.arraycopy(rawXML, 3, bomlessData, 0,
|
|
||||||
rawXML.length - 3);
|
|
||||||
} else {
|
|
||||||
bomlessData = rawXML;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new String(bomlessData);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the raw XML data as extracted from the ZUGFeRD PDF file.
|
* Returns the raw XML data as extracted from the ZUGFeRD PDF file.
|
||||||
*
|
*
|
||||||
@@ -790,14 +555,6 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static String convertStreamToString(java.io.InputStream is) {
|
|
||||||
try {
|
|
||||||
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns an instance of PostalTradeAddress for SellerTradeParty section
|
* returns an instance of PostalTradeAddress for SellerTradeParty section
|
||||||
*
|
*
|
||||||
@@ -809,7 +566,7 @@ public class ZUGFeRDImporter {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (getVersion() == 1) {
|
if (getVersion() == 1) {
|
||||||
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryDocument']//*[local-name() = 'SpecifiedSupplyChainTradeTransaction']/*[local-name() = 'ApplicableSupplyChainTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
nl = getNodeListByPath("//*[localname() = 'CrossIndustryDocument']//*[local-name() = 'SpecifiedSupplyChainTradeTransaction']/*[local-name() = 'ApplicableSupplyChainTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']");
|
||||||
} else {
|
} else {
|
||||||
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']");
|
||||||
}
|
}
|
||||||
@@ -953,7 +710,7 @@ public class ZUGFeRDImporter {
|
|||||||
if (node != null) {
|
if (node != null) {
|
||||||
final NodeList tradeAgreementChildren = node.getChildNodes();
|
final NodeList tradeAgreementChildren = node.getChildNodes();
|
||||||
node = getNodeByName(tradeAgreementChildren, "ChargeAmount");
|
node = getNodeByName(tradeAgreementChildren, "ChargeAmount");
|
||||||
lineItem.setPrice(tryBigDecimal(getNodeValue(node)));
|
lineItem.setPrice(XMLTools.tryBigDecimal(node));
|
||||||
node = getNodeByName(tradeAgreementChildren, "BasisQuantity");
|
node = getNodeByName(tradeAgreementChildren, "BasisQuantity");
|
||||||
if (node != null && node.getAttributes() != null) {
|
if (node != null && node.getAttributes() != null) {
|
||||||
final Node unitCodeAttribute = node.getAttributes().getNamedItem("unitCode");
|
final Node unitCodeAttribute = node.getAttributes().getNamedItem("unitCode");
|
||||||
@@ -966,48 +723,48 @@ public class ZUGFeRDImporter {
|
|||||||
node = getNodeByName(nn.getChildNodes(), "GrossPriceProductTradePrice");
|
node = getNodeByName(nn.getChildNodes(), "GrossPriceProductTradePrice");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "ChargeAmount");
|
node = getNodeByName(node.getChildNodes(), "ChargeAmount");
|
||||||
lineItem.setGrossPrice(tryBigDecimal(getNodeValue(node)));
|
lineItem.setGrossPrice(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "AssociatedDocumentLineDocument":
|
case "AssociatedDocumentLineDocument":
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "LineID");
|
node = getNodeByName(nn.getChildNodes(), "LineID");
|
||||||
lineItem.setId(getNodeValue(node));
|
lineItem.setId(XMLTools.getNodeValue(node));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SpecifiedTradeProduct":
|
case "SpecifiedTradeProduct":
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "SellerAssignedID");
|
node = getNodeByName(nn.getChildNodes(), "SellerAssignedID");
|
||||||
lineItem.getProduct().setSellerAssignedID(getNodeValue(node));
|
lineItem.getProduct().setSellerAssignedID(XMLTools.getNodeValue(node));
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "BuyerAssignedID");
|
node = getNodeByName(nn.getChildNodes(), "BuyerAssignedID");
|
||||||
lineItem.getProduct().setBuyerAssignedID(getNodeValue(node));
|
lineItem.getProduct().setBuyerAssignedID(XMLTools.getNodeValue(node));
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "Name");
|
node = getNodeByName(nn.getChildNodes(), "Name");
|
||||||
lineItem.getProduct().setName(getNodeValue(node));
|
lineItem.getProduct().setName(XMLTools.getNodeValue(node));
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "Description");
|
node = getNodeByName(nn.getChildNodes(), "Description");
|
||||||
lineItem.getProduct().setDescription(getNodeValue(node));
|
lineItem.getProduct().setDescription(XMLTools.getNodeValue(node));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SpecifiedLineTradeDelivery":
|
case "SpecifiedLineTradeDelivery":
|
||||||
case "SpecifiedSupplyChainTradeDelivery":
|
case "SpecifiedSupplyChainTradeDelivery":
|
||||||
node = getNodeByName(nn.getChildNodes(), "BilledQuantity");
|
node = getNodeByName(nn.getChildNodes(), "BilledQuantity");
|
||||||
lineItem.setQuantity(tryBigDecimal(getNodeValue(node)));
|
lineItem.setQuantity(XMLTools.tryBigDecimal(node));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SpecifiedLineTradeSettlement":
|
case "SpecifiedLineTradeSettlement":
|
||||||
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "RateApplicablePercent");
|
node = getNodeByName(node.getChildNodes(), "RateApplicablePercent");
|
||||||
lineItem.getProduct().setVATPercent(tryBigDecimal(getNodeValue(node)));
|
lineItem.getProduct().setVATPercent(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "CalculatedAmount");
|
node = getNodeByName(node.getChildNodes(), "CalculatedAmount");
|
||||||
lineItem.setTax(tryBigDecimal(getNodeValue(node)));
|
lineItem.setTax(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
node = getNodeByName(nn.getChildNodes(), "BillingSpecifiedPeriod");
|
node = getNodeByName(nn.getChildNodes(), "BillingSpecifiedPeriod");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
@@ -1021,13 +778,13 @@ public class ZUGFeRDImporter {
|
|||||||
if (end != null) {
|
if (end != null) {
|
||||||
dateTimeEnd = getNodeByName(end.getChildNodes(), "DateTimeString");
|
dateTimeEnd = getNodeByName(end.getChildNodes(), "DateTimeString");
|
||||||
}
|
}
|
||||||
lineItem.setDetailedDeliveryPeriod(tryDate(dateTimeStart), tryDate(dateTimeEnd));
|
lineItem.setDetailedDeliveryPeriod(XMLTools.tryDate(dateTimeStart), XMLTools.tryDate(dateTimeEnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementLineMonetarySummation");
|
node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementLineMonetarySummation");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "LineTotalAmount");
|
node = getNodeByName(node.getChildNodes(), "LineTotalAmount");
|
||||||
lineItem.setLineTotalAmount(tryBigDecimal(getNodeValue(node)));
|
lineItem.setLineTotalAmount(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "SpecifiedSupplyChainTradeSettlement":
|
case "SpecifiedSupplyChainTradeSettlement":
|
||||||
@@ -1036,19 +793,19 @@ public class ZUGFeRDImporter {
|
|||||||
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "ApplicablePercent");
|
node = getNodeByName(node.getChildNodes(), "ApplicablePercent");
|
||||||
lineItem.getProduct().setVATPercent(tryBigDecimal(getNodeValue(node)));
|
lineItem.getProduct().setVATPercent(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
node = getNodeByName(nn.getChildNodes(), "ApplicableTradeTax");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "CalculatedAmount");
|
node = getNodeByName(node.getChildNodes(), "CalculatedAmount");
|
||||||
lineItem.setTax(tryBigDecimal(getNodeValue(node)));
|
lineItem.setTax(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
|
|
||||||
node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementMonetarySummation");
|
node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementMonetarySummation");
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
node = getNodeByName(node.getChildNodes(), "LineTotalAmount");
|
node = getNodeByName(node.getChildNodes(), "LineTotalAmount");
|
||||||
lineItem.setLineTotalAmount(tryBigDecimal(getNodeValue(node)));
|
lineItem.setLineTotalAmount(XMLTools.tryBigDecimal(node));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1123,51 +880,4 @@ public class ZUGFeRDImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns the value of an node
|
|
||||||
*
|
|
||||||
* @param node the Node to get the value from
|
|
||||||
* @return A String or empty String, if no value was found
|
|
||||||
*/
|
|
||||||
private String getNodeValue(Node node) {
|
|
||||||
if (node != null && node.getFirstChild() != null) {
|
|
||||||
return node.getFirstChild().getNodeValue();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* tries to convert an String to BigDecimal.
|
|
||||||
*
|
|
||||||
* @param nodeValue The value as String
|
|
||||||
* @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs
|
|
||||||
*/
|
|
||||||
private BigDecimal tryBigDecimal(String nodeValue) {
|
|
||||||
try {
|
|
||||||
return new BigDecimal(nodeValue);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
try {
|
|
||||||
return BigDecimal.valueOf(Float.valueOf(nodeValue));
|
|
||||||
} catch (final Exception ex) {
|
|
||||||
return new BigDecimal("0.00");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Date tryDate(Node node) {
|
|
||||||
final String nodeValue = getNodeValue(node);
|
|
||||||
if (nodeValue.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return tryDate(nodeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Date tryDate(String toParse) {
|
|
||||||
final SimpleDateFormat formatter = ZUGFeRDDateFormat.DATE.getFormatter();
|
|
||||||
try {
|
|
||||||
return formatter.parse(toParse);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
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.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -11,43 +13,272 @@ import java.util.Base64;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
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.XPath;
|
||||||
import javax.xml.xpath.XPathConstants;
|
import javax.xml.xpath.XPathConstants;
|
||||||
import javax.xml.xpath.XPathExpression;
|
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.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.pdfbox.Loader;
|
||||||
|
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.mustangproject.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
public class ZUGFeRDInvoiceImporter {
|
||||||
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 ignoreCalculationErrors = false;
|
|
||||||
private ArrayList<FileAttachment> fileAttachments=new ArrayList<>();
|
|
||||||
|
|
||||||
public ZUGFeRDInvoiceImporter() {
|
/**
|
||||||
super();
|
* if metadata has been found
|
||||||
|
*/
|
||||||
|
protected boolean containsMeta = false;
|
||||||
|
/**
|
||||||
|
* map filenames of additional XML files to their contents
|
||||||
|
*/
|
||||||
|
protected final HashMap<String, byte[]> additionalXMLs = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* map filenames of all embedded files in the respective PDF
|
||||||
|
*/
|
||||||
|
protected final ArrayList<FileAttachment> PDFAttachments = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* Raw XML form of the extracted data - may be directly obtained.
|
||||||
|
*/
|
||||||
|
protected byte[] rawXML = null;
|
||||||
|
/**
|
||||||
|
* XMP metadata
|
||||||
|
*/
|
||||||
|
protected String xmpString = null; // XMP metadata
|
||||||
|
/**
|
||||||
|
* parsed Document
|
||||||
|
*/
|
||||||
|
protected Document document;
|
||||||
|
/***
|
||||||
|
* automatically parse into importedInvoice
|
||||||
|
*/
|
||||||
|
protected boolean parseAutomatically = true;
|
||||||
|
protected Integer version;
|
||||||
|
protected CalculatedInvoice importedInvoice = null;
|
||||||
|
protected boolean recalcPrice = false;
|
||||||
|
protected boolean ignoreCalculationErrors = false;
|
||||||
|
protected ArrayList<FileAttachment> fileAttachments = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
protected ZUGFeRDInvoiceImporter() {
|
||||||
|
//constructor for extending classes
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZUGFeRDInvoiceImporter(String filename) {
|
public ZUGFeRDInvoiceImporter(String pdfFilename) {
|
||||||
super(filename);
|
setPDFFilename(pdfFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZUGFeRDInvoiceImporter(InputStream stream) {
|
public ZUGFeRDInvoiceImporter(InputStream pdfStream) {
|
||||||
super(stream);
|
setInputStream(pdfStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fromXML(String XML) {
|
public void setPDFFilename(String pdfFilename){
|
||||||
|
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
|
||||||
|
extractLowLevel(bis);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
||||||
|
throw new ZUGFeRDExportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputStream(InputStream pdfStream) {
|
||||||
try {
|
try {
|
||||||
|
extractLowLevel(pdfStream);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOGGER.error("Failed to extract ZUGFeRD data", e);
|
||||||
|
throw new ZUGFeRDExportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* return the file names of all files embedded into the PDF
|
||||||
|
* @see for XML embedded files please use ZUGFeRDInvoiceImporter.getFileAttachmentsXML
|
||||||
|
* @return a ArrayList of FileAttachments, empty if none
|
||||||
|
*/
|
||||||
|
public List<FileAttachment> getFileAttachmentsPDF() {
|
||||||
|
return PDFAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
|
||||||
|
*
|
||||||
|
* @param inStream a inputstream of a pdf file
|
||||||
|
*/
|
||||||
|
private void extractLowLevel(InputStream inStream) throws IOException {
|
||||||
|
BufferedInputStream pdfStream = new BufferedInputStream(inStream);
|
||||||
|
byte[] pad = new byte[4];
|
||||||
|
pdfStream.mark(0);
|
||||||
|
pdfStream.read(pad);
|
||||||
|
pdfStream.reset();
|
||||||
|
byte[] pdfSignature = {'%', 'P', 'D', 'F'};
|
||||||
|
if (Arrays.equals(pad, pdfSignature)) { // we have a pdf
|
||||||
|
|
||||||
|
|
||||||
|
try (PDDocument doc = Loader.loadPDF(IOUtils.toByteArray(pdfStream))) {
|
||||||
|
// PDDocumentInformation info = doc.getDocumentInformation();
|
||||||
|
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
|
||||||
|
//start
|
||||||
|
|
||||||
|
if (doc.getDocumentCatalog() == null || doc.getDocumentCatalog().getMetadata() == null) {
|
||||||
|
LOGGER.info("no-xmlpart");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata();
|
||||||
|
xmpString = convertStreamToString(XMP);
|
||||||
|
|
||||||
|
final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles();
|
||||||
|
if (etn == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, PDComplexFileSpecification> efMap = etn.getNames();
|
||||||
|
// String filePath = "/tmp/";
|
||||||
|
|
||||||
|
if (efMap != null) {
|
||||||
|
extractFiles(efMap); // see
|
||||||
|
// https://memorynotfound.com/apache-pdfbox-extract-embedded-file-pdf-document/
|
||||||
|
} else {
|
||||||
|
|
||||||
|
final List<PDNameTreeNode<PDComplexFileSpecification>> kids = etn.getKids();
|
||||||
|
if (kids == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (final PDNameTreeNode<PDComplexFileSpecification> node : kids) {
|
||||||
|
final Map<String, PDComplexFileSpecification> namesL = node.getNames();
|
||||||
|
extractFiles(namesL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no PDF probably XML
|
||||||
containsMeta = true;
|
containsMeta = true;
|
||||||
setRawXML(XML.getBytes(StandardCharsets.UTF_8));
|
setRawXML(XMLTools.getBytesFromStream(pdfStream));
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* have the item prices be determined from the line total.
|
||||||
|
* That's a workaround for some invoices which just put 0 as item price
|
||||||
|
*/
|
||||||
|
public void doRecalculateItemPricesFromLineTotals() {
|
||||||
|
recalcPrice = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* do not raise ParseExceptions even if the reproduced invoice total does not match the given value
|
||||||
|
*/
|
||||||
|
public void doIgnoreCalculationErrors() {
|
||||||
|
ignoreCalculationErrors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* sets th pdf attachments, and if a file is recognized (e.g. a factur-x.xml) triggers processing
|
||||||
|
* @param names the Hashmap of String, PDComplexFileSpecification
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void extractFiles(Map<String, PDComplexFileSpecification> names) throws IOException {
|
||||||
|
for (final String alias : names.keySet()) {
|
||||||
|
|
||||||
|
final PDComplexFileSpecification fileSpec = names.get(alias);
|
||||||
|
final String filename = fileSpec.getFilename();
|
||||||
|
/**
|
||||||
|
* 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")) {
|
||||||
|
containsMeta = true;
|
||||||
|
|
||||||
|
// String embeddedFilename = filePath + filename;
|
||||||
|
// File file = new File(filePath + filename);
|
||||||
|
// System.out.println("Writing " + embeddedFilename);
|
||||||
|
// ByteArrayOutputStream fileBytes=new
|
||||||
|
// ByteArrayOutputStream();
|
||||||
|
// FileOutputStream fos = new FileOutputStream(file);
|
||||||
|
|
||||||
|
setRawXML(embeddedFile.toByteArray());
|
||||||
|
|
||||||
|
// fos.write(embeddedFile.getByteArray());
|
||||||
|
// fos.close();
|
||||||
|
}
|
||||||
|
if (filename.startsWith("additional_data")) {
|
||||||
|
additionalXMLs.put(filename, embeddedFile.toByteArray());
|
||||||
|
}
|
||||||
|
PDFAttachments.add(new FileAttachment(filename, embeddedFile.getSubtype(), "Data", embeddedFile.toByteArray()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* set the xml of a CII invoice
|
||||||
|
* @param rawXML the xml string
|
||||||
|
* @param doParse automatically parse input for zugferdImporter (not ZUGFeRDInvoiceImporter)
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void setRawXML(byte[] rawXML, boolean doParse) throws IOException {
|
||||||
|
this.containsMeta = true;
|
||||||
|
this.rawXML = rawXML;
|
||||||
|
this.version = null;
|
||||||
|
parseAutomatically = doParse;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setDocument();
|
||||||
|
} catch (ParserConfigurationException | SAXException e) {
|
||||||
|
LOGGER.error("Failed to parse XML", e);
|
||||||
|
throw new ZUGFeRDExportException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* set the xml of a CII invoice, simple version
|
||||||
|
* @param rawXML the cii(?) as a string
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void setRawXML(byte[] rawXML) throws IOException {
|
||||||
|
setRawXML(rawXML, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDocument() throws ParserConfigurationException, IOException, SAXException {
|
||||||
|
final DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
|
||||||
|
xmlFact.setNamespaceAware(true);
|
||||||
|
final DocumentBuilder builder = xmlFact.newDocumentBuilder();
|
||||||
|
final ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
|
||||||
|
/// is.skip(guessBOMSize(is));
|
||||||
|
document = builder.parse(is);
|
||||||
|
if (parseAutomatically) {
|
||||||
|
try {
|
||||||
|
importedInvoice = new CalculatedInvoice();
|
||||||
|
extractInto(importedInvoice);
|
||||||
|
} catch (XPathExpressionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +294,8 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
String number = "";
|
String number = "";
|
||||||
String typeCode = null;
|
String typeCode = null;
|
||||||
|
String deliveryPeriodStart = null;
|
||||||
|
String deliveryPeriodEnd = null;
|
||||||
/*
|
/*
|
||||||
* dummywerte sind derzeit noch setDueDate setIssueDate setDeliveryDate
|
* dummywerte sind derzeit noch setDueDate setIssueDate setDeliveryDate
|
||||||
* setSender setRecipient setnumber bspw. due date
|
* setSender setRecipient setnumber bspw. due date
|
||||||
@@ -72,6 +305,12 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
XPath xpath = xpathFact.newXPath();
|
XPath xpath = xpathFact.newXPath();
|
||||||
XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*");
|
XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*");
|
||||||
NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
|
XPathExpression shipEx = xpath.compile("//*[local-name()=\"ShipToTradeParty\"]");
|
||||||
|
NodeList deliveryNodes = (NodeList) shipEx.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
|
if (deliveryNodes!=null) {
|
||||||
|
zpp.setDeliveryAddress(new TradeParty(deliveryNodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*");
|
xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*");
|
||||||
NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
@@ -87,6 +326,12 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
NodeList totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList totalNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
if (totalNodes.getLength() > 0) {
|
if (totalNodes.getLength() > 0) {
|
||||||
expectedGrandTotal = new BigDecimal(totalNodes.item(0).getTextContent());
|
expectedGrandTotal = new BigDecimal(totalNodes.item(0).getTextContent());
|
||||||
|
if (zpp instanceof CalculatedInvoice) {
|
||||||
|
// usually we would re-calculate the invoice to get expectedGrandTotal
|
||||||
|
// however, for "minimal" invoices or other invoices without lines
|
||||||
|
// this will not work
|
||||||
|
((CalculatedInvoice) zpp).setGrandTotal(expectedGrandTotal);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]");
|
xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]");
|
||||||
@@ -209,6 +454,8 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String currency = extractString("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]/*[local-name()=\"InvoiceCurrencyCode\"]|*[local-name()=\"DocumentCurrencyCode\"]");
|
||||||
|
zpp.setCurrency(currency);
|
||||||
|
|
||||||
xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]");
|
xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]");
|
||||||
NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
@@ -265,9 +512,37 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
bankDetails.add(bd);
|
bankDetails.add(bd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null)
|
||||||
|
&& (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("BillingSpecifiedPeriod"))) {
|
||||||
|
NodeList periodChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes();
|
||||||
|
for (int periodChildIndex = 0; periodChildIndex < periodChilds.getLength(); periodChildIndex++) {
|
||||||
|
if ((periodChilds.item(periodChildIndex).getLocalName() != null) && (periodChilds.item(periodChildIndex).getLocalName().equals("StartDateTime"))) {
|
||||||
|
|
||||||
|
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 = startPeriodChilds.item(startPeriodIndex).getTextContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((periodChilds.item(periodChildIndex).getLocalName() != null) && (periodChilds.item(periodChildIndex).getLocalName().equals("EndDateTime"))) {
|
||||||
|
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 = endPeriodChilds.item(endPeriodIndex).getTextContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((deliveryPeriodStart != null) && (deliveryPeriodEnd != null)) {
|
||||||
|
zpp.setDetailedDeliveryPeriod(XMLTools.tryDate(deliveryPeriodStart), XMLTools.tryDate(deliveryPeriodEnd));
|
||||||
|
} else if (deliveryPeriodStart != null) {
|
||||||
|
zpp.setDeliveryDate(XMLTools.tryDate(deliveryPeriodStart));
|
||||||
|
}
|
||||||
|
|
||||||
xpr = xpath.compile("//*[local-name()=\"PaymentMeans\"]"); //UBL only
|
xpr = xpath.compile("//*[local-name()=\"PaymentMeans\"]"); //UBL only
|
||||||
NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
@@ -339,9 +614,9 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]");
|
xpr = xpath.compile("//*[local-name()=\"AttachmentBinaryObject\"]|//*[local-name()=\"EmbeddedDocumentBinaryObject\"]");
|
||||||
NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
NodeList attachmentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
|
||||||
for (int i = 0; i < attachmentNodes.getLength(); i++) {
|
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(attachmentNodes.item(i).getTextContent()));
|
FileAttachment fa = new FileAttachment(attachmentNodes.item(i).getAttributes().getNamedItem("filename").getNodeValue(), attachmentNodes.item(i).getAttributes().getNamedItem("mimeCode").getNodeValue(), "Data", Base64.getDecoder().decode(attachmentNodes.item(i).getTextContent()));
|
||||||
fileAttachments.add(fa);
|
fileAttachments.add(fa);
|
||||||
// filename = "Aufmass.png" mimeCode = "image/png"
|
// filename = "Aufmass.png" mimeCode = "image/png"
|
||||||
//EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png"
|
//EmbeddedDocumentBinaryObject cbc:EmbeddedDocumentBinaryObject mimeCode="image/png" filename="Aufmass.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,6 +713,90 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Document getDocument() {
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected String extractString(String xpathStr) {
|
||||||
|
if (!containsMeta) {
|
||||||
|
throw new ZUGFeRDExportException("No suitable data/ZUGFeRD file could be found.");
|
||||||
|
}
|
||||||
|
final String result;
|
||||||
|
try {
|
||||||
|
final Document document = getDocument();
|
||||||
|
final XPathFactory xpathFact = XPathFactory.newInstance();
|
||||||
|
final XPath xpath = xpathFact.newXPath();
|
||||||
|
result = xpath.evaluate(xpathStr, document);
|
||||||
|
} catch (final XPathExpressionException e) {
|
||||||
|
LOGGER.error("Failed to evaluate XPath", e);
|
||||||
|
throw new ZUGFeRDExportException(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EStandard getStandard() throws Exception {
|
||||||
|
if (!containsMeta) {
|
||||||
|
throw new Exception("Not yet parsed");
|
||||||
|
}
|
||||||
|
final String head = getUTF8();
|
||||||
|
String rootNode = extractString("local-name(/*)");
|
||||||
|
if (rootNode.equals("CrossIndustryDocument")) {
|
||||||
|
return EStandard.zugferd;
|
||||||
|
} else if (rootNode.equals("Invoice")) {
|
||||||
|
return EStandard.ubl;
|
||||||
|
} else if (rootNode.equals("CreditNote")) {
|
||||||
|
return EStandard.ubl;
|
||||||
|
} else if (rootNode.equals("CrossIndustryInvoice")) {
|
||||||
|
return EStandard.facturx;
|
||||||
|
} else if (rootNode.equals("SCRDMCCBDACIDAMessageStructure")) {
|
||||||
|
return EStandard.despatchadvice;
|
||||||
|
} else if (head.contains("<rsm:SCRDMCCBDACIOMessageStructure")) {
|
||||||
|
return EStandard.orderx;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("ZUGFeRD version could not be determined");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return UTF8 XML (without BOM) of the invoice
|
||||||
|
*/
|
||||||
|
public String getUTF8() {
|
||||||
|
if (rawXML == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (rawXML.length < 3) {
|
||||||
|
return new String(rawXML);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final byte[] bomlessData;
|
||||||
|
|
||||||
|
if ((rawXML[0] == (byte) 0xEF)
|
||||||
|
&& (rawXML[1] == (byte) 0xBB)
|
||||||
|
&& (rawXML[2] == (byte) 0xBF)) {
|
||||||
|
// I don't like BOMs, lets remove it
|
||||||
|
bomlessData = new byte[rawXML.length - 3];
|
||||||
|
System.arraycopy(rawXML, 3, bomlessData, 0,
|
||||||
|
rawXML.length - 3);
|
||||||
|
} else {
|
||||||
|
bomlessData = rawXML;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new String(bomlessData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static String convertStreamToString(java.io.InputStream is) {
|
||||||
|
try {
|
||||||
|
return IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
*
|
*
|
||||||
* @return the file attachments embedded in XML (using base64) decoded as byte array,
|
* @return the file attachments embedded in XML (using base64) decoded as byte array,
|
||||||
@@ -461,18 +820,18 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
|
||||||
* have the item prices be determined from the line total.
|
|
||||||
* That's a workaround for some invoices which just put 0 as item price
|
|
||||||
*/
|
|
||||||
public void doRecalculateItemPricesFromLineTotals() {
|
|
||||||
recalcPrice = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* do not raise ParseExceptions even if the reproduced invoice total does not match the given value
|
* sets the XML for the importer to parse
|
||||||
|
* @param XML the UBL or CII
|
||||||
*/
|
*/
|
||||||
public void doIgnoreCalculationErrors() {
|
public void fromXML(String XML) {
|
||||||
ignoreCalculationErrors = true;
|
try {
|
||||||
|
containsMeta = true;
|
||||||
|
setRawXML(XML.getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,8 +214,10 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
|
|||||||
|
|
||||||
public void testForeignImport() {
|
public void testForeignImport() {
|
||||||
InputStream inputStream = this.getClass().getResourceAsStream("/zugferd_invoice.pdf");
|
InputStream inputStream = this.getClass().getResourceAsStream("/zugferd_invoice.pdf");
|
||||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(inputStream);
|
ZUGFeRDImporter zi = new ZUGFeRDImporter();
|
||||||
|
zi.doRecalculateItemPricesFromLineTotals();
|
||||||
|
zi.doIgnoreCalculationErrors();
|
||||||
|
zi.setInputStream(inputStream);
|
||||||
// Reading ZUGFeRD
|
// Reading ZUGFeRD
|
||||||
String amount = zi.getAmount();
|
String amount = zi.getAmount();
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ public class ProfilesMinimumBasicWLTest extends TestCase {
|
|||||||
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM_INV);
|
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_PDF_FX_MINIMUM_INV);
|
||||||
|
|
||||||
// Reading ZUGFeRD
|
// Reading ZUGFeRD
|
||||||
assertEquals("145.37",zi.getAmount());
|
assertEquals("146.37",zi.getAmount());
|
||||||
// assertEquals(zi.getBIC(), ownBIC);
|
// assertEquals(zi.getBIC(), ownBIC);
|
||||||
// assertEquals(zi.getIBAN(), ownIBAN);
|
// assertEquals(zi.getIBAN(), ownIBAN);
|
||||||
assertEquals(ownOrgName, zi.getHolder());
|
assertEquals(ownOrgName, zi.getHolder());
|
||||||
|
|||||||
@@ -129,13 +129,12 @@ public class XRTest extends TestCase {
|
|||||||
Invoice readInvoice = new Invoice();
|
Invoice readInvoice = new Invoice();
|
||||||
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
|
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
|
||||||
try {
|
try {
|
||||||
|
zii.setRawXML(zf2p.getXML(), false);
|
||||||
zii.setRawXML(zf2p.getXML());
|
|
||||||
zii.extractInto(readInvoice);
|
zii.extractInto(readInvoice);
|
||||||
} catch (ParseException | XPathExpressionException xp) {
|
} catch (ParseException | XPathExpressionException xp) {
|
||||||
fail("Exception not expected");
|
fail("ParseException not expected");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
fail("IOException not expected");
|
||||||
}
|
}
|
||||||
List<FileAttachment> attachedFiles=zii.getFileAttachmentsXML();
|
List<FileAttachment> attachedFiles=zii.getFileAttachmentsXML();
|
||||||
assertNotNull(attachedFiles);
|
assertNotNull(attachedFiles);
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
|
|
||||||
/** **********************************************************************
|
/**
|
||||||
*
|
* *********************************************************************
|
||||||
|
* <p>
|
||||||
* Copyright 2019 Jochen Staerk
|
* Copyright 2019 Jochen Staerk
|
||||||
*
|
* <p>
|
||||||
* Use is subject to license terms.
|
* Use is subject to license terms.
|
||||||
*
|
* <p>
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
* use this file except in compliance with the License. You may obtain a copy
|
* use this file except in compliance with the License. You may obtain a copy
|
||||||
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
*
|
* <p>
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
*
|
* <p>
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
* <p>
|
||||||
*********************************************************************** */
|
* **********************************************************************
|
||||||
|
*/
|
||||||
package org.mustangproject.ZUGFeRD;
|
package org.mustangproject.ZUGFeRD;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -99,9 +101,9 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
public IZUGFeRDExportableItem[] getZFItems() {
|
public IZUGFeRDExportableItem[] getZFItems() {
|
||||||
final Item[] allItems = new Item[3];
|
final Item[] allItems = new Item[3];
|
||||||
final Product designProduct = new Product("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung", "HUR",
|
final Product designProduct = new Product("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung", "HUR",
|
||||||
new BigDecimal("7.000000"));
|
new BigDecimal("7.000000"));
|
||||||
final Product balloonProduct = new Product("", "Bestellerweiterung für E&F Umbau", "C62",
|
final Product balloonProduct = new Product("", "Bestellerweiterung für E&F Umbau", "C62",
|
||||||
new BigDecimal("19.000000"));// test for issue 103
|
new BigDecimal("19.000000"));// test for issue 103
|
||||||
final Product airProduct = new Product("", "Heiße Luft pro Liter", "LTR", new BigDecimal("19.000000"));
|
final Product airProduct = new Product("", "Heiße Luft pro Liter", "LTR", new BigDecimal("19.000000"));
|
||||||
|
|
||||||
allItems[0] = new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct);
|
allItems[0] = new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct);
|
||||||
@@ -166,12 +168,12 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
// the writing part
|
// the writing part
|
||||||
|
|
||||||
try (InputStream SOURCE_PDF = this.getClass()
|
try (InputStream SOURCE_PDF = this.getClass()
|
||||||
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf");
|
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf");
|
||||||
|
|
||||||
ZUGFeRDExporterFromA3 ze = new ZUGFeRDExporterFromA3().setProducer("My Application")
|
ZUGFeRDExporterFromA3 ze = new ZUGFeRDExporterFromA3().setProducer("My Application")
|
||||||
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("EN16931")
|
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("EN16931")
|
||||||
.load(SOURCE_PDF)) {
|
.load(SOURCE_PDF)) {
|
||||||
|
|
||||||
ze.setTransaction(this);
|
ze.setTransaction(this);
|
||||||
final String theXML = new String(ze.getProvider().getXML());
|
final String theXML = new String(ze.getProvider().getXML());
|
||||||
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
|
||||||
@@ -190,13 +192,13 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
assertEquals(zi.getInvoiceID(), "RE-20170509/505");
|
assertEquals(zi.getInvoiceID(), "RE-20170509/505");
|
||||||
assertEquals(zi.getZUGFeRDProfil(), "COMFORT");
|
assertEquals(zi.getZUGFeRDProfil(), "COMFORT");
|
||||||
assertEquals(zi.getInvoiceCurrencyCode(), "EUR");
|
assertEquals(zi.getInvoiceCurrencyCode(), "EUR");
|
||||||
assertEquals(zi.getIssuerAssignedID(),"");
|
assertEquals(zi.getIssuerAssignedID(), "");
|
||||||
assertEquals(zi.getIssueDate(), "20170509");
|
assertEquals(zi.getIssueDate(), "20170509");
|
||||||
assertEquals(zi.getTaxPointDate(), "20170507");
|
assertEquals(zi.getTaxPointDate(), "20170507");
|
||||||
assertEquals(zi.getPaymentTerms(), "Zahlbar ohne Abzug bis zum 30.05.2017");
|
assertEquals(zi.getPaymentTerms(), "Zahlbar ohne Abzug bis zum 30.05.2017");
|
||||||
assertEquals(zi.getLineTotalAmount(), "496.00");
|
assertEquals(zi.getLineTotalAmount(), "496.00");
|
||||||
assertEquals(zi.getTaxBasisTotalAmount(), "496.00");
|
assertEquals(zi.getTaxBasisTotalAmount(), "496.00");
|
||||||
assertEquals(zi.getTaxTotalAmount(),"75.04");
|
assertEquals(zi.getTaxTotalAmount(), "75.04");
|
||||||
assertEquals(zi.getRoundingAmount(), "");
|
assertEquals(zi.getRoundingAmount(), "");
|
||||||
assertEquals(zi.getPaidAmount(), "0.00");
|
assertEquals(zi.getPaidAmount(), "0.00");
|
||||||
assertEquals(zi.getBuyerTradePartyName(), "Theodor Est");
|
assertEquals(zi.getBuyerTradePartyName(), "Theodor Est");
|
||||||
@@ -206,8 +208,8 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
assertEquals(zi.getBuyertradePartySpecifiedTaxRegistrationID(), "DE999999999");
|
assertEquals(zi.getBuyertradePartySpecifiedTaxRegistrationID(), "DE999999999");
|
||||||
assertEquals(zi.getIncludedNote(), "");
|
assertEquals(zi.getIncludedNote(), "");
|
||||||
assertEquals(zi.getHolder(), getOwnOrganisationName());
|
assertEquals(zi.getHolder(), getOwnOrganisationName());
|
||||||
assertEquals(zi.getDocumentCode(),"380");
|
assertEquals(zi.getDocumentCode(), "380");
|
||||||
assertEquals(zi.getReference(),"AB321");
|
assertEquals(zi.getReference(), "AB321");
|
||||||
assertEquals(zi.getAmount(), "571.04");
|
assertEquals(zi.getAmount(), "571.04");
|
||||||
assertEquals(zi.getBIC(), "COBADEFFXXX");
|
assertEquals(zi.getBIC(), "COBADEFFXXX");
|
||||||
assertEquals(zi.getIBAN(), "DE88 2008 0000 0970 3757 00");
|
assertEquals(zi.getIBAN(), "DE88 2008 0000 0970 3757 00");
|
||||||
@@ -244,7 +246,9 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} /**
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* The exporter test bases on @{code
|
* The exporter test bases on @{code
|
||||||
* ./src/test/MustangBeispiel20221026.pdf}, adds
|
* ./src/test/MustangBeispiel20221026.pdf}, adds
|
||||||
* metadata, writes to @{code ./target/testout-*} and then imports to check the
|
* metadata, writes to @{code ./target/testout-*} and then imports to check the
|
||||||
@@ -264,7 +268,7 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
assertEquals("Innerhalb von 30 Tagen 2% Skonto, 60 Tage ohne Abzug", zi.getPaymentTerms());
|
assertEquals("Innerhalb von 30 Tagen 2% Skonto, 60 Tage ohne Abzug", zi.getPaymentTerms());
|
||||||
assertEquals("804.35", zi.getLineTotalAmount());
|
assertEquals("804.35", zi.getLineTotalAmount());
|
||||||
assertEquals("809.34", zi.getTaxBasisTotalAmount());
|
assertEquals("809.34", zi.getTaxBasisTotalAmount());
|
||||||
assertEquals("153.77",zi.getTaxTotalAmount());
|
assertEquals("153.77", zi.getTaxTotalAmount());
|
||||||
assertEquals("", zi.getRoundingAmount());
|
assertEquals("", zi.getRoundingAmount());
|
||||||
assertEquals("0.00", zi.getPaidAmount());
|
assertEquals("0.00", zi.getPaidAmount());
|
||||||
assertEquals("Beispiel AG", zi.getBuyerTradePartyName());
|
assertEquals("Beispiel AG", zi.getBuyerTradePartyName());
|
||||||
@@ -273,8 +277,8 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
assertEquals("10000", zi.getBuyerTradePartyID());
|
assertEquals("10000", zi.getBuyerTradePartyID());
|
||||||
assertEquals("\n weclapp.com\nSomestreet 42\n08155 Some city\nDE\n ", zi.getIncludedNote());
|
assertEquals("\n weclapp.com\nSomestreet 42\n08155 Some city\nDE\n ", zi.getIncludedNote());
|
||||||
assertEquals("weclapp.com", zi.getHolder());
|
assertEquals("weclapp.com", zi.getHolder());
|
||||||
assertEquals("380",zi.getDocumentCode());
|
assertEquals("380", zi.getDocumentCode());
|
||||||
assertEquals("01-95",zi.getReference());
|
assertEquals("01-95", zi.getReference());
|
||||||
assertEquals("RE1001", zi.getForeignReference());
|
assertEquals("RE1001", zi.getForeignReference());
|
||||||
assertEquals("54321", zi.getBuyerTradePartyAddress().getPostcodeCode());
|
assertEquals("54321", zi.getBuyerTradePartyAddress().getPostcodeCode());
|
||||||
assertEquals("Feldstraße 34", zi.getBuyerTradePartyAddress().getLineOne());
|
assertEquals("Feldstraße 34", zi.getBuyerTradePartyAddress().getLineOne());
|
||||||
@@ -284,13 +288,13 @@ public class ZF2Test extends MustangReaderTestCase {
|
|||||||
assertEquals("DE", zi.getBuyerTradePartyAddress().getCountryID());
|
assertEquals("DE", zi.getBuyerTradePartyAddress().getCountryID());
|
||||||
assertEquals("Hithausen", zi.getBuyerTradePartyAddress().getCityName());
|
assertEquals("Hithausen", zi.getBuyerTradePartyAddress().getCityName());
|
||||||
assertEquals("Beispiel Lager AG", zi.getDeliveryTradePartyName());
|
assertEquals("Beispiel Lager AG", zi.getDeliveryTradePartyName());
|
||||||
assertEquals("54321", zi.getDeliveryTradePartyAddress().getPostcodeCode());
|
assertEquals("54321", zi.getDeliveryTradePartyAddress().getPostcodeCode());
|
||||||
assertEquals("Feldstraße 39", zi.getDeliveryTradePartyAddress().getLineOne());
|
assertEquals("Feldstraße 39", zi.getDeliveryTradePartyAddress().getLineOne());
|
||||||
assertEquals(null, zi.getDeliveryTradePartyAddress().getLineTwo());
|
assertEquals(null, zi.getDeliveryTradePartyAddress().getLineTwo());
|
||||||
assertEquals(null, zi.getDeliveryTradePartyAddress().getLineThree());
|
assertEquals(null, zi.getDeliveryTradePartyAddress().getLineThree());
|
||||||
assertEquals(null, zi.getDeliveryTradePartyAddress().getCountrySubDivisionName());
|
assertEquals(null, zi.getDeliveryTradePartyAddress().getCountrySubDivisionName());
|
||||||
assertEquals("DE", zi.getDeliveryTradePartyAddress().getCountryID());
|
assertEquals("DE", zi.getDeliveryTradePartyAddress().getCountryID());
|
||||||
assertEquals("Hithausen", zi.getDeliveryTradePartyAddress().getCityName());
|
assertEquals("Hithausen", zi.getDeliveryTradePartyAddress().getCityName());
|
||||||
assertEquals("08155", zi.getSellerTradePartyAddress().getPostcodeCode());
|
assertEquals("08155", zi.getSellerTradePartyAddress().getPostcodeCode());
|
||||||
assertEquals("Somestreet 42", zi.getSellerTradePartyAddress().getLineOne());
|
assertEquals("Somestreet 42", zi.getSellerTradePartyAddress().getLineOne());
|
||||||
assertEquals(null, zi.getSellerTradePartyAddress().getLineTwo());
|
assertEquals(null, zi.getSellerTradePartyAddress().getLineTwo());
|
||||||
|
|||||||
Reference in New Issue
Block a user