Merge branch 'master' into issues/503-ubl

# Conflicts:
#	library/src/main/java/org/mustangproject/TradeParty.java
#	library/src/test/java/org/mustangproject/ZUGFeRD/ZF2ZInvoiceImporterTest.java
This commit is contained in:
jstaerk
2024-11-15 10:08:18 +01:00
47 changed files with 2392 additions and 17015 deletions

View File

@@ -1,7 +1,41 @@
- 481 2.15.0
- 494 =======
- 391 2024-
- 491 - 435 use invoiceimporter as common technical basis also for zugferdimporter
- also import delivery address
- 527 metrics may raise error on some pdf files
- 517 read product GlobalID
- 380 Added test for input stream validation
- 518 corrently validate more XRechnung versions
- make document charges and allowances serializable
- 523 Verapdf claims PDF-A/3s created witth visualize are invalid
- 530 duedate can not be set directly
- 532 support validation warnings!
- 534 new signature
- 538 Mustang validator always claims PDF is invalid if flavour is PDF/A-3A
- 555 be able to validate ubl credit notes
- when parsing now distinguishing between the parseExceptions StructureException and ArithmetricException
2.14.2
=======
2024-10-14
- also parse BICs in InvoiceImporter not only IBANs
- #509 CLI currently does not write a logfile
- #505 crash after invoking ZUGFeRD2PullProvider
- #506 Fix POM missing dependencies
2.14.1
=======
2024-10-06
- #481 also be able to convert XRechnung/UBL to PDF not only CII
- #494 Quantity/Price Decimal Places
- #391 Runden bei Negativwerten
- #491/501 non terminating decimal expansion
- upgraded en16931 cen schematron to 1.3.12
- #499/500 PDF layout corrections
2.14.0 2.14.0
======= =======

View File

@@ -35,11 +35,30 @@
<version>1.8.0</version> <version>1.8.0</version>
</dependency> </dependency>
<!-- logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<!-- This library is needed so that logback stderr output is sent to, well, stderr, otherwise it lands in stdout -->
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.1.7</version>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-simple</artifactId>
<version>2.0.12</version> <version>2.0.12</version>
</dependency> </dependency>
<!-- end of logging -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>

View File

@@ -1,8 +1,6 @@
package org.mustangproject.commandline; package org.mustangproject.commandline;
import java.io.BufferedReader; import java.io.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -10,10 +8,35 @@ import java.nio.file.Paths;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
public class CliIT { public class CliIT {
public static File getResourceAsFile(String resourcePath) {
try {
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
if (in == null) {
return null;
}
File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
tempFile.deleteOnExit();
try (FileOutputStream out = new FileOutputStream(tempFile)) {
// copy stream
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
return tempFile;
} catch (IOException e) {
return null;
}
}
@Test @Test
public void testCii2Ubl() throws Exception { public void testCii2Ubl() throws Exception {
Path output = Paths.get("target/ubl.xml"); Path output = Paths.get("target/ubl.xml");
@@ -43,4 +66,17 @@ public class CliIT {
return builder.toString(); return builder.toString();
} }
@Test
public void testMetric() {
StatRun sr = new StatRun();
File tempFile = getResourceAsFile("corrupt-factur-x-waytoosmall.pdf");
FileChecker fc = new FileChecker(tempFile.getAbsolutePath(), sr);
fc.checkForZUGFeRD();
System.out.print(fc.getOutputLine());
}
} }

View File

@@ -0,0 +1,2 @@
%PDF-1.4
%ェォャュ

View File

@@ -64,7 +64,8 @@
<groupId>net.sf.saxon</groupId> <groupId>net.sf.saxon</groupId>
<artifactId>Saxon-HE</artifactId> <artifactId>Saxon-HE</artifactId>
<version>12.4</version> <version>12.4</version>
</dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> </dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
@@ -74,7 +75,7 @@
<dependency> <dependency>
<groupId>org.apache.xmlgraphics</groupId> <groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId> <artifactId>fop</artifactId>
<version>2.9</version> <version>2.10</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>xml-apis</groupId> <groupId>xml-apis</groupId>
@@ -82,18 +83,24 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- JAXB -->
<dependency> <dependency>
<groupId>jakarta.xml.bind</groupId> <groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId> <artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version> <version>4.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.angus</groupId> <groupId>org.eclipse.angus</groupId>
<artifactId>angus-activation</artifactId> <artifactId>angus-activation</artifactId>
<version>2.0.2</version> <version>2.0.2</version>
</dependency> </dependency>
<!-- Apache PDFBox -->
<dependency> <dependency>
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>preflight</artifactId> <artifactId>preflight</artifactId>
@@ -104,11 +111,20 @@
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>3.0.2</version> <version>3.0.2</version>
</dependency> </dependency>
<!-- DOM4j -->
<dependency> <dependency>
<groupId>org.dom4j</groupId> <groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.4</version> <version>2.1.4</version>
</dependency> </dependency>
<!-- CII to UBL conversion -->
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>2.2.4</version>
</dependency>
<!-- test dependencies -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
@@ -121,27 +137,12 @@
<version>5.10.2</version> <version>5.10.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- CII to UBL conversion -->
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>2.2.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.5</version>
</dependency>
<dependency> <dependency>
<groupId>org.xmlunit</groupId> <groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId> <artifactId>xmlunit-core</artifactId>
<version>2.10.0</version> <version>2.10.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.xmlunit</groupId> <groupId>org.xmlunit</groupId>
<artifactId>xmlunit-assertj</artifactId> <artifactId>xmlunit-assertj</artifactId>
@@ -233,10 +234,8 @@
<version>3.5.3</version> <version>3.5.3</version>
<configuration> <configuration>
<shadedArtifactAttached>true</shadedArtifactAttached> <shadedArtifactAttached>true</shadedArtifactAttached>
<minimizeJar>false <createDependencyReducedPom>false</createDependencyReducedPom>
</minimizeJar><!-- no longer java 11 compatible if set to true because it removes e.g. javax/xml/bind/annotation/XmlSchema-->
<filters> <filters>
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
@@ -245,18 +244,6 @@
<exclude>META-INF/*.RSA</exclude> <exclude>META-INF/*.RSA</exclude>
</excludes> </excludes>
</filter> </filter>
<filter>
<artifact>log4j:log4j</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>commons-logging:commons-logging</artifact>
<includes>
<include>**</include>
</includes>
</filter>
</filters> </filters>
</configuration> </configuration>
<executions> <executions>
@@ -265,15 +252,6 @@
<goals> <goals>
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration>
<artifactSet>
<excludes>
<!--exclude>classworlds:classworlds</exclude> <exclude>junit:junit</exclude>
<exclude>jmock:*</exclude> <exclude>*:xml-apis</exclude> <exclude>org.apache.maven:lib:tests</exclude>
<exclude>log4j:log4j:jar:</exclude -->
</excludes>
</artifactSet>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>

View File

@@ -1,5 +1,7 @@
package org.mustangproject; package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.math.BigDecimal; import java.math.BigDecimal;
/*** /***
@@ -28,6 +30,7 @@ public class Allowance extends Charge {
* @return false since its not supposed to be calculated negatively * @return false since its not supposed to be calculated negatively
*/ */
@Override @Override
@JsonIgnore
public boolean isCharge() { public boolean isCharge() {
return false; return false;
} }

View File

@@ -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;
}
}

View File

