diff --git a/History.md b/History.md
index 42272163..81ce7e8c 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,52 @@
+1.7.4
+=====
+
+- #102 XML entities for ZF2 export
+
+
+1.7.3
+=====
+2019-08-01
+
+- #105 does not build in windows
+- #99 BOMs confuse parser
+- updated javadoc
+- fixed #104 nullpointerex when specifying no parameter
+- PR #112 complete profile options for v1
+- access to TotalPrepaidAmount in ZF v1 #118
+- have unit tests for nDigitFormat #23
+
+
+1.7.2
+=====
+2019-07-08
+
+Support BuyerReference (r+w), as well as SpecifiedLegalOrganization (w) and DefinedTradeContact (w)
+use dom4j to format output xml document
+corrected some exception logging glitches
+upgrade PDFBox to 2.0.15+
+extraction to use proper filename instead of alias #98
+NullPointerException in ZUGFeRDImporter.extractLowLevel #96
+Removed Bankleitzahl from ZF2
+
+
+1.7.1
+=====
+2019-05-26
+
+Corrected EN16931 ZF2 profile name
+
+1.7.0
+=====
+2019-05-05
+
+Export ZF2 final now seems halfway valid (new filename, new namespace prefixa)
+Migrates profile names in ZF1 to ZF2 migration
+Nullpointerex solved with certain invalid profiles
+Be able to access additional files ("additional data")
+Known bugs: src/test/resources/migration/reference/ZUGFeRD2_EXTENDED_Warenrechnung.xml is invalid
+
+
1.6.0
=====
2019-02-03
diff --git a/README.md b/README.md
index d38d0088..bc41c36f 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ As dependency use this
org.mustangproject.ZUGFeRDmustang
- 1.5.4
+ 1.7.1
diff --git a/ZUGFeRD-invoice.xml b/ZUGFeRD-invoice.xml
deleted file mode 100644
index e69de29b..00000000
diff --git a/doc/MustangWriter.java b/doc/MustangWriter.java
index 91c2cfe1..500923dc 100644
--- a/doc/MustangWriter.java
+++ b/doc/MustangWriter.java
@@ -5,12 +5,15 @@ import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableContact;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTransaction;
+import org.mustangproject.ZUGFeRD.ZUGFeRD2PullProvider;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporter;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromA1Factory;
@@ -32,8 +35,7 @@ public class MustangWriter implements IZUGFeRDExportableTransaction {
ze.export("./MustangGnuaccountingBeispielRE-20171118_506new.pdf");
System.out.println("Done.");
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(MustangWriter.class.getName()).log(Level.SEVERE, null, e1);
}
}
diff --git a/doc/development_documentation.md b/doc/development_documentation.md
new file mode 100644
index 00000000..f9439a5e
--- /dev/null
+++ b/doc/development_documentation.md
@@ -0,0 +1,97 @@
+## New build
+
+Target platform is java 1.7
+
+## Build
+
+Toecount is now part of the package which can be build with
+```
+mvn clean package
+```
+
+## Test
+
+`package -Dmaven.surefire.debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8001 -Xnoagent -Djava.compiler=NONE"`
+can be used as debug configuration goal in Eclipse. In that case you can set breakpoints in tests.
+
+## Validate
+
+[ZUV](https://github.com/ZUGFeRD/ZUV/) can be used to validate generated files.
+
+## Deployment
+
+A section in Maven's settings.xml is needed, in Linux (and MacOS) that's at ~/.m2/settings.xml
+
+As „servers“, enter the following
+```xml
+
+
+ github
+ GITHUB-TOKEN
+
+
+```
+The whole settings.xml then looks e.g. like this
+```xml
+
+
+
+
+
+
+
+
+ github
+ TOKEN
+
+
+
+
+
+
+
+```
+
+The password is generated on github.
+See the following screenshot:
+
+
+Sign in in GitHub and click on the profile picture -> Settings. Now just generate a new token and set the checkboxes from the screenshot.
+
+ The Token-ID is the password.
+
+
+## Release
+
+You will need a git client on the console, if that's available can e.g. be checked with "git --version" .
+Change to the root of the repo.
+
+Change to the project directory and run
+ * "/usr/local/Cellar/maven/3.5.2/bin/mvn clean install" . If that works you can
+ * clean the release with „/usr/local/Cellar/maven/3.5.2/bin/mvn release:clean“ and prepare the release with
+ * "/usr/local/Cellar/maven/3.5.2/bin/mvn release:prepare -DignoreSnapshots=true" and enter the version numbers.
+ * After that is through you can create a new release via "/usr/local/Cellar/maven/3.5.2/bin/mvn release:perform -Darguments="-Dmaven.javadoc.skip=true".This will also update the maven repo.
+
+ 
+
+
+Afterwards you can access the release page and update the documentation, e.g. upload the jar, the jar javadoc and ZugferdDev. You can also enter a changelog and a better title.
+
+
+## Release process
+
+ * Update documentation
+ * write/translate announcement
+ * release via/for mvn
+ * Publish a github release
+ * update mustangproject.org RE
+ * version number and release date
+ * sample file?
+ * Deployment jar
+ * put announcement on usegroup
+ * email the mailing list
+ * freshcode.club
+
\ No newline at end of file
diff --git a/doc/development_documentation_screenshot_github_settings.png b/doc/development_documentation_screenshot_github_settings.png
new file mode 100644
index 00000000..ff1ba41e
Binary files /dev/null and b/doc/development_documentation_screenshot_github_settings.png differ
diff --git a/doc/MustangDev.en.odt b/doc/development_documentation_screenshot_release.png
similarity index 55%
rename from doc/MustangDev.en.odt
rename to doc/development_documentation_screenshot_release.png
index b9b50a21..a7ea7074 100644
Binary files a/doc/MustangDev.en.odt and b/doc/development_documentation_screenshot_release.png differ
diff --git a/pom.xml b/pom.xml
index 650c1728..77463aa8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,327 +1,341 @@
-
- 4.0.0
- org.mustangproject.ZUGFeRD
- mustang
- 1.6.1-SNAPSHOT
- jar
- Mustang
- The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs
+
+ 4.0.0
+ org.mustangproject.ZUGFeRD
+ mustang
+ 1.7.4-SNAPSHOT
+ jar
+ Mustang
+ The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs
- http://www.mustangproject.org/
-
- scm:git:https://github.com/ZUGFeRD/mustangproject.git
- scm:git:https://github.com/ZUGFeRD/mustangproject.git
- https://github.com/ZUGFeRD/mustangproject
- mustang-1.5.2
-
-
-
- sonatype-oss-public
- https://oss.sonatype.org/content/groups/public/
-
- true
-
-
- true
-
-
-
-
-
- internal.repo
- Temporary Staging Repository
- file://${project.build.directory}/mvn-repo
-
-
-
- UTF-8
- github
- -Xdoclint:none
-
- 1.8
- 1.8
- 1.8
-
-
-
-
- net.sf.saxon
- Saxon-HE
- 9.8.0-11
-
-
- com.sun.xml.bind
- jaxb-impl
- 2.2.11
-
-
- com.sun.xml.bind
- jaxb-core
- 2.2.11
-
-
- org.glassfish.jaxb
- jaxb-runtime
- 2.2.11
-
-
- org.apache.pdfbox
- preflight
- 2.0.12
-
-
- org.apache.pdfbox
- pdfbox
- 2.0.12
-
-
- junit
- junit
- 4.12
- test
-
-
- com.helger
- ph-schematron
- 5.0.4
- test
-
-
- org.xmlunit
- xmlunit-core
- 2.6.2
- test
-
-
-
- com.sanityinc
- jargs
- 2.0-SNAPSHOT
-
+ http://www.mustangproject.org/
+
+ scm:git:https://github.com/ZUGFeRD/mustangproject.git
+ scm:git:https://github.com/ZUGFeRD/mustangproject.git
+ https://github.com/ZUGFeRD/mustangproject
+ mustang-1.7.3
+
+
+
+ sonatype-oss-public
+ https://oss.sonatype.org/content/groups/public/
+
+ true
+
+
+ true
+
+
+
+
+
+ internal.repo
+ Temporary Staging Repository
+ file://${project.build.directory}/mvn-repo
+
+
+
+ UTF-8
+ github
+ -Xdoclint:none
+
+ 1.8
+ 1.8
+ 1.8
+
+
+
+
+ net.sf.saxon
+ Saxon-HE
+ 9.8.0-11
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ 2.2.11
+
+
+ com.sun.xml.bind
+ jaxb-core
+ 2.2.11
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.2.11
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.2.11
+
+
+ javax.activation
+ activation
+ 1.1
+
-
-
-
-
-
+
+ com.sanityinc
+ jargs
+ 2.0-SNAPSHOT
+
+
+
+
+
+
+
- org.apache.maven.plugins
- maven-release-plugin
- 2.5.3
-
-
-
-
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.3
+
+
+
+
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.6.1
-
-
-
- true
- org.mustangproject.toecount.Toecount
-
-
-
- jar-with-dependencies
-
-
-
- 1.8
- 1.8
-
-
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.6.1
+
+
+
+ true
+ org.mustangproject.toecount.Toecount
+
+
+
+ jar-with-dependencies
+
+
+
+ 1.8
+ 1.8
+
+
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.0.1
-
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.0.1
+
- org.mustangproject.ZUGFeRD.model.*
+ org.mustangproject.ZUGFeRD.model.*
-
-
-
- maven-deploy-plugin
- 2.8.2
-
- internal.repo::default::file://${project.build.directory}/mvn-repo
-
-
-
-
- com.github.github
- site-maven-plugin
- 0.12
-
- Maven artifacts for ${project.version}
-
- true
-
- ${project.build.directory}/mvn-repo
-
- refs/heads/mvn-repo
-
-
- **/*
-
- true
- mustangproject
-
- ZUGFeRD
-
-
-
-
-
-
- site
-
- deploy
-
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.4.3
-
-
-
- org.mustangproject.toecount.Toecount
-
-
- true
-
+
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+ internal.repo::default::file://${project.build.directory}/mvn-repo
+
+
+
+
+ com.github.github
+ site-maven-plugin
+ 0.12
+
+ Maven artifacts for ${project.version}
+
+ true
+
+ ${project.build.directory}/mvn-repo
+
+ refs/heads/mvn-repo
+
+
+ **/*
+
+ true
+ mustangproject
+
+ ZUGFeRD
+
+
+
+
+
+
+ site
+
+ deploy
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.3
+
+
+
+ org.mustangproject.toecount.Toecount
+
+
+ true
+
-
- *:*
-
- META-INF/*.SF
- META-INF/*.DSA
- META-INF/*.RSA
-
-
-
- log4j:log4j
-
- **
-
-
-
- commons-logging:commons-logging
-
- **
-
-
-
- com.sun.xml.bind:jaxb-impl
-
- **
-
-
-
- com.sun.xml.bind:jaxb-core
-
- **
-
-
-
-
-
-
- package
-
- shade
-
-
-
-
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- templating-maven-plugin
- 1.0.0
-
-
- filtering-java-templates
-
- filter-sources
-
-
-
-
-
- org.codehaus.mojo
- jaxb2-maven-plugin
- 2.3.1
-
-
- xjc
-
- xjc
-
-
-
-
- 2.1
- org.mustangproject.ZUGFeRD.model
-
- src/main/resources/schema/ZUGFeRD1p0.xsd
-
-
-
-
-
-
-
- User List
- https://groups.google.com/forum/?hl=de#!forum/mustangproject
-
-
-
-
- Apache License, Version 2.0
- http://www.apache.org/licenses/LICENSE-2.0.txt
- repo
- A business-friendly OSS license
-
-
-
-
- Jochen Stärk
- jstaerk@usegroup.de
-
- architect
- developer
-
-
-
- Alexander Schmidt
- schmidt.alexander@mail.de
-
- developer
-
-
-
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+ log4j:log4j
+
+ **
+
+
+
+ commons-logging:commons-logging
+
+ **
+
+
+
+ com.sun.xml.bind:jaxb-impl
+
+ **
+
+
+
+ com.sun.xml.bind:jaxb-core
+
+ **
+
+
+
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ templating-maven-plugin
+ 1.0.0
+
+
+ filtering-java-templates
+
+ filter-sources
+
+
+
+
+
+ org.codehaus.mojo
+ jaxb2-maven-plugin
+ 2.5.0
+
+
+ xjc
+
+ xjc
+
+
+
+
+ 2.1
+ org.mustangproject.ZUGFeRD.model
+
+ src/main/resources/schema/ZUGFeRD1p0.xsd
+
+
+
+
+
+
+
+ User List
+ https://groups.google.com/forum/?hl=de#!forum/mustangproject
+
+
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+ A business-friendly OSS license
+
+
+
+
+ Jochen Stärk
+ jstaerk@usegroup.de
+
+ architect
+ developer
+
+
+
+ Alexander Schmidt
+ schmidt.alexander@mail.de
+
+ developer
+
+
+
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/CustomXMLProvider.java b/src/main/java/org/mustangproject/ZUGFeRD/CustomXMLProvider.java
index dfd8be88..6ac2c275 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/CustomXMLProvider.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/CustomXMLProvider.java
@@ -18,7 +18,7 @@
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
-public class CustomXMLProvider implements IXMLProvider {
+public class CustomXMLProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
@@ -48,4 +48,14 @@ public class CustomXMLProvider implements IXMLProvider {
}
+ @Override
+ public String getProfile() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void setProfile(ZUGFeRDConformanceLevel level) {
+ // TODO Auto-generated method stub
+ }
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IExporterFactory.java b/src/main/java/org/mustangproject/ZUGFeRD/IExporterFactory.java
index 47c22cc6..6822d744 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/IExporterFactory.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IExporterFactory.java
@@ -22,6 +22,13 @@ import java.io.IOException;
import java.io.InputStream;
public interface IExporterFactory {
+ /**
+ * factory: loads a PDF file and returns an appropriate exporter
+ *
+ * @param pdfFilename binary of a PDF/A1 compliant document
+ * @return the generated exporter
+ * @throws IOException if anything is wrong with filename
+ */
public ZUGFeRDExporter load(String pdfFilename) throws IOException;
/**
@@ -29,6 +36,8 @@ public interface IExporterFactory {
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfBinary binary of a PDF/A1 compliant document
+ * @return the generated exporter
+ * @throws IOException (should not happen at all)
*/
public ZUGFeRDExporter load(byte[] pdfBinary) throws IOException;
@@ -37,7 +46,8 @@ public interface IExporterFactory {
* metadata level, this will not e.g. convert graphics to JPG-2000)
*
* @param pdfSource source to read a PDF/A1 compliant document from
- * @throws IOException
+ * @throws IOException if anything is wrong with inputstream
+ * @return the generated ZUGFeRDExporter
*/
public ZUGFeRDExporter load(InputStream pdfSource) throws IOException;
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IProfileProvider.java b/src/main/java/org/mustangproject/ZUGFeRD/IProfileProvider.java
new file mode 100644
index 00000000..d4469edb
--- /dev/null
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IProfileProvider.java
@@ -0,0 +1,27 @@
+/** **********************************************************************
+ *
+ * Copyright 2018 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;
+
+public interface IProfileProvider {
+
+ public String getProfile();
+
+ public void setProfile(ZUGFeRDConformanceLevel level);
+
+}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java
index a1de8dd2..289fa0c9 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableContact.java
@@ -19,11 +19,11 @@
package org.mustangproject.ZUGFeRD;
/**
- * Mustangproject's ZUGFeRD implementation Neccessary interface for ZUGFeRD exporter Licensed under the APLv2
+ * Mustangproject's ZUGFeRD implementation neccessary interface for ZUGFeRD exporter Licensed under the APLv2
*
* @author jstaerk
* @version 1.2.0
- * @date 2014-05-10
+ * dated 2014-05-10
*/
@@ -38,6 +38,24 @@ public interface IZUGFeRDExportableContact {
return null;
}
+ /**
+ * customer global identification assigned by the seller
+ *
+ * @return customer identification
+ */
+ default String getGlobalID() {
+ return null;
+ }
+
+ /**
+ * customer global identification scheme
+ *
+ * @return customer identification
+ */
+ default String getGlobalIDScheme() {
+ return null;
+ }
+
/**
* First and last name of the recipient
@@ -48,6 +66,14 @@ public interface IZUGFeRDExportableContact {
return null;
}
+ default String getPhone() {
+ return null;
+ }
+
+ default String getEMail() {
+ return null;
+ }
+
/**
* Postal code of the recipient
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java
index d58a2588..ea41877f 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableProduct.java
@@ -27,7 +27,7 @@ import java.math.BigDecimal;
*
* @author jstaerk
* @version 1.2.0
- * @date 2014-05-10
+ * dated 2014-05-10
*/
public interface IZUGFeRDExportableProduct {
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java
index fd1308ef..20b18006 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDExportableTransaction.java
@@ -27,12 +27,21 @@ package org.mustangproject.ZUGFeRD;
* */
-import java.util.Date;
+import java.math.BigDecimal;
+import java.util.Date;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
public interface IZUGFeRDExportableTransaction {
+ /**
+ * appears in /rsm:CrossIndustryDocument/rsm:HeaderExchangedDocument/ram:Name
+ * @return Name of document
+ */
+ default String getDocumentName() {
+ return "RECHNUNG";
+ }
+
/**
*
*
@@ -65,7 +74,7 @@ public interface IZUGFeRDExportableTransaction {
/**
* this should be the full sender institution name, details, manager and tax registration. It is one of the few functions which may return null. e.g.
- *
+ *
* Lieferant GmbH Lieferantenstraße 20 80333 München Deutschland Geschäftsführer: Hans Muster Handelsregisternummer: H A 123
*
* @return null or full sender institution name, details, manager and tax registration
@@ -84,6 +93,15 @@ public interface IZUGFeRDExportableTransaction {
return null;
}
+ /**
+ * who processed the order
+ *
+ * @return the contact person at the supplier side
+ */
+ default IZUGFeRDExportableContact getOwnContact() {
+ return null;
+ }
+
IZUGFeRDAllowanceCharge[] getZFAllowances();
@@ -106,53 +124,11 @@ public interface IZUGFeRDExportableTransaction {
/**
- * BIC of the sender
- *
- * @return the BIC code of the recipient sender's bank
+ * the creditors payment informations
+ *
+ * @return an array of IZUGFeRDTradeSettlementPayment
*/
- default String getOwnBIC() {
- return null;
- }
-
-
- /**
- * BLZ of the sender
- *
- * @return the BLZ code of the recipient sender's bank
- */
- default String getOwnBLZ() {
- return null;
- }
-
-
- /**
- * Bank name of the sender
- *
- * @return the name of the sender's bank
- */
- default String getOwnBankName() {
- return null;
- }
-
-
- /**
- * IBAN of the sender
- *
- * @return the IBAN of the invoice sender's bank account
- */
- default String getOwnIBAN() {
- return null;
- }
-
-
- /**
- * IBAN of the sender
- *
- * @return the Account Number of the invoice sender's bank account
- */
- default String getOwnKto() {
- return null;
- }
+ IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment();
/**
@@ -253,16 +229,6 @@ public interface IZUGFeRDExportableTransaction {
}
- /**
- * get payment information text. e.g. Bank transfer
- *
- * @return payment information text
- */
- default String getOwnPaymentInfoText() {
- return null;
- }
-
-
/**
* get payment term descriptional text e.g. Bis zum 22.10.2015 ohne Abzug
*
@@ -282,7 +248,6 @@ public interface IZUGFeRDExportableTransaction {
return null;
}
-
/**
* get reference number of the purchase order this invoice is based on
*
@@ -352,4 +317,23 @@ public interface IZUGFeRDExportableTransaction {
return null;
}
+ /**
+ * get the ID of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
+ * @return the ID of the document
+ */
+ default String getBuyerOrderReferencedDocumentID() { return null; }
+
+ /**
+ * get the issue timestamp of the BuyerOrderReferencedDocument, which sits in the ApplicableSupplyChainTradeAgreement
+ * @return the IssueDateTime in format CCYY-MM-DDTHH:MM:SS
+ */
+ default String getBuyerOrderReferencedDocumentIssueDateTime() { return null; }
+
+ /**
+ * get the TotalPrepaidAmount located in SpecifiedTradeSettlementMonetarySummation (v1) or
+ * SpecifiedTradeSettlementHeaderMonetarySummation (v2)
+ * @return the total sum (incl. VAT) of prepayments, i.e. the difference between GrandTotalAmount
+ * and DuePayableAmount
+ */
+ default BigDecimal getTotalPrepaidAmount() { return BigDecimal.ZERO; }
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java
new file mode 100644
index 00000000..3b45b9c8
--- /dev/null
+++ b/src/main/java/org/mustangproject/ZUGFeRD/IZUGFeRDTradeSettlementPayment.java
@@ -0,0 +1,81 @@
+/** **********************************************************************
+ *
+ * Copyright 2019 by ak on 12.04.19.
+ *
+ * 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;
+
+public interface IZUGFeRDTradeSettlementPayment {
+
+ /**
+ * get payment information text. e.g. Bank transfer
+ *
+ * @return payment information text
+ */
+ default String getOwnPaymentInfoText() {
+ return null;
+ }
+
+ /**
+ * BIC of the sender
+ *
+ * @return the BIC code of the recipient sender's bank
+ */
+ default String getOwnBIC() {
+ return null;
+ }
+
+
+ /**
+ * BLZ of the sender
+ *
+ * @return the BLZ code of the recipient sender's bank
+ */
+ default String getOwnBLZ() {
+ return null;
+ }
+
+
+ /**
+ * Bank name of the sender
+ *
+ * @return the name of the sender's bank
+ */
+ default String getOwnBankName() {
+ return null;
+ }
+
+
+ /**
+ * IBAN of the sender
+ *
+ * @return the IBAN of the invoice sender's bank account
+ */
+ default String getOwnIBAN() {
+ return null;
+ }
+
+
+ /**
+ * IBAN of the sender
+ *
+ * @return the Account Number of the invoice sender's bank account
+ */
+ default String getOwnKto() {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java b/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java
index 408ea69f..3ba0acc1 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/VATAmount.java
@@ -27,20 +27,20 @@ import java.math.BigDecimal;
*
* @author jstaerk
* @version 1.2.0
- * @date 2015-10-29
+ * dated 2015-10-29
*/
public class VATAmount {
- public VATAmount(BigDecimal basis, BigDecimal calculated, String documentCode) {
+ public VATAmount(BigDecimal basis, BigDecimal calculated, String categoryCode) {
super();
this.basis = basis;
this.calculated = calculated;
- this.documentCode = documentCode;
+ this.categoryCode = categoryCode;
}
BigDecimal basis, calculated;
- String documentCode;
+ String categoryCode;
public BigDecimal getBasis() {
return basis;
@@ -58,20 +58,39 @@ public class VATAmount {
this.calculated = calculated;
}
+ /**
+ *
+ * @deprecated Use {@link #getCategoryCode() instead}
+ * @return String with category code
+ */
+ @Deprecated
public String getDocumentCode() {
- return documentCode;
+ return categoryCode;
}
+ /**
+ * @param documentCode as String
+ * @deprecated Use {@link #setCategoryCode(String)} instead
+ */
+ @Deprecated
public void setDocumentCode(String documentCode) {
- this.documentCode = documentCode;
+ this.categoryCode = documentCode;
+ }
+
+ public String getCategoryCode() {
+ return categoryCode;
+ }
+
+ public void setCategoryCode(String categoryCode) {
+ this.categoryCode = categoryCode;
}
public VATAmount add(VATAmount v) {
- return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.documentCode);
+ return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode);
}
public VATAmount subtract(VATAmount v) {
- return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.documentCode);
+ return new VATAmount(basis.subtract(v.getBasis()), calculated.subtract(v.getCalculated()), this.categoryCode);
}
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java b/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java
index 131638db..3e0dca57 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaPDFAExtensions.java
@@ -52,11 +52,13 @@ public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
public final String prefix_pdfaProperty = "pdfaProperty";
public String namespace = null;
public String prefix = null;
+
+ protected ZUGFeRDExporter exporter;
protected void setZUGFeRDVersion(int ver) {
- namespace = ZUGFeRDExporter.getNamespaceForVersion(ver);
- prefix = ZUGFeRDExporter.getPrefixForVersion(ver);
+ namespace = exporter.getNamespaceForVersion(ver);
+ prefix = exporter.getPrefixForVersion(ver);
}
@@ -87,14 +89,16 @@ public class XMPSchemaPDFAExtensions extends PDFAExtensionSchema {
return li;
}
- public XMPSchemaPDFAExtensions(XMPMetadata metadata, int ZFVersion) {
+ public XMPSchemaPDFAExtensions(ZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion) {
super(metadata);
+ exporter=ze;
setZUGFeRDVersion(ZFVersion);
attachExtensions(metadata, true);
}
- public XMPSchemaPDFAExtensions(XMPMetadata metadata, int ZFVersion, boolean withZF) {
+ public XMPSchemaPDFAExtensions(ZUGFeRDExporter ze, XMPMetadata metadata, int ZFVersion, boolean withZF) {
super(metadata);
+ exporter=ze;
setZUGFeRDVersion(ZFVersion);
attachExtensions(metadata, withZF);
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java b/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java
index 57cc5197..c2a4bde5 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/XMPSchemaZugferd.java
@@ -31,8 +31,13 @@ import org.apache.xmpbox.schema.XMPSchema;
public class XMPSchemaZugferd extends XMPSchema {
- /**
+ /***
* This is what needs to be added to the RDF metadata - basically the name of the embedded Zugferd file
+ * @param metadata the xmp to be added to
+ * @param conformanceLevel e.g. conformanceLevel.EN16931
+ * @param URN the xml URI for the XMP
+ * @param prefix the xml namespace prefix for the XMP, zf for ZUGFeRD, fx for Factur-X
+ * @param filename the filename of the invoice
*/
public XMPSchemaZugferd(XMPMetadata metadata, ZUGFeRDConformanceLevel conformanceLevel, String URN, String prefix, String filename) {
super(metadata, URN, prefix, "ZUGFeRD Schema");
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java
index 3c782866..f8278a32 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD1PullProvider.java
@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
+import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
import org.mustangproject.ZUGFeRD.model.ZFNamespacePrefixMapper;
import javax.xml.bind.JAXBContext;
@@ -27,13 +28,27 @@ import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.ByteArrayOutputStream;
-public class ZUGFeRD1PullProvider implements IXMLProvider {
+public class ZUGFeRD1PullProvider implements IXMLProvider, IProfileProvider {
protected byte[] zugferdData;
private Marshaller marshaller;
private boolean isTest;
+ private ZUGFeRDConformanceLevel level;
+
+
+ public void setProfile(ZUGFeRDConformanceLevel level) {
+ this.level = level;
+ }
+
+ public String getProfile() {
+ switch (level) {
+ case BASIC: return DocumentContextParameterTypeConstants.BASIC;
+ case COMFORT: return DocumentContextParameterTypeConstants.COMFORT;
+ default: return DocumentContextParameterTypeConstants.EXTENDED;
+ }
+ }
/**
@@ -59,7 +74,7 @@ public class ZUGFeRD1PullProvider implements IXMLProvider {
private String createZugferdXMLForTransaction(IZUGFeRDExportableTransaction trans) {
JAXBElement jaxElement =
- new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).convertToModel();
+ new ZUGFeRDTransactionModelConverter(trans).withTest(isTest).withProfile(getProfile()).convertToModel();
try {
return marshalJaxToXMLString(jaxElement);
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java
index 52a77fe5..bd8a70ff 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRD2PullProvider.java
@@ -18,28 +18,42 @@
*********************************************************************** */
package org.mustangproject.ZUGFeRD;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-public class ZUGFeRD2PullProvider implements IXMLProvider {
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.DocumentHelper;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+import org.mustangproject.toecount.Toecount;
+
+public class ZUGFeRD2PullProvider implements IXMLProvider, IProfileProvider {
private class LineCalc {
- private IZUGFeRDExportableItem currentItem = null;
private BigDecimal totalGross;
private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount;
public LineCalc(IZUGFeRDExportableItem currentItem) {
- this.currentItem = currentItem;
- BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100)).add(new BigDecimal(1));
-// priceGross=currentItem.getPrice().multiply(multiplicator);
+ BigDecimal multiplicator = currentItem.getProduct().getVATPercent().divide(new BigDecimal(100))
+ .add(new BigDecimal(1));
+ // priceGross=currentItem.getPrice().multiply(multiplicator);
totalGross = currentItem.getPrice().multiply(multiplicator).multiply(currentItem.getQuantity());
- itemTotalNetAmount = currentItem.getQuantity().multiply(currentItem.getPrice()).setScale(2, BigDecimal.ROUND_HALF_UP);
+ itemTotalNetAmount = currentItem.getQuantity().multiply(currentItem.getPrice()).setScale(2,
+ BigDecimal.ROUND_HALF_UP);
itemTotalVATAmount = totalGross.subtract(itemTotalNetAmount);
}
@@ -53,77 +67,90 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
-
//// MAIN CLASS
protected byte[] zugferdData;
private IZUGFeRDExportableTransaction trans;
- private boolean isTest;
+ private ZUGFeRDConformanceLevel level;
+
+ public void setProfile(ZUGFeRDConformanceLevel level) {
+ this.level = level;
+ }
/**
* enables the flag to indicate a test invoice in the XML structure
*/
public void setTest() {
- isTest = true;
}
- private String nDigitFormat(BigDecimal value, int scale) {
+ public static String nDigitFormat(BigDecimal value, int scale) {
/*
* I needed 123,45, locale independent.I tried
- * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is
- * locale specific.I also tried DecimalFormat df = new DecimalFormat(
- * "0,00" ); df.setDecimalSeparatorAlwaysShown(true);
- * df.setGroupingUsed(false); DecimalFormatSymbols symbols = new
- * DecimalFormatSymbols(); symbols.setDecimalSeparator(',');
- * symbols.setGroupingSeparator(' ');
+ * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is locale
+ * specific.I also tried DecimalFormat df = new DecimalFormat( "0,00" );
+ * df.setDecimalSeparatorAlwaysShown(true); df.setGroupingUsed(false);
+ * DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ * symbols.setDecimalSeparator(','); symbols.setGroupingSeparator(' ');
* df.setDecimalFormatSymbols(symbols);
*
- * but that would not switch off grouping. Although I liked very much
- * the (incomplete) "BNF diagram" in
- * http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
- * in the end I decided to calculate myself and take eur+sparator+cents
+ * but that would not switch off grouping. Although I liked very much the
+ * (incomplete) "BNF diagram" in
+ * http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html in the
+ * end I decided to calculate myself and take eur+sparator+cents
*
- * This function will cut off, i.e. floor() subcent values Tests:
- * System.err.println(utils.currencyFormat(new BigDecimal(0),
- * ".")+"\n"+utils.currencyFormat(new BigDecimal("-1.10"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.1"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.01"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3489"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3419"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("12"), ","));
- *
- * results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00
*/
- value = value.setScale(scale, BigDecimal.ROUND_HALF_UP); // first, round so that e.g. 1.189999999999999946709294817992486059665679931640625 becomes 1.19
- char[] repeat = new char[scale];
- Arrays.fill(repeat, '0');
-
- DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
- otherSymbols.setDecimalSeparator('.');
- DecimalFormat dec = new DecimalFormat("0." + new String(repeat), otherSymbols);
- return dec.format(value);
+ return value.setScale(scale, RoundingMode.HALF_UP).toPlainString();
}
private String vatFormat(BigDecimal value) {
- return nDigitFormat(value, 2);
+ return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String currencyFormat(BigDecimal value) {
- return nDigitFormat(value, 2);
+ return ZUGFeRD2PullProvider.nDigitFormat(value, 2);
}
private String priceFormat(BigDecimal value) {
- return nDigitFormat(value, 4);
+ return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
private String quantityFormat(BigDecimal value) {
- return nDigitFormat(value, 4);
+ return ZUGFeRD2PullProvider.nDigitFormat(value, 4);
}
@Override
public byte[] getXML() {
- return zugferdData;
+
+ byte[] res = zugferdData;
+
+ StringWriter sw = new StringWriter();
+ Document document=null;
+ try {
+ document = DocumentHelper.parseText(new String(zugferdData));
+ } catch (DocumentException e1) {
+ Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e1);
+ }
+ try {
+ OutputFormat format = OutputFormat.createPrettyPrint();
+ XMLWriter writer = new XMLWriter(sw, format);
+ writer.write(document);
+ res = sw.toString().getBytes("UTF-8");
+
+ } catch (IOException e) {
+ Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e);
+ }
+
+ return res;
+
+ }
+
+ private BigDecimal getTotalPrepaid() {
+ if (trans.getTotalPrepaidAmount()==null) {
+ return new BigDecimal(0);
+ } else {
+ return trans.getTotalPrepaidAmount();
+ }
}
private BigDecimal getTotalGross() {
@@ -147,20 +174,20 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
/**
- * which taxes have been used with which amounts in this transaction,
- * empty for no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable
- * to 19% VAT (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT)
- * 190 Eur
+ * which taxes have been used with which amounts in this transaction, empty for
+ * no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable to 19% VAT
+ * (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT) 190 Eur
*
* @return which taxes have been used with which amounts in this invoice
*/
private HashMap getVATPercentAmountMap() {
- HashMap hm = new HashMap();
+ HashMap hm = new HashMap<>();
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalc lc = new LineCalc(currentItem);
- VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), trans.getDocumentCode());
+ VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(),
+ trans.getDocumentCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -171,61 +198,101 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
return hm;
}
+ public String getProfile() {
+ return "urn:cen.eu:en16931:2017";
+ }
+
+ public static String encodeXML(CharSequence s) {
+ StringBuilder sb = new StringBuilder();
+ int len = s.length();
+ for (int i=0;i= 0xd800 && c <= 0xdbff && i + 1 < len) {
+ c = ((c-0xd7c0)<<10) | (s.charAt(++i)&0x3ff); // UTF16 decode
+ }
+ if (c < 0x80) { // ASCII range: test most common case first
+ if (c < 0x20 && (c != '\t' && c != '\r' && c != '\n')) {
+ // Illegal XML character, even encoded. Skip or substitute
+ sb.append("�"); // Unicode replacement character
+ } else {
+ switch(c) {
+ case '&': sb.append("&"); break;
+ case '>': sb.append(">"); break;
+ case '<': sb.append("<"); break;
+ // Uncomment next two if encoding for an XML attribute
+// case '\'' sb.append("'"); break;
+// case '\"' sb.append("""); break;
+ // Uncomment next three if you prefer, but not required
+// case '\n' sb.append("
"); break;
+// case '\r' sb.append("
"); break;
+// case '\t' sb.append(" "); break;
+
+ default: sb.append((char)c);
+ }
+ }
+ } else if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) {
+ // Illegal XML character, even encoded. Skip or substitute
+ sb.append("�"); // Unicode replacement character
+ } else {
+ sb.append("");
+ sb.append(Integer.toHexString(c));
+ sb.append(';');
+ }
+ }
+ return sb.toString();
+ }
@Override
public void generateXML(IZUGFeRDExportableTransaction trans) {
-
this.trans = trans;
SimpleDateFormat germanDateFormat = new SimpleDateFormat("dd.MM.yyyy"); //$NON-NLS-1$
SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd"); //$NON-NLS-1$
- String testBooleanStr = "false";
- if (isTest) {
- testBooleanStr = "true";
-
- }
String senderReg = "";
if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
- senderReg = ""
- + "\n"
- + " \n"
- + trans.getOwnOrganisationFullPlaintextInfo()
- + " \n"
- + "REG\n"
- + "\n";
+ senderReg = "" + "\n" + " \n"
+ + encodeXML(trans.getOwnOrganisationFullPlaintextInfo()) + " \n"
+ + "REG\n" + "\n";
}
-
String xml = "\n" //$NON-NLS-1$
+ "\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
-// + " "+testBooleanStr+"\n" //$NON-NLS-1$
+ // + "
+ // "+testBooleanStr+"\n"
+ // //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " urn:cen.eu:en16931:2017:compliant:factur-x.eu:1p0:en16931\n" //$NON-NLS-1$
+ + " " + getProfile() + "\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + trans.getNumber() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
-// + " RECHNUNG\n" //$NON-NLS-1$
+ + " " + encodeXML(trans.getNumber()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // + " RECHNUNG\n" //$NON-NLS-1$
+ " 380\n" //$NON-NLS-1$
- + " " + zugferdDateFormat.format(trans.getIssueDate()) + "\n" //date format was 20130605 //$NON-NLS-1$ //$NON-NLS-2$
+ + " " //$NON-NLS-1$
+ + zugferdDateFormat.format(trans.getIssueDate()) + "\n" // date //$NON-NLS-1$
+ // format
+ // was
+ // 20130605
+ senderReg
-// + " \n"
-// + " \n"
-// + "Rechnung gemäß Bestellung Nr. 2013-471331 vom 01.03.2013.\n"
-// + "\n"
-// + " \n"
-// + " \n"
-// + " \n"
-// + " \n"
-// + "Es bestehen Rabatt- und Bonusvereinbarungen.\n"
-// + " \n"
-// + " AAK\n"
-// + " \n"
+ // + " \n"
+ // + " \n"
+ // + "Rechnung gemäß Bestellung Nr. 2013-471331 vom 01.03.2013.\n"
+ // + "\n"
+ // + " \n"
+ // + " \n"
+ // + " \n"
+ // + " \n"
+ // + "Es bestehen Rabatt- und Bonusvereinbarungen.\n"
+ // + " \n"
+ // + " AAK\n"
+ // + " \n"
+ " \n" //$NON-NLS-1$
+ " \n"; //$NON-NLS-1$
int lineID = 0;
@@ -238,188 +305,243 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ " " + lineID + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
-// + " 4012345001235\n"
-// + " KR3M\n"
-// + " 55T01\n"
- + " " + currentItem.getProduct().getName() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + currentItem.getProduct().getDescription() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // + " 4012345001235\n"
+ // + " KR3M\n"
+ // + " 55T01\n"
+ + " " + encodeXML(currentItem.getProduct().getName()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(currentItem.getProduct().getDescription()) //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + priceFormat(currentItem.getPrice()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //currencyID=\"EUR\"
- + " 1.0000\n" //$NON-NLS-1$ //$NON-NLS-2$
-// + " \n"
-// + " false\n"
-// + " 0.6667\n"
-// + " Rabatt\n"
-// + " \n"
+ + " " + priceFormat(currentItem.getPrice()) //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$ //currencyID=\"EUR\"
+ + " 1.0000\n" //$NON-NLS-1$
+ // + " \n"
+ // + " false\n"
+ // + " 0.6667\n"
+ // + " Rabatt\n"
+ // + " \n"
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + priceFormat(currentItem.getPrice()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // currencyID=\"EUR\"
- + " 1.0000\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + priceFormat(currentItem.getPrice()) //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$ // currencyID=\"EUR\"
+ + " 1.0000\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + quantityFormat(currentItem.getQuantity()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ + " " //$NON-NLS-1$ //$NON-NLS-2$
+ + quantityFormat(currentItem.getQuantity()) + "\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " VAT\n" //$NON-NLS-1$
+ " S\n" //$NON-NLS-1$
- + " " + vatFormat(currentItem.getProduct().getVATPercent()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " //$NON-NLS-1$
+ + vatFormat(currentItem.getProduct().getVATPercent()) + "\n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + currencyFormat(lc.getItemTotalNetAmount()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // currencyID=\"EUR\"
+ + " " + currencyFormat(lc.getItemTotalNetAmount()) //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$ // currencyID=\"EUR\"
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n"; //$NON-NLS-1$
+ }
+
+ xml = xml + " \n"; //$NON-NLS-1$
+ if (trans.getReferenceNumber() != null) {
+ xml = xml + " " + encodeXML(trans.getReferenceNumber()) + "\n";
+
+ }
+ xml = xml + " \n" //$NON-NLS-1$
+ // + " 4000001123452\n"
+ + " " + encodeXML(trans.getOwnOrganisationName()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ if ((trans.getOwnVATID()!=null)&&(trans.getOwnOrganisationName()!=null)) {
+
+ xml = xml + " \n" + " "
+ + encodeXML(trans.getOwnVATID()) + "\n" + " "
+ + encodeXML(trans.getOwnOrganisationName()) + "\n"
+ + " ";
+ }
+
+ if (trans.getOwnContact() != null) {
+ xml = xml + "\n" + " " + encodeXML(trans.getOwnContact().getName())
+ + "\n";
+ if (trans.getOwnContact().getPhone() != null) {
+
+ xml = xml + " \n" + " "
+ + encodeXML(trans.getOwnContact().getPhone()) + "\n"
+ + " \n";
+ }
+ if (trans.getOwnContact().getEMail() != null) {
+
+ xml = xml + " \n" + " "
+ + encodeXML(trans.getOwnContact().getEMail()) + "\n"
+ + " \n";
+ }
+ xml = xml + " ";
}
- xml = xml + " \n" //$NON-NLS-1$
-// + " AB-312\n"
- + " \n" //$NON-NLS-1$
-// + " 4000001123452\n"
- + " " + trans.getOwnOrganisationName() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " \n"
- + " " + trans.getOwnZIP() + "\n"
- + " " + trans.getOwnStreet() + "\n"
- + " " + trans.getOwnLocation() + "\n"
- + " " + trans.getOwnCountry() + "\n"
- + " \n"
+ xml = xml + " \n" + " "
+ + encodeXML(trans.getOwnZIP()) + "\n" + " "
+ + encodeXML(trans.getOwnStreet()) + "\n" + " " + encodeXML(trans.getOwnLocation())
+ + "\n" + " " + encodeXML(trans.getOwnCountry())
+ + "\n" + " \n"
+ " \n" //$NON-NLS-1$
- + " " + trans.getOwnTaxID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(trans.getOwnTaxID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + trans.getOwnVATID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(trans.getOwnVATID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
-// + " GE2020211\n"
-// + " 4000001987658\n"
- + " " + trans.getRecipient().getName() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
-// + " \n"
-// + " xxx\n"
-// + " \n"
+ // + " GE2020211\n"
+ // + " 4000001987658\n"
+ + " " + encodeXML(trans.getRecipient().getName()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // + " \n"
+ // + " xxx\n"
+ // + " \n"
+ " \n" //$NON-NLS-1$
- + " " + trans.getRecipient().getZIP() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + trans.getRecipient().getStreet() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + trans.getRecipient().getLocation() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + trans.getRecipient().getCountry() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " \n" //$NON-NLS-1$
- + " \n" //$NON-NLS-1$
- + " " + trans.getRecipient().getVATID() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " \n" //$NON-NLS-1$
- + " \n" //$NON-NLS-1$
-// + " \n"
-// + " 20130301\n"
-// + " 2013-471331\n"
-// + " \n"
+ + " " + encodeXML(trans.getRecipient().getZIP()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(trans.getRecipient().getStreet()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(trans.getRecipient().getLocation()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " " + encodeXML(trans.getRecipient().getCountry()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n"; //$NON-NLS-1$
+ if (trans.getRecipient().getVATID() != null) {
+ xml += " \n" //$NON-NLS-1$
+ + " " + encodeXML(trans.getRecipient().getVATID()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " \n"; //$NON-NLS-1$
+ }
+ xml += " \n" //$NON-NLS-1$
+ // + " \n"
+ // + " 20130301\n"
+ // + " 2013-471331\n"
+ // + " \n"
+ " \n" //$NON-NLS-1$
- + " \n"
- + " \n"
- + " " + zugferdDateFormat.format(trans.getDeliveryDate()) + "\n"
+ + " \n" + " \n"
+ + " "
+ + zugferdDateFormat.format(trans.getDeliveryDate()) + "\n"
+ " \n"
- /*
- + " \n"
- + " 20130603\n"
- + " 2013-51112\n"
- + " \n" */
- + " \n"
- + " \n" //$NON-NLS-1$
- + " " + trans.getNumber() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " EUR\n" //$NON-NLS-1$
- + " \n" //$NON-NLS-1$
- + " 42\n" //$NON-NLS-1$
- + " Überweisung\n" //$NON-NLS-1$
- + " \n" //$NON-NLS-1$
- + " " + trans.getOwnIBAN() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + trans.getOwnKto() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " \n" //$NON-NLS-1$
- + " \n" //$NON-NLS-1$
- + " " + trans.getOwnBIC() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + trans.getOwnBLZ() + "\n" //$NON-NLS-1$ //$NON-NLS-2$
-// + " "+trans.getOwnBankName()+"\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " \n" //$NON-NLS-1$
- + " \n"; //$NON-NLS-1$
+ /*
+ * + " \n" +
+ * " 20130603\n" +
+ * " 2013-51112\n" +
+ * " \n"
+ */
+ + " \n" + " \n" //$NON-NLS-2$
+ + " " + encodeXML(trans.getNumber()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ + " EUR\n"; //$NON-NLS-1$
+ for (IZUGFeRDTradeSettlementPayment payment : trans.getTradeSettlementPayment()) {
+ xml += " \n" //$NON-NLS-1$
+ + " 42\n" //$NON-NLS-1$
+ + " Überweisung\n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " " + encodeXML(payment.getOwnIBAN()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
+ if (payment.getOwnKto()!=null) {
+ xml+= " " + encodeXML(payment.getOwnKto()) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ }
+ xml+= " \n" //$NON-NLS-1$
+ + " \n" //$NON-NLS-1$
+ + " " + encodeXML(payment.getOwnBIC()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // + " "+trans.getOwnBankName()+"\n" //$NON-NLS-1$
+ // //$NON-NLS-2$
+ + " \n" //$NON-NLS-1$
+ + " \n"; //$NON-NLS-1$
+ }
HashMap VATPercentAmountMap = getVATPercentAmountMap();
for (BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
if (amount != null) {
xml += " \n" //$NON-NLS-1$
- + " " + currencyFormat(amount.getCalculated()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //currencyID=\"EUR\"
+ + " " + currencyFormat(amount.getCalculated()) //$NON-NLS-1$
+ + "\n" //$NON-NLS-1$ //currencyID=\"EUR\"
+ " VAT\n" //$NON-NLS-1$
+ " " + currencyFormat(amount.getBasis()) + "\n" // currencyID=\"EUR\"
+ " S\n" //$NON-NLS-1$
- + " " + vatFormat(currentTaxPercent) + "\n" //$NON-NLS-1$
- + " \n"; //$NON-NLS-1$
-
+ + " " + vatFormat(currentTaxPercent) //$NON-NLS-1$
+ + "\n" + " \n"; //$NON-NLS-2$
}
}
- /* xml+= "
- + " \n"
- + " false\n"
- + " 10\n"
- + " 1.00\n"
- + " Sondernachlass\n"
- + " \n"
- + " VAT\n"
- + " S\n"
- + " 19\n"
- + " \n"
- + " \n"
- + " \n"
- + " false\n"
- + " 137.30\n"
- + " 13.73\n"
- + " Sondernachlass\n"
- + " \n"
- + " VAT\n"
- + " S\n"
- + " 7\n"
- + " \n"
- + " \n"
- + " \n"
- + " Versandkosten\n"
- + " 5.80\n"
- + " \n"
- + " VAT\n"
- + " S\n"
- + " 7\n"
- + " \n"
- + " \n"*/
+ /*
+ * xml+= " + " \n" +
+ * " false\n" +
+ * " 10\n" +
+ * " 1.00\n" +
+ * " Sondernachlass\n" +
+ * " \n" +
+ * " VAT\n" +
+ * " S\n" +
+ * " 19\n" +
+ * " \n" +
+ * " \n" +
+ * " \n" +
+ * " false\n" +
+ * " 137.30\n" +
+ * " 13.73\n" +
+ * " Sondernachlass\n" +
+ * " \n" +
+ * " VAT\n" +
+ * " S\n" +
+ * " 7\n" +
+ * " \n" +
+ * " \n" +
+ * " \n" +
+ * " Versandkosten\n" +
+ * " 5.80\n" +
+ * " \n" +
+ * " VAT\n" +
+ * " S\n" +
+ * " 7\n" +
+ * " \n" +
+ * " \n"
+ */
xml = xml + " \n" //$NON-NLS-1$
- + " Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate()) + "\n"
- + " " + zugferdDateFormat.format(trans.getDueDate()) + "\n"//20130704 //$NON-NLS-1$ //$NON-NLS-2$
+ + " Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate())
+ + "\n" + " " //$NON-NLS-2$
+ + zugferdDateFormat.format(trans.getDueDate()) + "\n"// 20130704 //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
+ " \n" //$NON-NLS-1$
- + " " + currencyFormat(getTotal()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ currencyID=\"EUR\"
- + " 0.00\n" //$NON-NLS-1$ currencyID=\"EUR\"
- + " 0.00\n" //$NON-NLS-1$ // currencyID=\"EUR\"
-// + " 5.80\n"
-// + " 14.73\n"
- + " " + currencyFormat(getTotal()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // currencyID=\"EUR\"
- + " " + currencyFormat(getTotalGross().subtract(getTotal())) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
- + " " + currencyFormat(getTotalGross()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // currencyID=\"EUR\"
-// + " 0.00\n"
- + " " + currencyFormat(getTotalGross()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$ // currencyID=\"EUR\"
+ + " " + currencyFormat(getTotal()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // currencyID=\"EUR\"
+ + " 0.00\n" //$NON-NLS-1$ currencyID=\"EUR\"
+ + " 0.00\n" //$NON-NLS-1$ //
+ // currencyID=\"EUR\"
+ // + " 5.80\n"
+ // + " 14.73\n"
+ + " " + currencyFormat(getTotal()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // //
+ // currencyID=\"EUR\"
+ + " " //$NON-NLS-1$
+ + currencyFormat(getTotalGross().subtract(getTotal())) + "\n" //$NON-NLS-1$
+ + " " + currencyFormat(getTotalGross()) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // //
+ // currencyID=\"EUR\"
+ + " " + currencyFormat(getTotalPrepaid()) + "\n"
+ + " " + currencyFormat(getTotalGross().subtract(getTotalPrepaid())) + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ // //
+ // currencyID=\"EUR\"
+ " \n" //$NON-NLS-1$
+ " \n"; //$NON-NLS-1$
-// + " \n"
-// + " \n"
-// + " \n"
-// + " Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr. 2013-51112 in Rechnung zu stellen:\n"
-// + " \n"
-// + " \n"
-// + " \n";
-
+ // + " \n"
+ // + " \n"
+ // + " \n"
+ // + " Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr.
+ // 2013-51112 in Rechnung zu stellen:\n"
+ // + " \n"
+ // + " \n"
+ // + " \n";
xml = xml + " \n" //$NON-NLS-1$
+ ""; //$NON-NLS-1$
@@ -436,9 +558,8 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
zugferdData = zugferdRaw;
}
} catch (UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } //$NON-NLS-1$
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+ } // $NON-NLS-1$
}
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java
index e5504667..0330c4e4 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporter.java
@@ -33,6 +33,7 @@ import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSBase;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
@@ -55,7 +56,7 @@ public class ZUGFeRDExporter implements Closeable {
public static final int DefaultZUGFeRDVersion = 1;
- private static boolean isFacturX = false;
+ private boolean isFacturX = false;
/**
* To use the ZUGFeRD exporter, implement IZUGFeRDExportableTransaction in
@@ -96,7 +97,7 @@ public class ZUGFeRDExporter implements Closeable {
*/
protected String producer = "mustangproject";
/**
- * Author/Creator attribute for PDF for PDF
+ * Author/Creator attribute for PDF
*/
protected String creator = "mustangproject";
/**
@@ -133,7 +134,7 @@ public class ZUGFeRDExporter implements Closeable {
additionalXMLs.put(filename, xml);
}
- public static String getNamespaceForVersion(int ver) {
+ public String getNamespaceForVersion(int ver) {
if (isFacturX) {
return "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#";
} else if (ver == 1) {
@@ -146,7 +147,7 @@ public class ZUGFeRDExporter implements Closeable {
}
}
- public static String getPrefixForVersion(int ver) {
+ public String getPrefixForVersion(int ver) {
if (isFacturX) {
return "fx";
} else {
@@ -154,11 +155,15 @@ public class ZUGFeRDExporter implements Closeable {
}
}
- public static String getFilenameForVersion(int ver) {
+ public String getFilenameForVersion(int ver) {
if (isFacturX) {
return "factur-x.xml";
} else {
- return "ZUGFeRD-invoice.xml";
+ if (ver==1) {
+ return "ZUGFeRD-invoice.xml";
+ } else {
+ return "zugferd-invoice.xml";
+ }
}
}
@@ -191,15 +196,16 @@ public class ZUGFeRDExporter implements Closeable {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
- *
+ *
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
- *
+ *
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
- *
+ *
* Feel free to pass "A" as new level if you know what you are doing :-)
+ * @param newLevel "A", "U" or "B"
*
* @deprecated Use {@link ZUGFeRDExporterFromA1Factory} instead
*/
@@ -213,16 +219,17 @@ public class ZUGFeRDExporter implements Closeable {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
- *
+ *
* PDF/A-3 has three conformance levels, called "A", "U" and "B".
- *
+ *
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
- *
+ *
* Feel free to pass "A" as new level if you know what you are doing :-)
*
+ * @param newLevel "A", "U" or "B"
* @deprecated Use
* {@link #setConformanceLevel(org.mustangproject.ZUGFeRD.PDFAConformanceLevel)}
* instead
@@ -247,27 +254,45 @@ public class ZUGFeRDExporter implements Closeable {
ignoreA1Errors = true;
}
- /**
+ /***
+ * load from a pdf which is already A/3
+ * @param filename the PDF
+ * @throws IOException if anything is wrong with filename
* @deprecated Use the factory method {@link #createFromPDFA3(String)} instead
*/
@Deprecated
public void loadPDFA3(String filename) throws IOException {
doc = PDDocument.load(new File(filename));
}
-
+
+ /***
+ * factory create a ZUGFeRD exporter for a PDF/A-3 pdf file
+ * @param filename of the pdf
+ * @return the created ZUGFeRDExporter
+ * @throws IOException if something is wrong with filename
+ */
public static ZUGFeRDExporter createFromPDFA3(String filename) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(new File(filename)));
}
/**
- * @deprecated Use the factory method {@link #createFromPDFA3(InputStream)}
- * instead
+ * load from a pdf which is already A/3 from filestream
+ * @param file InputStream to load pdf from
+ * @deprecated Use the factory method {@link #createFromPDFA3(InputStream)} instead
+ * @throws IOException in case anything is wrong with file
+ *
*/
@Deprecated
public void loadPDFA3(InputStream file) throws IOException {
doc = PDDocument.load(file);
}
+ /***
+ * factory create a ZUGFeRD exporter for a PDF/A-3 inputstream
+ * @param pdfSource the InputStream of the pdf
+ * @return the created ZUGFeRDExporter
+ * @throws IOException (should not happen)
+ */
public static ZUGFeRDExporter createFromPDFA3(InputStream pdfSource) throws IOException {
return new ZUGFeRDExporter(PDDocument.load(pdfSource));
}
@@ -275,7 +300,13 @@ public class ZUGFeRDExporter implements Closeable {
/**
* Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
* metadata level, this will not e.g. convert graphics to JPG-2000)
- *
+ * @param filename the pdf
+ * @param producer the name of the xmp producer (app)
+ * @param creator the name of the xmp creator (user)
+ * @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
+ * @return the created PDFbox PDDocumentCatalog
+ * @throws IOException if anything is wrong with file
+ * @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
@@ -287,7 +318,18 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog();
}
- /**
+ /***
+ *
+ * Makes A PDF/A3a-compliant document from a PDF-A1 compliant document (on the
+ * metadata level, this will not e.g. convert graphics to JPG-2000)
+
+ * @param file the InputStream of the pdf
+ * @param producer the name of the xmp producer (app)
+ * @param creator the name of the xmp creator (user)
+ * @param attachZugferdHeaders whether to attach XMP ZUGFeRD headers
+ * @return the created PDFbox PDDocumentCatalog
+ * @throws IOException if anything is wrong with file
+ * @throws TransformerException in case of xml (XMP) parsing issues
* @deprecated use the {@link ZUGFeRDExporterFromA1Factory} instead
*/
@Deprecated
@@ -299,6 +341,7 @@ public class ZUGFeRDExporter implements Closeable {
return doc.getDocumentCatalog();
}
+
private IExporterFactory createPDFA1Factory() {
ZUGFeRDExporterFromA1Factory factory = new ZUGFeRDExporterFromA1Factory();
if (ignoreA1Errors) {
@@ -321,10 +364,11 @@ public class ZUGFeRDExporter implements Closeable {
* populate the XML. This parameter may be null, if so the XML data
* should hav ebeen set via
* setZUGFeRDXMLData(byte[] zugferdData)
+ * @throws IOException if anything is wrong with already loaded PDF
*/
public void PDFattachZugferdFile(IZUGFeRDExportableTransaction trans) throws IOException {
prepareDocument();
-
+ ((IProfileProvider) xmlProvider).setProfile(profile);
xmlProvider.generateXML(trans);
String filename = getFilenameForVersion(ZFVersion);
PDFAttachGenericFile(doc, filename, "Alternative",
@@ -335,6 +379,11 @@ public class ZUGFeRDExporter implements Closeable {
}
}
+ /***
+ * Perform the final export to a now ZUGFeRD-enriched PDF file
+ * @param ZUGFeRDfilename the pdf file name
+ * @throws IOException if anything is wrong in the target location
+ */
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) {
prepareDocument();
@@ -349,8 +398,17 @@ public class ZUGFeRDExporter implements Closeable {
}
}
+
+ /***
+ * Perform the final export to a now ZUGFeRD-enriched PDF file as OutputStream
+ * @param output the OutputStream
+ * @throws IOException if anything is wrong in the OutputStream
+ */
public void export(OutputStream output) throws IOException {
- if (!fileAttached) {
+ if (!documentPrepared) {
+ prepareDocument();
+ }
+ if ((!fileAttached) && (attachZUGFeRDHeaders)) {
throw new IOException(
"File must be attached (usually with PDFattachZugferdFile) before perfoming this operation");
}
@@ -369,7 +427,7 @@ public class ZUGFeRDExporter implements Closeable {
* @param description Human-readable description of the file content
* @param subType type of the data e.g. could be "text/xml" - mime like
* @param data the binary data of the file/attachment
- * @throws java.io.IOException
+ * @throws java.io.IOException if anything is wrong with filename
*/
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException {
@@ -421,17 +479,27 @@ public class ZUGFeRDExporter implements Closeable {
doc.getDocumentCatalog().setNames(names);
// AF entry (Array) in catalog with the FileSpec
- COSArray cosArray = (COSArray) doc.getDocumentCatalog().getCOSObject().getItem("AF");
- if (cosArray == null) {
- cosArray = new COSArray();
- }
- cosArray.add(fs);
- COSDictionary dict2 = doc.getDocumentCatalog().getCOSObject();
- COSArray array = new COSArray();
- array.add(fs.getCOSObject()); // see below
- dict2.setItem("AF", array);
- doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
- }
+ COSBase AFEntry = (COSBase)doc.getDocumentCatalog().getCOSObject().getItem("AF");
+ if ((AFEntry == null))
+ {
+ COSArray cosArray = new COSArray();
+ cosArray.add(fs);
+ doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
+ } else if (AFEntry instanceof COSArray)
+ {
+ COSArray cosArray = (COSArray)AFEntry;
+ cosArray.add(fs);
+ doc.getDocumentCatalog().getCOSObject().setItem("AF", cosArray);
+ } else if ((AFEntry instanceof COSObject) &&
+ ((COSObject)AFEntry).getObject() instanceof COSArray)
+ {
+ COSArray cosArray = (COSArray)((COSObject)AFEntry).getObject();
+ cosArray.add(fs);
+ } else
+ {
+ throw new IOException("Unexpected object type for PDFDocument/Catalog/COSDictionary/Item(AF)");
+ }
+ }
/**
* Sets the ZUGFeRD XML data to be attached as a single byte array. This is
@@ -439,6 +507,7 @@ public class ZUGFeRDExporter implements Closeable {
* API or component.
*
* @param zugferdData XML data to be set as a byte array (XML file in raw form).
+ * @throws IOException (should not happen)
*/
public void setZUGFeRDXMLData(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider();
@@ -488,19 +557,19 @@ public class ZUGFeRDExporter implements Closeable {
* neccessary PDF/A schema extension description to be able to add this
* information to RDF
*
- * @param metadata
+ * @param metadata the PDFbox XMPMetadata object
*/
protected void addXMP(XMPMetadata metadata) {
if (attachZUGFeRDHeaders) {
XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, profile,
- ZUGFeRDExporter.getNamespaceForVersion(ZFVersion), ZUGFeRDExporter.getPrefixForVersion(ZFVersion),
- ZUGFeRDExporter.getFilenameForVersion(ZFVersion));
+ getNamespaceForVersion(ZFVersion), getPrefixForVersion(ZFVersion),
+ getFilenameForVersion(ZFVersion));
metadata.addSchema(zf);
}
- XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(metadata, ZFVersion, attachZUGFeRDHeaders);
+ XMPSchemaPDFAExtensions pdfaex = new XMPSchemaPDFAExtensions(this, metadata, ZFVersion, attachZUGFeRDHeaders);
pdfaex.setZUGFeRDVersion(ZFVersion);
metadata.addSchema(pdfaex);
@@ -517,12 +586,12 @@ public class ZUGFeRDExporter implements Closeable {
buffer.write(prefix.getBytes("UTF-8")); // see https://github.com/ZUGFeRD/mustangproject/issues/44
serializer.serialize(xmpMetadata, buffer, false);
buffer.write(suffix.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ } catch (UnsupportedEncodingException e)
+ {
+ throw new TransformerException(e);
+ } catch (IOException e)
+ {
+ throw new TransformerException(e);
}
return buffer.toByteArray();
}
@@ -600,7 +669,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the human author (use factory method instead)
*
- * @param creator
+ * @param creator the (human) name who created the PDF
*/
@Deprecated
public void setCreator(String creator) {
@@ -610,7 +679,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the CreatorTool attribute for the PDF
*
- * @param creator
+ * @param creatorTool the application which created the PDF
*/
protected void setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool;
@@ -619,7 +688,7 @@ public class ZUGFeRDExporter implements Closeable {
/**
* the authoring software (use factory method instead)
*
- * @param producer
+ * @param producer the authoring software
*/
@Deprecated
public void setProducer(String producer) {
@@ -640,4 +709,17 @@ public class ZUGFeRDExporter implements Closeable {
this.attachZUGFeRDHeaders = attachZUGFeRDHeaders;
}
+ /**
+ * encapsulate the deprecated setters
+ * @param zfVersion 1 or 2
+ * @param zugferdConformanceLevel BASIC, COMFORT, EN16931, etc.
+ * @param creator PDF creator
+ * @param producer PDF producer
+ */
+ public void configure(int zfVersion, ZUGFeRDConformanceLevel zugferdConformanceLevel, String creator, String producer) {
+ setZUGFeRDVersion(zfVersion);
+ setZUGFeRDConformanceLevel(zugferdConformanceLevel);
+ setCreator(creator);
+ setProducer(producer);
+ }
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3Factory.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3Factory.java
index a0a5cb20..483f174f 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3Factory.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDExporterFromA3Factory.java
@@ -63,20 +63,9 @@ public class ZUGFeRDExporterFromA3Factory implements IExporterFactory {
public ZUGFeRDExporter load(String pdfFilename) throws IOException {
ensurePDFIsValidPDFA(new FileDataSource(pdfFilename));
- ZUGFeRDExporter zugFeRDExporter;
- PDDocument doc = PDDocument.load(new File(pdfFilename));
- zugFeRDExporter = new ZUGFeRDExporter(doc);
- zugFeRDExporter.setZUGFeRDVersion(ZFVersion);
- zugFeRDExporter.setZUGFeRDConformanceLevel(zugferdConformanceLevel);
- zugFeRDExporter.setCreator(creator);
- // Use creator as default for compatibility
- zugFeRDExporter.setCreatorTool(creatorTool != null ? creatorTool : creator);
- zugFeRDExporter.setProducer(producer);
- zugFeRDExporter.setAttachZUGFeRDHeaders(attachZUGFeRDHeaders);
- zugFeRDExporter.setPDFA3(ensurePDFisUpgraded);
-
- return zugFeRDExporter;
-
+ try (FileInputStream pdf = new FileInputStream(pdfFilename)) {
+ return load(readAllBytes(pdf));
+ }
}
@@ -88,13 +77,9 @@ public class ZUGFeRDExporterFromA3Factory implements IExporterFactory {
*/
public ZUGFeRDExporter load(byte[] pdfBinary) throws IOException {
ensurePDFIsValidPDFA(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary)));
- ZUGFeRDExporter zugFeRDExporter;
PDDocument doc = PDDocument.load(pdfBinary);
- zugFeRDExporter = new ZUGFeRDExporter(doc);
- zugFeRDExporter.setZUGFeRDVersion(ZFVersion);
- zugFeRDExporter.setZUGFeRDConformanceLevel(zugferdConformanceLevel);
- zugFeRDExporter.setCreator(creator);
- zugFeRDExporter.setProducer(producer);
+ ZUGFeRDExporter zugFeRDExporter = new ZUGFeRDExporter(doc);
+ zugFeRDExporter.configure(ZFVersion, zugferdConformanceLevel, creator, producer);
zugFeRDExporter.setAttachZUGFeRDHeaders(attachZUGFeRDHeaders);
zugFeRDExporter.setPDFA3(ensurePDFisUpgraded);
@@ -173,14 +158,14 @@ public class ZUGFeRDExporterFromA3Factory implements IExporterFactory {
/**
* All files are PDF/A-3, setConformance refers to the level conformance.
- *
+ *
* PDF/A-3 has three coformance levels, called "A", "U" and "B".
- *
+ *
* PDF/A-3-B where B means only visually preservable, U -standard for Mustang-
* means visually and unicode preservable and A means full compliance, i.e.
* visually, unicode and structurally preservable and tagged PDF, i.e. useful
* metainformation for blind people.
- *
+ *
* Feel free to pass "A" as new level if you know what you are doing :-)
*/
public IExporterFactory setConformanceLevel(PDFAConformanceLevel newLevel) {
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java
index 6b2a1acb..e748332c 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDImporter.java
@@ -32,6 +32,7 @@ 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.toecount.Toecount;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
@@ -45,31 +46,41 @@ import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
public class ZUGFeRDImporter {
/**
- * @var if metadata has been found
+ * if metadata has been found
*/
private boolean containsMeta = false;
/**
- * @var the reference (i.e. invoice number) of the sender
+ * map filenames of additional XML files to their contents
*/
private HashMap additionalXMLs = new HashMap<>();
/**
* 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;
public ZUGFeRDImporter(String pdfFilename) {
- try {
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pdfFilename));
+ try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
extractLowLevel(bis);
- bis.close();
} catch (IOException e) {
- e.printStackTrace();
+ Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
@@ -78,7 +89,7 @@ public class ZUGFeRDImporter {
try {
extractLowLevel(pdfStream);
} catch (IOException e) {
- e.printStackTrace();
+ Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
}
@@ -122,15 +133,16 @@ public class ZUGFeRDImporter {
private void extractFiles(Map names) throws IOException {
- for (String filename : names.keySet()) {
+ for (String alias : names.keySet()) {
+
+ PDComplexFileSpecification fileSpec = names.get(alias);
+ String filename=fileSpec.getFilename();
/**
- * currently (in the release candidate of version 1) only one attached file with
- * the name ZUGFeRD-invoice.xml is allowed
+ * filenames for invoice data (ZUGFeRD v1 and v2, Factur-X)
*/
- if ((filename.equals("ZUGFeRD-invoice.xml") || filename.equals("factur-x.xml"))) { //$NON-NLS-1$
+ if ((filename.equals("ZUGFeRD-invoice.xml") || (filename.equals("zugferd-invoice.xml")) || filename.equals("factur-x.xml"))) { //$NON-NLS-1$
containsMeta = true;
- PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
// String embeddedFilename = filePath + filename;
// File file = new File(filePath + filename);
@@ -139,18 +151,14 @@ public class ZUGFeRDImporter {
// ByteArrayOutputStream();
// FileOutputStream fos = new FileOutputStream(file);
- rawXML = embeddedFile.toByteArray();
- setMeta(new String(rawXML));
+ setRawXML(embeddedFile.toByteArray());
// fos.write(embeddedFile.getByteArray());
// fos.close();
}
if (filename.startsWith("additional_data")) {
-
- PDComplexFileSpecification fileSpec = names.get(filename);
PDEmbeddedFile embeddedFile = fileSpec.getEmbeddedFile();
additionalXMLs.put(filename, embeddedFile.toByteArray());
-
}
}
}
@@ -161,7 +169,7 @@ public class ZUGFeRDImporter {
try {
transformer = tf.newTransformer();
} catch (TransformerConfigurationException e) {
- e.printStackTrace();
+ Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
}
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StringWriter writer = new StringWriter();
@@ -170,13 +178,52 @@ public class ZUGFeRDImporter {
System.err.println(output);
}
- private Document getDocument() throws ParserConfigurationException, IOException, SAXException, TransformerException {
+ private Document getDocument() { return document; }
+
+ private void setDocument() throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory xmlFact = DocumentBuilderFactory.newInstance();
xmlFact.setNamespaceAware(false);
DocumentBuilder builder = xmlFact.newDocumentBuilder();
- Document doc = builder.parse(new ByteArrayInputStream(rawXML));
- //prettyPrint(doc);
- return doc;
+ ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
+ is.skip(guessBOMSize(is));
+ document = builder.parse(is);
+ }
+
+ public void setRawXML(byte[] rawXML) throws IOException {
+ this.rawXML = rawXML;
+ try {
+ setDocument();
+ } catch (ParserConfigurationException | SAXException e) {
+ Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
+ throw new ZUGFeRDExportException(e);
+ }
+ }
+
+ /**
+ * Skips over a BOM at the beginning of the given ByteArrayInputStream, if one exists.
+ * @param is the ByteArrayInputStream used
+ * @throws IOException
+ * @see Autodetection of Character Encodings
+ */
+ private int guessBOMSize(ByteArrayInputStream is) throws IOException {
+ byte[] pad = new byte[4];
+ is.read(pad);
+ is.reset();
+ int test2 = ((pad[0] & 0xFF) << 8) | (pad[1] & 0xFF);
+ int test3 = ((test2 & 0xFFFF) << 8) | (pad[2] & 0xFF);
+ int test4 = ((test3 & 0xFFFFFF) << 8) | (pad[3] & 0xFF);
+ //
+ if (test4 == 0x0000FEFF || test4 == 0xFFFE0000 || test4 == 0x0000FFFE || test4 == 0xFEFF0000) {
+ // UCS-4: BOM takes 4 bytes
+ return 4;
+ } else if (test3 == 0xEFBBFF) {
+ // UTF-8: BOM takes 3 bytes
+ return 3;
+ } else if (test2 == 0xFEFF || test2 == 0xFFFE) {
+ // UTF-16: BOM takes 2 bytes
+ return 2;
+ }
+ return 0;
}
private String extractString(String xpathStr) {
@@ -189,11 +236,8 @@ public class ZUGFeRDImporter {
XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath();
result = xpath.evaluate(xpathStr, document);
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- throw new ZUGFeRDExportException(e);
- } catch (IOException | SAXException | TransformerException | XPathExpressionException e) {
- e.printStackTrace();
+ } catch (XPathExpressionException e) {
+ Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e);
throw new ZUGFeRDExportException(e);
}
return result;
@@ -217,12 +261,30 @@ public class ZUGFeRDImporter {
}
/**
- * @return the sender's bank's BLZ code
+ * @return the referred document
*/
+ public String getReference() {
+ return extractString("//ApplicableHeaderTradeAgreement/BuyerReference");
+ }
+
+ /**
+ * @return the sender's bank's BLZ code
+ * @deprecated use BIC and IBAN instead of BLZ and KTO
+ */
+ @Deprecated
public String getBLZ() {
return extractString("//PayeeSpecifiedCreditorFinancialInstitution/GermanBankleitzahlID");
}
+ /**
+ * @return the sender's account number
+ * @deprecated use BIC and IBAN instead of BLZ and KTO
+ */
+ @Deprecated
+ public String getKTO() {
+ return extractString("//PayeePartyCreditorFinancialAccount/ProprietaryID");
+ }
+
/**
* @return the sender's bank's BIC code
*/
@@ -230,21 +292,21 @@ public class ZUGFeRDImporter {
return extractString("//PayeeSpecifiedCreditorFinancialInstitution/BICID");
}
+
/**
- * @return the sender's bankname
+ * @return the sender's bank name
*/
public String getBankName() {
- return extractString("/CrossIndustryInvoice/SupplyChainTradeTransaction/ApplicableHeaderTradeSettlement/SpecifiedTradeSettlementPaymentMeans/PayeeSpecifiedCreditorFinancialInstitution/Name");
+ return extractString("//PayeeSpecifiedCreditorFinancialInstitution/Name");
}
+ /**
+ * @return the sender's account IBAN code
+ */
public String getIBAN() {
return extractString("//PayeePartyCreditorFinancialAccount/IBANID");
}
- public String getKTO() {
- return extractString("//PayeePartyCreditorFinancialAccount/ProprietaryID");
- }
-
public String getHolder() {
return extractString("//SellerTradeParty/Name");
}
@@ -290,8 +352,8 @@ public class ZUGFeRDImporter {
/**
* @param meta raw XML to be set
*/
- public void setMeta(String meta) {
- this.rawXML = meta.getBytes();
+ public void setMeta(String meta) throws IOException {
+ setRawXML(meta.getBytes());
}
/**
@@ -336,6 +398,7 @@ public class ZUGFeRDImporter {
/**
* Returns the raw XML data as extracted from the ZUGFeRD PDF file.
+ * @return the raw ZUGFeRD XML data
*/
public byte[] getRawXML() {
return rawXML;
@@ -359,7 +422,7 @@ public class ZUGFeRDImporter {
static String convertStreamToString(java.io.InputStream is) {
// source https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java referring to
// https://community.oracle.com/blogs/pat/2004/10/23/stupid-scanner-tricks
- Scanner s = new Scanner(is).useDelimiter("\\A");
+ Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDMigrator.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDMigrator.java
index 3b232c17..83d67468 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDMigrator.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDMigrator.java
@@ -63,8 +63,22 @@ public class ZUGFeRDMigrator {
*/
ByteArrayOutputStream baos = new ByteArrayOutputStream();
applySchematronXsl(new FileInputStream(xmlFilename), baos);
+
String res = null;
res = baos.toString("UTF-8");
+
+ //migrate the profiles
+
+ res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:basic", "urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic");
+ res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:comfort", "urn:cen.eu:en16931:2017");
+ res=res.replace("urn:ferd:CrossIndustryDocument:invoice:1p0:extended", "urn:cen.eu:en16931:2017#conformant#urn:zugferd.de:2p0:extended");
+
+ //somehow the XML parser seems to insert erreneous XML namespaces, depending on which one it is, saxon seems to be cleaner.
+ //nevertheless, remove them manually
+ res=res.replace("", "");
+ res=res.replace("", "");
+
+
return res;
}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDTransactionModelConverter.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDTransactionModelConverter.java
index c9343a8e..d4172098 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDTransactionModelConverter.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDTransactionModelConverter.java
@@ -19,6 +19,7 @@
package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
@@ -29,53 +30,7 @@ import java.util.HashMap;
import java.util.List;
import javax.xml.bind.JAXBElement;
-
-import org.mustangproject.ZUGFeRD.model.AmountType;
-import org.mustangproject.ZUGFeRD.model.CodeType;
-import org.mustangproject.ZUGFeRD.model.CountryIDType;
-import org.mustangproject.ZUGFeRD.model.CreditorFinancialAccountType;
-import org.mustangproject.ZUGFeRD.model.CreditorFinancialInstitutionType;
-import org.mustangproject.ZUGFeRD.model.CrossIndustryDocumentType;
-import org.mustangproject.ZUGFeRD.model.DateTimeType;
-import org.mustangproject.ZUGFeRD.model.DateTimeTypeConstants;
-import org.mustangproject.ZUGFeRD.model.DocumentCodeType;
-import org.mustangproject.ZUGFeRD.model.DocumentContextParameterType;
-import org.mustangproject.ZUGFeRD.model.DocumentContextParameterTypeConstants;
-import org.mustangproject.ZUGFeRD.model.DocumentLineDocumentType;
-import org.mustangproject.ZUGFeRD.model.ExchangedDocumentContextType;
-import org.mustangproject.ZUGFeRD.model.ExchangedDocumentType;
-import org.mustangproject.ZUGFeRD.model.IDType;
-import org.mustangproject.ZUGFeRD.model.IndicatorType;
-import org.mustangproject.ZUGFeRD.model.LogisticsServiceChargeType;
-import org.mustangproject.ZUGFeRD.model.NoteType;
-import org.mustangproject.ZUGFeRD.model.NoteTypeConstants;
-import org.mustangproject.ZUGFeRD.model.ObjectFactory;
-import org.mustangproject.ZUGFeRD.model.PaymentMeansCodeType;
-import org.mustangproject.ZUGFeRD.model.PaymentMeansCodeTypeConstants;
-import org.mustangproject.ZUGFeRD.model.PercentType;
-import org.mustangproject.ZUGFeRD.model.QuantityType;
-import org.mustangproject.ZUGFeRD.model.ReferencedDocumentType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainEventType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainTradeAgreementType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainTradeDeliveryType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainTradeLineItemType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainTradeSettlementType;
-import org.mustangproject.ZUGFeRD.model.SupplyChainTradeTransactionType;
-import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeType;
-import org.mustangproject.ZUGFeRD.model.TaxRegistrationType;
-import org.mustangproject.ZUGFeRD.model.TaxRegistrationTypeConstants;
-import org.mustangproject.ZUGFeRD.model.TaxTypeCodeType;
-import org.mustangproject.ZUGFeRD.model.TaxTypeCodeTypeConstants;
-import org.mustangproject.ZUGFeRD.model.TextType;
-import org.mustangproject.ZUGFeRD.model.TradeAddressType;
-import org.mustangproject.ZUGFeRD.model.TradeAllowanceChargeType;
-import org.mustangproject.ZUGFeRD.model.TradePartyType;
-import org.mustangproject.ZUGFeRD.model.TradePaymentTermsType;
-import org.mustangproject.ZUGFeRD.model.TradePriceType;
-import org.mustangproject.ZUGFeRD.model.TradeProductType;
-import org.mustangproject.ZUGFeRD.model.TradeSettlementMonetarySummationType;
-import org.mustangproject.ZUGFeRD.model.TradeSettlementPaymentMeansType;
-import org.mustangproject.ZUGFeRD.model.TradeTaxType;
+import org.mustangproject.ZUGFeRD.model.*;
class ZUGFeRDTransactionModelConverter {
private static final SimpleDateFormat zugferdDateFormat = new SimpleDateFormat("yyyyMMdd");
@@ -85,6 +40,7 @@ class ZUGFeRDTransactionModelConverter {
private final Totals totals;
private boolean isTest;
private String currency = "EUR";
+ private String profile;
ZUGFeRDTransactionModelConverter(IZUGFeRDExportableTransaction trans) {
@@ -114,7 +70,7 @@ class ZUGFeRDTransactionModelConverter {
DocumentContextParameterType contextParameter = xmlFactory
.createDocumentContextParameterType();
IDType idType = xmlFactory.createIDType();
- idType.setValue(DocumentContextParameterTypeConstants.EXTENDED);
+ idType.setValue(profile);
contextParameter.setID(idType);
context.getGuidelineSpecifiedDocumentContextParameter().add(
contextParameter);
@@ -150,7 +106,7 @@ class ZUGFeRDTransactionModelConverter {
document.setTypeCode(documentCodeType);
TextType name = xmlFactory.createTextType();
- name.setValue("RECHNUNG");
+ name.setValue(trans.getDocumentName());
document.getName().add(name);
if (trans.getOwnOrganisationFullPlaintextInfo() != null) {
@@ -168,7 +124,7 @@ class ZUGFeRDTransactionModelConverter {
if (trans.getReferenceNumber() != null && !"".equals(trans.getReferenceNumber())) {
NoteType referenceInfo = xmlFactory.createNoteType();
TextType referenceInfoContent = xmlFactory.createTextType();
- referenceInfoContent.setValue("Ursprungsbeleg: " + trans.getReferenceNumber());
+ referenceInfoContent.setValue(trans.getReferenceNumber());
referenceInfo.getContent().add(referenceInfoContent);
document.getIncludedNote().add(referenceInfo);
}
@@ -200,8 +156,13 @@ class ZUGFeRDTransactionModelConverter {
tradeAgreement.setBuyerTradeParty(getBuyer());
tradeAgreement.setSellerTradeParty(getSeller());
- if (getBuyerOrderReferencedDocument() != null) {
- tradeAgreement.getBuyerOrderReferencedDocument().add(getBuyerOrderReferencedDocument());
+ if (trans.getBuyerOrderReferencedDocumentID() != null) {
+ ReferencedDocumentType refdoc = xmlFactory.createReferencedDocumentType();
+ IDType id = xmlFactory.createIDType();
+ id.setValue(trans.getBuyerOrderReferencedDocumentID());
+ refdoc.getID().add(id);
+ refdoc.setIssueDateTime(trans.getBuyerOrderReferencedDocumentIssueDateTime());
+ tradeAgreement.getBuyerOrderReferencedDocument().add(refdoc);
}
return tradeAgreement;
@@ -232,6 +193,12 @@ class ZUGFeRDTransactionModelConverter {
buyerID.setValue(trans.getRecipient().getID());
buyerTradeParty.getID().add(buyerID);
}
+ if (trans.getRecipient().getGlobalID() != null) {
+ IDType globalID = xmlFactory.createIDType();
+ globalID.setValue(trans.getRecipient().getGlobalID());
+ globalID.setSchemeID(trans.getRecipient().getGlobalIDScheme());
+ buyerTradeParty.getGlobalID().add(globalID);
+ }
TextType buyerName = xmlFactory.createTextType();
buyerName.setValue(trans.getRecipient().getName());
@@ -410,7 +377,7 @@ class ZUGFeRDTransactionModelConverter {
currencyCode.setValue(currency);
tradeSettlement.setInvoiceCurrencyCode(currencyCode);
- tradeSettlement.getSpecifiedTradeSettlementPaymentMeans().add(
+ tradeSettlement.getSpecifiedTradeSettlementPaymentMeans().addAll(
getPaymentData());
tradeSettlement.getApplicableTradeTax().addAll(getTradeTax());
tradeSettlement.getSpecifiedTradePaymentTerms().addAll(
@@ -434,46 +401,50 @@ class ZUGFeRDTransactionModelConverter {
}
- private TradeSettlementPaymentMeansType getPaymentData() {
- TradeSettlementPaymentMeansType paymentData = xmlFactory
- .createTradeSettlementPaymentMeansType();
- PaymentMeansCodeType paymentDataType = xmlFactory
- .createPaymentMeansCodeType();
- paymentDataType.setValue(PaymentMeansCodeTypeConstants.BANKACCOUNT);
- paymentData.setTypeCode(paymentDataType);
+ private List getPaymentData() {
+ List result = new ArrayList<>();
+ for (IZUGFeRDTradeSettlementPayment settlementPayment : trans.getTradeSettlementPayment()) {
+ TradeSettlementPaymentMeansType paymentData = xmlFactory
+ .createTradeSettlementPaymentMeansType();
+ PaymentMeansCodeType paymentDataType = xmlFactory
+ .createPaymentMeansCodeType();
+ paymentDataType.setValue(PaymentMeansCodeTypeConstants.BANKACCOUNT);
+ paymentData.setTypeCode(paymentDataType);
- TextType paymentInfo = xmlFactory.createTextType();
- String paymentInfoText = trans.getOwnPaymentInfoText();
- if (paymentInfoText == null) {
- paymentInfoText = "";
+ TextType paymentInfo = xmlFactory.createTextType();
+ String paymentInfoText = settlementPayment.getOwnPaymentInfoText();
+ if (paymentInfoText == null) {
+ paymentInfoText = "";
+ }
+ paymentInfo.setValue(paymentInfoText);
+ paymentData.getInformation().add(paymentInfo);
+
+ CreditorFinancialAccountType bankAccount = xmlFactory
+ .createCreditorFinancialAccountType();
+ IDType iban = xmlFactory.createIDType();
+ iban.setValue(settlementPayment.getOwnIBAN());
+ bankAccount.setIBANID(iban);
+ IDType kto = xmlFactory.createIDType();
+ kto.setValue(settlementPayment.getOwnKto());
+ bankAccount.setProprietaryID(kto);
+ paymentData.setPayeePartyCreditorFinancialAccount(bankAccount);
+
+ CreditorFinancialInstitutionType bankData = xmlFactory
+ .createCreditorFinancialInstitutionType();
+ IDType bicId = xmlFactory.createIDType();
+ bicId.setValue(settlementPayment.getOwnBIC());
+ bankData.setBICID(bicId);
+ TextType bankName = xmlFactory.createTextType();
+ bankName.setValue(settlementPayment.getOwnBankName());
+ bankData.setName(bankName);
+ IDType blz = xmlFactory.createIDType();
+ blz.setValue(settlementPayment.getOwnBLZ());
+ bankData.setGermanBankleitzahlID(blz);
+
+ paymentData.setPayeeSpecifiedCreditorFinancialInstitution(bankData);
+ result.add(paymentData);
}
- paymentInfo.setValue(paymentInfoText);
- paymentData.getInformation().add(paymentInfo);
-
- CreditorFinancialAccountType bankAccount = xmlFactory
- .createCreditorFinancialAccountType();
- IDType iban = xmlFactory.createIDType();
- iban.setValue(trans.getOwnIBAN());
- bankAccount.setIBANID(iban);
- IDType kto = xmlFactory.createIDType();
- kto.setValue(trans.getOwnKto());
- bankAccount.setProprietaryID(kto);
- paymentData.setPayeePartyCreditorFinancialAccount(bankAccount);
-
- CreditorFinancialInstitutionType bankData = xmlFactory
- .createCreditorFinancialInstitutionType();
- IDType bicId = xmlFactory.createIDType();
- bicId.setValue(trans.getOwnBIC());
- bankData.setBICID(bicId);
- TextType bankName = xmlFactory.createTextType();
- bankName.setValue(trans.getOwnBankName());
- bankData.setName(bankName);
- IDType blz = xmlFactory.createIDType();
- blz.setValue(trans.getOwnBLZ());
- bankData.setGermanBankleitzahlID(blz);
-
- paymentData.setPayeeSpecifiedCreditorFinancialInstitution(bankData);
- return paymentData;
+ return result;
}
@@ -490,7 +461,7 @@ class ZUGFeRDTransactionModelConverter {
TaxCategoryCodeType taxCategoryCode = xmlFactory.createTaxCategoryCodeType();
VATAmount vatAmount = VATPercentAmountMap.get(currentTaxPercent);
- taxCategoryCode.setValue(vatAmount.getDocumentCode());
+ taxCategoryCode.setValue(vatAmount.getCategoryCode());
tradeTax.setCategoryCode(taxCategoryCode);
VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
@@ -754,9 +725,14 @@ class ZUGFeRDTransactionModelConverter {
grandTotalAmount.setValue(currencyFormat(totals.getTotalGross()));
monetarySummation.getGrandTotalAmount().add(grandTotalAmount);
+ AmountType totalPrepaidAmount = xmlFactory.createAmountType();
+ totalPrepaidAmount.setCurrencyID(currency);
+ totalPrepaidAmount.setValue(currencyFormat(trans.getTotalPrepaidAmount()));
+ monetarySummation.getTotalPrepaidAmount().add(totalPrepaidAmount);
+
AmountType duePayableAmount = xmlFactory.createAmountType();
duePayableAmount.setCurrencyID(currency);
- duePayableAmount.setValue(currencyFormat(totals.getTotalGross()));
+ duePayableAmount.setValue(currencyFormat(totals.getTotalGross().subtract(trans.getTotalPrepaidAmount())));
monetarySummation.getDuePayableAmount().add(duePayableAmount);
return monetarySummation;
@@ -911,70 +887,26 @@ class ZUGFeRDTransactionModelConverter {
private BigDecimal vatFormat(BigDecimal value) {
- return nDigitFormat(value, 2);
- }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
private BigDecimal currencyFormat(BigDecimal value) {
- return nDigitFormat(value, 2);
- }
+ return value.setScale(2, RoundingMode.HALF_UP);
+ }
private BigDecimal priceFormat(BigDecimal value) {
- return nDigitFormat(value, 4);
- }
+ return value.setScale(4, RoundingMode.HALF_UP);
+ }
private BigDecimal quantityFormat(BigDecimal value) {
- return nDigitFormat(value, 4);
- }
-
-
- private BigDecimal nDigitFormat(BigDecimal value, int scale) {
- /*
- * I needed 123,45, locale independent.I tried
- * NumberFormat.getCurrencyInstance().format( 12345.6789 ); but that is
- * locale specific.I also tried DecimalFormat df = new DecimalFormat(
- * "0,00" ); df.setDecimalSeparatorAlwaysShown(true);
- * df.setGroupingUsed(false); DecimalFormatSymbols symbols = new
- * DecimalFormatSymbols(); symbols.setDecimalSeparator(',');
- * symbols.setGroupingSeparator(' ');
- * df.setDecimalFormatSymbols(symbols);
- *
- * but that would not switch off grouping. Although I liked very much
- * the (incomplete) "BNF diagram" in
- * http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
- * in the end I decided to calculate myself and take eur+sparator+cents
- *
- * This function will cut off, i.e. floor() subcent values Tests:
- * System.err.println(utils.currencyFormat(new BigDecimal(0),
- * ".")+"\n"+utils.currencyFormat(new BigDecimal("-1.10"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.1"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("-1.01"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3489"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("20000123.3419"),
- * ",")+"\n"+utils.currencyFormat(new BigDecimal("12"), ","));
- *
- * results 0.00 -1,10 -1,10 -1,01 20000123,34 20000123,34 12,00
- */
- value = value.setScale(scale, BigDecimal.ROUND_HALF_UP); // first, round
- // so that
- // e.g.
- // 1.189999999999999946709294817992486059665679931640625
- // becomes
- // 1.19
- char[] repeat = new char[scale];
- Arrays.fill(repeat, '0');
-
- DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols();
- otherSymbols.setDecimalSeparator('.');
- DecimalFormat dec = new DecimalFormat("0." + new String(repeat),
- otherSymbols);
- return new BigDecimal(dec.format(value));
-
- }
+ return value.setScale(4, RoundingMode.HALF_UP);
+ }
+
/**
* which taxes have been used with which amounts in this transaction, empty for no taxes, or e.g. 19=>190 and 7=>14 if 1000 Eur were applicable to 19% VAT
* (=>190 EUR VAT) and 200 EUR were applicable to 7% (=>14 EUR VAT) 190 Eur
@@ -992,7 +924,7 @@ class ZUGFeRDTransactionModelConverter {
for (IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
BigDecimal percent = currentItem.getProduct().getVATPercent();
LineCalc lc = new LineCalc(currentItem);
- VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), trans.getDocumentCode());
+ VATAmount itemVATAmount = new VATAmount(lc.getItemTotalNetAmount(), lc.getItemTotalVATAmount(), lc.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -1027,8 +959,7 @@ class ZUGFeRDTransactionModelConverter {
VATAmount itemVATAmount = new VATAmount(
logisticsServiceCharge.getTotalAmount(),
logisticsServiceCharge.getTotalAmount()
- .multiply(percent).divide(new BigDecimal(100)),
- trans.getDocumentCode());
+ .multiply(percent).divide(new BigDecimal(100)), logisticsServiceCharge.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -1043,8 +974,7 @@ class ZUGFeRDTransactionModelConverter {
BigDecimal percent = charge.getTaxPercent();
VATAmount itemVATAmount = new VATAmount(
charge.getTotalAmount(), charge.getTotalAmount()
- .multiply(percent).divide(new BigDecimal(100)),
- trans.getDocumentCode());
+ .multiply(percent).divide(new BigDecimal(100)), charge.getCategoryCode());
VATAmount current = hm.get(percent);
if (current == null) {
hm.put(percent, itemVATAmount);
@@ -1063,12 +993,18 @@ class ZUGFeRDTransactionModelConverter {
return this;
}
+ public ZUGFeRDTransactionModelConverter withProfile(String profile) {
+ this.profile = profile;
+ return this;
+ }
+
private class LineCalc {
private BigDecimal totalGross;
private BigDecimal itemTotalNetAmount;
private BigDecimal itemTotalVATAmount;
private BigDecimal itemNetAmount;
+ private String categoryCode;
public LineCalc(IZUGFeRDExportableItem currentItem) {
@@ -1111,6 +1047,7 @@ class ZUGFeRDTransactionModelConverter {
.add(totalCharge)
.divide(currentItem.getQuantity(), 4,
BigDecimal.ROUND_HALF_UP);
+ categoryCode = currentItem.getCategoryCode();
}
@@ -1128,6 +1065,8 @@ class ZUGFeRDTransactionModelConverter {
return itemNetAmount;
}
+ public String getCategoryCode() { return categoryCode; }
+
}
private class Totals {
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java
new file mode 100644
index 00000000..eb75f9dd
--- /dev/null
+++ b/src/main/java/org/mustangproject/ZUGFeRD/ZUGFeRDVisualizer.java
@@ -0,0 +1,165 @@
+/** **********************************************************************
+ *
+ * Copyright 2018 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 javax.xml.transform.*;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ZUGFeRDVisualizer {
+
+ static final ClassLoader CLASS_LOADER = ZUGFeRDVisualizer.class.getClassLoader();
+ private static final String RESOURCE_PATH = ""; //$NON-NLS-1$
+ private static final Logger LOG = Logger.getLogger(ZUGFeRDVisualizer.class.getName());
+ // private static File createTempFileResult(final Transformer transformer, final
+ // StreamSource toTransform,
+ // final String suffix) throws TransformerException, IOException {
+ // File result = File.createTempFile("ZUV_", suffix); //$NON-NLS-1$
+ // result.deleteOnExit();
+ //
+ // try (FileOutputStream fos = new FileOutputStream(result)) {
+ // transformer.transform(toTransform, new StreamResult(fos));
+ // }
+ // return result;
+ // }
+ private TransformerFactory mFactory = null;
+ private Templates mXsltXRTemplate = null;
+ private Templates mXsltHTMLTemplate = null;
+ private Templates mXsltZF1HTMLTemplate = null;
+
+ public ZUGFeRDVisualizer() {
+ mFactory = new net.sf.saxon.TransformerFactoryImpl();
+ // fact = TransformerFactory.newInstance();
+ mFactory.setURIResolver(new ClasspathResourceURIResolver());
+ try {
+ mXsltXRTemplate = mFactory.newTemplates(
+ new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
+ mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource(
+ CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html.xsl")));
+ mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource(
+ CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt")));
+ } catch (TransformerConfigurationException ex) {
+ LOG.log(Level.SEVERE, null, ex);
+ }
+ }
+
+ public String visualize(String xmlFilename)
+ throws FileNotFoundException, TransformerException, UnsupportedEncodingException {
+ /**
+ * *
+ * http://www.unece.org/fileadmin/DAM/cefact/xml/XML-Naming-And-Design-Rules-V2_1.pdf
+ * http://www.ferd-net.de/upload/Dokumente/FACTUR-X_ZUGFeRD_2p0_Teil1_Profil_EN16931_1p03.pdf
+ * http://countwordsfree.com/xmlviewer
+ */
+ FileInputStream fis=new FileInputStream(xmlFilename);
+ String fileContent="";
+ try {
+ fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)));
+ } catch (IOException e2) {
+ LOG.log(Level.SEVERE, null, e2);
+ }
+
+ ByteArrayOutputStream iaos = new ByteArrayOutputStream();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ String zf1Signature = "rsm:CrossIndustryDocument";
+ if (fileContent.contains(zf1Signature)) {
+ applyZF1SchematronXsl(fis, baos);
+
+ } else {
+ //zf2 or fx
+ applySchematronXsl(fis, iaos);
+ // take the copy of the stream and re-write it to an InputStream
+ PipedInputStream in = new PipedInputStream();
+ PipedOutputStream out;
+ try {
+ out = new PipedOutputStream(in);
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ // write the original OutputStream to the PipedOutputStream
+ // note that in order for the below method to work, you need
+ // to ensure that the data has finished writing to the
+ // ByteArrayOutputStream
+ iaos.writeTo(out);
+ } catch (IOException e) {
+ LOG.log(Level.SEVERE, null, e);
+ } finally {
+ // close the PipedOutputStream here because we're done writing data
+ // once this thread has completed its run
+ if (out != null) {
+ // close the PipedOutputStream cleanly
+ try {
+ out.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ LOG.log(Level.SEVERE, null, e);
+ }
+ }
+ }
+ }
+ }).start();
+ applySchematronXsl2(in, baos);
+ } catch (IOException e1) {
+ LOG.log(Level.SEVERE, null, e1);
+ }
+ }
+
+ return baos.toString("UTF-8");
+ }
+
+ public void applySchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
+ throws TransformerException {
+ Transformer transformer = mXsltXRTemplate.newTransformer();
+
+ transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
+ }
+
+ public void applyZF1SchematronXsl(final InputStream xmlFile, final OutputStream HTMLOutstream)
+ throws TransformerException {
+ Transformer transformer = mXsltZF1HTMLTemplate.newTransformer();
+
+ transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
+ }
+
+ public void applySchematronXsl2(final InputStream xmlFile, final OutputStream HTMLOutstream)
+ throws TransformerException {
+ Transformer transformer = mXsltHTMLTemplate.newTransformer();
+
+ transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
+ }
+
+ private static class ClasspathResourceURIResolver implements URIResolver {
+ ClasspathResourceURIResolver() {
+ // Do nothing, just prevents synthetic access warning.
+ }
+
+ @Override
+ public Source resolve(String href, String base) throws TransformerException {
+ return new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + href));
+ }
+ }
+}
diff --git a/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentContextParameterTypeConstants.java b/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentContextParameterTypeConstants.java
index 57033f6d..d90eabd1 100644
--- a/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentContextParameterTypeConstants.java
+++ b/src/main/java/org/mustangproject/ZUGFeRD/model/DocumentContextParameterTypeConstants.java
@@ -19,7 +19,7 @@
package org.mustangproject.ZUGFeRD.model;
public class DocumentContextParameterTypeConstants {
+ public static final String BASIC = "urn:ferd:CrossIndustryDocument:invoice:1p0:basic";
public static final String COMFORT = "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort";
public static final String EXTENDED = "urn:ferd:CrossIndustryDocument:invoice:1p0:extended";
- // this does not look complete :-/
}
diff --git a/src/main/java/org/mustangproject/toecount/Toecount.java b/src/main/java/org/mustangproject/toecount/Toecount.java
index 2cf1c3b3..688dcda2 100755
--- a/src/main/java/org/mustangproject/toecount/Toecount.java
+++ b/src/main/java/org/mustangproject/toecount/Toecount.java
@@ -30,8 +30,14 @@ import org.mustangproject.ZUGFeRD.*;
import javax.xml.transform.TransformerException;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -69,8 +75,9 @@ public class Toecount {
+ " [--source ]: set input PDF file\n"
+ " [--source-xml ]: set input XML file\n"
+ " [--out ]: set output PDF file\n"
- + " [--zugferd-version <1|2>]: set ZUGFeRD version\n"
- + " [--zugferd-profile <...>]: set ZUGFeRD profile\n"
+ + " [--format ]: set ZUGFeRD or FacturX\n"
+ + " [--version <1|2>]: set ZUGFeRD version\n"
+ + " [--profile <...>]: set ZUGFeRD profile\n"
+ " For ZUGFeRD v1: ASIC, OMFORT or XTENDED\n"
+ " For ZUGFeRD v2: INIMUM, BASIC L, ASIC, IUS, N16931, ETENDED ";
}
@@ -102,8 +109,8 @@ public class Toecount {
+ "\t\t[--source ]: set input PDF file\r\n"
+ "\t\t[--source-xml ]: set input XML file\r\n"
+ "\t\t[--out ]: set output PDF file\r\n"
- + "\t\t[--zugferd-version <1|2>]: set ZUGFeRD version\r\n"
- + "\t\t[--zugferd-profile <...>]: set ZUGFeRD profile\r\n"
+ + "\t\t[--format ]: enable factur-x or ZUGFeRD\r\n"
+ + "\t\t[--version <1|2>]: set ZUGFeRD version\r\n" + "\t\t[--profile <...>]: set ZUGFeRD profile\r\n"
+ "\t\t\tFor ZUGFeRD v1: ASIC, OMFORT or XTENDED\r\n"
+ "\t\t\tFor ZUGFeRD v2: INIMUM, BASIC L, ASIC, IUS, N16931, ETENDED\r\n");
}
@@ -112,11 +119,15 @@ public class Toecount {
* Asks the user (repeatedly, if neccessary) on the command line for a String
* (offering a defaultValue) conforming to a Regex pattern
*
- * @param prompt the question to be asked to the user
- * @param defaultValue the default return value if user hits enter
- * @param pattern a regex of acceptable values
+ * @param prompt
+ * the question to be asked to the user
+ * @param defaultValue
+ * the default return value if user hits enter
+ * @param pattern
+ * a regex of acceptable values
* @return the user answer conforming to pattern
- * @throws Exception if pattern not compielable or IOexception on input
+ * @throws Exception
+ * if pattern not compielable or IOexception on input
*/
protected static String getStringFromUser(String prompt, String defaultValue, String pattern) throws Exception {
String input = "";
@@ -137,8 +148,8 @@ public class Toecount {
try {
input = buffer.readLine();
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+
}
if (input.isEmpty()) {
@@ -155,15 +166,15 @@ public class Toecount {
/**
* Prompts the user for a input or output filename
*
- * @param the text the user is asked
- * @param a default Filename
- * @param expectedExtension will warn if filename does not match expected file extension
- * @param ensureFileExists will warn if file does NOT exist (for input files)
+ * @param prompt the text the user is asked
+ * @param defaultFilename a default Filename
+ * @param expectedExtension will warn if filename does not match expected file extension
+ * @param ensureFileExists will warn if file does NOT exist (for input files)
* @param ensureFileNotExists will warn if file DOES exist (for output files)
* @return String
*/
protected static String getFilenameFromUser(String prompt, String defaultFilename, String expectedExtension,
- boolean ensureFileExists, boolean ensureFileNotExists) {
+ boolean ensureFileExists, boolean ensureFileNotExists) {
boolean fileExistenceOK = false;
String selectedName = "";
do {
@@ -174,8 +185,8 @@ public class Toecount {
try {
selectedName = buffer.readLine();
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+
}
if (selectedName.isEmpty()) {
@@ -191,8 +202,7 @@ public class Toecount {
try {
selectedAnswer = buffer.readLine();
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
}
if (!selectedAnswer.equals("Y") && !selectedAnswer.equals("y")) {
System.err.println("Aborted by user");
@@ -234,6 +244,8 @@ public class Toecount {
// Option: Help
Option helpOption = parser.addBooleanOption('h', "help");
+ // Option: Action
+ Option actionOption = parser.addStringOption('a', "action");
// Generic options available for multiple command
// --source: input file
@@ -264,12 +276,13 @@ public class Toecount {
// (--source: input PDF file)
// (--source-xml: input XML file)
// (--out: output PDF file)
- // (--zugferd-version: ZUGFeRD version)
- // (--zugferd-profile: ZUGFeRD profile)
+ // (--version: ZUGFeRD version)
+ // (--profile: ZUGFeRD profile)
Option combineOption = parser.addBooleanOption('c', "combine");
Option sourceXmlOption = parser.addStringOption("source-xml");
- Option zugferdVersionOption = parser.addStringOption("zugferd-version");
- Option zugferdProfileOption = parser.addStringOption("zugferd-profile");
+ Option formatOption = parser.addStringOption('f', "format");
+ Option zugferdVersionOption = parser.addStringOption("version");
+ Option zugferdProfileOption = parser.addStringOption("profile");
// Command: Show metrics in dir
// --directory
@@ -290,17 +303,19 @@ public class Toecount {
}
// Retrieve all options
+ String action = parser.getOptionValue(actionOption);
String directoryName = parser.getOptionValue(dirnameOption);
Boolean filesFromStdIn = parser.getOptionValue(filesFromStdInOption, Boolean.FALSE);
- Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE);
- Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE);
- Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE);
- Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE);
+ Boolean combineRequested = parser.getOptionValue(combineOption, Boolean.FALSE) || ((action!=null)&&(action.equals("combine")));
+ Boolean extractRequested = parser.getOptionValue(extractOption, Boolean.FALSE) || ((action!=null)&&(action.equals("extract")));
+ Boolean helpRequested = parser.getOptionValue(helpOption, Boolean.FALSE) || ((action!=null)&&(action.equals("help")));
+ Boolean upgradeRequested = parser.getOptionValue(upgradeOption, Boolean.FALSE) || ((action!=null)&&(action.equals("upgrade")));
Boolean ignoreFileExt = parser.getOptionValue(ignoreFileExtOption, Boolean.FALSE);
- Boolean a3only = parser.getOptionValue(a3onlyOption, Boolean.FALSE);
+ Boolean a3only = parser.getOptionValue(a3onlyOption, Boolean.FALSE) || ((action!=null)&&(action.equals("a3")));
String sourceName = parser.getOptionValue(sourceOption);
String sourceXMLName = parser.getOptionValue(sourceXmlOption);
String outName = parser.getOptionValue(outOption);
+ String format = parser.getOptionValue(formatOption);
String zugferdVersion = parser.getOptionValue(zugferdVersionOption);
String zugferdProfile = parser.getOptionValue(zugferdProfileOption);
@@ -309,7 +324,7 @@ public class Toecount {
} else if (((directoryName != null) && (directoryName.length() > 0)) || filesFromStdIn.booleanValue()) {
performMetrics(directoryName, filesFromStdIn, ignoreFileExt);
} else if (combineRequested) {
- performCombine(sourceName, sourceXMLName, outName, zugferdVersion, zugferdProfile);
+ performCombine(sourceName, sourceXMLName, outName, format, zugferdVersion, zugferdProfile);
} else if (extractRequested) {
performExtract(sourceName, outName);
} else if (a3only) {
@@ -322,7 +337,7 @@ public class Toecount {
System.exit(2);
}
} catch (Exception e) {
- e.printStackTrace();
+
Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
System.exit(-1);
}
@@ -338,7 +353,7 @@ public class Toecount {
System.out.println("ZUGFeRD 1.0 XML source set to " + xmlName);
}
if (outName == null) {
- outName = getFilenameFromUser("ZUGFeRD 2.0 XML target", "factur-x.xml", "xml", false, true);
+ outName = getFilenameFromUser("ZUGFeRD 2.0 XML target", "zugferd-invoice.xml", "xml", false, true);
} else {
System.out.println("ZUGFeRD 1.0 XML source set to " + outName);
}
@@ -417,15 +432,15 @@ public class Toecount {
}
}
- private static void performCombine(String pdfName, String xmlName, String outName, String zfVersion,
- String zfProfile) throws Exception {
+ private static void performCombine(String pdfName, String xmlName, String outName, String format, String zfVersion,
+ String zfProfile) throws Exception {
/*
* ZUGFeRDExporter ze= new ZUGFeRDExporterFromA1Factory()
* .setProducer("toecount") .setCreator(System.getProperty("user.name"))
* .loadFromPDFA1("invoice.pdf");
*/
try {
- int zfIntVersion = 1;
+ int zfIntVersion = ZUGFeRDExporter.DefaultZUGFeRDVersion;
ZUGFeRDConformanceLevel zfConformanceLevelProfile = ZUGFeRDConformanceLevel.EXTENDED;
if (pdfName == null) {
@@ -446,35 +461,42 @@ public class Toecount {
System.out.println("Ouput PDF set to " + outName);
}
- if (zfVersion == null) {
+ if (format == null) {
try {
- zfVersion = getStringFromUser("ZUGFeRD version (1 or 2)", "1", "1|2");
+ format = getStringFromUser("Format (fx=Factur-X, zf=ZUGFeRD,)", "zf", "fx|zf");
} catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
}
} else {
- System.out.println("ZUGFeRD version set to " + zfVersion);
+ System.out.println("Format set to " + format);
}
+ if (zfVersion == null) {
+ try {
+ zfVersion = getStringFromUser("Version (1 or 2)", "1", "1|2");
+ } catch (Exception e) {
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+ }
+ } else {
+ System.out.println("Version set to " + zfVersion);
+ }
zfIntVersion = Integer.valueOf(zfVersion);
if (zfProfile == null) {
try {
- if (zfIntVersion == 1) {
- zfProfile = getStringFromUser("ZUGFeRD profile b)asic, c)omfort or e)xtended", "e",
- "B|b|C|c|E|e");
+ if (format.equals("zf") && (zfIntVersion == 1)) {
+ zfProfile = getStringFromUser("Profile b)asic, c)omfort or e)xtended", "e", "B|b|C|c|E|e");
} else {
zfProfile = getStringFromUser(
- "ZUGFeRD profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED",
- "E", "M|m|W|w|B|b|C|c|E|e|X|x|");
+ "Profile [M]INIMUM, BASIC [W]L, [B]ASIC,\n" + "[C]IUS, [E]N16931, E[X]TENDED", "E",
+ "M|m|W|w|B|b|C|c|E|e|X|x|");
}
} catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+
}
} else {
- System.out.println("ZUGFeRD profile set to " + zfProfile);
+ System.out.println("Profile set to " + zfProfile);
}
zfProfile = zfProfile.toLowerCase();
@@ -483,7 +505,11 @@ public class Toecount {
ensureFileExists(xmlName);
ensureFileNotExists(outName);
- if (zfIntVersion == 1) {
+ if ((format.equals("fx")) && (zfIntVersion > 1)) {
+ throw new Exception("Factur-X is only available in version 1 (roughly corresponding to ZF2)");
+ }
+
+ if ((format.equals("zf")) && (zfIntVersion == 1)) {
if (zfProfile.equals("b")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.BASIC;
} else if (zfProfile.equals("c")) {
@@ -493,7 +519,7 @@ public class Toecount {
} else {
throw new Exception(String.format("Unknown ZUGFeRD profile '%s'", zfProfile));
}
- } else if (zfIntVersion == 2) {
+ } else if (((format.equals("zf")) && (zfIntVersion == 2)) || (format.equals("fx"))) {
if (zfProfile.equals("m")) {
zfConformanceLevelProfile = ZUGFeRDConformanceLevel.MINIMUM;
} else if (zfProfile.equals("w")) {
@@ -510,15 +536,18 @@ public class Toecount {
throw new Exception(String.format("Unknown ZUGFeRD profile '%s'", zfProfile));
}
} else {
- throw new Exception(String.format("Unknown ZUGFeRD version '%i'", zfIntVersion));
+ throw new Exception(String.format("Unknown version '%i'", zfIntVersion));
}
// All params are good! continue...
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setProducer("Toecount")
+ .setZUGFeRDVersion(zfIntVersion)
.setCreator(System.getProperty("user.name")).setZUGFeRDConformanceLevel(zfConformanceLevelProfile)
.load(pdfName);
- ze.setZUGFeRDVersion(zfIntVersion);
+ if (format.equals("fx")) {
+ ze.setFacturX();
+ }
ze.setZUGFeRDXMLData(Files.readAllBytes(Paths.get(xmlName)));
@@ -527,9 +556,8 @@ public class Toecount {
System.out.println("Written to " + outName);
} catch (IOException e) {
- e.printStackTrace();
- // } catch (JAXBException e) {
- // e.printStackTrace();
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+
}
}
diff --git a/src/main/resources/COMFORTtoEN16931.xsl b/src/main/resources/COMFORTtoEN16931.xsl
index d6eb2e03..c4f974a9 100644
--- a/src/main/resources/COMFORTtoEN16931.xsl
+++ b/src/main/resources/COMFORTtoEN16931.xsl
@@ -63,7 +63,12 @@ Migrated by Mustangproject XSLT
-
+
+
+
+
+
+
diff --git a/src/main/resources/stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt b/src/main/resources/stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt
new file mode 100644
index 00000000..bf0b71e4
--- /dev/null
+++ b/src/main/resources/stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt
@@ -0,0 +1,3008 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [if IE]><STYLE type="text/css">.altova-rotate-left-textbox{filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3)} .altova-rotate-right-textbox{filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1)} </STYLE><![endif]
+ [if !IE]><!
+
+ <![endif]
+
+
+
+
+
+
+ Stylesheet Lesbarmachung der XML-Daten von ZUGFeRD-Rechnungen
+
+
+
+ ZUGFeRD-Version : 1.0 vom 25.06.2014
+
+
+
+ Stylesheet-Version: 1.0 vom 25.06.2014
+
+
+
+ Codelisten-Version: 1.0 vom 25.06.2014
+
+
+
+ (c) AWV e.V. 2014
+
+
+