Merge remote-tracking branch 'remotes/origin/master'

This commit is contained in:
Sebastian Sieber
2021-02-15 11:26:13 +01:00
23 changed files with 2509 additions and 633 deletions

View File

@@ -1,10 +1,15 @@
2.1.1
=======
2020-02-09
- PR #217 seller order referenced document
- PR #217 seller order referenced document thanks to weclapp-dev
- PR #218 allow recipient contact in XRechnung thanks to seeeeew
- PR #221 remove outdated dependency thanks to heisej
- PR #223 allow XRechnung without street thanks to murygin
- only deploy library and validator to maven central, not core nor cli
- a correction should reference the original invoice via invoiceReferencedDocument, not buyerOrderReferencedDocument
- upgraded CEN Schematron validation (codes 24) to v1.3.4
- added unit tests for ubl conversion and visualization
2.1.0
=======

View File

@@ -3,14 +3,14 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>Mustang-CLI</artifactId>
<name>e-invoices commandline tool, allowing to create(embed), split and validate Factur-X/ZUGFeRD files. Validation should also work for XRechnung/CII. </name>
<packaging>jar</packaging>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
<repositories>
<repository>
<!-- for jargs -->
@@ -31,7 +31,7 @@
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
-->
@@ -140,6 +140,8 @@
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>LICENSE</exclude>
<exclude>NOTICE</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>

View File