@@ -1,5 +1,6 @@
package org.mustangproject; package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider; import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
@@ -151,6 +152,7 @@ public class Charge implements IZUGFeRDAllowanceCharge {
* @return true since it is supposed to be calculated negatively * @return true since it is supposed to be calculated negatively
*/ */
@Override @Override
@JsonIgnore
public boolean isCharge() { public boolean isCharge() {
return true; return true;
} }

View File

@@ -0,0 +1,13 @@
package org.mustangproject.Exceptions;
import java.text.ParseException;
/***
* will be thrown if a invoice cant be reproduced numerically
*/
public class ArithmetricException extends ParseException {
public ArithmetricException() {
super(
"Could not reproduce the invoice, this could mean that it could not be read properly", 0);
}
}

View File

@@ -0,0 +1,12 @@
package org.mustangproject.Exceptions;
import java.text.ParseException;
/***
* will be thrown if a invoice cant be read
*/
public class StructureException extends ParseException {
public StructureException(String message, int line) {
super(message, line);
}
}

View File

@@ -26,6 +26,7 @@ import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.*; import org.mustangproject.ZUGFeRD.*;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
@@ -37,6 +38,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* @see IExportableTransaction if you want to implement an interface instead * @see IExportableTransaction if you want to implement an interface instead
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Invoice implements IExportableTransaction { public class Invoice implements IExportableTransaction {
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null; protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
@@ -520,6 +522,20 @@ public class Invoice implements IExportableTransaction {
} }
} }
/***
* this is wrong and only used from jackson
* @param iza
* @return
*/
public Invoice setZFAllowances(Allowance[] iza) {
Allowances=new ArrayList<>();
for (IZUGFeRDAllowanceCharge cz:iza) {
Allowances.add(cz);
}
return this;
}
@Override @Override
public IZUGFeRDAllowanceCharge[] getZFCharges() { public IZUGFeRDAllowanceCharge[] getZFCharges() {
@@ -530,6 +546,18 @@ public class Invoice implements IExportableTransaction {
} }
} }
/***
* this is wrong and only used from jackson
* @param iza
* @return
*/
public Invoice setZFCharges(Charge[] iza) {
Charges=new ArrayList<>();
for (IZUGFeRDAllowanceCharge cz:iza) {
Charges.add(cz);
}
return this;
}
@Override @Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() { public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {

View File

@@ -1,6 +1,7 @@
package org.mustangproject; package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IReferencedDocument; import org.mustangproject.ZUGFeRD.IReferencedDocument;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
@@ -18,6 +19,7 @@ import java.util.Date;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Item implements IZUGFeRDExportableItem { public class Item implements IZUGFeRDExportableItem {
protected BigDecimal price = BigDecimal.ZERO; protected BigDecimal price = BigDecimal.ZERO;
protected BigDecimal quantity; protected BigDecimal quantity;

View File

@@ -1,6 +1,7 @@
package org.mustangproject; package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IDesignatedProductClassification; import org.mustangproject.ZUGFeRD.IDesignatedProductClassification;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import org.mustangproject.util.NodeMap; import org.mustangproject.util.NodeMap;
@@ -17,6 +18,8 @@ import java.util.Map;
* describes a product, good or service used in an invoice item line * describes a product, good or service used in an invoice item line
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Product implements IZUGFeRDExportableProduct { public class Product implements IZUGFeRDExportableProduct {
protected String unit, name, sellerAssignedID, buyerAssignedID; protected String unit, name, sellerAssignedID, buyerAssignedID;
protected String description=""; protected String description="";

View File

@@ -5,6 +5,7 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableContact; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableContact;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
import org.mustangproject.ZUGFeRD.IZUGFeRDLegalOrganisation; import org.mustangproject.ZUGFeRD.IZUGFeRDLegalOrganisation;
@@ -19,9 +20,10 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
* A organisation, i.e. usually a company * A organisation, i.e. usually a company
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class TradeParty implements IZUGFeRDExportableTradeParty { public class TradeParty implements IZUGFeRDExportableTradeParty {
protected String name, zip, street, location, country; protected String name, zip, street, location, country, taxScheme;
protected String taxID = null, vatID = null; protected String taxID = null, vatID = null;
protected String ID = null; protected String ID = null;
protected String description = null; protected String description = null;
@@ -89,21 +91,46 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
} }
} }
if (currentTopElementName.equals("PartyIdentification")) { if (currentTopElementName.equals("PartyTaxScheme")) {
NodeList partyTaxScheme = party.item(partyIndex).getChildNodes();
NodeList partyName = party.item(partyIndex).getChildNodes(); for (int partyTaxSchemeIndex = 0; partyTaxSchemeIndex < partyTaxScheme.getLength(); partyTaxSchemeIndex++) {
for (int partyNameIndex = 0; partyNameIndex < partyName.getLength(); partyNameIndex++) { if (partyTaxScheme.item(partyTaxSchemeIndex).getLocalName() != null) {
if (partyName.item(partyNameIndex).getLocalName() != null) { if (partyTaxScheme.item(partyTaxSchemeIndex).getLocalName().equals("CompanyID")) {
if (partyName.item(partyNameIndex).getLocalName().equals("ID")) { setTaxID(partyTaxScheme.item(partyTaxSchemeIndex).getTextContent());
setID(partyName.item(partyNameIndex).getTextContent());
}
} }
} }
} }
}
// if (currentTopElementName.equals("PartyTaxScheme")) {
// NodeList partyTaxScheme = party.item(partyIndex).getChildNodes();
// for (int partyTaxSchemeIndex = 0; partyTaxSchemeIndex < partyTaxScheme.getLength(); partyTaxSchemeIndex++) {
// if (partyTaxScheme.item(partyTaxSchemeIndex).getLocalName() != null) {
// if (partyTaxScheme.item(partyTaxSchemeIndex).getLocalName().equals("TaxScheme")) {
// NodeList taxScheme = partyTaxScheme.item(partyTaxSchemeIndex).getChildNodes();
// for (int taxSchemeIndex = 0 ; taxSchemeIndex < taxScheme.getLength(); taxSchemeIndex++) {
// if (taxScheme.item(taxSchemeIndex).getLocalName() != null) {
// if(taxScheme.item(taxSchemeIndex).getLocalName().equals("ID")){
// if (partyTaxScheme.item(partyTaxSchemeIndex).getLocalName().equals("CompanyID")) {
// setTaxID(partyTaxScheme.item(partyTaxSchemeIndex).getTextContent());
// } else {
// setVATID(partyTaxScheme.item(partyTaxSchemeIndex).getTextContent());
// }
// }
// }
// }
// }
//
// }
// }
// }
/*
UBL only: formally it can have a name as well but BT27 party name *should* be stored in
so overwrite if one exists
*/
// UBL only: formally it can have a name as well but BT27 party name *should* be stored in
// so overwrite if one exists
if (currentTopElementName.equals("PartyLegalEntity")) { if (currentTopElementName.equals("PartyLegalEntity")) {
NodeList legal = party.item(partyIndex).getChildNodes(); NodeList legal = party.item(partyIndex).getChildNodes();
for (int legalChildIndex = 0; legalChildIndex < legal.getLength(); legalChildIndex++) { for (int legalChildIndex = 0; legalChildIndex < legal.getLength(); legalChildIndex++) {
@@ -111,6 +138,9 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
if (legal.item(legalChildIndex).getLocalName().equals("RegistrationName")) { if (legal.item(legalChildIndex).getLocalName().equals("RegistrationName")) {
setName(legal.item(legalChildIndex).getTextContent()); setName(legal.item(legalChildIndex).getTextContent());
} }
if (legal.item(legalChildIndex).getLocalName().equals("CompanyLegalForm")) {
setDescription(legal.item(legalChildIndex).getTextContent());
}
} }
} }
} }
@@ -223,26 +253,27 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
} }
if (currentUBLChild.equals("SpecifiedTaxRegistration")) { if (currentUBLChild.equals("PartyTaxScheme")) {
NodeList taxChilds = nodes.item(nodeIndex).getChildNodes(); NodeList taxChilds = nodes.item(nodeIndex).getChildNodes();
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
if (taxChilds.item(taxChildIndex).getLocalName() != null) { if (taxChilds.item(taxChildIndex).getLocalName() != null) {
if ((taxChilds.item(taxChildIndex).getLocalName().equals("ID"))) { if ((taxChilds.item(taxChildIndex).getLocalName().equals("TaxScheme"))) {
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("schemeID") != null) { if (taxChilds.item(taxChildIndex).getLocalName().equals("CompanyID")) {
Node firstChild = taxChilds.item(taxChildIndex).getFirstChild(); if (taxChilds.item(taxChildIndex).getLocalName().equals("ID")) {
if (firstChild != null) { if (taxChilds.item(taxChildIndex).getLocalName().equals("CompanyID")) {
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("schemeID").getNodeValue().equals("VA")) { setVATID(taxChilds.item(taxChildIndex).getTextContent());
setVATID(firstChild.getNodeValue()); }
// setTaxID(partyTaxScheme.item(partyTaxSchemeIndex).getTextContent());
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("ID").getNodeValue().equals("FC")) {
if (taxChilds.item(taxChildIndex).getLocalName().equals("CompanyID")) {
setTaxID(taxChilds.item(taxChildIndex).getTextContent());
} }
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("schemeID").getNodeValue().equals("FC")) {
setTaxID(firstChild.getNodeValue());
} }
} }
} }
} }
} }
} }
} }
} }
} }
@@ -324,7 +355,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
NodeList itemChilds = currentItemNode.getChildNodes(); NodeList itemChilds = currentItemNode.getChildNodes();
for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) { for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) {
if (itemChilds.item(itemChildIndex).getLocalName() != null) { if (itemChilds.item(itemChildIndex).getLocalName() != null) {
if (itemChilds.item(itemChildIndex).getLocalName().equals("GlobalID")) { if (itemChilds.item(itemChildIndex).getLocalName().equals("ID")) {
setID(itemChilds.item(itemChildIndex).getTextContent()); setID(itemChilds.item(itemChildIndex).getTextContent());
} }
if (itemChilds.item(itemChildIndex).getLocalName().equals("Name")) { if (itemChilds.item(itemChildIndex).getLocalName().equals("Name")) {
@@ -513,6 +544,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
return this; return this;
} }
/** /**
* (optional) * (optional)
* *
@@ -524,6 +556,14 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
return this; return this;
} }
/**
* primarily for invoiceimporter and JSON
* @return the list of sepa mandates
*/
public List<DirectDebit> getDebitDetails() {
return debitDetails;
}
@Override @Override
public IZUGFeRDLegalOrganisation getLegalOrganisation() { public IZUGFeRDLegalOrganisation getLegalOrganisation() {
return legalOrg; return legalOrg;
@@ -660,7 +700,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
@Override @Override
public String getCountry() { public String getCountry() {
return country; return taxScheme;
} }
/*** /***
@@ -669,7 +709,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
* @return fluent setter * @return fluent setter
*/ */
public TradeParty setCountry(String country) { public TradeParty setCountry(String country) {
this.country = country; this.taxScheme = country;
return this; return this;
} }
@@ -683,6 +723,7 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
return contact; return contact;
} }
@JsonIgnore
public IZUGFeRDTradeSettlement[] getAsTradeSettlement() { public IZUGFeRDTradeSettlement[] getAsTradeSettlement() {
if (bankDetails.isEmpty() && debitDetails.isEmpty()) { if (bankDetails.isEmpty() && debitDetails.isEmpty()) {
return null; return null;

View File

@@ -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
@@ -21,7 +25,7 @@ public class XMLTools extends XMLWriter {
public static String nDigitFormat(BigDecimal value, int scale) { public static String nDigitFormat(BigDecimal value, int scale) {
/* /*
* I needed 123,45, locale independent.I tried * I needed 123.45, locale independent.I tried
* NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale
* specific.I also tried DecimalFormat df = new DecimalFormat( "0,00" ); * specific.I also tried DecimalFormat df = new DecimalFormat( "0,00" );
* df.setDecimalSeparatorAlwaysShown(true); df.setGroupingUsed(false); * df.setDecimalSeparatorAlwaysShown(true); df.setGroupingUsed(false);
@@ -39,6 +43,105 @@ 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.
* cuts potential 0s off the end until minDecimals
* @param value
* @param maxDecimals number of maximal scale
* @param minDecimals number of minimal scale
* @return value as String with decimals in the specified range
*/
public static String nDigitFormatDecimalRange(BigDecimal value, int maxDecimals, int minDecimals) {
if ((maxDecimals<minDecimals)||(maxDecimals<0)||(minDecimals<0)) {
throw new IllegalArgumentException("Invalid scale range provided");
}
int curDecimals=maxDecimals;
while ( (curDecimals>minDecimals) && (value.setScale(curDecimals, RoundingMode.HALF_UP).compareTo(value.setScale(curDecimals-1, RoundingMode.HALF_UP))==0)) {
curDecimals--;
}
return value.setScale(curDecimals, RoundingMode.HALF_UP).toPlainString();
}
/***
* 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 &lt;
* @param s the string
* @return the "safe" string
*/
public static String encodeXML(CharSequence s) { public static String encodeXML(CharSequence s) {
if (s == null) { if (s == null) {
return ""; return "";
@@ -111,4 +214,16 @@ public class XMLTools extends XMLWriter {
return IOUtils.toByteArray (fileinput); return IOUtils.toByteArray (fileinput);
} }
public static String trimOrNull(Node node) {
if (node != null) {
String textContent = node.getTextContent();
if (textContent != null) {
return textContent.trim();
}
}
return null;
}
} }

View File

@@ -34,13 +34,17 @@ public class LineCalculator {
} }
} }
BigDecimal vatPercent = currentItem.getProduct().getVATPercent(); BigDecimal vatPercent = null;
if (vatPercent == null) if (currentItem.getProduct()!=null) {
vatPercent = currentItem.getProduct().getVATPercent();
}
if (vatPercent == null) {
vatPercent = BigDecimal.ZERO; vatPercent = BigDecimal.ZERO;
}
BigDecimal multiplicator = vatPercent.divide(BigDecimal.valueOf(100)); BigDecimal multiplicator = vatPercent.divide(BigDecimal.valueOf(100));
priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159 priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159
price = priceGross.subtract(allowance).add(charge); price = priceGross.subtract(allowance).add(charge);
itemTotalNetAmount = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity(), RoundingMode.HALF_UP) itemTotalNetAmount = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity(), 18, RoundingMode.HALF_UP)
.subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP); .subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);

View File

@@ -76,11 +76,13 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
protected String priceFormat(BigDecimal value) { protected String priceFormat(BigDecimal value) {
return XMLTools.nDigitFormat(value, 18); // 18 decimals are max for price and qty due to xml restrictions,
// see Chapter 3.2.3 of https://www.w3.org/TR/xmlschema-2/
return XMLTools.nDigitFormatDecimalRange(value, 18, 4);
} }
protected String quantityFormat(BigDecimal value) { protected String quantityFormat(BigDecimal value) {
return XMLTools.nDigitFormat(value, 18); return XMLTools.nDigitFormatDecimalRange(value, 18, 4);
} }
@Override @Override
@@ -332,7 +334,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
this.trans = trans; this.trans = trans;
this.calc = new TransactionCalculator(trans); this.calc = new TransactionCalculator(trans);
boolean hasDueDate = false; boolean hasDueDate = trans.getDueDate()!=null;
final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); final SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy");
String exemptionReason = ""; String exemptionReason = "";
@@ -816,7 +818,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
} }
if ((trans.getPaymentTerms() == null) && (getProfile() != Profiles.getByName("Minimum")) && ((paymentTermsDescription != null) || (trans.getTradeSettlement() != null) || (hasDueDate))) { if ((trans.getPaymentTerms() == null) && (getProfile() != Profiles.getByName("Minimum")) && ((paymentTermsDescription != null) || (trans.getTradeSettlement() != null) || (hasDueDate))) {
xml += "<ram:SpecifiedTradePaymentTerms>"; xml += "<ram:SpecifiedTradePaymentTerms>";
@@ -832,7 +833,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} }
} }
if (hasDueDate && (trans.getDueDate() != null)) { if (trans.getDueDate() != null) {
xml += "<ram:DueDateDateTime>" // $NON-NLS-2$ xml += "<ram:DueDateDateTime>" // $NON-NLS-2$
+ DATE.udtFormat(trans.getDueDate()) + DATE.udtFormat(trans.getDueDate())
+ "</ram:DueDateDateTime>";// 20130704 + "</ram:DueDateDateTime>";// 20130704

View File

@@ -13,92 +13,34 @@ package org.mustangproject.ZUGFeRD;
* @version 1.1.0 * @version 1.1.0
* @author jstaerk * @author jstaerk
*/ */
import java.io.*; import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; 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.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 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);
}
} }
@@ -113,169 +55,21 @@ 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
* @param xpathStr the xpath expression to be evaluated
* @return the extracted String for the specific path in the document
*/
public String wExtractString(String xpathStr) {
return extractString(xpathStr);
}
/** /**
* @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 +93,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 +115,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 +198,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 +231,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 +285,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 +343,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 +366,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 +375,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 +476,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 +499,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 +524,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 +535,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 +679,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 +692,55 @@ 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(), "GlobalID");
if (node != null) {
SchemedID globalId = new SchemedID()
.setScheme(node.getAttributes()
.getNamedItem("schemeID").getNodeValue())
.setId(XMLTools.getNodeValue(node));
lineItem.getProduct().addGlobalID(globalId);
}
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 +754,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 +769,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 +856,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;
}
}
} }

View File

@@ -1,56 +1,277 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.IOException; import org.apache.commons.io.IOUtils;
import java.io.InputStream; import org.apache.pdfbox.Loader;
import java.math.BigDecimal; import org.apache.pdfbox.pdmodel.PDDocument;
import java.nio.charset.StandardCharsets; import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import java.text.ParseException; import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import java.text.SimpleDateFormat; import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import java.util.ArrayList; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import java.util.Base64; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.mustangproject.*; import org.mustangproject.*;
import org.mustangproject.Exceptions.ArithmetricException;
import org.mustangproject.Exceptions.StructureException;
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;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class ZUGFeRDInvoiceImporter {
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 ignoreCalculationErrors = false; * map filenames of additional XML files to their contents
private ArrayList<FileAttachment> fileAttachments=new ArrayList<>(); */
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<>();
/**
* if metadata has been found
*/
protected boolean containsMeta = false;
/**
* 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<>();
public ZUGFeRDInvoiceImporter() { public ZUGFeRDInvoiceImporter() {
super(); //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 {
containsMeta = true; extractLowLevel(pdfStream);
setRawXML(XML.getBytes(StandardCharsets.UTF_8)); } catch (final IOException e) {
} catch (IOException e) { LOGGER.error("Failed to extract ZUGFeRD data", e);
LOGGER.error(e.getMessage(), 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 = new String(XMLTools.getBytesFromStream(XMP), StandardCharsets.UTF_8);
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);
}
}
} catch (Exception e) {
LOGGER.error("Failed to parse PDF", e);
//ignore otherwise
}
} else {
// no PDF probably XML
containsMeta = true;
setRawXML(XMLTools.getBytesFromStream(pdfStream));
}
}
/***
* 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);
}
}
}
/*** /***
* This will parse a XML into the given invoice object * This will parse a XML into the given invoice object
@@ -63,6 +284,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 +295,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);
@@ -86,13 +315,19 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
BigDecimal expectedGrandTotal = null; BigDecimal expectedGrandTotal = null;
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(XMLTools.trimOrNull(totalNodes.item(0)));
if (zpp instanceof CalculatedInvoice) {
// usually we would re-calculate the invoice to get expectedGrandTotal
// however, for "minimal" invoices or other invoices without lines
// this will not work
((CalculatedInvoice) zpp).setGrandTotal(expectedGrandTotal);
}
} }
xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]"); xpr = xpath.compile("//*[local-name()=\"PrepaidAmount\"]");
NodeList prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (prepaidNodes.getLength() > 0) { if (prepaidNodes.getLength() > 0) {
zpp.setTotalPrepaidAmount(new BigDecimal(prepaidNodes.item(0).getTextContent())); zpp.setTotalPrepaidAmount(new BigDecimal(XMLTools.trimOrNull(prepaidNodes.item(0))));
} }
Date issueDate = null; Date issueDate = null;
@@ -100,24 +335,22 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
Date deliveryDate = null; Date deliveryDate = null;
String despatchAdviceReferencedDocument = null; String despatchAdviceReferencedDocument = null;
for (int i = 0; i < ExchangedDocumentNodes.getLength(); i++) { for (int i = 0; i < ExchangedDocumentNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) {
Node exchangedDocumentNode = ExchangedDocumentNodes.item(i); Node exchangedDocumentNode = ExchangedDocumentNodes.item(i);
NodeList exchangedDocumentChilds = exchangedDocumentNode.getChildNodes(); NodeList exchangedDocumentChilds = exchangedDocumentNode.getChildNodes();
for (int documentChildIndex = 0; documentChildIndex < exchangedDocumentChilds.getLength(); documentChildIndex++) { for (int documentChildIndex = 0; documentChildIndex < exchangedDocumentChilds.getLength(); documentChildIndex++) {
Node item = exchangedDocumentChilds.item(documentChildIndex); Node item = exchangedDocumentChilds.item(documentChildIndex);
if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) { if ((item.getLocalName() != null) && (item.getLocalName().equals("ID"))) {
number = item.getTextContent(); number = XMLTools.trimOrNull(item);
} }
if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) { if ((item.getLocalName() != null) && (item.getLocalName().equals("TypeCode"))) {
typeCode = item.getTextContent(); typeCode = XMLTools.trimOrNull(item);
} }
if ((item.getLocalName() != null) && (item.getLocalName().equals("IssueDateTime"))) { if ((item.getLocalName() != null) && (item.getLocalName().equals("IssueDateTime"))) {
NodeList issueDateTimeChilds = item.getChildNodes(); NodeList issueDateTimeChilds = item.getChildNodes();
for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) { for (int issueDateChildIndex = 0; issueDateChildIndex < issueDateTimeChilds.getLength(); issueDateChildIndex++) {
if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null) if ((issueDateTimeChilds.item(issueDateChildIndex).getLocalName() != null)
&& (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) { && (issueDateTimeChilds.item(issueDateChildIndex).getLocalName().equals("DateTimeString"))) {
issueDate = new SimpleDateFormat("yyyyMMdd").parse(issueDateTimeChilds.item(issueDateChildIndex).getTextContent()); issueDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(issueDateTimeChilds.item(issueDateChildIndex)));
} }
} }
} }
@@ -127,6 +360,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if (rootNode.equals("Invoice")) { if (rootNode.equals("Invoice")) {
// UBL... // UBL...
number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim(); number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim();
typeCode = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"InvoiceTypeCode\"]").trim();
issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim()); issueDate = new SimpleDateFormat("yyyy-MM-dd").parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim());
String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim(); String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim();
if (dueDt.length() > 0) { if (dueDt.length() > 0) {
@@ -141,7 +375,6 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
for (int i = 0; i < headerTradeDeliveryNodes.getLength(); i++) { for (int i = 0; i < headerTradeDeliveryNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) {
Node headerTradeDeliveryNode = headerTradeDeliveryNodes.item(i); Node headerTradeDeliveryNode = headerTradeDeliveryNodes.item(i);
NodeList headerTradeDeliveryChilds = headerTradeDeliveryNode.getChildNodes(); NodeList headerTradeDeliveryChilds = headerTradeDeliveryNode.getChildNodes();
for (int deliveryChildIndex = 0; deliveryChildIndex < headerTradeDeliveryChilds.getLength(); deliveryChildIndex++) { for (int deliveryChildIndex = 0; deliveryChildIndex < headerTradeDeliveryChilds.getLength(); deliveryChildIndex++) {
@@ -155,7 +388,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
for (int occurenceChildIndex = 0; occurenceChildIndex < occurenceChilds.getLength(); occurenceChildIndex++) { for (int occurenceChildIndex = 0; occurenceChildIndex < occurenceChilds.getLength(); occurenceChildIndex++) {
if ((occurenceChilds.item(occurenceChildIndex).getLocalName() != null) if ((occurenceChilds.item(occurenceChildIndex).getLocalName() != null)
&& (occurenceChilds.item(occurenceChildIndex).getLocalName().equals("DateTimeString"))) { && (occurenceChilds.item(occurenceChildIndex).getLocalName().equals("DateTimeString"))) {
deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(occurenceChilds.item(occurenceChildIndex).getTextContent()); deliveryDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(occurenceChilds.item(occurenceChildIndex)));
} }
} }
} }
@@ -167,7 +400,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
for (int despatchAdviceChildIndex = 0; despatchAdviceChildIndex < despatchAdviceChilds.getLength(); despatchAdviceChildIndex++) { for (int despatchAdviceChildIndex = 0; despatchAdviceChildIndex < despatchAdviceChilds.getLength(); despatchAdviceChildIndex++) {
if (despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName() != null if (despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName() != null
&& despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName().equals("IssuerAssignedID")) { && despatchAdviceChilds.item(despatchAdviceChildIndex).getLocalName().equals("IssuerAssignedID")) {
despatchAdviceReferencedDocument = despatchAdviceChilds.item(despatchAdviceChildIndex).getTextContent(); despatchAdviceReferencedDocument = XMLTools.trimOrNull(despatchAdviceChilds.item(despatchAdviceChildIndex));
} }
} }
} }
@@ -180,7 +413,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
String buyerOrderIssuerAssignedID = null; String buyerOrderIssuerAssignedID = null;
String sellerOrderIssuerAssignedID = null; String sellerOrderIssuerAssignedID = null;
for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) { for (int i = 0; i < headerTradeAgreementNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) { // XMLTools.trimOrNull(nodes.item(i)))) {
Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i); Node headerTradeAgreementNode = headerTradeAgreementNodes.item(i);
NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes(); NodeList headerTradeAgreementChilds = headerTradeAgreementNode.getChildNodes();
for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) { for (int agreementChildIndex = 0; agreementChildIndex < headerTradeAgreementChilds.getLength(); agreementChildIndex++) {
@@ -190,7 +423,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) { for (int buyerOrderChildIndex = 0; buyerOrderChildIndex < buyerOrderChilds.getLength(); buyerOrderChildIndex++) {
if ((buyerOrderChilds.item(buyerOrderChildIndex).getLocalName() != null) if ((buyerOrderChilds.item(buyerOrderChildIndex).getLocalName() != null)
&& (buyerOrderChilds.item(buyerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { && (buyerOrderChilds.item(buyerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) {
buyerOrderIssuerAssignedID = buyerOrderChilds.item(buyerOrderChildIndex).getTextContent(); buyerOrderIssuerAssignedID = XMLTools.trimOrNull(buyerOrderChilds.item(buyerOrderChildIndex));
} }
} }
} }
@@ -200,7 +433,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
for (int sellerOrderChildIndex = 0; sellerOrderChildIndex < sellerOrderChilds.getLength(); sellerOrderChildIndex++) { for (int sellerOrderChildIndex = 0; sellerOrderChildIndex < sellerOrderChilds.getLength(); sellerOrderChildIndex++) {
if ((sellerOrderChilds.item(sellerOrderChildIndex).getLocalName() != null) if ((sellerOrderChilds.item(sellerOrderChildIndex).getLocalName() != null)
&& (sellerOrderChilds.item(sellerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) { && (sellerOrderChilds.item(sellerOrderChildIndex).getLocalName().equals("IssuerAssignedID"))) {
sellerOrderIssuerAssignedID = sellerOrderChilds.item(sellerOrderChildIndex).getTextContent(); sellerOrderIssuerAssignedID = XMLTools.trimOrNull(sellerOrderChilds.item(sellerOrderChildIndex));
} }
} }
} }
@@ -210,13 +443,19 @@ 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);
List<BankDetails> bankDetails = new ArrayList<>(); List<BankDetails> bankDetails = new ArrayList<>();
String directDebitMandateID = null;
String IBAN = null, BIC = null;
for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) { for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) { // XMLTools.trimOrNull(nodes.item(i)))) {
Node headerTradeSettlementNode = headerTradeSettlementNodes.item(i); Node headerTradeSettlementNode = headerTradeSettlementNodes.item(i);
NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes(); NodeList headerTradeSettlementChilds = headerTradeSettlementNode.getChildNodes();
for (int settlementChildIndex = 0; settlementChildIndex < headerTradeSettlementChilds.getLength(); settlementChildIndex++) { for (int settlementChildIndex = 0; settlementChildIndex < headerTradeSettlementChilds.getLength(); settlementChildIndex++) {
if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null)
@@ -227,34 +466,41 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
NodeList dueDateChilds = paymentTermChilds.item(paymentTermChildIndex).getChildNodes(); NodeList dueDateChilds = paymentTermChilds.item(paymentTermChildIndex).getChildNodes();
for (int dueDateChildIndex = 0; dueDateChildIndex < dueDateChilds.getLength(); dueDateChildIndex++) { for (int dueDateChildIndex = 0; dueDateChildIndex < dueDateChilds.getLength(); dueDateChildIndex++) {
if ((dueDateChilds.item(dueDateChildIndex).getLocalName() != null) && (dueDateChilds.item(dueDateChildIndex).getLocalName().equals("DateTimeString"))) { if ((dueDateChilds.item(dueDateChildIndex).getLocalName() != null) && (dueDateChilds.item(dueDateChildIndex).getLocalName().equals("DateTimeString"))) {
dueDate = new SimpleDateFormat("yyyyMMdd").parse(dueDateChilds.item(dueDateChildIndex).getTextContent()); dueDate = new SimpleDateFormat("yyyyMMdd").parse(XMLTools.trimOrNull(dueDateChilds.item(dueDateChildIndex)));
} }
} }
} }
if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("DirectDebitMandateID"))) {
directDebitMandateID = paymentTermChilds.item(paymentTermChildIndex).getTextContent();
}
} }
} }
if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null) if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null)
&& (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradeSettlementPaymentMeans"))) { && (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName().equals("SpecifiedTradeSettlementPaymentMeans"))) {
NodeList paymentMeansChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes(); NodeList paymentMeansChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes();
IBAN = null;
BIC = null;
for (int paymentMeansChildIndex = 0; paymentMeansChildIndex < paymentMeansChilds.getLength(); paymentMeansChildIndex++) { for (int paymentMeansChildIndex = 0; paymentMeansChildIndex < paymentMeansChilds.getLength(); paymentMeansChildIndex++) {
String IBAN = null, BIC = null;
if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialAccount"))) { if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialAccount") || paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayerPartyDebtorFinancialAccount"))) {
NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes();
for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) {
if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII
IBAN = accountChilds.item(accountChildIndex).getTextContent(); IBAN = XMLTools.trimOrNull(accountChilds.item(accountChildIndex));
} }
} }
} }
if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialInstitution"))) { if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds.item(paymentMeansChildIndex).getLocalName().equals("PayeeSpecifiedCreditorFinancialInstitution"))) {
NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes(); NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes();
for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) { for (int accountChildIndex = 0; accountChildIndex < accountChilds.getLength(); accountChildIndex++) {
if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII
BIC = accountChilds.item(accountChildIndex).getTextContent(); BIC = XMLTools.trimOrNull(accountChilds.item(accountChildIndex));
} }
} }
} }
}
if (IBAN != null) { if (IBAN != null) {
BankDetails bd = new BankDetails(IBAN); BankDetails bd = new BankDetails(IBAN);
if (BIC != null) { if (BIC != null) {
@@ -263,16 +509,43 @@ 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 = XMLTools.trimOrNull(startPeriodChilds.item(startPeriodIndex));
}
}
}
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 = XMLTools.trimOrNull(endPeriodChilds.item(endPeriodIndex));
}
}
}
}
} }
} }
} }
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);
for (int i = 0; i < paymentMeansNodes.getLength(); i++) { for (int i = 0; i < paymentMeansNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) { // XMLTools.trimOrNull(nodes.item(i)))) {
Node paymentMeansNode = paymentMeansNodes.item(i); Node paymentMeansNode = paymentMeansNodes.item(i);
NodeList paymentMeansChilds = paymentMeansNode.getChildNodes(); NodeList paymentMeansChilds = paymentMeansNode.getChildNodes();
for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds.getLength(); meansChildIndex++) { for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds.getLength(); meansChildIndex++) {
@@ -281,7 +554,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes(); NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes();
for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) { for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds.getLength(); paymentTermChildIndex++) {
if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("ID"))) { if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("ID"))) {
String IBAN = paymentTermChilds.item(paymentTermChildIndex).getTextContent(); IBAN = XMLTools.trimOrNull(paymentTermChilds.item(paymentTermChildIndex));
if (IBAN != null) { if (IBAN != null) {
BankDetails bd = new BankDetails(IBAN); BankDetails bd = new BankDetails(IBAN);
bankDetails.add(bd); bankDetails.add(bd);
@@ -294,6 +567,11 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode); zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate).setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number).setDocumentCode(typeCode);
if ((directDebitMandateID != null) && (IBAN != null)) {
DirectDebit d = new DirectDebit(IBAN, directDebitMandateID);
zpp.getSender().addDebitDetails(d);
}
bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail)); bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail));
if (payeeNodes.getLength() > 0) { if (payeeNodes.getLength() > 0) {
@@ -303,6 +581,9 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
if (buyerOrderIssuerAssignedID != null) { if (buyerOrderIssuerAssignedID != null) {
zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID); zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID);
} }
else {
zpp.setBuyerOrderReferencedDocumentID(extractString("//*[local-name()=\"OrderReference\"]/*[local-name()=\"ID\"]"));
}
if (sellerOrderIssuerAssignedID != null) { if (sellerOrderIssuerAssignedID != null) {
zpp.setSellerOrderReferencedDocumentID(sellerOrderIssuerAssignedID); zpp.setSellerOrderReferencedDocumentID(sellerOrderIssuerAssignedID);
} }
@@ -316,7 +597,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
String buyerReference = null; String buyerReference = null;
prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); prepaidNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (prepaidNodes.getLength() > 0) { if (prepaidNodes.getLength() > 0) {
buyerReference = prepaidNodes.item(0).getTextContent(); buyerReference = XMLTools.trimOrNull(prepaidNodes.item(0));
} }
if (buyerReference != null) { if (buyerReference != null) {
zpp.setReferenceNumber(buyerReference); zpp.setReferenceNumber(buyerReference);
@@ -338,7 +619,7 @@ 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(XMLTools.trimOrNull(attachmentNodes.item(i))));
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"
@@ -366,21 +647,21 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) { for (int indicatorChildIndex = 0; indicatorChildIndex < indicatorChilds.getLength(); indicatorChildIndex++) {
if ((indicatorChilds.item(indicatorChildIndex).getLocalName() != null) if ((indicatorChilds.item(indicatorChildIndex).getLocalName() != null)
&& (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator"))) { && (indicatorChilds.item(indicatorChildIndex).getLocalName().equals("Indicator"))) {
isCharge = indicatorChilds.item(indicatorChildIndex).getTextContent().equalsIgnoreCase("true"); isCharge = XMLTools.trimOrNull(indicatorChilds.item(indicatorChildIndex)).equalsIgnoreCase("true");
} }
} }
} else if (chargeChildName.equals("ActualAmount")) { } else if (chargeChildName.equals("ActualAmount")) {
chargeAmount = chargeNodeChilds.item(chargeChildIndex).getTextContent(); chargeAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("Reason")) { } else if (chargeChildName.equals("Reason")) {
reason = chargeNodeChilds.item(chargeChildIndex).getTextContent(); reason = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("ReasonCode")) { } else if (chargeChildName.equals("ReasonCode")) {
reasonCode = chargeNodeChilds.item(chargeChildIndex).getTextContent(); reasonCode = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("CategoryTradeTax")) { } else if (chargeChildName.equals("CategoryTradeTax")) {
NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes(); NodeList taxChilds = chargeNodeChilds.item(chargeChildIndex).getChildNodes();
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) { for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
String taxItemName = taxChilds.item(taxChildIndex).getLocalName(); String taxItemName = taxChilds.item(taxChildIndex).getLocalName();
if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent") || taxItemName.equals("ApplicablePercent"))) { if ((taxItemName != null) && (taxItemName.equals("RateApplicablePercent") || taxItemName.equals("ApplicablePercent"))) {
taxPercent = taxChilds.item(taxChildIndex).getTextContent(); taxPercent = XMLTools.trimOrNull(taxChilds.item(taxChildIndex));
} }
} }
} }
@@ -422,21 +703,92 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
try { try {
whichType = getStandard(); whichType = getStandard();
} catch (Exception e) { } catch (Exception e) {
throw new ParseException("Could not find out if it's an invoice, order, or delivery advice", 0); throw new StructureException("Could not find out if it's an invoice, order, or delivery advice", 0);
} }
if ((whichType != EStandard.despatchadvice) if ((whichType != EStandard.despatchadvice)
&& ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2))) && ((!expectedStringTotalGross.equals(XMLTools.nDigitFormat(expectedGrandTotal, 2)))
&& (!ignoreCalculationErrors))) { && (!ignoreCalculationErrors))) {
throw new ParseException( throw new ArithmetricException();
"Could not reproduce the invoice, this could mean that it could not be read properly", 0);
} }
} }
return zpp; return zpp;
} }
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);
}
/*** /***
* *
* @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,
@@ -460,18 +812,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);
} }
}
} }

View File

@@ -198,7 +198,7 @@
<entry key="xr:Third_party_payment_type" id="BT-DEX-001">Art der Fremdforderung</entry> <entry key="xr:Third_party_payment_type" id="BT-DEX-001">Art der Fremdforderung</entry>
<entry key="xr:Third_party_payment_amount" id="BT-DEX-002">Betrag der Fremdforderung</entry> <entry key="xr:Third_party_payment_amount" id="BT-DEX-002">Betrag der Fremdforderung</entry>
<entry key="xr:Third_party_payment_description" id="BT-DEX-003">Beschreibung der Fremdforderung</entry> <entry key="xr:Third_party_payment_description" id="BT-DEX-003">Beschreibung der Fremdforderung</entry>
<entry key="uebersicht">Übersicht</entry> <entry key="uebersicht">Daten der E-Rechnung</entry>
<entry key="uebersichtKaeufer" id="BG-7">Informationen zum Käufer</entry> <entry key="uebersichtKaeufer" id="BG-7">Informationen zum Käufer</entry>
<entry key="uebersichtVerkaeufer" id="BG-4">Informationen zum Verkäufer</entry> <entry key="uebersichtVerkaeufer" id="BG-4">Informationen zum Verkäufer</entry>
<entry key="uebersichtRechnungsInfo" id="invoice-data">Rechnungsdaten</entry> <entry key="uebersichtRechnungsInfo" id="invoice-data">Rechnungsdaten</entry>
@@ -214,7 +214,7 @@
<entry key="uebersichtZahlungLastschrift" id="BG-19">Lastschrift</entry> <entry key="uebersichtZahlungLastschrift" id="BG-19">Lastschrift</entry>
<entry key="uebersichtZahlungUeberweisung" id="BG-17">Überweisung</entry> <entry key="uebersichtZahlungUeberweisung" id="BG-17">Überweisung</entry>
<entry key="uebersichtBemerkungen" id="BG-1">Bemerkungen zur Rechnung</entry> <entry key="uebersichtBemerkungen" id="BG-1">Bemerkungen zur Rechnung</entry>
<entry key="details">Details</entry> <entry key="details">Rechnungspositionen</entry>
<entry key="detailsPositionAbrechnungszeitraum" id="BG-26">Abrechnungszeitraum</entry> <entry key="detailsPositionAbrechnungszeitraum" id="BG-26">Abrechnungszeitraum</entry>
<entry key="detailsPositionPreiseinzelheiten" id="BG-29">Preiseinzelheiten</entry> <entry key="detailsPositionPreiseinzelheiten" id="BG-29">Preiseinzelheiten</entry>
<entry key="detailsPositionNachlaesse" id="BG-27">Nachlässe auf Ebene der Rechnungsposition</entry> <entry key="detailsPositionNachlaesse" id="BG-27">Nachlässe auf Ebene der Rechnungsposition</entry>

View File

@@ -807,10 +807,11 @@
</xsl:template> </xsl:template>
<xsl:template name="zusaetzeVertrag"> <xsl:template name="zusaetzeVertrag">
<xsl:call-template name="box"> <xsl:call-template name="spanned-box">
<xsl:with-param name="identifier" select="'zusaetzeVertrag'"/> <xsl:with-param name="identifier" select="'zusaetzeVertrag'"/>
<xsl:with-param name="content"> <xsl:with-param name="content">
<xsl:call-template name="list"> <xsl:call-template name="list">
<xsl:with-param name="layout" select="'einspaltig'"/>
<xsl:with-param name="content"> <xsl:with-param name="content">
<xsl:apply-templates mode="list-entry" select="xr:Tender_or_lot_reference"/> <xsl:apply-templates mode="list-entry" select="xr:Tender_or_lot_reference"/>
<xsl:apply-templates mode="list-entry" select="xr:Receiving_advice_reference"/> <xsl:apply-templates mode="list-entry" select="xr:Receiving_advice_reference"/>

View File

@@ -64,7 +64,7 @@
<x:xmpmeta xmlns:x="adobe:ns:meta/"> <x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"> <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
<dc:title><xsl:value-of select="xr:Invoice_number"/></dc:title> <dc:title><rdf:Alt><rdf:li xml:lang="x-default"><xsl:value-of select="xr:Invoice_number"/></rdf:li></rdf:Alt></dc:title>
<!-- <!--
<dc:creator></dc:creator> <dc:creator></dc:creator>
<dc:description></dc:description> <dc:description></dc:description>

View File

@@ -52,6 +52,15 @@ public class BaseTest extends TestCase {
assertEquals("12.00", XMLTools.nDigitFormat(new BigDecimal("12"),2)); assertEquals("12.00", XMLTools.nDigitFormat(new BigDecimal("12"),2));
assertEquals("12", XMLTools.nDigitFormat(new BigDecimal("12"),0)); assertEquals("12", XMLTools.nDigitFormat(new BigDecimal("12"),0));
assertEquals("20000123.342", XMLTools.nDigitFormat(new BigDecimal("20000123.3419"),3)); assertEquals("20000123.342", XMLTools.nDigitFormat(new BigDecimal("20000123.3419"),3));
assertEquals("0.00", XMLTools.nDigitFormatDecimalRange(BigDecimal.ZERO,2, 2));
assertEquals("-1.10", XMLTools.nDigitFormatDecimalRange(new BigDecimal("-1.100000"), 4,2));
assertEquals("-1.101", XMLTools.nDigitFormatDecimalRange(new BigDecimal("-1.101000"),10, 3));
assertEquals("-1.10", XMLTools.nDigitFormatDecimalRange(new BigDecimal("-1.103"), 2,2));
assertEquals("4", XMLTools.nDigitFormatDecimalRange(new BigDecimal("4"),2, 0));
assertEquals("3.14", XMLTools.nDigitFormatDecimalRange(new BigDecimal("3.141526"),2, 0));
} }
} }

View File

@@ -189,7 +189,7 @@ public class CalculationTest {
/** /**
* LineCalculator should not throw an exception when calculating a non-terminating decimal expansion * LineCalculator should not throw an exception when calculating a non-terminating decimal expansion
*/ * */
@Test @Test
public void testNonTerminatingDecimalExpansion() { public void testNonTerminatingDecimalExpansion() {
final Product product = new Product(); final Product product = new Product();

View File

@@ -49,4 +49,141 @@ public class DeSerializationTest extends TestCase {
assertEquals("info@company.com", fromJSON.getSender().getUriUniversalCommunicationID()); assertEquals("info@company.com", fromJSON.getSender().getUriUniversalCommunicationID());
} }
public void testAllowanceRead() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// [{"stringValue":"a","intValue":1,"booleanValue":true},
// {"stringValue":"bc","intValue":3,"booleanValue":false}]
Invoice fromJSON = mapper.readValue("{\n" +
" \"documentCode\": \"380\",\n" +
" \"number\": \"471102\",\n" +
" \"ownOrganisationName\": \"Lieferant GmbH\",\n" +
" \"currency\": \"EUR\",\n" +
" \"issueDate\": \"2018-03-03T23:00:00.000+00:00\",\n" +
" \"deliveryDate\": \"2018-03-03T23:00:00.000+00:00\",\n" +
" \"sender\": {\n" +
" \"name\": \"Lieferant GmbH\",\n" +
" \"zip\": \"80333\",\n" +
" \"street\": \"Lieferantenstraße 20\",\n" +
" \"location\": \"München\",\n" +
" \"country\": \"DE\",\n" +
" \"taxID\": \"201/113/40209\",\n" +
" \"vatID\": \"DE123456789\",\n" +
" \"vatid\": \"DE123456789\"\n" +
" },\n" +
" \"recipient\": {\n" +
" \"name\": \"Kunden AG Mitte\",\n" +
" \"zip\": \"69876\",\n" +
" \"street\": \"Kundenstraße 15\",\n" +
" \"location\": \"Frankfurt\",\n" +
" \"country\": \"DE\"\n" +
" },\n" +
" \"grandTotal\": 234.43,\n" +
" \"zfitems\": [\n" +
" {\n" +
" \"price\": 9.9,\n" +
" \"quantity\": 20,\n" +
" \"tax\": null,\n" +
" \"grossPrice\": null,\n" +
" \"lineTotalAmount\": null,\n" +
" \"basisQuantity\": 1,\n" +
" \"detailedDeliveryPeriodFrom\": null,\n" +
" \"detailedDeliveryPeriodTo\": null,\n" +
" \"id\": null,\n" +
" \"product\": {\n" +
" \"unit\": \"H87\",\n" +
" \"name\": \"Trennblätter A4\",\n" +
" \"taxCategoryCode\": \"S\",\n" +
" \"attributes\": null,\n" +
" \"vatpercent\": 19\n" +
" },\n" +
" \"value\": 9.9\n" +
" }\n" +
" ],\n" +
" \"tradeSettlement\": null,\n" +
" \"ownTaxID\": \"201/113/40209\",\n" +
" \"ownVATID\": \"DE123456789\",\n" +
" \"ownStreet\": \"Lieferantenstraße 20\",\n" +
" \"ownZIP\": \"80333\",\n" +
" \"ownLocation\": \"München\",\n" +
" \"ownCountry\": \"DE\",\n" +
" \"zfallowances\": [\n" +
" {\n" +
" \"totalAmount\": 1,\n" +
" \"taxPercent\": 19,\n" +
" \"reason\": \"Sondernachlass\",\n" +
" \"reasonCode\": null,\n" +
" \"categoryCode\": \"S\",\n" +
" \"charge\": false\n" +
" }\n" +
" ]\n" +
"}", Invoice.class);
TransactionCalculator tc=new TransactionCalculator(fromJSON);
assertEquals(tc.getGrandTotal(),new BigDecimal("234.43"));
assertEquals(fromJSON.getNumber(), fromJSON.getNumber());
assertEquals(fromJSON.getZFItems().length, fromJSON.getZFItems().length);
}
public void testDueDateRoundtrip() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// [{"stringValue":"a","intValue":1,"booleanValue":true},
// {"stringValue":"bc","intValue":3,"booleanValue":false}]
Invoice fromJSON = mapper.readValue("{\n" +
" \"number\": \"RE - 228\",\n" +
" \"currency\": \"EUR\",\n" +
" \"issueDate\": \"2024-10-24\",\n" +
" \"dueDate\": \"2024-10-26\",\n" +
" \"deliveryDate\": \"2024-10-25\",\n" +
" \"sender\": {\n" +
" \"name\": \"Amazing Company\",\n" +
" \"zip\": \"10000\",\n" +
" \"street\": \"Straße der Kosmonauten 20\",\n" +
" \"location\": \"Berlin\",\n" +
" \"country\": \"DE\",\n" +
" \"taxID\": \"201/113/40209\",\n" +
" \"vatID\": \"DE123456789\",\n" +
" \"globalID\": \"4000001123452\",\n" +
" \"globalIDScheme\": \"0088\"\n" +
" },\n" +
" \"recipient\": {\n" +
" \"name\": \"Amazing Company\",\n" +
" \"zip\": \"1000\",\n" +
" \"street\": \"Straße der Kosmonauten 10\",\n" +
" \"location\": \"Berlin\",\n" +
" \"taxID\": \"201/113/40209\",\n" +
" \"vatID\": \"DE123456789\",\n" +
" \"country\": \"DE\"\n" +
" },\n" +
" \"zfitems\": [\n" +
" {\n" +
" \"price\": 99.9,\n" +
" \"quantity\": 10,\n" +
" \"product\": {\n" +
" \"unit\": \"H87\",\n" +
" \"name\": \"Amazing Archives\",\n" +
" \"description\": \"123\",\n" +
" \"vatpercent\": \"19\",\n" +
" \"taxCategoryCode\": \"3\"\n" +
" }\n" +
" }\n" +
" ]\n" +
"}\n", Invoice.class);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(fromJSON);
String theXML = new String(zf2p.getXML());
assertTrue(theXML.contains("<udt:DateTimeString format=\"102\">20241026</udt:DateTimeString>"));
}
} }

View File

@@ -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();

View File

@@ -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());

View File

@@ -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);

View File

@@ -297,7 +297,8 @@ public class ZF2EdgeTest extends MustangReaderTestCase {
InputStream SOURCE_PDF = this.getClass() InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();ze.ignorePDFAErrors(); ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
ze.ignorePDFAErrors();
ze.load(SOURCE_PDF); ze.load(SOURCE_PDF);
ze.setProducer("My Application") ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("Extended")); .setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("Extended"));

View File

@@ -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;
@@ -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());

View File

@@ -21,8 +21,9 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.mustangproject.FileAttachment; import com.fasterxml.jackson.core.JsonProcessingException;
import org.mustangproject.Invoice; import com.fasterxml.jackson.databind.ObjectMapper;
import org.mustangproject.*;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import java.io.*; import java.io.*;
@@ -33,6 +34,7 @@ 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; import java.util.Arrays;
import java.util.Date;
/*** /***
@@ -42,20 +44,6 @@ import java.util.Arrays;
public class ZF2ZInvoiceImporterTest extends ResourceCase { public class ZF2ZInvoiceImporterTest extends ResourceCase {
public void testInvoiceImportSupportCase145() {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20241004_\\IGEPA-Rechnung_41102839_00200_20240918.PDF");
boolean hasExceptions = false;
Invoice invoice = null;
try {
invoice = zii.extractInvoice();
} catch (XPathExpressionException | ParseException e) {
hasExceptions = true;
}
assertFalse(hasExceptions);
}
public void testInvoiceImport() { public void testInvoiceImport() {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2new.pdf"); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2new.pdf");
@@ -301,6 +289,12 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
assertFalse(hasExceptions); assertFalse(hasExceptions);
TransactionCalculator tc = new TransactionCalculator(invoice); TransactionCalculator tc = new TransactionCalculator(invoice);
assertEquals(new BigDecimal("1.00"), tc.getGrandTotal()); assertEquals(new BigDecimal("1.00"), tc.getGrandTotal());
assertTrue(invoice.getTradeSettlement().length==1);
assertTrue(invoice.getTradeSettlement()[0] instanceof IZUGFeRDTradeSettlementPayment);
IZUGFeRDTradeSettlementPayment paym=(IZUGFeRDTradeSettlementPayment)invoice.getTradeSettlement()[0];
assertEquals("DE12500105170648489890", paym.getOwnIBAN());
assertEquals("COBADEFXXX", paym.getOwnBIC());
assertTrue(invoice.getPayee() != null); assertTrue(invoice.getPayee() != null);
assertEquals("VR Factoring GmbH", invoice.getPayee().getName()); assertEquals("VR Factoring GmbH", invoice.getPayee().getName());
@@ -333,52 +327,105 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
public void testEEISI_300_cii_Import() { public void testImportDebit() {
boolean hasExceptions = false; File CIIinputFile = getResourceAsFile("cii/minimalDebit.xml");
File input = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel2.ubl.xml");
File inputCorrect = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel2.cii.xml");
ZUGFeRDInvoiceImporter cii = new ZUGFeRDInvoiceImporter();
try { try {
cii.fromXML(new String(Files.readAllBytes(inputCorrect.toPath()), StandardCharsets.UTF_8)); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(CIIinputFile));
Invoice i=zii.extractInvoice();
assertEquals("DE21860000000086001055", i.getSender().getBankDetails().get(0).getIBAN());
ObjectMapper mapper = new ObjectMapper();
String jsonArray = mapper.writeValueAsString(i);
// assertEquals("",jsonArray);
} catch (IOException e) { } catch (IOException e) {
hasExceptions = true; fail("IOException not expected");
}
Invoice ciiinvoice = null;
try {
ciiinvoice = cii.extractInvoice();
} catch (XPathExpressionException e) { } catch (XPathExpressionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} catch (ParseException e) { } catch (ParseException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}
public void testImportMinimum() {
File CIIinputFile = getResourceAsFile("cii/facturFrMinimum.xml");
try {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(CIIinputFile));
CalculatedInvoice i=new CalculatedInvoice();
zii.extractInto(i);
assertEquals("671.15", i.getGrandTotal().toString());
} catch (IOException e) {
fail("IOException not expected");
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
/*
this would test if for all elements/attributes
*/
public void testEEISI_300_cii_Import() throws XPathExpressionException, ParseException {
boolean hasExceptions = false;
File inputCII = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.cii.xml");
File inputUBL = getResourceAsFile("not_validating_full_invoice_based_onTest_EeISI_300_CENfullmodel.ubl.xml");
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter();
try { try {
zii.fromXML(new String(Files.readAllBytes(input.toPath()), StandardCharsets.UTF_8)); zii.fromXML(new String(Files.readAllBytes(inputCII.toPath()), StandardCharsets.UTF_8));
} catch (IOException e) { } catch (IOException e) {
hasExceptions = true; hasExceptions = true;
} }
Invoice invoice = null; Invoice invoiceUBL = null;
invoiceUBL = zii.extractInvoice();
try { try {
invoice = zii.extractInvoice(); zii.fromXML(new String(Files.readAllBytes(inputUBL.toPath()), StandardCharsets.UTF_8));
assertEquals("Seller name",invoice.getSender().getName());
assertEquals(ciiinvoice.getRecipient().getID(),invoice.getRecipient().getID()); } catch (IOException e) {
hasExceptions = true;
}
Invoice invoiceCII = null;
try {
invoiceCII = zii.extractInvoice();
ObjectMapper mapper = new ObjectMapper();
String ubl=mapper.writeValueAsString(invoiceUBL).replace("," ,"\n");
String cii=mapper.writeValueAsString(invoiceCII).replace("," ,"\n");
assertEquals(cii,ubl);
/* /*
<cbc:Name>Seller contact point</cbc:Name> <cbc:Name>Seller contact point</cbc:Name>
<cbc:Telephone>+41 345 654455</cbc:Telephone> <cbc:Telephone>+41 345 654455</cbc:Telephone>
<cbc:ElectronicMail>seller@contact.de);*/ <cbc:ElectronicMail>seller@contact.de);*/
} catch (XPathExpressionException | ParseException e) { } catch (XPathExpressionException | ParseException e) {
hasExceptions = true; hasExceptions = true;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} }
assertFalse(hasExceptions); assertFalse(hasExceptions);
TransactionCalculator tc = new TransactionCalculator(invoice);
assertEquals(new BigDecimal("205.00"), tc.getGrandTotal()); // TransactionCalculator tc = new TransactionCalculator(invoiceCII);
// assertEquals(new BigDecimal("205.00"), tc.getGrandTotal());
} }