@@ -26,6 +26,8 @@ import org.mustangproject.validator.Validator;
import org.mustangproject.validator.ZUGFeRDValidator;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -48,6 +50,7 @@ public class Main {
private static String getUsage() {
return "Usage: --action metrics|combine|extract|a3only|ubl|validate|visualize [-d,--directory] [-l,--listfromstdin] [-i,--ignore fileextension, PDF/A errors] | [-h,--help] \r\n"
+ " --action=license display open source license and notice\n"
+ " --action=metrics\n"
+ " -d, --directory count ZUGFeRD files in directory to be scanned\n"
+ " If it is a directory, it will recurse.\n"
@@ -288,6 +291,37 @@ public class Main {
c2u.convert(new File(xmlName), new File(outName));
System.out.println("Written to " + outName);
}
public static void printLicense() {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("LICENSE");
System.out.println(convertInputStreamToString(is));
is = Thread.currentThread().getContextClassLoader().getResourceAsStream("NOTICE");
System.out.println(convertInputStreamToString(is));
}
// Plain Java
// based on https://mkyong.com/java/how-to-convert-inputstream-to-string-in-java/
private static String convertInputStreamToString(InputStream is) {
int DEFAULT_BUFFER_SIZE = 8192;
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int length;
try {
while ((length = is.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
// Java 1.1
return result.toString(StandardCharsets.UTF_8.name());
} catch (IOException e) {
e.printStackTrace();
}
return null;
// Java 10
// return result.toString(StandardCharsets.UTF_8);
}
/***
* the main function of the commandline tool...
@@ -295,9 +329,9 @@ public class Main {
*/
public static void main(String[] args) {
try {
CmdLineParser parser = new CmdLineParser();
// Option: Help
Option<Boolean> helpOption = parser.addBooleanOption('h', "help");
// Option: Action
@@ -344,8 +378,9 @@ public class Main {
String action = parser.getOptionValue(actionOption);
String directoryName = parser.getOptionValue(dirnameOption);
Boolean filesFromStdIn = parser.getOptionValue(filesFromStdInOption, Boolean.FALSE);
Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE) || ((action!=null)&&(action.equals("help")));
Boolean ignoreFileExt = parser.getOptionValue(ignoreFileExtOption, Boolean.FALSE);
Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE) || ((action!=null)&&(action.equals("help")));
String sourceName = parser.getOptionValue(sourceOption);
String sourceXMLName = parser.getOptionValue(sourceXmlOption);
String outName = parser.getOptionValue(outOption);
@@ -358,7 +393,10 @@ public class Main {
if (helpRequested) {
printHelp();
optionsRecognized=true;
} else if ((action!=null)&&(action.equals("metrics"))) {
} /* else if ((action!=null)&&(action.equals("license"))) {
printLicense();
optionsRecognized=true;
} */ else if ((action!=null)&&(action.equals("metrics"))) {
performMetrics(directoryName, filesFromStdIn, ignoreFileExt);
optionsRecognized=true;
} else if ((action!=null)&&(action.equals("combine"))) {

1
NOTICE
View File

@@ -31,3 +31,4 @@ Based on https://github.com/itplr-kosit/xrechnung-schematron licensed under the
Based on https://github.com/itplr-kosit/xrechnung-visualization licensed under the APL2.0
Based on Saxon-HE https://sourceforge.net/projects/saxon/ licensed under the MPL 2.0
Based on DOM4j https://dom4j.github.io/ licensed under the BSD license
Based on EN16931 validation artefacts https://github.com/ConnectingEurope/eInvoicing-EN16931 licensed under the EUPL 2.0

View File

@@ -25,7 +25,7 @@ If you setup a Maven project, you can grab the artifacts using
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.0.2</version>
<version>2.1.1</version>
</dependency>
```

View File

@@ -3,13 +3,13 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD and to a limited extend XRechnung/CII). </name>
<description>The Mustang project is a java library to read, write and validate Factur-X/ZUGFeRD meta data inside your invoice PDFs. To write files, a provided PDF/A will be combined with generated or provided XML.
@@ -117,12 +117,6 @@
<artifactId>xmlunit-assertj</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>com.helger</groupId>
<artifactId>ph-commons</artifactId>
<version>9.1.1</version>
<scope>compile</scope>
</dependency>
<!-- API -->
<dependency>
<groupId>jakarta.xml.bind</groupId>

View File

@@ -18,6 +18,8 @@
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import java.nio.charset.StandardCharsets;
public class CustomXMLProvider implements IXMLProvider {
protected byte[] zugferdData;
@@ -29,7 +31,7 @@ public class CustomXMLProvider implements IXMLProvider {
}
public void setXML(byte[] newData) {
String zf = new String(newData);
String zf = new String(newData, StandardCharsets.UTF_8);
if (!zf.contains("CrossIndustry")) {
throw new RuntimeException("ZUGFeRD XML does not contain (<rsm:)CrossIndustry and can thus not be valid");
}

View File

@@ -156,9 +156,11 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
xml += " <ram:PostalTradeAddress>\n"
+ " <ram:PostcodeCode>" + XMLTools.encodeXML(party.getZIP())
+ "</ram:PostcodeCode>\n"
+ " <ram:LineOne>" + XMLTools.encodeXML(party.getStreet())
+ "</ram:PostcodeCode>\n";
if (party.getStreet() != null) {
xml += " <ram:LineOne>" + XMLTools.encodeXML(party.getStreet())
+ "</ram:LineOne>\n";
}
if (party.getAdditionalAddress() != null) {
xml += " <ram:LineTwo>" + XMLTools.encodeXML(party.getAdditionalAddress())
+ "</ram:LineTwo>\n";

View File

@@ -23,6 +23,7 @@ import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
@@ -77,7 +78,7 @@ public class ZUGFeRDVisualizer {
FileInputStream fis=new FileInputStream(xmlFilename);
String fileContent="";
try {
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)));
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8);
} catch (IOException e2) {
LOG.log(Level.SEVERE, null, e2);
}

View File

@@ -33,6 +33,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
@@ -270,7 +271,7 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
.setZUGFeRDVersion(1)
.load(inputStream);
ze.setTransaction(this);
String theXML = new String(ze.getProvider().getXML());
String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryDocument"));
ze.export(targetPath);
} catch (IOException e) {

View File

@@ -0,0 +1,40 @@
package org.mustangproject.ZUGFeRD;
import junit.framework.TestCase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class ResourceCase extends TestCase {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceCase.class.getCanonicalName()); // log output is
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) {
LOGGER.error(e.getMessage(), e);
return null;
}
}
}

View File

@@ -0,0 +1,57 @@
/**
* *********************************************************************
* <p>
* Copyright 2019 Jochen Staerk
* <p>
* Use is subject to license terms.
* <p>
* 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
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* <p>
* See the License for the specific language governing permissions and
* limitations under the License.
* <p>
* **********************************************************************
*/
package org.mustangproject.ZUGFeRD;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import org.mustangproject.CII.CIIToUBL;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UBLTest extends ResourceCase {
public void testUBLBasic() {
// the writing part
CIIToUBL c2u = new CIIToUBL();
String sourceFilename = "factur-x.xml";
File input = getResourceAsFile(sourceFilename);
File expectedFile = getResourceAsFile("ubl-conv-ubl-output-factur-x.xml");
String expected = null;
String result = null;
try {
File tempFile = File.createTempFile("ZUGFeRD-UBL-", "-test");
c2u.convert(input, tempFile);
expected = ResourceUtilities.readFile(StandardCharsets.UTF_8, expectedFile.getAbsolutePath());
result = ResourceUtilities.readFile(StandardCharsets.UTF_8, tempFile.getAbsolutePath());
} catch (IOException e) {
fail("Exception should not happen: "+e.getMessage());
}
assertNotNull(result);
assertEquals(expected, result);
}
}

View File

@@ -0,0 +1,69 @@
/** **********************************************************************
*
* Copyright 2019 Jochen Staerk
*
* Use is subject to license terms.
*
* 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
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import org.mustangproject.CII.CIIToUBL;
import javax.xml.transform.TransformerException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class VisualizationTest extends ResourceCase {
public void testVisualizationBasic() {
// the writing part
CIIToUBL c2u = new CIIToUBL();
String sourceFilename = "factur-x.xml";
File input = getResourceAsFile(sourceFilename);
String expected = null;
String result = null;
try {
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
*/
result = zvi.visualize(input.getAbsolutePath()).replace("\r","").replace("\n","");
File expectedResult=getResourceAsFile("factur-x.html");
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r","").replace("\n","");
// remove linebreaks as well...
} catch (TransformerException e) {
fail("Exception should not happen: "+e.getMessage());
} catch (IOException e) {
fail("Exception should not happen: "+e.getMessage());
}
assertNotNull(result);
// Reading ZUGFeRD
assertEquals(expected, result);
}
}

View File

@@ -31,6 +31,7 @@ import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import static org.xmlunit.assertj.XmlAssert.assertThat;
@@ -43,22 +44,13 @@ public class XRTest extends TestCase {
public void testXRExport() {
// the writing part
String orgname = "Test company";
String number = "123";
String amountStr = "1.00";
BigDecimal amount = new BigDecimal(amountStr);
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)));
TradeParty recipient = new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE");
Invoice i = createInvoice(recipient);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML());
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt()
@@ -77,6 +69,7 @@ public class XRTest extends TestCase {
}
}
public void testXREdgeExport() {
// the writing part
@@ -101,7 +94,7 @@ public class XRTest extends TestCase {
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML());
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt()
@@ -121,4 +114,46 @@ public class XRTest extends TestCase {
}
public void testXRExportWithoutStreet() {
// the writing part
TradeParty recipient = new TradeParty("Franz Müller", null, "55232", "Entenhausen", "DE");
Invoice i = createInvoice(recipient);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt()
.isEqualTo(1); //2 errors are OK because there is a known bug
assertThat(theXML).valueByXPath("//*[local-name()='DuePayableAmount']")
.asDouble()
.isEqualTo(1);
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(TARGET_XML));
writer.write(theXML);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private org.mustangproject.Invoice createInvoice(TradeParty recipient) {
String orgname = "Test company";
String number = "123";
String amountStr = "1.00";
BigDecimal amount = new BigDecimal(amountStr);
return new Invoice().setDueDate(new java.util.Date()).setIssueDate(new java.util.Date()).setDeliveryDate(new java.util.Date())
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new org.mustangproject.Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(recipient)
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new org.mustangproject.Item(new org.mustangproject.Product("Testprodukt", "", "C62", java.math.BigDecimal.ZERO), amount, new java.math.BigDecimal(1.0)));
}
}

View File

@@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
@@ -266,7 +267,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase implements IExportableTra
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors()
.load(SOURCE_PDF)) {
ze.setTransaction(this);
String theXML = new String(ze.getProvider().getXML());
String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_PDF);
} catch (IOException e) {
@@ -314,7 +315,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase implements IExportableTra
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).disableFacturX()
.load(SOURCE_PDF)) {
ze.setTransaction(this);
String theXML = new String(ze.getProvider().getXML());
String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(bos);
} catch (IOException e) {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,191 @@
<?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">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>RE-20201121/508</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20201121</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Design (hours)</ram:Name>
<ram:Description>Of a sample invoice</ram:Description>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>160.0000</ram:ChargeAmount>
<ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>160.0000</ram:ChargeAmount>
<ram:BasisQuantity unitCode="HUR">1.0000</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="HUR">1.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>160.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>2</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Ballons</ram:Name>
<ram:Description>various colors, ~2000ml</ram:Description>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>0.7900</ram:ChargeAmount>
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>0.7900</ram:ChargeAmount>
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">400.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>316.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>3</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Hot air „heiße Luft“ (litres)</ram:Name>
<ram:Description/>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>0.0250</ram:ChargeAmount>
<ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity>
</ram:GrossPriceProductTradePrice>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>0.0250</ram:ChargeAmount>
<ram:BasisQuantity unitCode="LTR">1.0000</ram:BasisQuantity>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="LTR">800.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>20.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:BuyerReference>AB321</ram:BuyerReference>
<ram:SellerTradeParty>
<ram:Name>Bei Spiel GmbH</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>12345</ram:PostcodeCode>
<ram:LineOne>Ecke 12</ram:LineOne>
<ram:CityName>Stadthausen</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE136695976</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:ID>2</ram:ID>
<ram:Name>Theodor Est</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>88802</ram:PostcodeCode>
<ram:LineOne>Bahnstr. 42</ram:LineOne>
<ram:CityName>Spielkreis</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery>
<ram:ActualDeliverySupplyChainEvent>
<ram:OccurrenceDateTime>
<udt:DateTimeString format="102">20201110</udt:DateTimeString>
</ram:OccurrenceDateTime>
</ram:ActualDeliverySupplyChainEvent>
</ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement>
<ram:PaymentReference>RE-20201121/508</ram:PaymentReference>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>42</ram:TypeCode>
<ram:Information>Bank transfer</ram:Information>
<ram:PayeePartyCreditorFinancialAccount>
<ram:IBANID>DE88200800000970375700</ram:IBANID>
<ram:AccountName>Max Mustermann</ram:AccountName>
</ram:PayeePartyCreditorFinancialAccount>
<ram:PayeeSpecifiedCreditorFinancialInstitution>
<ram:BICID>COBADEFFXXX</ram:BICID>
</ram:PayeeSpecifiedCreditorFinancialInstitution>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>11.20</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>160.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>7.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>63.84</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>336.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradePaymentTerms>
<ram:Description>Zahlbar ohne Abzug bis 12.12.2020</ram:Description>
<ram:DueDateDateTime>
<udt:DateTimeString format="102">20201212</udt:DateTimeString>
</ram:DueDateDateTime>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>496.00</ram:LineTotalAmount>
<ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>496.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">75.04</ram:TaxTotalAmount>
<ram:GrandTotalAmount>571.04</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>571.04</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

View File

@@ -0,0 +1,156 @@
<?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: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:extended:urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>RE-20201121/508</cbc:ID>
<cbc:IssueDate>2020-11-21</cbc:IssueDate>
<cbc:DueDate>2020-12-12</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>AB321</cbc:BuyerReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyName>
<cbc:Name>Bei Spiel GmbH</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Ecke 12</cbc:StreetName>
<cbc:CityName>Stadthausen</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE136695976</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Bei Spiel GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>2</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Theodor Est</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Bahnstr. 42</cbc:StreetName>
<cbc:CityName>Spielkreis</cbc:CityName>
<cbc:PostalZone>88802</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Theodor Est</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2020-11-10</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentTerms>
<cbc:Note>Zahlbar ohne Abzug bis 12.12.2020</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">75.04</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">160.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">11.20</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">336.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">63.84</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">496.00</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">496.00</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">571.04</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0.00</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0.00</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0.00</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">571.04</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">1.0000</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">160.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Of a sample invoice</cbc:Description>
<cbc:Name>Design (hours)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">160.0000</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="HUR">1.0000</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">400.0000</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">316.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>various colors, ~2000ml</cbc:Description>
<cbc:Name>Ballons</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.7900</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="H87">1.0000</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:InvoicedQuantity unitCode="LTR">800.0000</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">20.00</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description></cbc:Description>
<cbc:Name>Hot air „heiße Luft“ (litres)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.0250</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="LTR">1.0000</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Mustang</name>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
@@ -11,7 +11,7 @@
<name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name>
<packaging>jar</packaging>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
<repositories>
<repository>
<!-- for jargs -->
@@ -77,7 +77,7 @@
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.1.1-SNAPSHOT</version>
<version>2.1.2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
@@ -176,6 +176,8 @@
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>LICENSE</exclude>
<exclude>NOTICE</exclude>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>

View File

@@ -62,7 +62,7 @@ public class XMLValidator extends Validator {
// file existence must have been checked before
try {
zfXML = new String(XMLTools.removeBOM(Files.readAllBytes(Paths.get(name))));
zfXML = new String(XMLTools.removeBOM(Files.readAllBytes(Paths.get(name))), StandardCharsets.UTF_8);
} catch (IOException e) {
ValidationResultItem vri = new ValidationResultItem(ESeverity.exception, e.getMessage()).setSection(9)

View File

@@ -1,6 +1,7 @@
package org.mustangproject.validator;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -166,7 +167,7 @@ public class ZUGFeRDValidator {
byte[] content=Files.readAllBytes(file.toPath());
content= XMLTools.removeBOM(content);
String s=new String(content);
String s=new String(content, StandardCharsets.UTF_8);
InputSource is = new InputSource(new StringReader(s));
Document doc = db.parse(is);

File diff suppressed because one or more lines are too long