View File

@@ -0,0 +1,56 @@
<?xml version='1.0' encoding='UTF-8'?>
<rsm:CrossIndustryInvoice xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:minimum</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>FA-2017-0010</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20171113</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>Au bon moulin</ram:Name>
<ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0002">99999999800010</ram:ID>
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress>
<ram:CountryID>FR</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">FR11999999998</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Ma jolie boutique</ram:Name>
<ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0002">78787878400035</ram:ID>
</ram:SpecifiedLegalOrganization>
<ram:PostalTradeAddress>
<ram:CountryID>FR</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">FR19787878784</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:BuyerTradeParty>
<ram:BuyerOrderReferencedDocument>
<ram:IssuerAssignedID>PO445</ram:IssuerAssignedID>
</ram:BuyerOrderReferencedDocument>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery/>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:TaxBasisTotalAmount currencyID="EUR">624.90</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">46.25</ram:TaxTotalAmount>
<ram:GrandTotalAmount currencyID="EUR">671.15</ram:GrandTotalAmount>
<ram:DuePayableAmount currencyID="EUR">470.15</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100">
<!-- generated by: mustangproject.org vnull-->
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>471102</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20180304</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Trennblätter A4</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>9.9000</ram:ChargeAmount>
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">20.0000</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>198.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>2</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Joghurt Banane</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>5.5000</ram:ChargeAmount>
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">50.0000</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>275.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>Lieferant GmbH</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>80333</ram:PostcodeCode>
<ram:LineOne>Lieferantenstraße 20</ram:LineOne>
<ram:CityName>München</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE123456789</ram:ID>
</ram:SpecifiedTaxRegistration>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="FC">201/113/40209</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Kunden AG Mitte</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>69876</ram:PostcodeCode>
<ram:LineOne>Kundenstraße 15</ram:LineOne>
<ram:CityName>Frankfurt</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery>
<ram:ActualDeliverySupplyChainEvent>
<ram:OccurrenceDateTime>
<udt:DateTimeString format="102">20180304</udt:DateTimeString>
</ram:OccurrenceDateTime>
</ram:ActualDeliverySupplyChainEvent>
</ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement>
<ram:CreditorReferenceID>DE98ZZZ09999999999</ram:CreditorReferenceID>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>59</ram:TypeCode>
<ram:PayerPartyDebtorFinancialAccount>
<ram:IBANID>DE21860000000086001055</ram:IBANID></ram:PayerPartyDebtorFinancialAccount>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>19.25</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>275.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>37.62</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>198.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradePaymentTerms>
<ram:Description>Der Betrag in Höhe von EUR 529,87 wird am 20.03.2018 von Ihrem Konto per SEPA-Lastschrift eingezogen.
</ram:Description>
<ram:DirectDebitMandateID>REF A-123</ram:DirectDebitMandateID>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>473.00</ram:LineTotalAmount>
<ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>473.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">56.87</ram:TaxTotalAmount>
<ram:GrandTotalAmount>529.87</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>529.87</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"> <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext> <rsm:ExchangedDocumentContext>
<ram:BusinessProcessSpecifiedDocumentContextParameter> <ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:ID>BT-23 Business Process Type</ram:ID> <ram:ID>BT-23 Business Process Type</ram:ID>
@@ -16,11 +19,11 @@
</ram:IssueDateTime> </ram:IssueDateTime>
<ram:IncludedNote> <ram:IncludedNote>
<ram:Content>invoice note text</ram:Content> <ram:Content>invoice note text</ram:Content>
<ram:SubjectCode>#AAA#</ram:SubjectCode> <ram:SubjectCode>AAA</ram:SubjectCode>
</ram:IncludedNote> </ram:IncludedNote>
<ram:IncludedNote> <ram:IncludedNote>
<ram:Content>invoice note text 2</ram:Content> <ram:Content>invoice note text 2</ram:Content>
<ram:SubjectCode>#AAA#</ram:SubjectCode> <ram:SubjectCode>AAA</ram:SubjectCode>
</ram:IncludedNote> </ram:IncludedNote>
</rsm:ExchangedDocument> </rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction> <rsm:SupplyChainTradeTransaction>
@@ -32,7 +35,9 @@
</ram:IncludedNote> </ram:IncludedNote>
</ram:AssociatedDocumentLineDocument> </ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct> <ram:SpecifiedTradeProduct>
<ram:GlobalID>Item standar identifier</ram:GlobalID> <ram:GlobalID schemeID="0060">Item standar identifier
</ram:GlobalID>
<ram:SellerAssignedID>Item seller's identifier</ram:SellerAssignedID> <ram:SellerAssignedID>Item seller's identifier</ram:SellerAssignedID>
<ram:BuyerAssignedID>Item buyer's identifier</ram:BuyerAssignedID> <ram:BuyerAssignedID>Item buyer's identifier</ram:BuyerAssignedID>
<ram:Name>Item name</ram:Name> <ram:Name>Item name</ram:Name>
@@ -93,9 +98,9 @@
<udt:Indicator>false</udt:Indicator> <udt:Indicator>false</udt:Indicator>
</ram:ChargeIndicator> </ram:ChargeIndicator>
<ram:CalculationPercent>1.00</ram:CalculationPercent> <ram:CalculationPercent>1.00</ram:CalculationPercent>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:ActualAmount>10.00</ram:ActualAmount> <ram:ActualAmount>10.00</ram:ActualAmount>
<ram:ReasonCode>55</ram:ReasonCode> <ram:ReasonCode>95</ram:ReasonCode>
<ram:Reason>Invoice line allowance reason</ram:Reason> <ram:Reason>Invoice line allowance reason</ram:Reason>
</ram:SpecifiedTradeAllowanceCharge> </ram:SpecifiedTradeAllowanceCharge>
<ram:SpecifiedTradeAllowanceCharge> <ram:SpecifiedTradeAllowanceCharge>
@@ -103,18 +108,17 @@
<udt:Indicator>true</udt:Indicator> <udt:Indicator>true</udt:Indicator>
</ram:ChargeIndicator> </ram:ChargeIndicator>
<ram:CalculationPercent>1.00</ram:CalculationPercent> <ram:CalculationPercent>1.00</ram:CalculationPercent>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:ActualAmount>10.00</ram:ActualAmount> <ram:ActualAmount>10.00</ram:ActualAmount>
<ram:ReasonCode>AAA</ram:ReasonCode> <ram:ReasonCode>AAA</ram:ReasonCode>
<ram:Reason>Invoice line charge reason</ram:Reason> <ram:Reason>Invoice line charge reason</ram:Reason>
</ram:SpecifiedTradeAllowanceCharge> </ram:SpecifiedTradeAllowanceCharge>
<ram:SpecifiedTradeSettlementLineMonetarySummation> <ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>1000.00</ram:LineTotalAmount> <ram:LineTotalAmount>100.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation> </ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:AdditionalReferencedDocument> <ram:AdditionalReferencedDocument>
<ram:IssuerAssignedID>Line object identifier</ram:IssuerAssignedID> <ram:IssuerAssignedID>Line object identifier</ram:IssuerAssignedID>
<ram:TypeCode>130</ram:TypeCode> <ram:TypeCode>130</ram:TypeCode>
<ram:ReferenceTypeCode />
</ram:AdditionalReferencedDocument> </ram:AdditionalReferencedDocument>
<ram:ReceivableSpecifiedTradeAccountingAccount> <ram:ReceivableSpecifiedTradeAccountingAccount>
<ram:ID>6789</ram:ID> <ram:ID>6789</ram:ID>
@@ -143,7 +147,7 @@
<ram:RateApplicablePercent>0.00</ram:RateApplicablePercent> <ram:RateApplicablePercent>0.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax> </ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation> <ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>1000.00</ram:LineTotalAmount> <ram:LineTotalAmount>100.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation> </ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement> </ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem> </ram:IncludedSupplyChainTradeLineItem>
@@ -155,7 +159,7 @@
<ram:Name>Seller name</ram:Name> <ram:Name>Seller name</ram:Name>
<ram:Description>Seller additional legal information</ram:Description> <ram:Description>Seller additional legal information</ram:Description>
<ram:SpecifiedLegalOrganization> <ram:SpecifiedLegalOrganization>
<ram:ID schemeID="0310">Seller legal identifier</ram:ID> <!-- <ram:ID schemeID="0310">Seller legal identifier</ram:ID> -->
<ram:TradingBusinessName>Seller trading name</ram:TradingBusinessName> <ram:TradingBusinessName>Seller trading name</ram:TradingBusinessName>
</ram:SpecifiedLegalOrganization> </ram:SpecifiedLegalOrganization>
<ram:DefinedTradeContact> <ram:DefinedTradeContact>
@@ -177,7 +181,7 @@
<ram:CountrySubDivisionName>Seller country subdivision</ram:CountrySubDivisionName> <ram:CountrySubDivisionName>Seller country subdivision</ram:CountrySubDivisionName>
</ram:PostalTradeAddress> </ram:PostalTradeAddress>
<ram:URIUniversalCommunication> <ram:URIUniversalCommunication>
<ram:URIID schemeID="SMTP">Seller electronic address</ram:URIID> <ram:URIID schemeID="EM">Seller electronic address</ram:URIID>
</ram:URIUniversalCommunication> </ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration> <ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE12345677</ram:ID> <ram:ID schemeID="VA">DE12345677</ram:ID>
@@ -212,7 +216,7 @@
<ram:CountrySubDivisionName>Buyer country subdivision</ram:CountrySubDivisionName> <ram:CountrySubDivisionName>Buyer country subdivision</ram:CountrySubDivisionName>
</ram:PostalTradeAddress> </ram:PostalTradeAddress>
<ram:URIUniversalCommunication> <ram:URIUniversalCommunication>
<ram:URIID schemeID="DE:SMTP">Buyer electronic address</ram:URIID> <ram:URIID schemeID="EM">Buyer electronic address</ram:URIID>
</ram:URIUniversalCommunication> </ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration> <ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">IE394838894</ram:ID> <ram:ID schemeID="VA">IE394838894</ram:ID>
@@ -252,7 +256,7 @@
<ram:AdditionalReferencedDocument> <ram:AdditionalReferencedDocument>
<ram:IssuerAssignedID>rst</ram:IssuerAssignedID> <ram:IssuerAssignedID>rst</ram:IssuerAssignedID>
<ram:TypeCode>130</ram:TypeCode> <ram:TypeCode>130</ram:TypeCode>
<ram:ReferenceTypeCode>0090</ram:ReferenceTypeCode> <ram:ReferenceTypeCode>AAA</ram:ReferenceTypeCode>
</ram:AdditionalReferencedDocument> </ram:AdditionalReferencedDocument>
<ram:SpecifiedProcuringProject> <ram:SpecifiedProcuringProject>
<ram:ID>456</ram:ID> <ram:ID>456</ram:ID>
@@ -311,32 +315,32 @@
<ram:IBANID>IT1212341234123412</ram:IBANID> <ram:IBANID>IT1212341234123412</ram:IBANID>
<ram:AccountName>Payment account name</ram:AccountName> <ram:AccountName>Payment account name</ram:AccountName>
</ram:PayeePartyCreditorFinancialAccount> </ram:PayeePartyCreditorFinancialAccount>
<ram:PayerSpecifiedDebtorFinancialInstitution> <!-- <ram:BICID>BSCTCH22</ram:BICID> -->
<ram:BICID>BSCTCH22</ram:BICID> <!-- <ram:PayerSpecifiedDebtorFinancialInstitution>
</ram:PayerSpecifiedDebtorFinancialInstitution> </ram:PayerSpecifiedDebtorFinancialInstitution> -->
<ram:PayeePartyCreditorFinancialAccount> <!-- <ram:PayeePartyCreditorFinancialAccount>
<ram:IBANID>IT1212341234123413</ram:IBANID> <ram:IBANID>IT1212341234123413</ram:IBANID>
<ram:AccountName>Payment account name 2</ram:AccountName> <ram:AccountName>Payment account name 2</ram:AccountName>
</ram:PayeePartyCreditorFinancialAccount> </ram:PayeePartyCreditorFinancialAccount>
<ram:PayerSpecifiedDebtorFinancialInstitution> <ram:PayerSpecifiedDebtorFinancialInstitution>
<ram:BICID>BSCTCH22</ram:BICID> <ram:BICID>BSCTCH22</ram:BICID>
</ram:PayerSpecifiedDebtorFinancialInstitution> </ram:PayerSpecifiedDebtorFinancialInstitution> -->
</ram:SpecifiedTradeSettlementPaymentMeans> </ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax> <ram:ApplicableTradeTax>
<ram:CalculatedAmount>50.00</ram:CalculatedAmount> <ram:CalculatedAmount>5.00</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode> <ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode> <ram:CategoryCode>S</ram:CategoryCode>
<ram:DueDateTypeCode>29</ram:DueDateTypeCode> <!-- <ram:DueDateTypeCode>29</ram:DueDateTypeCode> -->
<ram:RateApplicablePercent>5.00</ram:RateApplicablePercent> <ram:RateApplicablePercent>5.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax> </ram:ApplicableTradeTax>
<ram:ApplicableTradeTax> <ram:ApplicableTradeTax>
<ram:CalculatedAmount>0.00</ram:CalculatedAmount> <ram:CalculatedAmount>0.00</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode> <ram:TypeCode>VAT</ram:TypeCode>
<ram:ExemptionReason>Exemtion reason text</ram:ExemptionReason> <ram:ExemptionReason>Exemtion reason text</ram:ExemptionReason>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:CategoryCode>E</ram:CategoryCode> <ram:CategoryCode>E</ram:CategoryCode>
<ram:ExemptionReasonCode>Exemption reason code</ram:ExemptionReasonCode> <ram:ExemptionReasonCode>VATEX-EU-O</ram:ExemptionReasonCode>
<ram:DueDateTypeCode>29</ram:DueDateTypeCode> <ram:DueDateTypeCode>29</ram:DueDateTypeCode>
<ram:RateApplicablePercent>0.00</ram:RateApplicablePercent> <ram:RateApplicablePercent>0.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax> </ram:ApplicableTradeTax>
@@ -353,9 +357,9 @@
<udt:Indicator>false</udt:Indicator> <udt:Indicator>false</udt:Indicator>
</ram:ChargeIndicator> </ram:ChargeIndicator>
<ram:CalculationPercent>1.00</ram:CalculationPercent> <ram:CalculationPercent>1.00</ram:CalculationPercent>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:ActualAmount>10.00</ram:ActualAmount> <ram:ActualAmount>10.00</ram:ActualAmount>
<ram:ReasonCode>55</ram:ReasonCode> <ram:ReasonCode>95</ram:ReasonCode>
<ram:Reason>Doc allowance reason text</ram:Reason> <ram:Reason>Doc allowance reason text</ram:Reason>
<ram:CategoryTradeTax> <ram:CategoryTradeTax>
<ram:TypeCode>VAT</ram:TypeCode> <ram:TypeCode>VAT</ram:TypeCode>
@@ -368,7 +372,7 @@
<udt:Indicator>true</udt:Indicator> <udt:Indicator>true</udt:Indicator>
</ram:ChargeIndicator> </ram:ChargeIndicator>
<ram:CalculationPercent>1.00</ram:CalculationPercent> <ram:CalculationPercent>1.00</ram:CalculationPercent>
<ram:BasisAmount>1000.00</ram:BasisAmount> <ram:BasisAmount>100.00</ram:BasisAmount>
<ram:ActualAmount>10.00</ram:ActualAmount> <ram:ActualAmount>10.00</ram:ActualAmount>
<ram:ReasonCode>AAA</ram:ReasonCode> <ram:ReasonCode>AAA</ram:ReasonCode>
<ram:Reason>Doc charge reason text</ram:Reason> <ram:Reason>Doc charge reason text</ram:Reason>
@@ -386,16 +390,16 @@
<ram:DirectDebitMandateID>Mandate reference identifier</ram:DirectDebitMandateID> <ram:DirectDebitMandateID>Mandate reference identifier</ram:DirectDebitMandateID>
</ram:SpecifiedTradePaymentTerms> </ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation> <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>2000.00</ram:LineTotalAmount> <ram:LineTotalAmount>200.00</ram:LineTotalAmount>
<ram:ChargeTotalAmount>10.00</ram:ChargeTotalAmount> <ram:ChargeTotalAmount>10.00</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>10.00</ram:AllowanceTotalAmount> <ram:AllowanceTotalAmount>10.00</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>2000.00</ram:TaxBasisTotalAmount> <ram:TaxBasisTotalAmount>200.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">50.00</ram:TaxTotalAmount> <ram:TaxTotalAmount currencyID="EUR">5.00</ram:TaxTotalAmount>
<ram:TaxTotalAmount currencyID="NOK">46.00</ram:TaxTotalAmount> <ram:TaxTotalAmount currencyID="NOK">4.60</ram:TaxTotalAmount>
<ram:RoundingAmount>0.00</ram:RoundingAmount> <ram:RoundingAmount>0.00</ram:RoundingAmount>
<ram:GrandTotalAmount>2050.00</ram:GrandTotalAmount> <ram:GrandTotalAmount>205.00</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount> <ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>2050.00</ram:DuePayableAmount> <ram:DuePayableAmount>205.00</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation> </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:InvoiceReferencedDocument> <ram:InvoiceReferencedDocument>
<ram:IssuerAssignedID>abc123</ram:IssuerAssignedID> <ram:IssuerAssignedID>abc123</ram:IssuerAssignedID>

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ccts="urn:un:unece:uncefact:documentation:2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDataTypes-2" xmlns:udt="urn:oasis:names:specification:ubl:schema:xsd:UnqualifiedDataTypes-2"> <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID> <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>BT-23 Business Process Type</cbc:ProfileID> <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>Test_EeISI_100</cbc:ID> <cbc:ID>Test_EeISI_100</cbc:ID>
<cbc:IssueDate>2018-11-12</cbc:IssueDate> <cbc:IssueDate>2018-11-12</cbc:IssueDate>
<cbc:DueDate>2018-11-30</cbc:DueDate> <cbc:DueDate>2018-11-30</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>##AAA##invoice note text</cbc:Note> <cbc:Note>#AAA#invoice note text</cbc:Note>
<cbc:Note>##AAA##invoice note text 2</cbc:Note> <cbc:Note>#AAA#invoice note text 2</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:TaxCurrencyCode>NOK</cbc:TaxCurrencyCode> <cbc:TaxCurrencyCode>NOK</cbc:TaxCurrencyCode>
<cbc:AccountingCost>uvz</cbc:AccountingCost> <cbc:AccountingCost>uvz</cbc:AccountingCost>
@@ -15,7 +15,6 @@
<cac:InvoicePeriod> <cac:InvoicePeriod>
<cbc:StartDate>2018-11-12</cbc:StartDate> <cbc:StartDate>2018-11-12</cbc:StartDate>
<cbc:EndDate>2018-11-30</cbc:EndDate> <cbc:EndDate>2018-11-30</cbc:EndDate>
<cbc:DescriptionCode>35</cbc:DescriptionCode>
</cac:InvoicePeriod> </cac:InvoicePeriod>
<cac:OrderReference> <cac:OrderReference>
<cbc:ID>abc</cbc:ID> <cbc:ID>abc</cbc:ID>
@@ -33,16 +32,9 @@
<cac:ReceiptDocumentReference> <cac:ReceiptDocumentReference>
<cbc:ID>ghi</cbc:ID> <cbc:ID>ghi</cbc:ID>
</cac:ReceiptDocumentReference> </cac:ReceiptDocumentReference>
<cac:OriginatorDocumentReference>
<cbc:ID>opq</cbc:ID>
</cac:OriginatorDocumentReference>
<cac:ContractDocumentReference> <cac:ContractDocumentReference>
<cbc:ID>789</cbc:ID> <cbc:ID>789</cbc:ID>
</cac:ContractDocumentReference> </cac:ContractDocumentReference>
<cac:AdditionalDocumentReference>
<cbc:ID schemeID="0090">rst</cbc:ID>
<cbc:DocumentTypeCode>130</cbc:DocumentTypeCode>
</cac:AdditionalDocumentReference>
<cac:AdditionalDocumentReference> <cac:AdditionalDocumentReference>
<cbc:ID>Supporting document ref</cbc:ID> <cbc:ID>Supporting document ref</cbc:ID>
<cbc:DocumentDescription>Supporting document descr</cbc:DocumentDescription> <cbc:DocumentDescription>Supporting document descr</cbc:DocumentDescription>
@@ -53,21 +45,22 @@
</cac:ExternalReference> </cac:ExternalReference>
</cac:Attachment> </cac:Attachment>
</cac:AdditionalDocumentReference> </cac:AdditionalDocumentReference>
<cac:AdditionalDocumentReference>
<cbc:ID schemeID="AAA">rst</cbc:ID>
<cbc:DocumentTypeCode>130</cbc:DocumentTypeCode>
</cac:AdditionalDocumentReference>
<cac:ProjectReference> <cac:ProjectReference>
<cbc:ID>456</cbc:ID> <cbc:ID>456</cbc:ID>
</cac:ProjectReference> </cac:ProjectReference>
<cac:AccountingSupplierParty> <cac:AccountingSupplierParty>
<cac:Party> <cac:Party>
<cbc:EndpointID schemeID="SMTP">Seller electronic address</cbc:EndpointID> <cbc:EndpointID schemeID="EM">Seller electronic address</cbc:EndpointID>
<cac:PartyIdentification> <cac:PartyIdentification>
<cbc:ID schemeID="0100">Seller identifier 1</cbc:ID> <cbc:ID schemeID="0100">Seller identifier 1</cbc:ID>
</cac:PartyIdentification> </cac:PartyIdentification>
<cac:PartyIdentification> <cac:PartyIdentification>
<cbc:ID schemeID="0110">Seller identifier 2</cbc:ID> <cbc:ID schemeID="0110">Seller identifier 2</cbc:ID>
</cac:PartyIdentification> </cac:PartyIdentification>
<cac:PartyIdentification>
<cbc:ID schemeID="SEPA">Bank assigned creditor identifier</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName> <cac:PartyName>
<cbc:Name>Seller trading name</cbc:Name> <cbc:Name>Seller trading name</cbc:Name>
</cac:PartyName> </cac:PartyName>
@@ -93,12 +86,11 @@
<cac:PartyTaxScheme> <cac:PartyTaxScheme>
<cbc:CompanyID>DE49294093</cbc:CompanyID> <cbc:CompanyID>DE49294093</cbc:CompanyID>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>NOVAT</cbc:ID> <cbc:ID>FC</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
</cac:PartyTaxScheme> </cac:PartyTaxScheme>
<cac:PartyLegalEntity> <cac:PartyLegalEntity>
<cbc:RegistrationName>Seller name</cbc:RegistrationName> <cbc:RegistrationName>Seller name</cbc:RegistrationName>
<cbc:CompanyID schemeID="0310">Seller legal identifier</cbc:CompanyID>
<cbc:CompanyLegalForm>Seller additional legal information</cbc:CompanyLegalForm> <cbc:CompanyLegalForm>Seller additional legal information</cbc:CompanyLegalForm>
</cac:PartyLegalEntity> </cac:PartyLegalEntity>
<cac:Contact> <cac:Contact>
@@ -110,9 +102,9 @@
</cac:AccountingSupplierParty> </cac:AccountingSupplierParty>
<cac:AccountingCustomerParty> <cac:AccountingCustomerParty>
<cac:Party> <cac:Party>
<cbc:EndpointID schemeID="DE:SMTP">Buyer electronic address</cbc:EndpointID> <cbc:EndpointID schemeID="EM">Buyer electronic address</cbc:EndpointID>
<cac:PartyIdentification> <cac:PartyIdentification>
<cbc:ID>0190:Buyer identifier</cbc:ID> <cbc:ID schemeID="0190">Buyer identifier</cbc:ID>
</cac:PartyIdentification> </cac:PartyIdentification>
<cac:PartyName> <cac:PartyName>
<cbc:Name>Buyer trading name</cbc:Name> <cbc:Name>Buyer trading name</cbc:Name>
@@ -138,7 +130,7 @@
</cac:PartyTaxScheme> </cac:PartyTaxScheme>
<cac:PartyLegalEntity> <cac:PartyLegalEntity>
<cbc:RegistrationName>Buyer name</cbc:RegistrationName> <cbc:RegistrationName>Buyer name</cbc:RegistrationName>
<cbc:CompanyID>Buyer legal registration identifier</cbc:CompanyID> <cbc:CompanyID schemeID="0089">Buyer legal registration identifier</cbc:CompanyID>
</cac:PartyLegalEntity> </cac:PartyLegalEntity>
<cac:Contact> <cac:Contact>
<cbc:Name>Buyer contact point</cbc:Name> <cbc:Name>Buyer contact point</cbc:Name>
@@ -154,9 +146,6 @@
<cac:PartyName> <cac:PartyName>
<cbc:Name>Payee name</cbc:Name> <cbc:Name>Payee name</cbc:Name>
</cac:PartyName> </cac:PartyName>
<cac:PartyLegalEntity>
<cbc:CompanyID schemeID="0099">Payee legal registration identifier</cbc:CompanyID>
</cac:PartyLegalEntity>
</cac:PayeeParty> </cac:PayeeParty>
<cac:TaxRepresentativeParty> <cac:TaxRepresentativeParty>
<cac:PartyName> <cac:PartyName>
@@ -206,48 +195,19 @@
</cac:PartyName> </cac:PartyName>
</cac:DeliveryParty> </cac:DeliveryParty>
</cac:Delivery> </cac:Delivery>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="SEPA">4</cbc:PaymentMeansCode>
<cbc:PaymentID>Remittance information</cbc:PaymentID>
<cac:CardAccount>
<cbc:PrimaryAccountNumberID>1234</cbc:PrimaryAccountNumberID>
<cbc:NetworkID>mandatory network id</cbc:NetworkID>
<cbc:HolderName>Payment card holder name</cbc:HolderName>
</cac:CardAccount>
<cac:PayeeFinancialAccount>
<cbc:ID>IT1212341234123412</cbc:ID>
<cbc:Name>Payment account name</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>BSCTCH22</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
<cac:PayeeFinancialAccount>
<cbc:ID>IT1212341234123413</cbc:ID>
<cbc:Name>Payment account name 2</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>BSCTCH22</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
<cac:PaymentMandate>
<cbc:ID>Mandate reference identifier</cbc:ID>
<cac:PayerFinancialAccount>
<cbc:ID>Debited account identifier</cbc:ID>
</cac:PayerFinancialAccount>
</cac:PaymentMandate>
</cac:PaymentMeans>
<cac:PaymentTerms> <cac:PaymentTerms>
<cbc:Note>total amount</cbc:Note> <cbc:Note>total amount</cbc:Note>
</cac:PaymentTerms> </cac:PaymentTerms>
<cac:AllowanceCharge> <cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>55</cbc:AllowanceChargeReasonCode> <cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Doc allowance reason text</cbc:AllowanceChargeReason> <cbc:AllowanceChargeReason>Doc allowance reason text</cbc:AllowanceChargeReason>
<cbc:MultiplierFactorNumeric>1.0000</cbc:MultiplierFactorNumeric> <cbc:MultiplierFactorNumeric>1.00</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="EUR">10.00</cbc:Amount> <cbc:Amount currencyID="EUR">10</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">1000.00</cbc:BaseAmount> <cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount>
<cac:TaxCategory> <cac:TaxCategory>
<cbc:ID>S</cbc:ID> <cbc:ID>S</cbc:ID>
<cbc:Percent>5.00</cbc:Percent> <cbc:Percent>5</cbc:Percent>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
@@ -257,40 +217,37 @@
<cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>AAA</cbc:AllowanceChargeReasonCode> <cbc:AllowanceChargeReasonCode>AAA</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Doc charge reason text</cbc:AllowanceChargeReason> <cbc:AllowanceChargeReason>Doc charge reason text</cbc:AllowanceChargeReason>
<cbc:MultiplierFactorNumeric>1.0000</cbc:MultiplierFactorNumeric> <cbc:MultiplierFactorNumeric>1.00</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="EUR">10.00</cbc:Amount> <cbc:Amount currencyID="EUR">10</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">1000.00</cbc:BaseAmount> <cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount>
<cac:TaxCategory> <cac:TaxCategory>
<cbc:ID>S</cbc:ID> <cbc:ID>S</cbc:ID>
<cbc:Percent>5.00</cbc:Percent> <cbc:Percent>5</cbc:Percent>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
</cac:TaxCategory> </cac:TaxCategory>
</cac:AllowanceCharge> </cac:AllowanceCharge>
<cac:TaxTotal> <cac:TaxTotal>
<cbc:TaxAmount currencyID="NOK">46.00</cbc:TaxAmount> <cbc:TaxAmount currencyID="EUR">50</cbc:TaxAmount>
</cac:TaxTotal>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">50.00</cbc:TaxAmount>
<cac:TaxSubtotal> <cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxableAmount currencyID="EUR">1000</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">50.00</cbc:TaxAmount> <cbc:TaxAmount currencyID="EUR">50</cbc:TaxAmount>
<cac:TaxCategory> <cac:TaxCategory>
<cbc:ID>S</cbc:ID> <cbc:ID>S</cbc:ID>
<cbc:Percent>5.00</cbc:Percent> <cbc:Percent>5</cbc:Percent>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
</cac:TaxCategory> </cac:TaxCategory>
</cac:TaxSubtotal> </cac:TaxSubtotal>
<cac:TaxSubtotal> <cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">1000.00</cbc:TaxableAmount> <cbc:TaxableAmount currencyID="EUR">1000</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">0.00</cbc:TaxAmount> <cbc:TaxAmount currencyID="EUR">0</cbc:TaxAmount>
<cac:TaxCategory> <cac:TaxCategory>
<cbc:ID>E</cbc:ID> <cbc:ID>E</cbc:ID>
<cbc:Percent>0.00</cbc:Percent> <cbc:Percent>0</cbc:Percent>
<cbc:TaxExemptionReasonCode>Exemption reason code</cbc:TaxExemptionReasonCode> <cbc:TaxExemptionReasonCode>VATEX-EU-O</cbc:TaxExemptionReasonCode>
<cbc:TaxExemptionReason>Exemtion reason text</cbc:TaxExemptionReason> <cbc:TaxExemptionReason>Exemtion reason text</cbc:TaxExemptionReason>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
@@ -298,19 +255,23 @@
</cac:TaxCategory> </cac:TaxCategory>
</cac:TaxSubtotal> </cac:TaxSubtotal>
</cac:TaxTotal> </cac:TaxTotal>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="NOK">46</cbc:TaxAmount>
</cac:TaxTotal>
<cac:LegalMonetaryTotal> <cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">2000.00</cbc:LineExtensionAmount> <cbc:LineExtensionAmount currencyID="EUR">200</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">2000.00</cbc:TaxExclusiveAmount> <cbc:TaxExclusiveAmount currencyID="EUR">200</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">2050.00</cbc:TaxInclusiveAmount> <cbc:TaxInclusiveAmount currencyID="EUR">205</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">10.00</cbc:AllowanceTotalAmount> <cbc:AllowanceTotalAmount currencyID="EUR">10</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">10.00</cbc:ChargeTotalAmount> <cbc:ChargeTotalAmount currencyID="EUR">10</cbc:ChargeTotalAmount>
<cbc:PayableAmount currencyID="EUR">2050.00</cbc:PayableAmount> <cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">205</cbc:PayableAmount>
</cac:LegalMonetaryTotal> </cac:LegalMonetaryTotal>
<cac:InvoiceLine> <cac:InvoiceLine>
<cbc:ID>1a</cbc:ID> <cbc:ID>1a</cbc:ID>
<cbc:Note>Invoice line note</cbc:Note> <cbc:Note>Invoice line note</cbc:Note>
<cbc:InvoicedQuantity unitCode="EA">10.00000000</cbc:InvoicedQuantity> <cbc:InvoicedQuantity unitCode="EA">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:LineExtensionAmount currencyID="EUR">1000</cbc:LineExtensionAmount>
<cbc:AccountingCost>6789</cbc:AccountingCost> <cbc:AccountingCost>6789</cbc:AccountingCost>
<cac:InvoicePeriod> <cac:InvoicePeriod>
<cbc:StartDate>2018-11-12</cbc:StartDate> <cbc:StartDate>2018-11-12</cbc:StartDate>
@@ -320,14 +281,14 @@
<cbc:LineID>12345</cbc:LineID> <cbc:LineID>12345</cbc:LineID>
</cac:OrderLineReference> </cac:OrderLineReference>
<cac:DocumentReference> <cac:DocumentReference>
<cbc:ID schemeID="ZZZ">Line object identifier</cbc:ID> <cbc:ID>Line object identifier</cbc:ID>
<cbc:DocumentTypeCode>130</cbc:DocumentTypeCode> <cbc:DocumentTypeCode>130</cbc:DocumentTypeCode>
</cac:DocumentReference> </cac:DocumentReference>
<cac:AllowanceCharge> <cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>55</cbc:AllowanceChargeReasonCode> <cbc:AllowanceChargeReasonCode>95</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Invoice line allowance reason</cbc:AllowanceChargeReason> <cbc:AllowanceChargeReason>Invoice line allowance reason</cbc:AllowanceChargeReason>
<cbc:MultiplierFactorNumeric>1</cbc:MultiplierFactorNumeric> <cbc:MultiplierFactorNumeric>1.00</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="EUR">10</cbc:Amount> <cbc:Amount currencyID="EUR">10</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount> <cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount>
</cac:AllowanceCharge> </cac:AllowanceCharge>
@@ -335,7 +296,7 @@
<cbc:ChargeIndicator>true</cbc:ChargeIndicator> <cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReasonCode>AAA</cbc:AllowanceChargeReasonCode> <cbc:AllowanceChargeReasonCode>AAA</cbc:AllowanceChargeReasonCode>
<cbc:AllowanceChargeReason>Invoice line charge reason</cbc:AllowanceChargeReason> <cbc:AllowanceChargeReason>Invoice line charge reason</cbc:AllowanceChargeReason>
<cbc:MultiplierFactorNumeric>1</cbc:MultiplierFactorNumeric> <cbc:MultiplierFactorNumeric>1.00</cbc:MultiplierFactorNumeric>
<cbc:Amount currencyID="EUR">10</cbc:Amount> <cbc:Amount currencyID="EUR">10</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount> <cbc:BaseAmount currencyID="EUR">1000</cbc:BaseAmount>
</cac:AllowanceCharge> </cac:AllowanceCharge>
@@ -349,7 +310,7 @@
<cbc:ID>Item seller's identifier</cbc:ID> <cbc:ID>Item seller's identifier</cbc:ID>
</cac:SellersItemIdentification> </cac:SellersItemIdentification>
<cac:StandardItemIdentification> <cac:StandardItemIdentification>
<cbc:ID>Item standar identifier</cbc:ID> <cbc:ID schemeID="0060">Item standar identifier</cbc:ID>
</cac:StandardItemIdentification> </cac:StandardItemIdentification>
<cac:OriginCountry> <cac:OriginCountry>
<cbc:IdentificationCode>IT</cbc:IdentificationCode> <cbc:IdentificationCode>IT</cbc:IdentificationCode>
@@ -359,7 +320,7 @@
</cac:CommodityClassification> </cac:CommodityClassification>
<cac:ClassifiedTaxCategory> <cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID> <cbc:ID>S</cbc:ID>
<cbc:Percent>5.00</cbc:Percent> <cbc:Percent>5</cbc:Percent>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
@@ -374,8 +335,8 @@
</cac:AdditionalItemProperty> </cac:AdditionalItemProperty>
</cac:Item> </cac:Item>
<cac:Price> <cac:Price>
<cbc:PriceAmount currencyID="EUR">10.00</cbc:PriceAmount> <cbc:PriceAmount currencyID="EUR">10</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="EA">1.00</cbc:BaseQuantity> <cbc:BaseQuantity unitCode="EA">1</cbc:BaseQuantity>
<cac:AllowanceCharge> <cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator> <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">1</cbc:Amount> <cbc:Amount currencyID="EUR">1</cbc:Amount>
@@ -385,20 +346,20 @@
</cac:InvoiceLine> </cac:InvoiceLine>
<cac:InvoiceLine> <cac:InvoiceLine>
<cbc:ID>1b</cbc:ID> <cbc:ID>1b</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">10.00000000</cbc:InvoicedQuantity> <cbc:InvoicedQuantity unitCode="EA">10</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">1000.00</cbc:LineExtensionAmount> <cbc:LineExtensionAmount currencyID="EUR">1000</cbc:LineExtensionAmount>
<cac:Item> <cac:Item>
<cbc:Name>Item name 2</cbc:Name> <cbc:Name>Item name 2</cbc:Name>
<cac:ClassifiedTaxCategory> <cac:ClassifiedTaxCategory>
<cbc:ID>E</cbc:ID> <cbc:ID>E</cbc:ID>
<cbc:Percent>0.00</cbc:Percent> <cbc:Percent>0</cbc:Percent>
<cac:TaxScheme> <cac:TaxScheme>
<cbc:ID>VAT</cbc:ID> <cbc:ID>VAT</cbc:ID>
</cac:TaxScheme> </cac:TaxScheme>
</cac:ClassifiedTaxCategory> </cac:ClassifiedTaxCategory>
</cac:Item> </cac:Item>
<cac:Price> <cac:Price>
<cbc:PriceAmount currencyID="EUR">10.00</cbc:PriceAmount> <cbc:PriceAmount currencyID="EUR">10</cbc:PriceAmount>
</cac:Price> </cac:Price>
</cac:InvoiceLine> </cac:InvoiceLine>
</Invoice> </Invoice>

View File

@@ -54,7 +54,6 @@
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>3.0.2</version> <version>3.0.2</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>jakarta.xml.bind</groupId> <groupId>jakarta.xml.bind</groupId>

View File

@@ -52,7 +52,7 @@ public class PDFValidator extends Validator {
} }
private static final Logger LOGGER = LoggerFactory.getLogger(PDFValidator.class.getCanonicalName()); // log output private static final Logger LOGGER = LoggerFactory.getLogger(PDFValidator.class.getCanonicalName()); // log output
private static final PDFAFlavour[] PDF_A_3_FLAVOURS = {PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_A}; private static final PDFAFlavour[] PDF_A_3_FLAVOURS = {PDFAFlavour.PDFA_3_A, PDFAFlavour.PDFA_3_B, PDFAFlavour.PDFA_3_U};
private String pdfFilename; private String pdfFilename;
@@ -239,7 +239,8 @@ public class PDFValidator extends Validator {
boolean versionValid = false; boolean versionValid = false;
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
final String[] valueArray = {"1.0", "2p0", "1.2", "2.0", "2.1"}; //1.2, 2.0 and 2.1 are for xrechnung 1.2, 2p0 can be ZF 2.0, 2.1, 2.1.1 final String[] valueArray = {"1.0", "2p0", "1.2", "2.0", "2.1", "2.2", "2.3", "3.0"}; //1.2, 2.0, 2.1, 2.2, 2.3 and 3.0 are for xrechnung 1.2, 2p0 can be ZF 2.0, 2.1, 2.1.1
if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) { if (stringArrayContains(valueArray, nodes.item(i).getTextContent())) {
versionValid = true; versionValid = true;
} // e.g. 1.0 } // e.g. 1.0
@@ -263,6 +264,7 @@ public class PDFValidator extends Validator {
final byte[] konikSignature = "Konik".getBytes(StandardCharsets.UTF_8); final byte[] konikSignature = "Konik".getBytes(StandardCharsets.UTF_8);
final byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes(StandardCharsets.UTF_8); final byte[] pdfMachineSignature = "pdfMachine from Broadgun Software".getBytes(StandardCharsets.UTF_8);
final byte[] ghostscriptSignature = "%%Invocation:".getBytes(StandardCharsets.UTF_8); final byte[] ghostscriptSignature = "%%Invocation:".getBytes(StandardCharsets.UTF_8);
final byte[] cibpdfbrewerSignature = "CIB pdf brewer".getBytes(StandardCharsets.UTF_8);
if (ByteArraySearcher.contains(fileContents, symtraxSignature)) { if (ByteArraySearcher.contains(fileContents, symtraxSignature)) {
Signature = "Symtrax"; Signature = "Symtrax";
@@ -278,6 +280,8 @@ public class PDFValidator extends Validator {
Signature = "pdfMachine"; Signature = "pdfMachine";
} else if (ByteArraySearcher.contains(fileContents, ghostscriptSignature)) { } else if (ByteArraySearcher.contains(fileContents, ghostscriptSignature)) {
Signature = "Ghostscript"; Signature = "Ghostscript";
} else if (ByteArraySearcher.contains(fileContents, cibpdfbrewerSignature)) {
Signature = "CIB pdf brewer";
} }
context.setSignature(Signature); context.setSignature(Signature);
@@ -297,8 +301,10 @@ public class PDFValidator extends Validator {
if (!processorResult.getValidationResult().isCompliant()) { if (!processorResult.getValidationResult().isCompliant()) {
context.setInvalid(); context.setInvalid();
} }
PDFAFlavour pdfaFlavourFromValidationResult = processorResult.getValidationResult().getPDFAFlavour();
if (Arrays.stream(PDF_A_3_FLAVOURS) if (Arrays.stream(PDF_A_3_FLAVOURS)
.anyMatch(pdfaFlavour -> processorResult.getValidationResult().getPDFAFlavour().equals(pdfaFlavour))) { .noneMatch(pdfaFlavourFromValidationResult::equals)) {
context.addResultItem( context.addResultItem(
new ValidationResultItem(ESeverity.error, "Not a PDF/A-3").setSection(23).setPart(EPart.pdf)); new ValidationResultItem(ESeverity.error, "Not a PDF/A-3").setSection(23).setPart(EPart.pdf));

View File

@@ -269,14 +269,14 @@ public class XMLValidator extends Validator {
// saxon java net.sf.saxon.Transform -o tcdl2.0.tsdtf.sch.tmp.xsl -s // saxon java net.sf.saxon.Transform -o tcdl2.0.tsdtf.sch.tmp.xsl -s
// tcdl2.0.tsdtf.sch iso_svrl.xsl // tcdl2.0.tsdtf.sch iso_svrl.xsl
} else if (root.getLocalName().equalsIgnoreCase("Invoice")) { } else if (root.getLocalName().equalsIgnoreCase("Invoice") || root.getLocalName().equalsIgnoreCase("CreditNote") ) {
context.setGeneration("2"); context.setGeneration("2");
context.setFormat("UBL"); context.setFormat("UBL");
isXRechnung = context.getProfile().contains("xrechnung"); isXRechnung = context.getProfile().contains("xrechnung");
// UBL // UBL
LOGGER.debug("UBL"); LOGGER.debug("UBL");
validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "UBL_21/maindoc/UBL-Invoice-2.1.xsd", 18, EPart.fx); validateSchema(zfXML.getBytes(StandardCharsets.UTF_8), "UBL_21/maindoc/UBL-"+root.getLocalName()+"-2.1.xsd", 18, EPart.fx);
xsltFilename = "/xslt/UBL_21/EN16931-UBL-validation.xslt"; xsltFilename = "/xslt/en16931schematron/EN16931-UBL-validation.xslt";
mainSchematronSectionErrorTypeCode=24; mainSchematronSectionErrorTypeCode=24;
@@ -364,7 +364,7 @@ public class XMLValidator extends Validator {
if (context.getGeneration().equals("2") if (context.getGeneration().equals("2")
&& (isBasic || isEN16931 || isXRechnung)) { && (isBasic || isEN16931 || isXRechnung)) {
//additionally validate against CEN //additionally validate against CEN
validateSchematron(zfXML, "/xslt/cii16931schematron/EN16931-CII-validation.xslt", 24, ESeverity.error); validateSchematron(zfXML, "/xslt/en16931schematron/EN16931-CII-validation.xslt", 24, ESeverity.error);
if (!disableNotices || XrechnungSeverity != ESeverity.notice) { if (!disableNotices || XrechnungSeverity != ESeverity.notice) {
validateXR(zfXML, XrechnungSeverity); validateXR(zfXML, XrechnungSeverity);
} }
@@ -419,11 +419,16 @@ public class XMLValidator extends Validator {
* @param xml the xml to be checked * @param xml the xml to be checked
* @param xsltFilename the filename of the intermediate XSLT file * @param xsltFilename the filename of the intermediate XSLT file
* @param section the error type code, if one arises * @param section the error type code, if one arises
* @param severity how serious a error should be treated - may only be notice * @param defaultSeverity how serious a error should be treated - may only be notice
* @throws IrrecoverableValidationError if anything happened that prevents further checks * @throws IrrecoverableValidationError if anything happened that prevents further checks
*/ */
public void validateSchematron(String xml, String xsltFilename, int section, ESeverity severity) throws IrrecoverableValidationError { public void validateSchematron(String xml, String xsltFilename, int section, ESeverity defaultSeverity) throws IrrecoverableValidationError {
ISchematronResource aResSCH = null; ISchematronResource aResSCH = null;
ESeverity severity=defaultSeverity;
if (defaultSeverity!=ESeverity.notice) {
severity=ESeverity.error;
}
aResSCH = SchematronResourceXSLT.fromClassPath(xsltFilename); aResSCH = SchematronResourceXSLT.fromClassPath(xsltFilename);
if (aResSCH != null) { if (aResSCH != null) {
@@ -465,6 +470,16 @@ public class XMLValidator extends Validator {
thisFailLocation = currentFailNode.getAttributes().getNamedItem("location").getNodeValue(); thisFailLocation = currentFailNode.getAttributes().getNamedItem("location").getNodeValue();
} }
if (currentFailNode.getAttributes().getNamedItem("flag") != null) {
// the XR issues warnings with flag=warning
if (currentFailNode.getAttributes().getNamedItem("flag").getNodeValue().equals("warning")) {
if (defaultSeverity!=ESeverity.notice) {
severity=ESeverity.warning;
}
}
}
NodeList failChilds = currentFailNode.getChildNodes(); NodeList failChilds = currentFailNode.getChildNodes();
for (int failChildIndex = 0; failChildIndex < failChilds.getLength(); failChildIndex++) { for (int failChildIndex = 0; failChildIndex < failChilds.getLength(); failChildIndex++) {
if (failChilds.item(failChildIndex).getLocalName() != null) { if (failChilds.item(failChildIndex).getLocalName() != null) {

File diff suppressed because one or more lines are too long

View File

@@ -106,7 +106,7 @@ public class PDFValidatorTest extends ResourceCase {
public void testPDFXMLValidation() { public void testPDFXMLValidation() {
final ValidationContext vc = new ValidationContext(null); final ValidationContext vc = new ValidationContext(null);
try { /*@todo try {
final PDFValidator pv = new PDFValidator(vc); final PDFValidator pv = new PDFValidator(vc);
// need a more // need a more
// invalid file here // invalid file here
@@ -141,7 +141,7 @@ public class PDFValidatorTest extends ResourceCase {
assertEquals(true, xmlvres.contains("valid") && !xmlvres.contains("invalid")); assertEquals(true, xmlvres.contains("valid") && !xmlvres.contains("invalid"));
} catch (final IrrecoverableValidationError e) { } catch (final IrrecoverableValidationError e) {
// ignore, will be in XML output anyway // ignore, will be in XML output anyway
} }*/
} }

View File

@@ -298,6 +298,22 @@ public class XMLValidatorTest extends ResourceCase {
noExceptions = false; noExceptions = false;
} }
assertTrue(noExceptions); assertTrue(noExceptions);
tempFile = getResourceAsFile("ubl-tc434-creditnote1.xml");
try {
xv.setFilename(tempFile.getAbsolutePath());
xv.validate();
Source source = Input.fromString("<validation>" + xv.getXMLResult() + "</validation>").build();
String content = xpath.evaluate("/validation/summary/@status", source);
assertEquals("valid", content);
} catch (IrrecoverableValidationError e) {
noExceptions = false;
}
assertTrue(noExceptions);
} }

View File

@@ -2,6 +2,15 @@ package org.mustangproject.validator;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.xmlunit.builder.Input;
import org.xmlunit.xpath.JAXPXPathEngine;
import org.xmlunit.xpath.XPathEngine;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
@@ -132,6 +141,17 @@ public class ZUGFeRDValidatorTest extends ResourceCase {
} }
public void testPDFA3AValidation() {
File tempFile = getResourceAsFile("zugferd_2p1_EXTENDED_PDFA-3A.pdf");
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
String res = zfv.validate(tempFile.getAbsolutePath());
assertThat(res).valueByXPath("/validation/pdf/summary/@status")
.isEqualTo("valid");
}
/*** /***
* the XMLValidatorTests only cover the <xml></xml> part, this one includes the root element and * the XMLValidatorTests only cover the <xml></xml> part, this one includes the root element and
* the global <summary></summary> part as well * the global <summary></summary> part as well
@@ -205,6 +225,9 @@ public class ZUGFeRDValidatorTest extends ResourceCase {
assertThat(res).valueByXPath("count(//error)") assertThat(res).valueByXPath("count(//error)")
.asInt() .asInt()
.isEqualTo(3); .isEqualTo(3);
assertThat(res).valueByXPath("count(//warning)")
.asInt()
.isEqualTo(1);
assertThat(res).valueByXPath("count(//notice)") assertThat(res).valueByXPath("count(//notice)")
.asInt() .asInt()

View File

@@ -115,9 +115,6 @@
<ram:CityName>[Seller city]</ram:CityName> <ram:CityName>[Seller city]</ram:CityName>
<ram:CountryID>DE</ram:CountryID> <ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress> </ram:PostalTradeAddress>
<ram:URIUniversalCommunication>
<ram:URIID schemeID="EM">seller@email.de</ram:URIID>
</ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration> <ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE 123456789</ram:ID> <ram:ID schemeID="VA">DE 123456789</ram:ID>
</ram:SpecifiedTaxRegistration> </ram:SpecifiedTaxRegistration>

View File

@@ -0,0 +1,136 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!--
Licensed under European Union Public Licence (EUPL) version 1.2.
-->
<CreditNote xmlns="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>018304 / 28865</cbc:ID>
<cbc:IssueDate>2019-09-23</cbc:IssueDate>
<cbc:CreditNoteTypeCode>381</cbc:CreditNoteTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>018304 / 28865</cbc:BuyerReference>
<cac:InvoicePeriod>
<cbc:StartDate>2019-02-01</cbc:StartDate>
<cbc:EndDate>2019-02-28</cbc:EndDate>
</cac:InvoicePeriod>
<cac:AccountingSupplierParty>
<cac:Party>
<cbc:EndpointID schemeID="0201">0000000196</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>My Supplier Company N.V.</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>De Grote Meir 22</cbc:StreetName>
<cbc:CityName>ANTWERPEN</cbc:CityName>
<cbc:PostalZone>2000</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>BE0000000196</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>My Supplier Company</cbc:RegistrationName>
<cbc:CompanyID>0000000196</cbc:CompanyID>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:ElectronicMail>john.doole@mysuppliercompany.be</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="0201">0000000295</cbc:EndpointID>
<cac:PartyName>
<cbc:Name>My Customer Company S.A.</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Boulevard Sint Michel 53</cbc:StreetName>
<cbc:CityName>BRUXELLES</cbc:CityName>
<cbc:PostalZone>1000</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>BE0000000295</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>My Customer Company</cbc:RegistrationName>
<cbc:CompanyID>0000000295</cbc:CompanyID>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:ElectronicMail>pete.smith@mycustomercompany.be</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>1</cbc:PaymentMeansCode>
<cbc:PaymentID>010676609538</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>BE91000000143476</cbc:ID>
<cac:FinancialInstitutionBranch>
<cbc:ID>BPOTBEB1</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">0.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">100.11</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">0.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>E</cbc:ID>
<cbc:Percent>0.00</cbc:Percent>
<cbc:TaxExemptionReason>Taxes are not applicable</cbc:TaxExemptionReason>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">100.11</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">100.11</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">100.11</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">100.11</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:CreditNoteLine>
<cbc:ID>1</cbc:ID>
<cbc:CreditedQuantity unitCode="C62">1.00</cbc:CreditedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">100.11</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Exonération du versement du PP</cbc:Description>
<cbc:Name>Exonération du versement du PP</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>V55</cbc:ID>
</cac:SellersItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>E</cbc:ID>
<cbc:Percent>0.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
<cac:AdditionalItemProperty>
<cbc:Name>2</cbc:Name>
<cbc:Value>Contributions - précompte professionnel</cbc:Value>
</cac:AdditionalItemProperty>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">100.11</cbc:PriceAmount>
</cac:Price>
</cac:CreditNoteLine>
</CreditNote>