Merge branch 'mustang-master' into forked-master

This commit is contained in:
Sebastian Sieber
2024-07-30 08:46:09 +02:00
149 changed files with 50064 additions and 19410 deletions

View File

@@ -16,7 +16,7 @@ jobs:
- uses: actions/setup-java@v3 # the log states the default ~/.m2/toolchains.xml is being created pointing to the JDK - uses: actions/setup-java@v3 # the log states the default ~/.m2/toolchains.xml is being created pointing to the JDK
with: with:
distribution: 'adopt' # for latest JDKs use temurin https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/ distribution: 'adopt' # for latest JDKs use temurin https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/
java-version: '8' java-version: '11'
cache: 'maven' #cache/restore any dependencies to improve the workflow execution time cache: 'maven' #cache/restore any dependencies to improve the workflow execution time
- name: Build with Maven - name: Build with Maven
run: mvn -B package --file pom.xml run: mvn -B package --file pom.xml

View File

@@ -1,3 +1,43 @@
2.12.0
=======
2024-07-20
- support pdf export/visualization to PDF, thanks to Heavenfighter #387 allow embedd fonts from jar-file for pdf creation #388
- Fix #389: ClassCastException: ZUGFeRDExporterFromA3
- jakarta support #372
- Upgrade to PDFBox 3 #373
- Requires Java 11
- #397 Build succeeds but file is unusable on alpine/docker
- #392 CLI: action combine: --ignorefileextension to ignore PDF/A input file errors dosen't work
- for CLI combine, fx is now default
- set profile to XR if XR is imported #395
- Powershell compatibility: added --no-additional-attachments command line option for better batch processing:
In cmd also --attachments "" worked but in powershell it was hard to figure out that one had to use --attachments '""'
- Be able to validate XRechnung/UBL files #337
- ph-schematron aktualisiert, logback zugungsten log4j entfernt #402
- java.util Logging zugungsten log4j entfernt #407
- ZF extended no longer requires deliverydate #411
- Return all BankDetails from parsed CII xml. Closes #408.
- ubl visualization: do not require ubl namespace prefix #416
2.11.0
=======
2024-05-22
- EN16931 validation 1.3.12 codelists v11 #357
- Fonts removed #358
- xrechnungimporter to read from filename, inputstream
- invoice's getSender/getRecipient() now return tradeparty no IZUGFeRDExportableTradeParty
- (first) IBAN is now parsed into sender's getBankDetails
- zugferdimporter to accept xml files
- UBL importer to also parse contacts
- https://github.com/ZUGFeRD/mustangproject/pull/369
- support inputstreams https://github.com/ZUGFeRD/mustangproject/pull/379
- #314 ZUGFeRDInvoiceImporter additional constructur
- add XML cash discount write support (new class, previously only possible for XRechnung, not ZF Extended, using a manually encoded setPaymentTermDescription)
- surrendered to XRechnung 3 compromises, e.g. no longer put gross amount if it does not deviate from net
- be able to programmatically access validation messages https://github.com/ZUGFeRD/mustangproject/pull/382
2.10.0 2.10.0
======= =======
2023-12-30 2023-12-30

View File

@@ -1,258 +1,244 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<parent> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.12.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>Mustang-CLI</artifactId>
<version>2.9.1-SNAPSHOT</version> <name>e-invoices commandline tool, allowing to create(embed), split and validate Factur-X/ZUGFeRD files. Validation
</parent> should also work for XRechnung/CII.
<modelVersion>4.0.0</modelVersion> </name>
<groupId>org.mustangproject</groupId> <packaging>jar</packaging>
<artifactId>Mustang-CLI</artifactId> <version>2.12.0-SNAPSHOT</version>
<name>e-invoices commandline tool, allowing to create(embed), split and validate Factur-X/ZUGFeRD files. Validation should also work for XRechnung/CII. </name> <properties>
<packaging>jar</packaging> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version>2.9.1-SNAPSHOT</version> <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
<properties> <maven.compiler.source>11</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.target>11</maven.compiler.target>
<maven.compiler.compilerVersion>8</maven.compiler.compilerVersion> </properties>
<maven.compiler.source>8</maven.compiler.source> <dependencies>
<maven.compiler.target>8</maven.compiler.target> <dependency>
</properties> <groupId>org.mustangproject</groupId>
<dependencies> <artifactId>validator</artifactId>
<dependency> <version>2.12.0-SNAPSHOT</version>
<groupId>org.mustangproject</groupId> <!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
<artifactId>validator</artifactId> mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
<version>2.9.1-SNAPSHOT</version> -->
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with </dependency>
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true <!-- https://mvnrepository.com/artifact/commons-cli/commons-cli -->
--> <dependency><!-- apache commons cli to parse command line -->
</dependency> <groupId>commons-cli</groupId>
<!-- https://mvnrepository.com/artifact/commons-cli/commons-cli --> <artifactId>commons-cli</artifactId>
<dependency><!-- apache commons cli to parse command line --> <version>1.8.0</version>
<groupId>commons-cli</groupId> </dependency>
<artifactId>commons-cli</artifactId>
<version>1.5.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.riversun</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>bigdoc</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>0.3.0</version> <version>5.10.2</version>
</dependency> <scope>test</scope>
<dependency> </dependency>
<groupId>org.junit.jupiter</groupId> <dependency>
<artifactId>junit-jupiter-api</artifactId> <groupId>org.junit.vintage</groupId>
<version>5.9.1</version> <artifactId>junit-vintage-engine</artifactId>
<scope>test</scope> <version>5.10.2</version>
</dependency> <scope>test</scope>
<dependency> </dependency>
<groupId>org.junit.vintage</groupId> <!-- for directory validation: -->
<artifactId>junit-vintage-engine</artifactId> <dependency>
<version>5.9.1</version> <groupId>org.xmlunit</groupId>
<scope>test</scope> <artifactId>xmlunit-core</artifactId>
</dependency> <version>2.10.0</version>
<!-- for directory validation: --> </dependency>
<dependency> <dependency>
<groupId>org.xmlunit</groupId> <groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId> <artifactId>xmlunit-assertj</artifactId>
<version>2.9.0</version> <version>2.10.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-assertj</artifactId>
<version>2.9.1</version>
</dependency>
<!-- /directory validation: --> <!-- /directory validation: -->
</dependencies> <dependency>
<build> <groupId>jakarta.xml.bind</groupId>
<pluginManagement> <artifactId>jakarta.xml.bind-api</artifactId>
<plugins> <version>4.0.2</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<!-- mvn help:effective-pom will otherwise tell it just defaults
to 2.3.2 - which does not release in the maven repo, and neither shows any
error message :-( -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin> <plugin>
<!-- mvn help:effective-pom will otherwise tell it just defaults <groupId>org.apache.maven.plugins</groupId>
to 2.3.2 - which does not release in the maven repo, and neither shows any <artifactId>maven-surefire-plugin</artifactId>
error message :-( --> <configuration>
<groupId>org.apache.maven.plugins</groupId> <runOrder>alphabetical</runOrder>
<artifactId>maven-release-plugin</artifactId> </configuration>
<version>2.5.3</version>
</plugin> </plugin>
</plugins> <!-- allow getImplementationVersion for the pom.xml -->
</pluginManagement> <plugin>
<plugins> <groupId>org.apache.maven.plugins</groupId>
<plugin> <artifactId>maven-jar-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId> <version>3.4.2</version>
<artifactId>maven-surefire-plugin</artifactId> <configuration>
<configuration> <archive>
<runOrder>alphabetical</runOrder> <manifest>
</configuration> <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</plugin> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
<!-- allow getImplementationVersion for the pom.xml --> </manifest>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestSections>
<manifestSection>
<name>FreeSans.ttf</name>
<manifestEntries>
<Content-Type>font/ttf</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>FreeSerif.ttf</name>
<manifestEntries>
<Content-Type>application/x-font</Content-Type>
</manifestEntries>
</manifestSection>
<manifestSection>
<name>Times-Bold.ttf</name>
<manifestEntries>
<Content-Type>application/x-font</Content-Type>
</manifestEntries>
</manifestSection>
</manifestSections>
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version> <version>2.3.2</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>
<addClasspath>true</addClasspath> <addClasspath>true</addClasspath>
<mainClass>org.mustangproject.commandline.main</mainClass> <mainClass>org.mustangproject.commandline.main</mainClass>
</manifest> </manifest>
</archive> </archive>
<descriptorRefs> <descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </descriptorRefs>
<!-- http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven <!-- http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven
mvn clean compile assembly:single --> mvn clean compile assembly:single -->
<!-- or whatever version you use --> <!-- or whatever version you use -->
<source>8</source> <source>11</source>
<target>8</target> <target>11</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version> <version>3.5.3</version>
<configuration> <configuration>
<transformers> <transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <transformer
<mainClass>org.mustangproject.commandline.Main</mainClass> implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
</transformer> <mainClass>org.mustangproject.commandline.Main</mainClass>
</transformers> </transformer>
<minimizeJar>false</minimizeJar> </transformers>
<filters> <minimizeJar>false</minimizeJar>
<filter> <filters>
<artifact>*:*</artifact> <filter>
<excludes> <artifact>*:*</artifact>
<exclude>LICENSE</exclude> <excludes>
<exclude>NOTICE</exclude> <exclude>LICENSE</exclude>
<exclude>META-INF/*.SF</exclude> <exclude>NOTICE</exclude>
<exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.RSA</exclude> <exclude>META-INF/*.DSA</exclude>
</excludes> <exclude>META-INF/*.RSA</exclude>
</filter> </excludes>
<filter> </filter>
<artifact>log4j:log4j</artifact> <filter>
<includes> <artifact>log4j:log4j</artifact>
<include>**</include> <includes>
</includes> <include>**</include>
</filter> </includes>
<filter> </filter>
<artifact>commons-logging:commons-logging</artifact> <filter>
<includes> <artifact>commons-logging:commons-logging</artifact>
<include>**</include> <includes>
</includes> <include>**</include>
</filter> </includes>
<filter> </filter>
<artifact>com.sun.xml.bind:jaxb-impl</artifact> <filter>
<includes> <artifact>com.sun.xml.bind:jaxb-impl</artifact>
<include>**</include> <includes>
</includes> <include>**</include>
</filter> </includes>
</filters> </filter>
</configuration> </filters>
<executions> </configuration>
<execution> <executions>
<phase>package</phase> <execution>
<goals> <phase>package</phase>
<goal>shade</goal> <goals>
</goals> <goal>shade</goal>
<configuration> </goals>
<artifactSet> <configuration>
<excludes /> <artifactSet>
</artifactSet> <excludes/>
</configuration> </artifactSet>
</execution> </configuration>
</executions> </execution>
</plugin> </executions>
</plugins> </plugin>
</build> </plugins>
<profiles> </build>
<profile> <!-- enforce building binaries with Java 1.8 for Maven Central, otherwise using them e.g. as jar will <profiles>
<profile> <!-- enforce building binaries with Java 1.8 for Maven Central, otherwise using them e.g. as jar will
throw a version exception, triggered automatically on mvn release:release (hopefully) and requires an according throw a version exception, triggered automatically on mvn release:release (hopefully) and requires an according
~/.m2/toolchains.xml file, @see doc/development_documentation.md --> ~/.m2/toolchains.xml file, @see doc/development_documentation.md -->
<id>release-sign-artifacts</id> <id>release-sign-artifacts</id>
<activation> <activation>
<property> <property>
<name>performRelease</name> <name>performRelease</name>
<value>true</value> <value>true</value>
</property> </property>
</activation> </activation>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId> <artifactId>maven-toolchains-plugin</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
<goal>toolchain</goal> <goal>toolchain</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
<configuration> <configuration>
<toolchains> <toolchains>
<jdk> <jdk>
<version>8</version> <version>11</version>
<vendor>adopt</vendor> <vendor>adopt</vendor>
</jdk> </jdk>
</toolchains> </toolchains>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>
</profiles> </profiles>
<licenses> <licenses>
<license> <license>
<name>Apache License, Version 2.0</name> <name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution> <distribution>repo</distribution>
<comments>A business-friendly OSS license</comments> <comments>A business-friendly OSS license</comments>
</license> </license>
</licenses> </licenses>
<developers> <developers>
<developer> <developer>
<name>Jochen Stärk</name> <name>Jochen Stärk</name>
<email>jstaerk@usegroup.de</email> <email>jstaerk@usegroup.de</email>
<roles> <roles>
<role>architect</role> <role>architect</role>
<role>developer</role> <role>developer</role>
</roles> </roles>
</developer> </developer>
</developers> </developers>
</project> </project>

View File

@@ -36,64 +36,66 @@ import java.util.ArrayList;
public class Main { public class Main {
private static org.slf4j.Logger LOGGER; // log output private static org.slf4j.Logger LOGGER; // log output
private static void printUsage() { private static void printUsage() {
System.err.println(getUsage()); System.err.println(getUsage());
} }
private static String getUsage() { private static String getUsage() {
return "Usage: --action metrics|combine|extract|a3only|ubl|validate|validateExpectInvalid|validateExpectValid|visualize [-d,--directory] [-l,--listfromstdin] [-i,--ignore fileextension, PDF/A errors] [--disable-file-logging] | [-h,--help] \r\n" return "Usage: --action metrics|combine|extract|a3only|ubl|validate|validateExpectInvalid|validateExpectValid|visualize [-d,--directory] [-l,--listfromstdin] [-i,--ignore fileextension, PDF/A errors] [--disable-file-logging] | [-h,--help] \r\n"
+ " --action license display open source license and notice\n" + " --action license display open source license and notice\n"
+ " --action metrics\n" + " --action metrics\n"
+ " -d, --directory count ZUGFeRD files in directory to be scanned\n" + " -d, --directory count ZUGFeRD files in directory to be scanned\n"
+ " If it is a directory, it will recurse.\n" + " If it is a directory, it will recurse.\n"
+ " -l, --listfromstdin count ZUGFeRD files from a list of linefeed separated files on runtime.\n" + " -l, --listfromstdin count ZUGFeRD files from a list of linefeed separated files on runtime.\n"
+ " It will start once a blank line has been entered.\n" + "\n" + " It will start once a blank line has been entered.\n" + "\n"
+ " Additional parameter for both count operations\n" + " Additional parameter for both count operations\n"
+ " [-i, --ignorefileextension] Check for all files (*.*) instead of PDF files only (*.pdf) in metrics, ignore PDF/A input file errors in combine\n" + " [-i, --ignorefileextension] Check for all files (*.*) instead of PDF files only (*.pdf) in metrics, ignore PDF/A input file errors in combine\n"
+ " [--disable-file-logging] disable logging to file.\n" + " [--disable-file-logging] disable logging to file.\n"
+ " --action extract extract Factur-X PDF to XML file\n" + " --action extract extract Factur-X PDF to XML file\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--source <filename>]: set input PDF file\n" + " [--source <filename>]: set input PDF file\n"
+ " [--out <filename>]: set output XML file\n" + " [--out <filename>]: set output XML file\n"
+ " --action a3only upgrade from PDF/A1 to A3 only (no ZUGFeRD data attached)\n" + " --action a3only upgrade from PDF/A1 to A3 only (no ZUGFeRD data attached)\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--source <filename>]: set input PDF file\n" + " [--source <filename>]: set input PDF file\n"
+ " [--out <filename>]: set output PDF file\n" + " [--out <filename>]: set output PDF file\n"
+ " --action combine combine XML and PDF file to Factur-X PDF file\n" + " --action combine combine XML and PDF file to Factur-X PDF file\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--source <filename>]: set input PDF file\n" + " [--source <filename>]: set input PDF file\n"
+ " [--source-xml <filename>]: set input XML file\n" + " [--source-xml <filename>]: set input XML file\n"
+ " [--out <filename>]: set output PDF file\n" + " [--out <filename>]: set output PDF file\n"
+ " [--format <fx|zf|ox|da>]: set Factur-X, ZUGFeRD, Order-X or Cross Industry Despatch Advice\n" + " [--format <fx|zf|ox|da>]: set Factur-X, ZUGFeRD, Order-X or Cross Industry Despatch Advice\n"
+ " [--version <1|2>]: set ZUGFeRD version\n" + " [--version <1|2>]: set ZUGFeRD version\n"
+ " [--profile <...>]: set ZUGFeRD profile\n" + " [--profile <...>]: set ZUGFeRD profile\n"
+ " For ZUGFeRD v1 or Order-X: <B>ASIC, <C>OMFORT or EX<T>ENDED\n" + " For ZUGFeRD v1 or Order-X: <B>ASIC, <C>OMFORT or EX<T>ENDED\n"
+ " For ZUGFeRD v2: <M>INIMUM, BASIC <W>L, <B>ASIC, <C>IUS, <E>N16931, <X>Rechnung, EX<T>ENDED\n" + " For ZUGFeRD v2: <M>INIMUM, BASIC <W>L, <B>ASIC, <C>IUS, <E>N16931, <X>Rechnung, EX<T>ENDED\n"
+ " [--attachments <filenames>]: list of file attachments (passing a single empty file name prevents prompting)\n" + " [--attachments <filenames>]: list of file attachments (passing a single empty file name prevents prompting)\n"
+ " --action ubl convert UN/CEFACT 2016b CII XML to UBL XML\n" + " [--no-additional-attachments]: prevent prompting for attachments\n"
+ " [--source <filename>]: set input XML file\n" + " --action ubl convert UN/CEFACT 2016b CII XML to UBL XML\n"
+ " [--out <filename>]: set output XML file\n" + " [--source <filename>]: set input XML file\n"
+ " --action upgrade upgrade ZUGFeRD XML to ZUGFeRD 2 XML\n" + " [--out <filename>]: set output XML file\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " --action upgrade upgrade ZUGFeRD XML to ZUGFeRD 2 XML\n"
+ " [--source <filename>]: set input XML ZUGFeRD 1 file\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " [--out <filename>]: set output XML ZUGFeRD 2 file\n" + " [--source <filename>]: set input XML ZUGFeRD 1 file\n"
+ " --action validate validate XML or PDF file \n" + " [--out <filename>]: set output XML ZUGFeRD 2 file\n"
+ " [--no-notices]: refrain from reporting notices\n" + " --action validate validate XML or PDF file \n"
+ " [--logAppend <text>]: text to be added to log line\n" + " [--no-notices]: refrain from reporting notices\n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " [--logAppend <text>]: text to be added to log line\n"
+ " [--source <filename>]: input PDF or XML file\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " --action validateExpectInvalid validate directory expecting negative results \n" + " [--source <filename>]: input PDF or XML file\n"
+ " [--no-notices]: refrain from reporting notices\n" + " --action validateExpectInvalid validate directory expecting negative results \n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " [--no-notices]: refrain from reporting notices\n"
+ " -d, --directory to check recursively\n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " --action validateExpectValid validate directory expecting positive results \n" + " -d, --directory to check recursively\n"
+ " [--no-notices]: refrain from reporting notices\n" + " --action validateExpectValid validate directory expecting positive results \n"
+ " Additional parameters (optional - user will be prompted if not defined)\n" + " [--no-notices]: refrain from reporting notices\n"
+ " -d, --directory to check recursively \n" + " Additional parameters (optional - user will be prompted if not defined)\n"
+ " --action visualize convert XML to HTML \n" + " -d, --directory to check recursively \n"
+ " [--language <lang>]: set output lang (en, fr or de)\n" + " --action visualize convert XML to HTML \n"
+ " [--source <filename>]: set input XML file\n" + " [--language <lang>]: set output lang (en, fr or de)\n"
+ " [--out <filename>]: set output HTML file\n" + " [--source <filename>]: set input XML file\n"
+ " [--out <filename>]: set output HTML file\n"
+ " --action pdf convert XML to PDF \n" + " --action pdf convert XML to PDF \n"
+ " [--source <filename>]: set input XML file\n" + " [--source <filename>]: set input XML file\n"
+ " [--out <filename>]: set output PDF file\n" + " [--out <filename>]: set output PDF file\n"
@@ -330,32 +332,36 @@ public class Main {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
CommandLine cmd; CommandLine cmd;
CommandLineParser parser = new BasicParser(); CommandLineParser parser = new DefaultParser();
// create Options object // create Options object
Options options = new Options(); Options options = new Options();
options.addOption(new Option("h", "help", false, "display usage")); options.addOption(new Option("h", "help", false, "display usage"));
options.addOption(new Option("a", "action", true, "which action to perform")); options.addOption(new Option("a", "action", true, "which action to perform"));
options.addOption(new Option("f", "format", true, "which format to output")); options.addOption(new Option("f", "format", true, "which format to output"));
options.addOption(new Option("", "version", true, "which version of the standard to use")); options.addOption(new Option("version", "version", true, "which version of the standard to use"));
options.addOption(new Option("", "profile", true, "which profile of the standard to use")); options.addOption(new Option("profile", "profile", true, "which profile of the standard to use"));
Option attachmentOpt = new Option("", "attachments", true, "File attachments"); options.addOption(new Option("no-additional-attachments", "no-additional-attachments", false, "prevent prompting for attachments"));
Option attachmentOpt = new Option("attachments", "attachments", true, "File attachments");
attachmentOpt.setValueSeparator(','); attachmentOpt.setValueSeparator(',');
attachmentOpt.setArgs(Option.UNLIMITED_VALUES); attachmentOpt.setArgs(Option.UNLIMITED_VALUES);
options.addOption(attachmentOpt); options.addOption(attachmentOpt);
options.addOption(new Option("", "source",true, "which source file to use")); options.addOption(new Option("source", "source", true, "which source file to use"));
options.addOption(new Option("", "source-xml",true, "which source file to use")); options.addOption(new Option("source-xml", "source-xml", true, "which source file to use"));
options.addOption(new Option("", "language",true, "output language (en, de or fr)")); options.addOption(new Option("language", "language", true, "output language (en, de or fr)"));
options.addOption(new Option("", "out",true, "which output file to write to")); options.addOption(new Option("out", "out", true, "which output file to write to"));
options.addOption(new Option("", "no-notices",false, "suppress non-fatal errors")); options.addOption(new Option("no-notices", "no-notices", false, "suppress non-fatal errors"));
options.addOption(new Option("", "logAppend",true, "freeform text to be appended to log messages")); options.addOption(new Option("logAppend", "logAppend", true, "freeform text to be appended to log messages"));
options.addOption(new Option("", "disable-file-logging", false, "suppress logging to file")); options.addOption(new Option("disable-file-logging", "disable-file-logging", false, "suppress logging to file"));
options.addOption(new Option("d", "directory",true, "which directory to operate on")); options.addOption(new Option("d", "directory", true, "which directory to operate on"));
options.addOption(new Option("i", "ignorefileextension",false, "ignore non-matching file extensions")); options.addOption(new Option("i", "ignorefileextension", false, "ignore non-matching file extensions"));
options.addOption(new Option("l", "listfromstdin",false, "take list of files from commandline")); options.addOption(new Option("l", "listfromstdin", false, "take list of files from commandline"));
boolean optionsRecognized=false;
boolean optionsRecognized = false;
String action = ""; String action = "";
Boolean disableFileLogging = false;
Boolean disableFileLogging = false;
try { try {
cmd = parser.parse(options, args); cmd = parser.parse(options, args);
@@ -364,7 +370,8 @@ public class Main {
String directoryName = cmd.getOptionValue("directory"); String directoryName = cmd.getOptionValue("directory");
Boolean filesFromStdIn = cmd.hasOption("listfromstdin");//((Number)cmdLine.getParsedOptionValue("integer-option")).intValue(); Boolean filesFromStdIn = cmd.hasOption("listfromstdin");//((Number)cmdLine.getParsedOptionValue("integer-option")).intValue();
Boolean ignoreFileExt = cmd.hasOption("ignorefileextension"); Boolean ignoreFileExt = cmd.hasOption("ignorefileextension");
Boolean helpRequested = cmd.hasOption("help") || ((action!=null)&&(action.equals("help"))); Boolean noAttachments = cmd.hasOption("no-additional-attachments");
Boolean helpRequested = cmd.hasOption("help") || ((action != null) && (action.equals("help")));
disableFileLogging = cmd.hasOption("disable-file-logging"); disableFileLogging = cmd.hasOption("disable-file-logging");
String sourceName = cmd.getOptionValue("source"); String sourceName = cmd.getOptionValue("source");
@@ -380,14 +387,12 @@ public class Main {
String[] attachmentFilenames = cmd.hasOption("attachments") ? cmd.getOptionValues("attachments") : null; String[] attachmentFilenames = cmd.hasOption("attachments") ? cmd.getOptionValues("attachments") : null;
ArrayList<FileAttachment> attachments = new ArrayList<>();
ArrayList<FileAttachment> attachments=new ArrayList <>();
/* /*
setting system property to disable FILE appender and setting system property to disable FILE appender and
suppress creation of files and folders. suppress creation of files and folders.
*/ */
System.setProperty("FILE_APPENDER_ENABLED", ((Boolean)(disableFileLogging==false)).toString()); System.setProperty("FILE_APPENDER_ENABLED", ((Boolean) (disableFileLogging == false)).toString());
LOGGER = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); LOGGER = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
@@ -401,7 +406,7 @@ public class Main {
performMetrics(directoryName, filesFromStdIn, ignoreFileExt); performMetrics(directoryName, filesFromStdIn, ignoreFileExt);
optionsRecognized = true; optionsRecognized = true;
} else if ((action != null) && (action.equals("combine"))) { } else if ((action != null) && (action.equals("combine"))) {
performCombine(sourceName, sourceXMLName, outName, format, zugferdVersion, zugferdProfile, ignoreFileExt, attachmentFilenames, attachments); performCombine(sourceName, sourceXMLName, outName, format, zugferdVersion, zugferdProfile, ignoreFileExt, attachmentFilenames, attachments, noAttachments);
optionsRecognized = true; optionsRecognized = true;
} else if ((action != null) && (action.equals("extract"))) { } else if ((action != null) && (action.equals("extract"))) {
performExtract(sourceName, outName); performExtract(sourceName, outName);
@@ -422,7 +427,7 @@ public class Main {
performUBL(sourceName, outName); performUBL(sourceName, outName);
optionsRecognized = true; optionsRecognized = true;
} else if ((action != null) && (action.equals("validate"))) { } else if ((action != null) && (action.equals("validate"))) {
optionsRecognized = performValidate(sourceName, noNotices != null && noNotices, cmd.getOptionValue("logAppend")); optionsRecognized = performValidate(sourceName, noNotices, cmd.getOptionValue("logAppend"));
} else if ((action != null) && (action.equals("validateExpectValid"))) { } else if ((action != null) && (action.equals("validateExpectValid"))) {
optionsRecognized = performValidateExpect(true, directoryName); optionsRecognized = performValidateExpect(true, directoryName);
} else if ((action != null) && (action.equals("validateExpectInvalid"))) { } else if ((action != null) && (action.equals("validateExpectInvalid"))) {
@@ -439,7 +444,11 @@ public class Main {
System.exit(2); System.exit(2);
} }
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e); if (LOGGER != null) {
LOGGER.error(e.getMessage(), e);
} else {
System.err.println(e.getMessage());
}
System.exit(-1); System.exit(-1);
} }
@@ -510,10 +519,11 @@ public class Main {
ensureFileNotExists(outName); ensureFileNotExists(outName);
// All params are good! continue... // All params are good! continue...
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().convertOnly().load(pdfName); try (ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1()) {
ze.convertOnly().load(pdfName);
ze.export(outName); ze.export(outName);
System.out.println("Written to " + outName); System.out.println("Written to " + outName);
}
} }
private static void performExtract(String pdfName, String xmlName) throws IOException { private static void performExtract(String pdfName, String xmlName) throws IOException {
@@ -546,7 +556,7 @@ public class Main {
} }
private static void performCombine(String pdfName, String xmlName, String outName, String format, String zfVersion, private static void performCombine(String pdfName, String xmlName, String outName, String format, String zfVersion,
String zfProfile, Boolean ignoreInputErrors, String[] attachmentFilenames, ArrayList<FileAttachment> attachments) throws Exception { String zfProfile, Boolean ignoreInputErrors, String[] attachmentFilenames, ArrayList<FileAttachment> attachments, Boolean noAttachments) throws Exception {
/* /*
* ZUGFeRDExporter ze= new ZUGFeRDExporterFromA1Factory() * ZUGFeRDExporter ze= new ZUGFeRDExporterFromA1Factory()
* .setProducer("toecount") .setCreator(System.getProperty("user.name")) * .setProducer("toecount") .setCreator(System.getProperty("user.name"))
@@ -576,12 +586,14 @@ public class Main {
if (attachmentFilenames == null) { if (attachmentFilenames == null) {
byte attachmentContents[] = null; byte attachmentContents[] = null;
String attachmentFilename, attachmentMime, attachmentDescription; String attachmentFilename, attachmentMime;
attachmentFilename = getFilenameFromUser("Additional file attachments filename (empty for none)", "", "pdf", true, false); if (!noAttachments) {
if (attachmentFilename.length() != 0) { attachmentFilename = getFilenameFromUser("Additional file attachments filename (empty for none)", "", "pdf", true, false);
attachmentContents = Files.readAllBytes(Paths.get(attachmentFilename)); if (attachmentFilename.length() != 0) {
attachmentMime = Files.probeContentType(Paths.get(attachmentFilename)); attachmentContents = Files.readAllBytes(Paths.get(attachmentFilename));
attachments.add(new FileAttachment(attachmentFilename, attachmentMime, "Data", attachmentContents)); attachmentMime = Files.probeContentType(Paths.get(attachmentFilename));
attachments.add(new FileAttachment(attachmentFilename, attachmentMime, "Data", attachmentContents));
}
} }
} else { } else {
for (int i = 0; i < attachmentFilenames.length; i++) { for (int i = 0; i < attachmentFilenames.length; i++) {
@@ -597,7 +609,7 @@ public class Main {
if (format == null) { if (format == null) {
try { try {
format = getStringFromUser("Format (fx=Factur-X, zf=ZUGFeRD, ox=Order-X, da=Cross Industry Despatch Advice)", "zf", "fx|zf|ox|da"); format = getStringFromUser("Format (fx=Factur-X, zf=ZUGFeRD, ox=Order-X, da=Cross Industry Despatch Advice)", "fx", "fx|zf|ox|da");
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
} }
@@ -607,7 +619,7 @@ public class Main {
if (zfVersion == null) { if (zfVersion == null) {
try { try {
zfVersion = getStringFromUser("Version (1 or 2)", Integer.toString(ZUGFeRDExporterFromA3.DefaultZUGFeRDVersion), "1|2"); zfVersion = getStringFromUser("Version (1 or 2)", "1", "1|2");//fx default version is 1
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
} }
@@ -691,41 +703,33 @@ public class Main {
IZUGFeRDExporter ze = null; IZUGFeRDExporter ze = null;
// All params are good! continue... // All params are good! continue...
if (format.equals("ox")) { if (format.equals("ox")) {
ze = new OXExporterFromA1().setProducer("Mustang-cli") ze = new OXExporterFromA1();
.setZUGFeRDVersion(zfIntVersion)
.setCreator(System.getProperty("user.name")).setProfile(zfConformanceLevelProfile);
if (ignoreInputErrors) { if (ignoreInputErrors) {
((OXExporterFromA1) ze).ignorePDFAErrors(); ((OXExporterFromA1) ze).ignorePDFAErrors();
} }
} else if (format.equals("da")) { } else if (format.equals("da")) {
ze = new DXExporterFromA1().setProducer("Mustang-cli") ze = new DXExporterFromA1();
.setZUGFeRDVersion(zfIntVersion)
.setCreator(System.getProperty("user.name")).setProfile(zfConformanceLevelProfile);
if (ignoreInputErrors) { if (ignoreInputErrors) {
((DXExporterFromA1) ze).ignorePDFAErrors(); ((DXExporterFromA1) ze).ignorePDFAErrors();
} }
} else { } else {
if (format.equals("fx")) { if (format.equals("fx")) {
zfIntVersion=2;// actually we are talking of generation, not version zfIntVersion = 2;// actually we are talking of generation, not version
// so even if someone correctly requested factur-x 1 internally we call it zugferd 2 :-( // so even if someone correctly requested factur-x 1 internally we call it zugferd 2 :-(
} }
ze = new ZUGFeRDExporterFromPDFA().load(pdfName); ze = new ZUGFeRDExporterFromPDFA();
ze.setProducer("Mustang-cli")
.setZUGFeRDVersion(zfIntVersion)
.setCreator(System.getProperty("user.name")).setProfile(zfConformanceLevelProfile);
if (ignoreInputErrors) { if (ignoreInputErrors) {
((ZUGFeRDExporterFromA1) ze).ignorePDFAErrors(); ((ZUGFeRDExporterFromPDFA) ze).ignorePDFAErrors();
} }
} }
for (FileAttachment attachment : attachments) { for (FileAttachment attachment : attachments) {
((ZUGFeRDExporterFromA3) ze).attachFile(attachment.getFilename(), attachment.getData(), attachment.getMimetype(), attachment.getRelation()); ((ZUGFeRDExporterFromA3) ze).attachFile(attachment.getFilename(), attachment.getData(), attachment.getMimetype(), attachment.getRelation());
} }
// ze = ze.load(pdfName); ze.load(pdfName);
ze.setProducer("Mustang-cli")
.setZUGFeRDVersion(zfIntVersion)
.setCreator(System.getProperty("user.name")).setProfile(zfConformanceLevelProfile);
if (format.equals("zf")) { if (format.equals("zf")) {
ze.disableFacturX(); ze.disableFacturX();
@@ -737,7 +741,9 @@ public class Main {
System.out.println("Written to " + outName); System.out.println("Written to " + outName);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e); if (LOGGER != null) {
LOGGER.error(e.getMessage(), e);
}
System.err.println(e.getMessage()); System.err.println(e.getMessage());
} }
} }
@@ -837,14 +843,9 @@ public class Main {
} else { } else {
zvi.toPDF(sourceName, outName); zvi.toPDF(sourceName, outName);
} }
} catch (FileNotFoundException e) { } catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} catch (UnsupportedEncodingException e) {
LOGGER.error(e.getMessage(), e);
} catch (TransformerException e) {
LOGGER.error(e.getMessage(), e);
} catch (IOException e) {
LOGGER.error(e.getMessage(), e); LOGGER.error(e.getMessage(), e);
System.err.println(e.getMessage());
} }
System.out.println("Written to " + outName); System.out.println("Written to " + outName);
@@ -871,11 +872,8 @@ public class Main {
* @throws Exception e.g. if the specified resource does not exist at the specified location * @throws Exception e.g. if the specified resource does not exist at the specified location
*/ */
static public String ExportResource(String resourceName) throws Exception { static public String ExportResource(String resourceName) throws Exception {
InputStream stream = null;
OutputStream resStreamOut = null;
String jarFolder; String jarFolder;
try { try (InputStream stream = Main.class.getResourceAsStream(resourceName)) {//note that each / is a directory down in the "jar tree" been the jar the root of the tree
stream = Main.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
if (stream == null) { if (stream == null) {
throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
} }
@@ -883,15 +881,11 @@ public class Main {
int readBytes; int readBytes;
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
jarFolder = System.getProperty("user.dir"); jarFolder = System.getProperty("user.dir");
resStreamOut = new FileOutputStream(jarFolder + resourceName); try (FileOutputStream resStreamOut = new FileOutputStream(jarFolder + resourceName)) {
while ((readBytes = stream.read(buffer)) > 0) { while ((readBytes = stream.read(buffer)) > 0) {
resStreamOut.write(buffer, 0, readBytes); resStreamOut.write(buffer, 0, readBytes);
}
} }
} catch (Exception ex) {
throw ex;
} finally {
stream.close();
resStreamOut.close();
} }
return jarFolder + resourceName; return jarFolder + resourceName;
@@ -913,7 +907,8 @@ public class Main {
if (fileName == null) if (fileName == null)
return false; return false;
File f = new File(fileName); File f = new File(fileName);
return f.exists(); // "exists" also returns true for directories
return f.isFile();
} }
} }

View File

@@ -14,9 +14,6 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.xmlunit.builder.Input;
import org.xmlunit.xpath.JAXPXPathEngine;
import org.xmlunit.xpath.XPathEngine;
import org.mustangproject.validator.ZUGFeRDValidator; import org.mustangproject.validator.ZUGFeRDValidator;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
@@ -50,25 +47,23 @@ public class ValidatorFileWalker
Date date = new Date(); Date date = new Date();
String expectedString="valid"; String expectedString="valid";
if (!expectValid) { if (!expectValid) {
expectedString="invalid"; expectedString="invalid";
} }
if ((attr!=null)&&(attr.isRegularFile())) { if ((attr!=null)&&(attr.isRegularFile())) {
if (matcher.matches(file.getFileName())) { if (matcher.matches(file.getFileName())) {
boolean thisResultValid=true;
String thisResultString=" valid"; String thisResultString=" valid";
try { try {
assertThat(zul.validate(file.toAbsolutePath().toString())).valueByXPath("/validation/summary/@status") assertThat(zul.validate(file.toAbsolutePath().toString())).valueByXPath("/validation/summary/@status")
.asString() .asString()
.isEqualTo(expectedString); .isEqualTo(expectedString);
} catch (AssertionError ae) { } catch (AssertionError ae) {
thisResultValid=false; thisResultString="invalid";
thisResultString="invalid"; allValid=false;
allValid=false; }
} LOGGER.info(String.format("\n@%s Testing file %d: %s (%s)", dateFormat.format(date), fileCount++, thisResultString, file));
LOGGER.info(String.format("\n@%s Testing file %d: %s (%s)", dateFormat.format(date), fileCount++, thisResultString, file));
} }
} }
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }

View File

@@ -4,33 +4,53 @@ Mustangproject
Source code repository for the [Mustang project](http://www.mustangproject.org/) open source java PDF invoice metadata library in ZUGFeRD format. Source code repository for the [Mustang project](http://www.mustangproject.org/) open source java PDF invoice metadata library in ZUGFeRD format.
License Build
----- -----
Subject to the Apache license http://www.apache.org/licenses/LICENSE-2.0.html These are the recommended dependencies for the project:
Running - OpenJDK 21.0.2 2024-01-16
----- - Apache Maven 3.9.6
This project requires Maven to run. Build project with "mvn clean install". This will build the project, test it and install the artifacts to local cache. After that the mustang jar can be used. You can build the project with:
More information in [the mustang documentation](https://github.com/ZUGFeRD/mustangproject/blob/master/doc/ZugferdDev.en.pdf?raw=true). ```shell
mvn clean install
```
This will run the tests, build the project artefacts, and install the artifacts to the local
cache. After that the mustang JAR can be used.
More information on how to develop **on** mustang:
- [Developer documentation](https://github.com/ZUGFeRD/mustangproject/blob/master/doc/development_documentation.md)
Usage Usage
----- -----
If you setup a Maven project, you can grab the artifacts using If you set up a Maven project, you can reference the mustang artifact like this:
```xml ```xml
<dependency> <dependency>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>library</artifactId> <artifactId>library</artifactId>
<version>2.10.0</version> <version>2.12.0</version>
</dependency> </dependency>
``` ```
Further docs on how to develop **with** mustang:
- [ZugferdDev.en.pdf](https://github.com/ZUGFeRD/mustangproject/blob/master/doc/ZugferdDev.en.pdf?raw=true): The English mustang user documentation.
- [ZugferdDev.de.pdf](https://github.com/ZUGFeRD/mustangproject/blob/master/doc/ZugferdDev.de.pdf?raw=true): The German mustang user documentation.
- [Usage examples](https://www.mustangproject.org/use/): Read and write electronic invoices.
- [Mustang classes](https://www.mustangproject.org/invoice-class/): Using the mustang classes.
Contact Contact
----- -----
Developer: Jochen Stärk. For questions please contact Jochen at jstaerk [at] usegroup.de Developer: Jochen Stärk. For questions please contact Jochen at jstaerk [at] usegroup.de
License
-----
Subject to the [Apache-2.0 license](http://www.apache.org/licenses/LICENSE-2.0.html).

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -1,26 +0,0 @@
= Demo Documentation Component B
// URLs
:url-project: https://antora.org
:url-docs: https://docs.antora.org
:url-org: https://gitlab.com/antora
:url-group: {url-org}/demo
:url-demo-site: https://antora.gitlab.io/demo/docs-site
:url-site-readme: {url-group}/docs-site/blob/master/README.adoc
:url-opendevise: https://opendevise.com
Component B is an example of a documentation component.
It is used by the {url-demo-site}[Antora Demo site].
The playbook and instructions for the Demo site are located in the {url-site-readme}[Demo site playbook project].
== Resources
* {url-project}[Antora Project Site]
* {url-docs}[Antora Docs]
* {url-org}[Antora Repositories]
== Copyright and License
Copyright (C) 2017-present by OpenDevise Inc. and the individual contributors to Antora.
Use of this software is granted under the terms of the https://www.mozilla.org/en-US/MPL/2.0/[Mozilla Public License Version 2.0] (MPL-2.0).
See link:LICENSE[] to find the full license text.

View File

@@ -1,21 +0,0 @@
# This file represents "software".
# This file helps show that documentation files can be in the same repository as software.
rescue => ex
begin
context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
if ex.respond_to? :exception
# The original message must be explicitely preserved when wrapping a Ruby exception
wrapped_ex = ex.exception %(#{context} - #{ex.message})
# JRuby automatically sets backtrace, but not MRI
wrapped_ex.set_backtrace ex.backtrace
else
# Likely a Java exception class
wrapped_ex = ex.class.new context, ex
wrapped_ex.stack_trace = ex.stack_trace
end
rescue
wrapped_ex = ex
end
raise wrapped_ex
end

View File

@@ -1,7 +0,0 @@
name: mustang
title: Mustang Library
version: 2.6.1
prerelease: -beta.1
nav:
- modules/ROOT/nav.adoc
- modules/module-one/nav.adoc

View File

@@ -1 +0,0 @@
* xref:index.adoc[]

View File

@@ -1,33 +0,0 @@
= Welcome to Component B!
:navtitle: Welcome
This is the automatic start page for version *{page-component-display-version}* of Component B.
Component B is one of two documentation components in the Antora Demo.
The other docs component, Component A, can be accessed using the component selector menu (aka component drawer) at the bottom of the menu on the left side of the page.
== Page source
This page is sourced from the AsciiDoc file named [.path]_index.adoc_ that is located at [.path]_demo-component-b/docs/modules/ROOT/pages_.
=== Why is this the home page of Component B?
This page is automatically used as the start page of Component B because it is stored in the ROOT module and named [.path]_index.adoc_.
=== Why is this the home page of the Antora Demo site?
This page is used as the home page for the Antora Demo because it is assigned as the site `start_page` in the Demo site's playbook file [.path]_site.yml_.
== Cross reference syntax to target this page
To create a cross reference *to this page from another page in the ROOT module of Component B*, the xref syntax would be `\xref:index.adoc[]`.
To create a cross reference *to this page from a page in Module One of Component B*, the xref syntax would be `\xref:ROOT:index.adoc[]`.
=== Always target the latest version of this page
To create a cross reference *to the latest version of this page from a page in Component A*, the xref syntax would be `\xref:component-b::index.adoc[]`.
=== Target a specific version of this page
To create a cross reference *to version 1.0 of this page from a page in Component A*, the xref syntax would be `\xref:1.0@component-b::index.adoc[]`.

View File

@@ -1 +0,0 @@
* xref:overview.adoc[]

View File

@@ -1,21 +0,0 @@
= Module One Overview
This is version *{page-component-display-version}* of Module One in Component B.
== Page source
This page is sourced from [.path]_demo-component-b/docs/modules/module-one/pages/overview.adoc_.
== Cross reference syntax to target this page
To create a cross reference *to this page from another page in Module One*, the xref syntax would be `\xref:overview.adoc[]`.
To create a cross reference *to this page from a page in the ROOT module of Component B*, the xref syntax would be `\xref:module-one:overview.adoc[]`.
=== Always target the latest version of this page
To create a cross reference *to the latest version of this page from a page in Component A*, the xref syntax would be `\xref:component-b:module-one:overview.adoc[]`.
=== Target a specific version of this page
To create a cross reference *to version 1.0 of this page from a page in Component A*, the xref syntax would be `\xref:1.0@component-b:module-one:overview.adoc[]`.

View File

@@ -1,5 +1,5 @@
## General approach ## Typical process
1. build 1. build
@@ -29,6 +29,18 @@ If you do a pull request, please do a feature branch, e.g. if you are working on
Most of mustang is a library, adding (autmated junit) test cases is often not only the most sustainable but also the fastest way to see if new/changed functionality works. If something is changed so that old test cases break on purpose please do not just remove them but take the time to fix the test cases Most of mustang is a library, adding (autmated junit) test cases is often not only the most sustainable but also the fastest way to see if new/changed functionality works. If something is changed so that old test cases break on purpose please do not just remove them but take the time to fix the test cases
## Typical workflow
If e.g. new elements or attributes are added, they are often added
* in the object so that a developer can use them
* in the interface so that a old fashioned developer could use them as well
* in the pullprovider so that it actually finds it's way into the XML
* in at least one test (the ...edgeTest are supposed to handle edge cases, ~all bells and whistles, maybe it fits there), after the test has been run this should at least once be
* validated. If that works one can start implementing the
* reading part (along with tests), then it needs to be
* documented e.g. on the homepage and
* communicated, at the very least by mentioning it in the history.md
## Architecture ## Architecture
Mustang contains a library to read/write e-invoices, Mustang contains a library to read/write e-invoices,
@@ -155,16 +167,16 @@ In .m2 also need a toolchains.xml which defines a Sun JDK 1.8 target like the fo
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<toolchains> <toolchains>
<!-- JDK toolchains --> <!-- JDK toolchains -->
<toolchain> <toolchain>
<type>jdk</type> <type>jdk</type>
<provides> <provides>
<version>8</version> <version>11</version>
<vendor>adopt</vendor> <vendor>adopt</vendor>
</provides> </provides>
<configuration> <configuration>
<jdkHome>C:\Program Files\Eclipse Adoptium\jdk-8.0.352.8-hotspot</jdkHome> <jdkHome>C:\Program Files\Eclipse Adoptium\jdk-11.0.23.9-hotspot</jdkHome>
</configuration> </configuration>
</toolchain> </toolchain>
</toolchains> </toolchains>
``` ```

183
doc/mustang-validation.xsd Normal file
View File

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="validation">
<xs:complexType>
<xs:sequence>
<xs:element ref="pdf"/>
<xs:element ref="messages"/>
<xs:element ref="summary"/>
</xs:sequence>
<xs:attribute name="datetime" use="required"/>
<xs:attribute name="filename" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="pdf">
<xs:complexType>
<xs:sequence>
<xs:element ref="report"/>
<xs:element ref="info"/>
<xs:element ref="messages"/>
<xs:element ref="summary"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="report">
<xs:complexType>
<xs:sequence>
<xs:element ref="buildInformation"/>
<xs:element ref="jobs"/>
<xs:element ref="batchSummary"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="buildInformation">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="releaseDetails"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="releaseDetails">
<xs:complexType>
<xs:attribute name="buildDate" use="required" type="xs:dateTime"/>
<xs:attribute name="id" use="required" type="xs:NCName"/>
<xs:attribute name="version" use="required" type="xs:NMTOKEN"/>
</xs:complexType>
</xs:element>
<xs:element name="jobs">
<xs:complexType>
<xs:sequence>
<xs:element ref="job"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="job">
<xs:complexType>
<xs:sequence>
<xs:element ref="item"/>
<xs:element ref="validationReport"/>
<xs:element ref="duration"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element ref="name"/>
</xs:sequence>
<xs:attribute name="size" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="name" type="xs:string"/>
<xs:element name="validationReport">
<xs:complexType>
<xs:sequence>
<xs:element ref="details"/>
</xs:sequence>
<xs:attribute name="isCompliant" use="required" type="xs:boolean"/>
<xs:attribute name="profileName" use="required"/>
<xs:attribute name="statement" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="details">
<xs:complexType>
<xs:attribute name="failedChecks" use="required" type="xs:integer"/>
<xs:attribute name="failedRules" use="required" type="xs:integer"/>
<xs:attribute name="passedChecks" use="required" type="xs:integer"/>
<xs:attribute name="passedRules" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="batchSummary">
<xs:complexType>
<xs:sequence>
<xs:element ref="validationReports"/>
<xs:element ref="featureReports"/>
<xs:element ref="repairReports"/>
<xs:element ref="duration"/>
</xs:sequence>
<xs:attribute name="encrypted" use="required" type="xs:integer"/>
<xs:attribute name="failedToParse" use="required" type="xs:integer"/>
<xs:attribute name="outOfMemory" use="required" type="xs:integer"/>
<xs:attribute name="totalJobs" use="required" type="xs:integer"/>
<xs:attribute name="veraExceptions" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="validationReports">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="compliant" use="required" type="xs:integer"/>
<xs:attribute name="failedJobs" use="required" type="xs:integer"/>
<xs:attribute name="nonCompliant" use="required" type="xs:integer"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="featureReports">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="failedJobs" use="required" type="xs:integer"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="repairReports">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:integer">
<xs:attribute name="failedJobs" use="required" type="xs:integer"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="info">
<xs:complexType>
<xs:sequence>
<xs:element ref="signature"/>
<xs:element ref="duration"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="signature" type="xs:NCName"/>
<xs:element name="duration">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:NMTOKEN">
<xs:attribute name="finish" type="xs:integer"/>
<xs:attribute name="start" type="xs:integer"/>
<xs:attribute name="unit" type="xs:NCName"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="messages">
<xs:complexType>
<xs:choice>
<xs:element ref="exception"/>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="error"/>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="notice"/>
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="exception">
<xs:complexType mixed="true">
<xs:attribute name="type" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="error">
<xs:complexType mixed="true">
<xs:attribute name="type" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="notice">
<xs:complexType mixed="true">
<xs:attribute name="type" use="required" type="xs:integer"/>
</xs:complexType>
</xs:element>
<xs:element name="summary">
<xs:complexType>
<xs:attribute name="status" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@@ -1,345 +1,364 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<parent> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.12.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId> <groupId>org.mustangproject</groupId>
<artifactId>core</artifactId> <artifactId>library</artifactId>
<version>2.9.1-SNAPSHOT</version> <version>2.12.0-SNAPSHOT</version>
</parent> <packaging>jar</packaging>
<name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII)</name>
<modelVersion>4.0.0</modelVersion> <description>FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT
<groupId>org.mustangproject</groupId> Cross Industry Invoice based formats Factur-X/ZUGFeRD, XRechnung and Order-X in your invoice PDFs.
<artifactId>library</artifactId>
<version>2.9.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII)</name>
<description>FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT Cross Industry Invoice based formats Factur-X/ZUGFeRD, XRechnung and Order-X in your invoice PDFs.
</description> </description>
<url>http://www.mustangproject.org/</url> <url>http://www.mustangproject.org/</url>
<scm> <scm>
<connection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</connection> <connection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</connection>
<developerConnection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</developerConnection> <developerConnection>scm:git:https://github.com/ZUGFeRD/mustangproject.git</developerConnection>
<url>https://github.com/ZUGFeRD/mustangproject</url> <url>https://github.com/ZUGFeRD/mustangproject</url>
<tag>core-2.3.2</tag> <tag>core-2.3.2</tag>
</scm> </scm>
<repositories> <repositories>
<repository><!-- for jargs --> <repository><!-- for jargs -->
<id>sonatype-oss-public</id> <id>sonatype-oss-public</id>
<url>https://oss.sonatype.org/content/groups/public/</url> <url>https://oss.sonatype.org/content/groups/public/</url>
<releases> <releases>
<enabled>true</enabled> <enabled>true</enabled>
</releases> </releases>
<snapshots> <snapshots>
<enabled>true</enabled> <enabled>true</enabled>
</snapshots> </snapshots>
</repository> </repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>
<repository> <repository>
<id>internal.repo</id> <id>internal.repo</id>
<name>Temporary Staging Repository</name> <name>Temporary Staging Repository</name>
<url>file://${project.build.directory}/mvn-repo</url> <url>file://${project.build.directory}/mvn-repo</url>
</repository> </repository>
</distributionManagement> </distributionManagement>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<github.global.server>github</github.global.server> <github.global.server>github</github.global.server>
<additionalparam>-Xdoclint:none</additionalparam> <additionalparam>-Xdoclint:none</additionalparam>
<!-- Skip error check for javadoc --> <!-- Skip error check for javadoc -->
<maven.compiler.compilerVersion>8</maven.compiler.compilerVersion> <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
<maven.deploy.skip>true</maven.deploy.skip><!-- do deploy to maven central, parent project does not and inherits --> <maven.deploy.skip>true
</maven.deploy.skip><!-- do deploy to maven central, parent project does not and inherits -->
</properties> </properties>
<dependencies> <dependencies>
<!-- https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE --> <dependency>
<dependency> <groupId>org.slf4j</groupId>
<groupId>net.sf.saxon</groupId> <artifactId>slf4j-api</artifactId>
<artifactId>Saxon-HE</artifactId> <version>2.0.9</version>
<version>9.9.0-1</version> </dependency>
</dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <!-- https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>net.sf.saxon</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>Saxon-HE</artifactId>
<version>2.14.2</version> <version>12.4</version>
</dependency> </dependency><!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/fop --> <dependency>
<dependency> <groupId>com.fasterxml.jackson.core</groupId>
<groupId>org.apache.xmlgraphics</groupId> <artifactId>jackson-databind</artifactId>
<artifactId>fop</artifactId> <version>2.17.1</version>
<version>2.9</version> </dependency>
</dependency> <!-- https://mvnrepository.com/artifact/org.apache.xmlgraphics/fop -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.9</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>javax.activation</groupId> <groupId>jakarta.xml.bind</groupId>
<artifactId>activation</artifactId> <artifactId>jakarta.xml.bind-api</artifactId>
<version>1.1.1</version> <version>4.0.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-activation</artifactId>
<version>2.0.2</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>preflight</artifactId> <artifactId>preflight</artifactId>
<version>2.0.27</version> <version>3.0.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.pdfbox</groupId> <groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId> <artifactId>pdfbox</artifactId>
<version>2.0.27</version> <version>3.0.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dom4j</groupId> <groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId> <artifactId>dom4j</artifactId>
<version>2.1.3</version> <version>2.1.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>5.9.1</version> <version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<!-- CII to UBL conversion -->
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>1.4.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<version>2.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-assertj</artifactId>
<version>2.9.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- API --> <dependency>
<dependency> <groupId>org.junit.vintage</groupId>
<groupId>jakarta.xml.bind</groupId> <artifactId>junit-vintage-engine</artifactId>
<artifactId>jakarta.xml.bind-api</artifactId> <version>5.10.2</version>
<version>2.3.3</version> <scope>test</scope>
</dependency> </dependency>
</dependencies> <!-- CII to UBL conversion -->
<build> <dependency>
<plugins> <groupId>com.helger</groupId>
<plugin> <artifactId>en16931-cii2ubl</artifactId>
<groupId>org.apache.maven.plugins</groupId> <version>2.2.4</version>
<artifactId>maven-surefire-plugin</artifactId> </dependency>
<configuration> <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<runOrder>alphabetical</runOrder> <dependency>
</configuration> <groupId>org.glassfish.jaxb</groupId>
</plugin> <artifactId>jaxb-runtime</artifactId>
<plugin> <version>4.0.5</version>
<groupId>org.apache.maven.plugins</groupId> </dependency>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version> <dependency>
<configuration> <groupId>org.xmlunit</groupId>
<descriptorRefs> <artifactId>xmlunit-core</artifactId>
<descriptorRef>jar-with-dependencies</descriptorRef> <version>2.10.0</version>
</descriptorRefs> <scope>test</scope>
<!-- http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven </dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-assertj</artifactId>
<version>2.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<runOrder>alphabetical</runOrder>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven
mvn clean compile assembly:single --> mvn clean compile assembly:single -->
<!-- or whatever version you use --> <!-- or whatever version you use -->
<source>8</source> <source>11</source>
<target>8</target> <target>11</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version> <version>3.2.0</version>
<configuration> <configuration>
<excludes> <excludes>
<exclude>**/logback.xml</exclude> <exclude>**/logback.xml</exclude>
</excludes> </excludes>
<archive> <archive>
<manifest> <manifest>
<addClasspath>true</addClasspath> <addClasspath>true</addClasspath>
</manifest> </manifest>
<manifestSections> <manifestSections>
<manifestSection> <manifestSection>
<name>FreeSans.ttf</name> <name>FreeSans.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>font/ttf</Content-Type> <Content-Type>font/ttf</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
<manifestSection> <manifestSection>
<name>FreeSerif.ttf</name> <name>FreeSerif.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>application/x-font</Content-Type> <Content-Type>application/x-font</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
<manifestSection> <manifestSection>
<name>Times-Bold.ttf</name> <name>Times-Bold.ttf</name>
<manifestEntries> <manifestEntries>
<Content-Type>application/x-font</Content-Type> <Content-Type>application/x-font</Content-Type>
</manifestEntries> </manifestEntries>
</manifestSection> </manifestSection>
</manifestSections> </manifestSections>
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version> <version>3.3.1</version>
<executions> <executions>
<execution> <execution>
<id>attach-sources</id> <id>attach-sources</id>
<goals> <goals>
<goal>jar-no-fork</goal> <goal>jar-no-fork</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version> <version>3.5.3</version>
<configuration> <configuration>
<shadedArtifactAttached>true</shadedArtifactAttached> <shadedArtifactAttached>true</shadedArtifactAttached>
<minimizeJar>false</minimizeJar><!-- no longer java 11 compatible if set to true because it removes e.g. javax/xml/bind/annotation/XmlSchema--> <minimizeJar>false
<filters> </minimizeJar><!-- no longer java 11 compatible if set to true because it removes e.g. javax/xml/bind/annotation/XmlSchema-->
<filters>
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
<exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude> <exclude>META-INF/*.RSA</exclude>
</excludes> </excludes>
</filter> </filter>
<filter> <filter>
<artifact>log4j:log4j</artifact> <artifact>log4j:log4j</artifact>
<includes> <includes>
<include>**</include> <include>**</include>
</includes> </includes>
</filter> </filter>
<filter> <filter>
<artifact>commons-logging:commons-logging</artifact> <artifact>commons-logging:commons-logging</artifact>
<includes> <includes>
<include>**</include> <include>**</include>
</includes> </includes>
</filter> </filter>
</filters> </filters>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
<goals> <goals>
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<artifactSet> <artifactSet>
<excludes> <excludes>
<!--exclude>classworlds:classworlds</exclude> <exclude>junit:junit</exclude> <!--exclude>classworlds:classworlds</exclude> <exclude>junit:junit</exclude>
<exclude>jmock:*</exclude> <exclude>*:xml-apis</exclude> <exclude>org.apache.maven:lib:tests</exclude> <exclude>jmock:*</exclude> <exclude>*:xml-apis</exclude> <exclude>org.apache.maven:lib:tests</exclude>
<exclude>log4j:log4j:jar:</exclude --> <exclude>log4j:log4j:jar:</exclude -->
</excludes> </excludes>
</artifactSet> </artifactSet>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>templating-maven-plugin</artifactId> <artifactId>templating-maven-plugin</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
<executions> <executions>
<execution> <execution>
<id>filtering-java-templates</id> <id>filtering-java-templates</id>
<goals> <goals>
<goal>filter-sources</goal> <goal>filter-sources</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<profiles> <profiles>
<profile> <!-- enforce building binaries with Java 1.8 for Maven Central, otherwise using them e.g. as jar will throw a version exception--> <profile> <!-- enforce building binaries with Java 1.8 for Maven Central, otherwise using them e.g. as jar will throw a version exception-->
<id>release-sign-artifacts</id> <id>release-sign-artifacts</id>
<activation> <activation>
<property> <property>
<name>performRelease</name> <name>performRelease</name>
<value>true</value> <value>true</value>
</property> </property>
</activation> </activation>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-toolchains-plugin</artifactId> <artifactId>maven-toolchains-plugin</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
<goal>toolchain</goal> <goal>toolchain</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
<configuration> <configuration>
<toolchains> <toolchains>
<jdk> <jdk>
<version>8</version> <version>11</version>
<vendor>adopt</vendor> <vendor>adopt</vendor>
</jdk> </jdk>
</toolchains> </toolchains>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>
</profiles> </profiles>
<mailingLists> <mailingLists>
<mailingList> <mailingList>
<name>User List</name> <name>User List</name>
<archive>https://groups.google.com/forum/?hl=de#!forum/mustangproject</archive> <archive>https://groups.google.com/forum/?hl=de#!forum/mustangproject</archive>
</mailingList> </mailingList>
</mailingLists> </mailingLists>
<licenses> <licenses>
<license> <license>
<name>Apache License, Version 2.0</name> <name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution> <distribution>repo</distribution>
<comments>A business-friendly OSS license</comments> <comments>A business-friendly OSS license</comments>
</license> </license>
</licenses> </licenses>
<developers> <developers>
<developer> <developer>
<name>Jochen Stärk</name> <name>Jochen Stärk</name>
<email>jstaerk@usegroup.de</email> <email>jstaerk@usegroup.de</email>
<roles> <roles>
<role>architect</role> <role>architect</role>
<role>developer</role> <role>developer</role>
</roles> </roles>
</developer> </developer>
<developer> <developer>
<name>Alexander Schmidt</name> <name>Alexander Schmidt</name>
<email>schmidt.alexander@mail.de</email> <email>schmidt.alexander@mail.de</email>
<roles> <roles>
<role>developer</role> <role>developer</role>
</roles> </roles>
</developer> </developer>
</developers> </developers>
</project> </project>

View File

@@ -1,14 +1,11 @@
package org.mustangproject; package org.mustangproject;
import org.mustangproject.ZUGFeRD.IExportableTransaction;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import java.math.BigDecimal; import java.math.BigDecimal;
/*** /***
* (absolute) allowances on item and document level * (absolute) allowances on item and document level
*/ */
public class Allowance extends Charge implements IZUGFeRDAllowanceCharge { public class Allowance extends Charge {
/*** /***
* bean constructor * bean constructor

View File

@@ -5,9 +5,7 @@ import java.io.Serializable;
import com.helger.commons.error.list.ErrorList; import com.helger.commons.error.list.ErrorList;
import com.helger.en16931.cii2ubl.CIIToUBL23Converter; import com.helger.en16931.cii2ubl.CIIToUBL23Converter;
import com.helger.ubl21.UBL21Writer; import com.helger.ubl23.UBL23Marshaller;
import com.helger.ubl22.UBL22Writer;
import com.helger.ubl23.UBL23Writer;
/*** /***
* converts a Cross Industry Invoice XML file to a UBL XML file * converts a Cross Industry Invoice XML file to a UBL XML file
@@ -23,39 +21,15 @@ public class CIIToUBL {
final ErrorList occurred=new ErrorList(); final ErrorList occurred=new ErrorList();
final CIIToUBL23Converter cc=new CIIToUBL23Converter(); final CIIToUBL23Converter cc=new CIIToUBL23Converter();
final Serializable aUBL = cc.convertCIItoUBL(input, occurred); final Serializable aUBL = cc.convertCIItoUBL(input, occurred);
if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_21.InvoiceType) if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_23.InvoiceType)
{ {
UBL21Writer.invoice () UBL23Marshaller.invoice ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.invoice_21.InvoiceType) aUBL, output);
}
else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_21.CreditNoteType)
{
UBL21Writer.creditNote ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.creditnote_21.CreditNoteType) aUBL, output);
}
else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_22.InvoiceType)
{
UBL22Writer.invoice ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.invoice_22.InvoiceType) aUBL, output);
}
else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_22.CreditNoteType)
{
UBL22Writer.creditNote ()
.setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.creditnote_22.CreditNoteType) aUBL, output);
}
else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.invoice_23.InvoiceType)
{
UBL23Writer.invoice ()
.setFormattedOutput (true) .setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.invoice_23.InvoiceType) aUBL, output); .write ((oasis.names.specification.ubl.schema.xsd.invoice_23.InvoiceType) aUBL, output);
} }
else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_23.CreditNoteType) else if (aUBL instanceof oasis.names.specification.ubl.schema.xsd.creditnote_23.CreditNoteType)
{ {
UBL23Writer.creditNote () UBL23Marshaller.creditNote ()
.setFormattedOutput (true) .setFormattedOutput (true)
.write ((oasis.names.specification.ubl.schema.xsd.creditnote_23.CreditNoteType) aUBL, output); .write ((oasis.names.specification.ubl.schema.xsd.creditnote_23.CreditNoteType) aUBL, output);
} }

View File

@@ -0,0 +1,59 @@
package org.mustangproject;
import org.mustangproject.ZUGFeRD.IZUGFeRDCashDiscount;
import java.math.BigDecimal;
/***
* A class to represent discounts for early payments ("Skonto")
*/
public class CashDiscount implements IZUGFeRDCashDiscount {
/***
* the reduction percent allowed in the period
*/
protected BigDecimal percent;
/***
* the period (usually days) count how long the percent apply
*/
protected Integer days=null;
/***
* Create a cash discount (skonto) with the specified height in the specified period.
* Should someone add more period types than just "days" there
* is be space for a (optional) third parameter
*
* @param percent max 3 decimals "behind the dot", more precision is currently ignored
* @param days the count of the periods (usually days) the percentage applies
*/
public CashDiscount(BigDecimal percent, int days) {
this.percent = percent;
this.days = days;
}
/***
* @return this particular cash discount as cross industry invoice XML
*/
public String getAsCII() {
return "<ram:SpecifiedTradePaymentTerms>"+
"<ram:Description>Cash Discount</ram:Description>"+
" <ram:ApplicableTradePaymentDiscountTerms>"+
" <ram:BasisPeriodMeasure unitCode=\"DAY\">"+days+"</ram:BasisPeriodMeasure>"+
" <ram:CalculationPercent>"+XMLTools.nDigitFormat(percent,3)+"</ram:CalculationPercent>"+
" </ram:ApplicableTradePaymentDiscountTerms>"+
"</ram:SpecifiedTradePaymentTerms>";
}
/***
* since EN16931 voted not to have (or even allow) cash discounts in their core invoice the german
* XRechnung CIUS defined it's own proprietary format for a freetext field
* @return this particular cash discount in proprietary xrechnung format
*/
public String getAsXRechnung() {
return "#SKONTO#TAGE="+days+"#PROZENT="+XMLTools.nDigitFormat(percent,2)+"#\n";
}
}

View File

@@ -1,9 +1,7 @@
package org.mustangproject; package org.mustangproject;
import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider; import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import org.mustangproject.ZUGFeRD.IExportableTransaction;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@@ -111,10 +111,10 @@ public class Contact implements IZUGFeRDExportableContact {
Node currentItemNode = nodes.item(nodeIndex); Node currentItemNode = nodes.item(nodeIndex);
if (currentItemNode.getLocalName() != null) { if (currentItemNode.getLocalName() != null) {
if (currentItemNode.getLocalName().equals("PersonName")) { if (currentItemNode.getLocalName().equals("PersonName")/*CII*/||currentItemNode.getLocalName().equals("Name")/*UBL*/) {
setName(currentItemNode.getFirstChild().getNodeValue()); setName(currentItemNode.getFirstChild().getNodeValue());
} }
if (currentItemNode.getLocalName().equals("TelephoneUniversalCommunication")) { if (currentItemNode.getLocalName().equals("TelephoneUniversalCommunication")) { /*CII*/
NodeList tel = currentItemNode.getChildNodes(); NodeList tel = currentItemNode.getChildNodes();
for (int telChildIndex = 0; telChildIndex < tel.getLength(); telChildIndex++) { for (int telChildIndex = 0; telChildIndex < tel.getLength(); telChildIndex++) {
if (tel.item(telChildIndex).getLocalName() != null) { if (tel.item(telChildIndex).getLocalName() != null) {
@@ -123,8 +123,10 @@ public class Contact implements IZUGFeRDExportableContact {
} }
} }
} }
} else if (currentItemNode.getLocalName().equals("Telephone")) { /* UBL */
setPhone(currentItemNode.getTextContent());
} }
if (currentItemNode.getLocalName().equals("EmailURIUniversalCommunication")) { if (currentItemNode.getLocalName().equals("EmailURIUniversalCommunication")) { /* CII */
NodeList email = currentItemNode.getChildNodes(); NodeList email = currentItemNode.getChildNodes();
for (int emailChildIndex = 0; emailChildIndex < email.getLength(); emailChildIndex++) { for (int emailChildIndex = 0; emailChildIndex < email.getLength(); emailChildIndex++) {
if (email.item(emailChildIndex).getLocalName() != null) { if (email.item(emailChildIndex).getLocalName() != null) {
@@ -133,6 +135,8 @@ public class Contact implements IZUGFeRDExportableContact {
} }
} }
} }
} else if (currentItemNode.getLocalName().equals("ElectronicMail")) { /* UBL */
setEMail(currentItemNode.getTextContent());
} }
} }
} }

View File

@@ -26,12 +26,7 @@ import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.mustangproject.ZUGFeRD.IExportableTransaction; import org.mustangproject.ZUGFeRD.*;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
import org.mustangproject.ZUGFeRD.IZUGFeRDPaymentTerms;
import org.mustangproject.ZUGFeRD.IZUGFeRDTradeSettlement;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@@ -47,20 +42,21 @@ public class Invoice implements IExportableTransaction {
protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null; protected String documentName = null, documentCode = null, number = null, ownOrganisationFullPlaintextInfo = null, referenceNumber = null, shipToOrganisationID = null, shipToOrganisationName = null, shipToStreet = null, shipToZIP = null, shipToLocation = null, shipToCountry = null, buyerOrderReferencedDocumentID = null, invoiceReferencedDocumentID = null, buyerOrderReferencedDocumentIssueDateTime = null, ownForeignOrganisationID = null, ownOrganisationName = null, currency = null, paymentTermDescription = null;
protected Date issueDate = null, dueDate = null, deliveryDate = null; protected Date issueDate = null, dueDate = null, deliveryDate = null;
protected TradeParty sender = null, recipient = null, deliveryAddress = null; protected TradeParty sender = null, recipient = null, deliveryAddress = null;
@JsonDeserialize(contentAs=Item.class) protected ArrayList<CashDiscount> cashDiscounts = null;
@JsonDeserialize(contentAs = Item.class)
protected ArrayList<IZUGFeRDExportableItem> ZFItems = null; protected ArrayList<IZUGFeRDExportableItem> ZFItems = null;
protected ArrayList<String> notes = null; protected ArrayList<String> notes = null;
private List<IncludedNote> includedNotes = null; private List<IncludedNote> includedNotes = null;
protected String sellerOrderReferencedDocumentID; protected String sellerOrderReferencedDocumentID;
protected String contractReferencedDocument = null; protected String contractReferencedDocument = null;
protected ArrayList<FileAttachment> xmlEmbeddedFiles=null; protected ArrayList<FileAttachment> xmlEmbeddedFiles = null;
protected BigDecimal totalPrepaidAmount = null; protected BigDecimal totalPrepaidAmount = null;
protected Date detailedDeliveryDateStart = null; protected Date detailedDeliveryDateStart = null;
protected Date detailedDeliveryPeriodEnd = null; protected Date detailedDeliveryPeriodEnd = null;
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(), protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(),
Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>(); Charges = new ArrayList<>(), LogisticsServiceCharges = new ArrayList<>();
protected IZUGFeRDPaymentTerms paymentTerms = null; protected IZUGFeRDPaymentTerms paymentTerms = null;
protected Date invoiceReferencedIssueDate; protected Date invoiceReferencedIssueDate;
protected String specifiedProcuringProjectID = null; protected String specifiedProcuringProjectID = null;
@@ -70,6 +66,7 @@ public class Invoice implements IExportableTransaction {
public Invoice() { public Invoice() {
ZFItems = new ArrayList<>(); ZFItems = new ArrayList<>();
cashDiscounts = new ArrayList<>();
setCurrency("EUR"); setCurrency("EUR");
} }
@@ -79,7 +76,7 @@ public class Invoice implements IExportableTransaction {
} }
@Override @Override
public String getContractReferencedDocument() { public String getContractReferencedDocument() {
return contractReferencedDocument; return contractReferencedDocument;
} }
@@ -100,14 +97,14 @@ public class Invoice implements IExportableTransaction {
public Invoice embedFileInXML(FileAttachment fa) { public Invoice embedFileInXML(FileAttachment fa) {
if (xmlEmbeddedFiles == null) { if (xmlEmbeddedFiles == null) {
xmlEmbeddedFiles= new ArrayList<>(); xmlEmbeddedFiles = new ArrayList<>();
} }
xmlEmbeddedFiles.add(fa); xmlEmbeddedFiles.add(fa);
return this; return this;
} }
@Override @Override
public FileAttachment[] getAdditionalReferencedDocuments() { public FileAttachment[] getAdditionalReferencedDocuments() {
if (xmlEmbeddedFiles == null) { if (xmlEmbeddedFiles == null) {
return null; return null;
} }
@@ -115,6 +112,10 @@ public class Invoice implements IExportableTransaction {
} }
@Override
public IZUGFeRDCashDiscount[] getCashDiscounts() {
return cashDiscounts.toArray(new IZUGFeRDCashDiscount[0]);
}
@Override @Override
public String getNumber() { public String getNumber() {
@@ -139,6 +140,7 @@ public class Invoice implements IExportableTransaction {
documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE; documentCode = DocumentCodeTypeConstants.CORRECTEDINVOICE;
return this; return this;
} }
public Invoice setCreditNote() { public Invoice setCreditNote() {
documentCode = DocumentCodeTypeConstants.CREDITNOTE; documentCode = DocumentCodeTypeConstants.CREDITNOTE;
return this; return this;
@@ -229,16 +231,18 @@ public class Invoice implements IExportableTransaction {
public String getBuyerOrderReferencedDocumentID() { public String getBuyerOrderReferencedDocumentID() {
return buyerOrderReferencedDocumentID; return buyerOrderReferencedDocumentID;
} }
@Override @Override
public String getSellerOrderReferencedDocumentID() { public String getSellerOrderReferencedDocumentID() {
return sellerOrderReferencedDocumentID; return sellerOrderReferencedDocumentID;
} }
public Invoice setSellerOrderReferencedDocumentID(String sellerOrderReferencedDocumentID) { public Invoice setSellerOrderReferencedDocumentID(String sellerOrderReferencedDocumentID) {
this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID; this.sellerOrderReferencedDocumentID = sellerOrderReferencedDocumentID;
return this; return this;
} }
/*** /***
* usually the order number * usually the order number
* @param buyerOrderReferencedDocumentID string with number * @param buyerOrderReferencedDocumentID string with number
@@ -258,22 +262,23 @@ public class Invoice implements IExportableTransaction {
this.invoiceReferencedDocumentID = invoiceReferencedDocumentID; this.invoiceReferencedDocumentID = invoiceReferencedDocumentID;
return this; return this;
} }
@Override @Override
public String getInvoiceReferencedDocumentID() { public String getInvoiceReferencedDocumentID() {
return invoiceReferencedDocumentID; return invoiceReferencedDocumentID;
} }
@Override @Override
public Date getInvoiceReferencedIssueDate() { public Date getInvoiceReferencedIssueDate() {
return invoiceReferencedIssueDate; return invoiceReferencedIssueDate;
} }
public Invoice setInvoiceReferencedIssueDate(Date issueDate) { public Invoice setInvoiceReferencedIssueDate(Date issueDate) {
this.invoiceReferencedIssueDate = issueDate; this.invoiceReferencedIssueDate = issueDate;
return this; return this;
} }
@Override @Override
public String getBuyerOrderReferencedDocumentIssueDateTime() { public String getBuyerOrderReferencedDocumentIssueDateTime() {
return buyerOrderReferencedDocumentIssueDateTime; return buyerOrderReferencedDocumentIssueDateTime;
} }
@@ -285,7 +290,7 @@ public class Invoice implements IExportableTransaction {
* @return fluent setter * @return fluent setter
*/ */
public Invoice setTotalPrepaidAmount(BigDecimal prepaid) { public Invoice setTotalPrepaidAmount(BigDecimal prepaid) {
totalPrepaidAmount=prepaid; totalPrepaidAmount = prepaid;
return this; return this;
} }
@@ -380,13 +385,13 @@ public class Invoice implements IExportableTransaction {
@Override @Override
public String getOwnLocation() { public String getOwnLocation() {
return sender.getLocation(); return sender.getLocation();
} }
@Override @Override
public String getOwnCountry() { public String getOwnCountry() {
return sender.getCountry(); return sender.getCountry();
} }
@@ -399,12 +404,12 @@ public class Invoice implements IExportableTransaction {
return notes.toArray(new String[0]); return notes.toArray(new String[0]);
} }
@Override @Override
public List<IncludedNote> getNotesWithSubjectCode() { public List<IncludedNote> getNotesWithSubjectCode() {
return includedNotes; return includedNotes;
} }
@Override @Override
public String getCurrency() { public String getCurrency() {
return currency; return currency;
} }
@@ -455,7 +460,7 @@ public class Invoice implements IExportableTransaction {
} }
@Override @Override
public IZUGFeRDExportableTradeParty getSender() { public TradeParty getSender() {
return sender; return sender;
} }
@@ -473,13 +478,14 @@ public class Invoice implements IExportableTransaction {
} }
@Override @Override
public IZUGFeRDExportableTradeParty getRecipient() { public TradeParty getRecipient() {
return recipient; return recipient;
} }
/** /**
* required. * required.
* sets the invoice receiving institution = invoicee * sets the invoice receiving institution = invoicee
*
* @param recipient the invoicee organisation * @param recipient the invoicee organisation
* @return fluent setter * @return fluent setter
*/ */
@@ -491,6 +497,7 @@ public class Invoice implements IExportableTransaction {
/** /**
* required. * required.
* sets the invoicing institution = invoicer * sets the invoicing institution = invoicer
*
* @param sender the invoicer * @param sender the invoicer
* @return fluent setter * @return fluent setter
*/ */
@@ -508,8 +515,8 @@ public class Invoice implements IExportableTransaction {
if (Allowances.isEmpty()) { if (Allowances.isEmpty()) {
return null; return null;
} else { } else {
return Allowances.toArray(new IZUGFeRDAllowanceCharge[0]); return Allowances.toArray(new IZUGFeRDAllowanceCharge[0]);
} }
} }
@@ -518,8 +525,8 @@ public class Invoice implements IExportableTransaction {
if (Charges.isEmpty()) { if (Charges.isEmpty()) {
return null; return null;
} else { } else {
return Charges.toArray(new IZUGFeRDAllowanceCharge[0]); return Charges.toArray(new IZUGFeRDAllowanceCharge[0]);
} }
} }
@@ -528,8 +535,8 @@ public class Invoice implements IExportableTransaction {
if (LogisticsServiceCharges.isEmpty()) { if (LogisticsServiceCharges.isEmpty()) {
return null; return null;
} else { } else {
return LogisticsServiceCharges.toArray(new IZUGFeRDAllowanceCharge[0]); return LogisticsServiceCharges.toArray(new IZUGFeRDAllowanceCharge[0]);
} }
} }
@@ -540,7 +547,7 @@ public class Invoice implements IExportableTransaction {
return null; return null;
} }
return ((TradeParty) getSender()).getAsTradeSettlement(); return getSender().getAsTradeSettlement();
} }
@@ -569,22 +576,33 @@ public class Invoice implements IExportableTransaction {
this.deliveryAddress = deliveryAddress; this.deliveryAddress = deliveryAddress;
return this; return this;
} }
/***
* Adds a cash discount (skonto)
* @param c the CashDiscount percent/period combination
* @return fluent setter
*/
public Invoice addCashDiscount(CashDiscount c) {
this.cashDiscounts.add(c);
return this;
}
@Override @Override
public IZUGFeRDExportableItem[] getZFItems() { public IZUGFeRDExportableItem[] getZFItems() {
return ZFItems.toArray(new IZUGFeRDExportableItem[0]); return ZFItems.toArray(new IZUGFeRDExportableItem[0]);
} }
public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) { public void setZFItems(ArrayList<IZUGFeRDExportableItem> ims) {
ZFItems=ims; ZFItems = ims;
} }
/** /**
* required * required
* adds invoice "lines" :-) * adds invoice "lines" :-)
* @see Item *
* @param item the invoice line * @param item the invoice line
* @return fluent setter * @return fluent setter
* @see Item
*/ */
public Invoice addItem(IZUGFeRDExportableItem item) { public Invoice addItem(IZUGFeRDExportableItem item) {
ZFItems.add(item); ZFItems.add(item);
@@ -669,6 +687,7 @@ public class Invoice implements IExportableTransaction {
/** /**
* adds a free text paragraph, which will become an includedNote element * adds a free text paragraph, which will become an includedNote element
*
* @param text freeform UTF8 plain text * @param text freeform UTF8 plain text
* @return fluent setter * @return fluent setter
*/ */
@@ -680,127 +699,136 @@ public class Invoice implements IExportableTransaction {
return this; return this;
} }
public Invoice addNotes(Collection<IncludedNote> notes) { public Invoice addNotes(Collection<IncludedNote> notes) {
if (notes == null) { if (notes == null) {
return this; return this;
} }
if (includedNotes == null) { if (includedNotes == null) {
includedNotes = new ArrayList<>(); includedNotes = new ArrayList<>();
} }
includedNotes.addAll(notes); includedNotes.addAll(notes);
return this; return this;
} }
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAI} * subjectCode {@link SubjectCode#AAI}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addGeneralNote(String content) { */
if (includedNotes == null) { public Invoice addGeneralNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.generalNote(content)); }
return this; includedNotes.add(IncludedNote.generalNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#REG} * subjectCode {@link SubjectCode#REG}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addRegulatoryNote(String content) { */
if (includedNotes == null) { public Invoice addRegulatoryNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.regulatoryNote(content)); }
return this; includedNotes.add(IncludedNote.regulatoryNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ABL} * subjectCode {@link SubjectCode#ABL}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addLegalNote(String content) { */
if (includedNotes == null) { public Invoice addLegalNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.legalNote(content)); }
return this; includedNotes.add(IncludedNote.legalNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#CUS} * subjectCode {@link SubjectCode#CUS}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addCustomsNote(String content) { */
if (includedNotes == null) { public Invoice addCustomsNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.customsNote(content)); }
return this; includedNotes.add(IncludedNote.customsNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#SUR} * subjectCode {@link SubjectCode#SUR}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addSellerNote(String content) { */
if (includedNotes == null) { public Invoice addSellerNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.sellerNote(content)); }
return this; includedNotes.add(IncludedNote.sellerNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#TXD} * subjectCode {@link SubjectCode#TXD}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addTaxNote(String content) { */
if (includedNotes == null) { public Invoice addTaxNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.taxNote(content)); }
return this; includedNotes.add(IncludedNote.taxNote(content));
} return this;
}
/** /**
* adds a free text paragraph, which will become an includedNote element with explicit * adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#ACY} * subjectCode {@link SubjectCode#ACY}
* @param content freeform UTF8 plain text *
* @return fluent setter * @param content freeform UTF8 plain text
*/ * @return fluent setter
public Invoice addIntroductionNote(String content) { */
if (includedNotes == null) { public Invoice addIntroductionNote(String content) {
includedNotes = new ArrayList<>(); if (includedNotes == null) {
} includedNotes = new ArrayList<>();
includedNotes.add(IncludedNote.introductionNote(content)); }
return this; includedNotes.add(IncludedNote.introductionNote(content));
} return this;
/** }
* adds a free text paragraph, which will become an includedNote element with explicit
* subjectCode {@link SubjectCode#AAK} /**
* @param content freeform UTF8 plain text * adds a free text paragraph, which will become an includedNote element with explicit
* @return fluent setter * subjectCode {@link SubjectCode#AAK}
*/ *
public Invoice addDiscountBonusNote(String content) { * @param content freeform UTF8 plain text
if (includedNotes == null) { * @return fluent setter
includedNotes = new ArrayList<>(); */
} public Invoice addDiscountBonusNote(String content) {
includedNotes.add(IncludedNote.discountBonusNote(content)); if (includedNotes == null) {
return this; includedNotes = new ArrayList<>();
} }
includedNotes.add(IncludedNote.discountBonusNote(content));
return this;
}
@Override @Override
public String getSpecifiedProcuringProjectID() { public String getSpecifiedProcuringProjectID() {
@@ -841,6 +869,7 @@ public class Invoice implements IExportableTransaction {
/** /**
* Decide when the VAT should be collected. * Decide when the VAT should be collected.
*
* @param vatDueDateTypeCode use EventTimeCodeTypeConstants * @param vatDueDateTypeCode use EventTimeCodeTypeConstants
* @return fluent setter * @return fluent setter
*/ */

View File

@@ -1,7 +1,6 @@
package org.mustangproject; package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.apache.fop.util.XMLUtil;
import org.mustangproject.ZUGFeRD.IReferencedDocument; import org.mustangproject.ZUGFeRD.IReferencedDocument;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge; import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
@@ -12,7 +11,6 @@ import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
/*** /***
* describes any invoice line * describes any invoice line
@@ -28,8 +26,8 @@ public class Item implements IZUGFeRDExportableItem {
protected Product product; protected Product product;
protected ArrayList<String> notes = null; protected ArrayList<String> notes = null;
protected ArrayList<ReferencedDocument> referencedDocuments = null; protected ArrayList<ReferencedDocument> referencedDocuments = null;
protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<IZUGFeRDAllowanceCharge>(), protected ArrayList<IZUGFeRDAllowanceCharge> Allowances = new ArrayList<>(),
Charges = new ArrayList<IZUGFeRDAllowanceCharge>(); Charges = new ArrayList<>();
/*** /***
* default constructor * default constructor
@@ -62,6 +60,7 @@ public class Item implements IZUGFeRDExportableItem {
String vatPercent = null; String vatPercent = null;
String lineTotal = "0"; String lineTotal = "0";
String unitCode = "0"; String unitCode = "0";
String referencedLineID = null;
ArrayList<ReferencedDocument> rdocs = null; ArrayList<ReferencedDocument> rdocs = null;
@@ -138,12 +137,22 @@ public class Item implements IZUGFeRDExportableItem {
ReferencedDocument rd = new ReferencedDocument(IssuerAssignedID, TypeCode, ReferencedDocument rd = new ReferencedDocument(IssuerAssignedID, TypeCode,
ReferenceTypeCode); ReferenceTypeCode);
if (rdocs == null) { if (rdocs == null) {
rdocs = new ArrayList<ReferencedDocument>(); rdocs = new ArrayList<>();
} }
rdocs.add(rd); rdocs.add(rd);
} }
if ((tradeLineChilds.item(tradeLineChildIndex).getLocalName() != null) && tradeLineChilds.item(tradeLineChildIndex).getLocalName().equals("BuyerOrderReferencedDocument")) {
NodeList docChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes();
for (int docIndex = 0; docIndex < docChilds.getLength(); docIndex++) {
String localName = docChilds.item(docIndex).getLocalName();
if ((localName != null) && (localName.equals("LineID"))) {
referencedLineID = docChilds.item(docIndex).getTextContent();
}
}
}
if ((tradeLineChilds.item(tradeLineChildIndex).getLocalName() != null) && tradeLineChilds if ((tradeLineChilds.item(tradeLineChildIndex).getLocalName() != null) && tradeLineChilds
.item(tradeLineChildIndex).getLocalName().equals("NetPriceProductTradePrice")) { .item(tradeLineChildIndex).getLocalName().equals("NetPriceProductTradePrice")) {
NodeList netChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes(); NodeList netChilds = tradeLineChilds.item(tradeLineChildIndex).getChildNodes();
@@ -265,6 +274,7 @@ public class Item implements IZUGFeRDExportableItem {
addReferencedDocument(rdoc); addReferencedDocument(rdoc);
} }
} }
addReferencedLineID( referencedLineID );
} }
@@ -428,7 +438,7 @@ public class Item implements IZUGFeRDExportableItem {
*/ */
public Item addNote(String text) { public Item addNote(String text) {
if (notes == null) { if (notes == null) {
notes = new ArrayList<String>(); notes = new ArrayList<>();
} }
notes.add(text); notes.add(text);
return this; return this;
@@ -441,7 +451,7 @@ public class Item implements IZUGFeRDExportableItem {
*/ */
public Item addReferencedDocument(ReferencedDocument doc) { public Item addReferencedDocument(ReferencedDocument doc) {
if (referencedDocuments == null) { if (referencedDocuments == null) {
referencedDocuments = new ArrayList<ReferencedDocument>(); referencedDocuments = new ArrayList<>();
} }
referencedDocuments.add(doc); referencedDocuments.add(doc);
return this; return this;

View File

@@ -2,13 +2,6 @@ package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.mustangproject.ZUGFeRD.*; import org.mustangproject.ZUGFeRD.*;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/*** /***
* A organisation, i.e. usually a company * A organisation, i.e. usually a company

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct; import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashMap;
/*** /***
* describes a product, good or service used in an invoice item line * describes a product, good or service used in an invoice item line
@@ -15,6 +16,8 @@ public class Product implements IZUGFeRDExportableProduct {
protected boolean isReverseCharge = false; protected boolean isReverseCharge = false;
protected boolean isIntraCommunitySupply = false; protected boolean isIntraCommunitySupply = false;
protected SchemedID globalId = null; protected SchemedID globalId = null;
protected String countryOfOrigin = null;
protected HashMap<String, String> attributes = null;
/*** /***
* default constructor * default constructor
@@ -63,6 +66,7 @@ public class Product implements IZUGFeRDExportableProduct {
} }
@Override
public String getSellerAssignedID() { public String getSellerAssignedID() {
return sellerAssignedID; return sellerAssignedID;
} }
@@ -77,6 +81,7 @@ public class Product implements IZUGFeRDExportableProduct {
return this; return this;
} }
@Override
public String getBuyerAssignedID() { public String getBuyerAssignedID() {
return buyerAssignedID; return buyerAssignedID;
} }
@@ -183,4 +188,32 @@ public class Product implements IZUGFeRDExportableProduct {
this.VATPercent = VATPercent; this.VATPercent = VATPercent;
return this; return this;
} }
@Override
public String getCountryOfOrigin() {
return this.countryOfOrigin;
}
public Product setCountryOfOrigin(String countryOfOrigin) {
this.countryOfOrigin = countryOfOrigin;
return this;
}
@Override
public HashMap<String, String> getAttributes() {
return this.attributes;
}
public Product setAttributes(HashMap<String, String> attributes) {
this.attributes = attributes;
return this;
}
public Product addAttribute(String name, String value ) {
if ( this.attributes == null ) {
this.attributes = new HashMap<>();
}
this.attributes.put(name, value);
return this;
}
} }

View File

@@ -65,154 +65,159 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) { for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) {
//nodes.item(i).getTextContent())) { //nodes.item(i).getTextContent())) {
Node currentItemNode = nodes.item(nodeIndex); Node currentItemNode = nodes.item(nodeIndex);
NodeList itemChilds = currentItemNode.getChildNodes();
for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) {
if (itemChilds.item(itemChildIndex).getLocalName() != null) {
if (itemChilds.item(itemChildIndex).getLocalName().equals("Party")) { if (currentItemNode.getLocalName() != null) {
String debcurrentChild = currentItemNode.getLocalName();
if (debcurrentChild.equals("Party")) {
NodeList party = itemChilds.item(itemChildIndex).getChildNodes(); NodeList party = currentItemNode.getChildNodes();
for (int partyIndex = 0; partyIndex < party.getLength(); partyIndex++) { for (int partyIndex = 0; partyIndex < party.getLength(); partyIndex++) {
if (party.item(partyIndex).getLocalName() != null) { if (party.item(partyIndex).getLocalName() != null) {
String debCN = party.item(partyIndex).getLocalName();
if (debCN.equals("PartyName")) {
if (party.item(partyIndex).getLocalName().equals("PartyName")) { NodeList partyName = party.item(partyIndex).getChildNodes();
for (int partyNameIndex = 0; partyNameIndex < partyName.getLength(); partyNameIndex++) {
NodeList partyName = party.item(partyIndex).getChildNodes(); if (partyName.item(partyNameIndex).getLocalName() != null) {
for (int partyNameIndex = 0; partyNameIndex < partyName.getLength(); partyNameIndex++) {
if (partyName.item(partyNameIndex).getLocalName() != null) {
if (partyName.item(partyNameIndex).getLocalName().equals("Name")) {
setName(partyName.item(partyNameIndex).getTextContent());
}
if (partyName.item(partyNameIndex).getLocalName().equals("Name")) {
setName(partyName.item(partyNameIndex).getTextContent());
} }
} }
} }
if (party.item(partyIndex).getLocalName().equals("PostalAddress")) { }
if (debCN.equals("PostalAddress")) {
NodeList postal = party.item(partyIndex).getChildNodes(); NodeList postal = party.item(partyIndex).getChildNodes();
for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) { for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) {
if (postal.item(postalChildIndex).getLocalName() != null) { if (postal.item(postalChildIndex).getLocalName() != null) {
if (postal.item(postalChildIndex).getLocalName().equals("StreetName")) { if (postal.item(postalChildIndex).getLocalName().equals("StreetName")) {
setStreet(postal.item(postalChildIndex).getTextContent()); setStreet(postal.item(postalChildIndex).getTextContent());
} }
if (postal.item(postalChildIndex).getLocalName().equals("AdditionalStreetName")) { if (postal.item(postalChildIndex).getLocalName().equals("AdditionalStreetName")) {
setAdditionalAddress(postal.item(postalChildIndex).getTextContent()); setAdditionalAddress(postal.item(postalChildIndex).getTextContent());
} }
//unknow correspondence if (postal.item(postalChildIndex).getLocalName().equals("LineThree")) { //unknow correspondence if (postal.item(postalChildIndex).getLocalName().equals("LineThree")) {
if (postal.item(postalChildIndex).getLocalName().equals("CityName")) { if (postal.item(postalChildIndex).getLocalName().equals("CityName")) {
setLocation(postal.item(postalChildIndex).getTextContent()); setLocation(postal.item(postalChildIndex).getTextContent());
} }
if (postal.item(postalChildIndex).getLocalName().equals("PostalZone")) { if (postal.item(postalChildIndex).getLocalName().equals("PostalZone")) {
setZIP(postal.item(postalChildIndex).getTextContent()); setZIP(postal.item(postalChildIndex).getTextContent());
} }
if (postal.item(postalChildIndex).getLocalName().equals("Country")) { if (postal.item(postalChildIndex).getLocalName().equals("Country")) {
NodeList country = postal.item(postalChildIndex).getChildNodes(); NodeList country = postal.item(postalChildIndex).getChildNodes();
for (int countryIndex = 0; countryIndex < country.getLength(); countryIndex++) { for (int countryIndex = 0; countryIndex < country.getLength(); countryIndex++) {
if (country.item(countryIndex).getLocalName() != null) { if (country.item(countryIndex).getLocalName() != null) {
if (country.item(countryIndex).getLocalName().equals("IdentificationCode")) {
setCountry(country.item(countryIndex).getTextContent());
}
if (country.item(countryIndex).getLocalName().equals("IdentificationCode")) {
setCountry(country.item(countryIndex).getTextContent());
} }
} }
} }
if (postal.item(postalChildIndex).getLocalName().equals("AddressLine")) {
NodeList AddressLine = postal.item(postalChildIndex).getChildNodes();
for (int lineIndex = 0; lineIndex < AddressLine.getLength(); lineIndex++) {
if (AddressLine.item(lineIndex).getLocalName() != null) {
if (AddressLine.item(lineIndex).getLocalName().equals("Line")) { }
setAdditionalAddressExtension(AddressLine.item(lineIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("AddressLine")) {
NodeList AddressLine = postal.item(postalChildIndex).getChildNodes();
for (int lineIndex = 0; lineIndex < AddressLine.getLength(); lineIndex++) {
if (AddressLine.item(lineIndex).getLocalName() != null) {
if (AddressLine.item(lineIndex).getLocalName().equals("Line")) {
setAdditionalAddressExtension(AddressLine.item(lineIndex).getTextContent());
} }
} }
}
if (postal.item(postalChildIndex).getLocalName().equals("Name")) {
setName(postal.item(postalChildIndex).getTextContent());
} }
} }
if (postal.item(postalChildIndex).getLocalName().equals("Name")) {
setName(postal.item(postalChildIndex).getTextContent());
}
} }
} }
} }
}
if (debCN.equals("Contact")) {
} NodeList contact = party.item(partyIndex).getChildNodes();
setContact(new Contact(contact));
if (itemChilds.item(itemChildIndex).getLocalName().equals("GlobalID")) {
if (itemChilds.item(itemChildIndex).getAttributes().getNamedItem("schemeID") != null) {
SchemedID gid = new SchemedID().setScheme(itemChilds.item(itemChildIndex).getAttributes().getNamedItem("schemeID").getNodeValue()).setId(itemChilds.item(itemChildIndex).getTextContent());
addGlobalID(gid);
}
}
if (itemChilds.item(itemChildIndex).getLocalName().equals("DefinedTradeContact")) {
NodeList contact = itemChilds.item(itemChildIndex).getChildNodes();
setContact(new Contact(contact));
}
if (itemChilds.item(itemChildIndex).getLocalName().equals("PostalTradeAddress")) {
NodeList postal = itemChilds.item(itemChildIndex).getChildNodes();
for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) {
if (postal.item(postalChildIndex).getLocalName() != null) {
if (postal.item(postalChildIndex).getLocalName().equals("LineOne")) {
setStreet(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("LineTwo")) {
setAdditionalAddress(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("LineThree")) {
setAdditionalAddressExtension(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("CityName")) {
setLocation(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("PostcodeCode")) {
setZIP(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("CountryID")) {
setCountry(postal.item(postalChildIndex).getTextContent());
}
} }
} }
} }
if (itemChilds.item(itemChildIndex).getLocalName().equals("SpecifiedTaxRegistration")) {
NodeList taxChilds = itemChilds.item(itemChildIndex).getChildNodes(); }
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
if (taxChilds.item(taxChildIndex).getLocalName() != null) { if (debcurrentChild.equals("GlobalID")) {
if ((taxChilds.item(taxChildIndex).getLocalName().equals("ID"))) { if (nodes.item(nodeIndex).getAttributes().getNamedItem("schemeID") != null) {
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("schemeID") != null) { SchemedID gid = new SchemedID().setScheme(nodes.item(nodeIndex).getAttributes().getNamedItem("schemeID").getNodeValue()).setId(nodes.item(nodeIndex).getTextContent());
Node firstChild = taxChilds.item(taxChildIndex).getFirstChild(); addGlobalID(gid);
if (firstChild != null) { }
if (taxChilds.item(taxChildIndex).getAttributes()
.getNamedItem("schemeID").getNodeValue().equals("VA")) { }
setVATID(firstChild.getNodeValue()); if (debcurrentChild.equals("DefinedTradeContact")) {
} NodeList contact = nodes.item(nodeIndex).getChildNodes();
if (taxChilds.item(taxChildIndex).getAttributes() setContact(new Contact(contact));
.getNamedItem("schemeID").getNodeValue().equals("FC")) { }
setTaxID(firstChild.getNodeValue());
} if (debcurrentChild.equals("PostalTradeAddress")) {
NodeList postal = nodes.item(nodeIndex).getChildNodes();
for (int postalChildIndex = 0; postalChildIndex < postal.getLength(); postalChildIndex++) {
if (postal.item(postalChildIndex).getLocalName() != null) {
if (postal.item(postalChildIndex).getLocalName().equals("LineOne")) {
setStreet(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("LineTwo")) {
setAdditionalAddress(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("LineThree")) {
setAdditionalAddressExtension(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("CityName")) {
setLocation(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("PostcodeCode")) {
setZIP(postal.item(postalChildIndex).getTextContent());
}
if (postal.item(postalChildIndex).getLocalName().equals("CountryID")) {
setCountry(postal.item(postalChildIndex).getTextContent());
}
}
}
}
if (debcurrentChild.equals("SpecifiedTaxRegistration")) {
NodeList taxChilds = nodes.item(nodeIndex).getChildNodes();
for (int taxChildIndex = 0; taxChildIndex < taxChilds.getLength(); taxChildIndex++) {
if (taxChilds.item(taxChildIndex).getLocalName() != null) {
if ((taxChilds.item(taxChildIndex).getLocalName().equals("ID"))) {
if (taxChilds.item(taxChildIndex).getAttributes().getNamedItem("schemeID") != null) {
Node firstChild = taxChilds.item(taxChildIndex).getFirstChild();
if (firstChild != null) {
if (taxChilds.item(taxChildIndex).getAttributes()
.getNamedItem("schemeID").getNodeValue().equals("VA")) {
setVATID(firstChild.getNodeValue());
}
if (taxChilds.item(taxChildIndex).getAttributes()
.getNamedItem("schemeID").getNodeValue().equals("FC")) {
setTaxID(firstChild.getNodeValue());
} }
} }
} }
} }
} }
} }
} }
} }
} }
@@ -284,15 +289,16 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) { for (int nodeIndex = 0; nodeIndex < nodes.getLength(); nodeIndex++) {
//nodes.item(i).getTextContent())) { //nodes.item(i).getTextContent())) {
String debLN = nodes.item(nodeIndex).getLocalName();
if (debLN.equals("Party")) {
// take one step back and parse from top
parseFromUBL(nodes);
return;
}
Node currentItemNode = nodes.item(nodeIndex); Node currentItemNode = nodes.item(nodeIndex);
NodeList itemChilds = currentItemNode.getChildNodes(); NodeList itemChilds = currentItemNode.getChildNodes();
for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) { for (int itemChildIndex = 0; itemChildIndex < itemChilds.getLength(); itemChildIndex++) {
if (itemChilds.item(itemChildIndex).getLocalName() != null) { if (itemChilds.item(itemChildIndex).getLocalName() != null) {
if (itemChilds.item(itemChildIndex).getLocalName().equals("Party")) {
parseFromUBL(nodes);
return;
}
if (itemChilds.item(itemChildIndex).getLocalName().equals("Name")) { if (itemChilds.item(itemChildIndex).getLocalName().equals("Name")) {
setName(itemChilds.item(itemChildIndex).getTextContent()); setName(itemChilds.item(itemChildIndex).getTextContent());
} }
@@ -328,10 +334,8 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
if (postal.item(postalChildIndex).getLocalName().equals("CountryID")) { if (postal.item(postalChildIndex).getLocalName().equals("CountryID")) {
setCountry(postal.item(postalChildIndex).getTextContent()); setCountry(postal.item(postalChildIndex).getTextContent());
} }
} }
} }
} }
if (itemChilds.item(itemChildIndex).getLocalName().equals("URIUniversalCommunication")) { if (itemChilds.item(itemChildIndex).getLocalName().equals("URIUniversalCommunication")) {
NodeList URIchilds = itemChilds.item(itemChildIndex).getChildNodes(); NodeList URIchilds = itemChilds.item(itemChildIndex).getChildNodes();
@@ -419,8 +423,9 @@ public class TradeParty implements IZUGFeRDExportableTradeParty {
/*** /***
* sets the email of the organization (not the one of the contact person) * sets the email of the organization (not the one of the contact person)
* (while setEmail has to be defined here getEmail comes from IZUGFeRDExportableTradeParty) * (while setEmail has to be defined here getEmail comes from IZUGFeRDExportableTradeParty)
* @param eMail address of institution (not contact)
* @return fluent setter * @return fluent setter
*/ */
public TradeParty setEmail(String eMail) { public TradeParty setEmail(String eMail) {

View File

@@ -1,6 +1,7 @@
package org.mustangproject; package org.mustangproject;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.AbstractList; import java.util.AbstractList;
@@ -8,39 +9,47 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.RandomAccess; import java.util.RandomAccess;
import org.apache.commons.io.IOUtils;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
public class XMLTools extends XMLWriter { public class XMLTools extends XMLWriter {
@Override @Override
public String escapeAttributeEntities(String s) { public String escapeAttributeEntities(String s) {
return super.escapeAttributeEntities(s); return super.escapeAttributeEntities(s);
} }
@Override @Override
public String escapeElementEntities(String s) { public String escapeElementEntities(String s) {
return super.escapeElementEntities(s); return super.escapeElementEntities(s);
} }
public static List<Node> asList(NodeList n) { public static List<Node> asList(NodeList n) {
return n.getLength()==0? return n.getLength() == 0 ?
Collections.<Node>emptyList(): new NodeListWrapper(n); Collections.<Node>emptyList() : new NodeListWrapper(n);
} }
static final class NodeListWrapper extends AbstractList<Node> static final class NodeListWrapper extends AbstractList<Node>
implements RandomAccess { implements RandomAccess {
private final NodeList list; private final NodeList list;
NodeListWrapper(NodeList l) { NodeListWrapper(NodeList l) {
list=l; list = l;
} }
public Node get(int index) {
@Override
public Node get(int index) {
return list.item(index); return list.item(index);
} }
public int size() {
@Override
public int size() {
return list.getLength(); return list.getLength();
} }
} }
public static String nDigitFormat(BigDecimal value, int scale) { public static String nDigitFormat(BigDecimal value, int scale) {
/* /*
* I needed 123,45, locale independent.I tried * I needed 123,45, locale independent.I tried
@@ -63,9 +72,9 @@ public class XMLTools extends XMLWriter {
public static String encodeXML(CharSequence s) { public static String encodeXML(CharSequence s) {
if (s == null) { if (s == null) {
return ""; return "";
} }
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
final int len = s.length(); final int len = s.length();
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
@@ -112,36 +121,6 @@ public class XMLTools extends XMLWriter {
return sb.toString(); return sb.toString();
} }
/**
* Returns the Byte Order Mark size and thus allows to skips over a BOM
* at the beginning of the given ByteArrayInputStream, if one exists.
*
* @param is the ByteArrayInputStream used
* @throws IOException if can not be read from is
* @see <a href="https://www.w3.org/TR/xml/#sec-guessing">Autodetection of Character Encodings</a>
*
public static 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;
}*/
/*** /***
* removes utf8 byte order marks from byte arrays, in case one is there * removes utf8 byte order marks from byte arrays, in case one is there
* @param zugferdRaw the CII XML * @param zugferdRaw the CII XML
@@ -149,6 +128,7 @@ public class XMLTools extends XMLWriter {
*/ */
public static byte[] removeBOM(byte[] zugferdRaw) { public static byte[] removeBOM(byte[] zugferdRaw) {
final byte[] zugferdData; final byte[] zugferdData;
// This handles the UTF-8 BOM
if ((zugferdRaw[0] == (byte) 0xEF) && (zugferdRaw[1] == (byte) 0xBB) && (zugferdRaw[2] == (byte) 0xBF)) { if ((zugferdRaw[0] == (byte) 0xEF) && (zugferdRaw[1] == (byte) 0xBB) && (zugferdRaw[2] == (byte) 0xBF)) {
// I don't like BOMs, lets remove it // I don't like BOMs, lets remove it
zugferdData = new byte[zugferdRaw.length - 3]; zugferdData = new byte[zugferdRaw.length - 3];
@@ -159,5 +139,8 @@ public class XMLTools extends XMLWriter {
return zugferdData; return zugferdData;
} }
public static byte[] getBytesFromStream(InputStream fileinput) throws IOException {
return IOUtils.toByteArray (fileinput);
}
} }

View File

@@ -22,18 +22,18 @@ package org.mustangproject.ZUGFeRD;
import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE; import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE;
import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.mustangproject.*; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment;
import org.mustangproject.Invoice;
import org.mustangproject.XMLTools;
public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider { public class DAPullProvider extends ZUGFeRD2PullProvider {
protected IExportableTransaction trans; protected IExportableTransaction trans;
private String paymentTermsDescription;
protected Profile profile = Profiles.getByName(EStandard.despatchadvice,"pilot", 1); protected Profile profile = Profiles.getByName(EStandard.despatchadvice,"pilot", 1);
@@ -249,13 +249,9 @@ public class DAPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider
+ "</SCRDMCCBDACIDAMessageStructure>"; + "</SCRDMCCBDACIDAMessageStructure>";
final byte[] zugferdRaw; final byte[] zugferdRaw;
try { zugferdRaw = xml.getBytes(StandardCharsets.UTF_8);
zugferdRaw = xml.getBytes("UTF-8");
zugferdData = XMLTools.removeBOM(zugferdRaw); zugferdData = XMLTools.removeBOM(zugferdRaw);
} catch (final UnsupportedEncodingException e) {
Logger.getLogger(OXPullProvider.class.getName()).log(Level.SEVERE, null, e);
}
} }

View File

@@ -19,31 +19,35 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import javax.activation.DataSource;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class DXExporterFromA1 extends DXExporterFromA3 implements IZUGFeRDExporter { import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.ValidationResult;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import jakarta.activation.DataSource;
public class DXExporterFromA1 extends DXExporterFromA3 {
protected boolean ignorePDFAErrors = false; protected boolean ignorePDFAErrors = false;
public DXExporterFromA1 ignorePDFAErrors() { @Override
public DXExporterFromA1 ignorePDFAErrors() {
this.ignorePDFAErrors = true; this.ignorePDFAErrors = true;
return this; return this;
} }
private static boolean isValidA1(DataSource dataSource) throws IOException { private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(new PreflightParser(dataSource)); return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser (dataSource));
} }
/*** /***
* internal helper function: get namespace for order-x * internal helper function: get namespace for order-x
* @param ver the delivery-x version * @param ver the delivery-x version
* @return the URN of the namespace * @return the URN of the namespace
*/ */
public String getNamespaceForVersion(int ver) { @Override
public String getNamespaceForVersion(int ver) {
// As of late 2022 the Delivery-X standard is not yet published. See specification: // As of late 2022 the Delivery-X standard is not yet published. See specification:
// Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022 // Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022
// Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3 // Chapter 7.1 XMP-Erweiterungsschema für PDF/A-3
@@ -55,7 +59,8 @@ public class DXExporterFromA1 extends DXExporterFromA3 implements IZUGFeRDExport
* @param ver the ox version * @param ver the ox version
* @return the namespace prefix as string, without colon * @return the namespace prefix as string, without colon
*/ */
public String getPrefixForVersion(int ver) { @Override
public String getPrefixForVersion(int ver) {
return "fx"; return "fx";
} }
@@ -65,19 +70,19 @@ public class DXExporterFromA1 extends DXExporterFromA3 implements IZUGFeRDExport
* NonSequentialParser. Some additional controls are present to check a set of * NonSequentialParser. Some additional controls are present to check a set of
* PDF/A requirements. (Stream length consistency, EOL after some Keyword...) * PDF/A requirements. (Stream length consistency, EOL after some Keyword...)
*/ */
parser.parse();// might add a Format.PDF_A1A as parameter and iterate through A1 and A3 // might add a Format.PDF_A1A as parameter and iterate through A1 and A3
try (PreflightDocument document = parser.getPreflightDocument()) { try (PreflightDocument document = (PreflightDocument) parser.parse()) {
/* /*
* Once the syntax validation is done, the parser can provide a * Once the syntax validation is done, the parser can provide a
* PreflightDocument (that inherits from PDDocument) This document process the * PreflightDocument (that inherits from PDDocument) This document process the
* end of PDF/A validation. * end of PDF/A validation.
*/ */
document.validate(); ValidationResult res = document.validate();
// Get validation result // Get validation result
return document.getResult().isValid(); return res.isValid();
} catch (ValidationException e) { } catch (ValidationException e) {
/* /*
* the parse method can throw a SyntaxValidationException if the PDF file can't * the parse method can throw a SyntaxValidationException if the PDF file can't
@@ -89,14 +94,17 @@ public class DXExporterFromA1 extends DXExporterFromA3 implements IZUGFeRDExport
} }
public DXExporterFromA1 setProfile(Profile p) { @Override
public DXExporterFromA1 setProfile(Profile p) {
return (DXExporterFromA1)super.setProfile(p); return (DXExporterFromA1)super.setProfile(p);
} }
public DXExporterFromA1 setProfile(String profileName) { @Override
public DXExporterFromA1 setProfile(String profileName) {
return (DXExporterFromA1)super.setProfile(profileName); return (DXExporterFromA1)super.setProfile(profileName);
} }
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException { @Override
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException {
if (!ignorePDFAErrors && !isValidA1(dataSource)) { if (!ignorePDFAErrors && !isValidA1(dataSource)) {
throw new IOException("File is not a valid PDF/A input file"); throw new IOException("File is not a valid PDF/A input file");
} }
@@ -108,32 +116,41 @@ public class DXExporterFromA1 extends DXExporterFromA3 implements IZUGFeRDExport
} }
public DXExporterFromA1 load(String pdfFilename) throws IOException { @Override
public DXExporterFromA1 load(String pdfFilename) throws IOException {
return (DXExporterFromA1) super.load(pdfFilename); return (DXExporterFromA1) super.load(pdfFilename);
} }
public DXExporterFromA1 load(byte[] pdfBinary) throws IOException { @Override
public DXExporterFromA1 load(byte[] pdfBinary) throws IOException {
return (DXExporterFromA1) super.load(pdfBinary); return (DXExporterFromA1) super.load(pdfBinary);
} }
public DXExporterFromA1 load(InputStream pdfSource) throws IOException{ @Override
public DXExporterFromA1 load(InputStream pdfSource) throws IOException{
return (DXExporterFromA1) super.load(pdfSource); return (DXExporterFromA1) super.load(pdfSource);
} }
public DXExporterFromA1 setCreator(String creator) { @Override
public DXExporterFromA1 setCreator(String creator) {
return (DXExporterFromA1) super.setCreator(creator); return (DXExporterFromA1) super.setCreator(creator);
} }
public DXExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) { @Override
public DXExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) {
return (DXExporterFromA1) super.setConformanceLevel(newLevel); return (DXExporterFromA1) super.setConformanceLevel(newLevel);
} }
public DXExporterFromA1 setProducer(String producer){ @Override
public DXExporterFromA1 setProducer(String producer){
return (DXExporterFromA1) super.setProducer(producer); return (DXExporterFromA1) super.setProducer(producer);
} }
public DXExporterFromA1 setZUGFeRDVersion(int version){ @Override
public DXExporterFromA1 setZUGFeRDVersion(int version){
return (DXExporterFromA1) super.setZUGFeRDVersion(version); return (DXExporterFromA1) super.setZUGFeRDVersion(version);
} }
public DXExporterFromA1 setXML(byte[] zugferdData) throws IOException{ @Override
public DXExporterFromA1 setXML(byte[] zugferdData) throws IOException{
return (DXExporterFromA1) super.setXML(zugferdData); return (DXExporterFromA1) super.setXML(zugferdData);
} }
public DXExporterFromA1 disableAutoClose(boolean disableAutoClose){ @Override
public DXExporterFromA1 disableAutoClose(boolean disableAutoClose){
return (DXExporterFromA1) super.disableAutoClose(disableAutoClose); return (DXExporterFromA1) super.disableAutoClose(disableAutoClose);
} }
public DXExporterFromA1 convertOnly() { public DXExporterFromA1 convertOnly() {

View File

@@ -20,16 +20,38 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.cos.*; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.TransformerException;
import org.apache.pdfbox.Loader;
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.io.IOUtils; import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo; import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent; import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema; import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema; import org.apache.xmpbox.schema.DublinCoreSchema;
@@ -43,16 +65,13 @@ import org.apache.xmpbox.xml.XmpSerializer;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
import javax.activation.DataSource; import jakarta.activation.DataSource;
import javax.activation.FileDataSource; import jakarta.activation.FileDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.*;
public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 { public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE; protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE;
protected ArrayList<FileAttachment> fileAttachments = new ArrayList<FileAttachment>(); protected ArrayList<FileAttachment> fileAttachments = new ArrayList<>();
/** /**
* This flag controls whether or not the metadata is overwritten, or kind of merged. * This flag controls whether or not the metadata is overwritten, or kind of merged.
@@ -108,9 +127,6 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
protected String despatchAdviceDocumentType = "DESPATCHADVICE"; protected String despatchAdviceDocumentType = "DESPATCHADVICE";
private HashMap<String, byte[]> additionalXMLs = new HashMap<String, byte[]>();
private boolean attachZUGFeRDHeaders = true; private boolean attachZUGFeRDHeaders = true;
/** /**
@@ -119,7 +135,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfFilename filename of an PDF/A1 compliant document * @param pdfFilename filename of an PDF/A1 compliant document
*/ */
public DXExporterFromA3 load(String pdfFilename) throws IOException { @Override
public DXExporterFromA3 load(String pdfFilename) throws IOException {
ensurePDFIsValid(new FileDataSource(pdfFilename)); ensurePDFIsValid(new FileDataSource(pdfFilename));
try (FileInputStream pdf = new FileInputStream(pdfFilename)) { try (FileInputStream pdf = new FileInputStream(pdfFilename)) {
@@ -127,11 +144,13 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
} }
} }
public IXMLProvider getProvider() { @Override
public IXMLProvider getProvider() {
return xmlProvider; return xmlProvider;
} }
public DXExporterFromA3 setProfile(Profile p) { @Override
public DXExporterFromA3 setProfile(Profile p) {
this.profile = p; this.profile = p;
if (xmlProvider != null) { if (xmlProvider != null) {
xmlProvider.setProfile(p); xmlProvider.setProfile(p);
@@ -139,7 +158,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
public DXExporterFromA3 setProfile(String profilename) { @Override
public DXExporterFromA3 setProfile(String profilename) {
this.profile = Profiles.getByName(profilename); this.profile = Profiles.getByName(profilename);
if (xmlProvider != null) { if (xmlProvider != null) {
@@ -148,7 +168,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
public DXExporterFromA3 addAdditionalFile(String name, byte[] content) { @Override
public DXExporterFromA3 addAdditionalFile(String name, byte[] content) {
fileAttachments.add(new FileAttachment(name, "text/xml", "Supplement", content).setDescription("ZUGFeRD extension/additional data")); fileAttachments.add(new FileAttachment(name, "text/xml", "Supplement", content).setDescription("ZUGFeRD extension/additional data"));
return this; return this;
} }
@@ -161,9 +182,10 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfBinary binary of a PDF/A1 compliant document * @param pdfBinary binary of a PDF/A1 compliant document
*/ */
public DXExporterFromA3 load(byte[] pdfBinary) throws IOException { @Override
public DXExporterFromA3 load(byte[] pdfBinary) throws IOException {
ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary))); ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary)));
doc = PDDocument.load(pdfBinary); doc = Loader.loadPDF(pdfBinary);
return this; return this;
} }
@@ -171,11 +193,13 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
super(); super();
} }
public void attachFile(FileAttachment file) { @Override
public void attachFile(FileAttachment file) {
fileAttachments.add(file); fileAttachments.add(file);
} }
public void attachFile(String filename, byte[] data, String mimetype, String relation) { @Override
public void attachFile(String filename, byte[] data, String mimetype, String relation) {
FileAttachment fa = new FileAttachment(filename, mimetype, relation, data); FileAttachment fa = new FileAttachment(filename, mimetype, relation, data);
fileAttachments.add(fa); fileAttachments.add(fa);
} }
@@ -185,7 +209,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param ZUGFeRDfilename the pdf file name * @param ZUGFeRDfilename the pdf file name
* @throws IOException if anything is wrong in the target location * @throws IOException if anything is wrong in the target location
*/ */
public void export(String ZUGFeRDfilename) throws IOException { @Override
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -212,7 +237,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param output the OutputStream * @param output the OutputStream
* @throws IOException if anything is wrong in the OutputStream * @throws IOException if anything is wrong in the OutputStream
*/ */
public void export(OutputStream output) throws IOException { @Override
public void export(OutputStream output) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -238,7 +264,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param data the binary data of the file/attachment * @param data the binary data of the file/attachment
* @throws IOException if anything is wrong with filename * @throws IOException if anything is wrong with filename
*/ */
public void PDFAttachGenericFile(String filename, String relationship, String description, @Override
public void PDFAttachGenericFile(String filename, String relationship, String description,
String subType, byte[] data) throws IOException { String subType, byte[] data) throws IOException {
PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data); PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data);
} }
@@ -255,7 +282,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param data the binary data of the file/attachment * @param data the binary data of the file/attachment
* @throws IOException if anything is wrong with filename * @throws IOException if anything is wrong with filename
*/ */
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description, @Override
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException { String subType, byte[] data) throws IOException {
fileAttached = true; fileAttached = true;
@@ -274,7 +302,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
ef.setSize(data.length); ef.setSize(data.length);
ef.setCreationDate(new GregorianCalendar()); ef.setCreationDate(new GregorianCalendar());
ef.setModDate(GregorianCalendar.getInstance()); ef.setModDate(Calendar.getInstance());
fs.setEmbeddedFile(ef); fs.setEmbeddedFile(ef);
@@ -306,7 +334,7 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
doc.getDocumentCatalog().setNames(names); doc.getDocumentCatalog().setNames(names);
// AF entry (Array) in catalog with the FileSpec // AF entry (Array) in catalog with the FileSpec
COSBase AFEntry = (COSBase) doc.getDocumentCatalog().getCOSObject().getItem("AF"); COSBase AFEntry = doc.getDocumentCatalog().getCOSObject().getItem("AF");
if ((AFEntry == null)) { if ((AFEntry == null)) {
COSArray cosArray = new COSArray(); COSArray cosArray = new COSArray();
cosArray.add(fs); cosArray.add(fs);
@@ -332,7 +360,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param zugferdData XML data to be set as a byte array (XML file in raw form). * @param zugferdData XML data to be set as a byte array (XML file in raw form).
* @throws IOException (should not happen) * @throws IOException (should not happen)
*/ */
public DXExporterFromA3 setXML(byte[] zugferdData) throws IOException { @Override
public DXExporterFromA3 setXML(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider(); CustomXMLProvider cus = new CustomXMLProvider();
// As of late 2022 the Delivery-X standard is not yet published. See specification: // As of late 2022 the Delivery-X standard is not yet published. See specification:
// Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022 // Die digitale Ablösung des Papier-Lieferscheins, Version 1.1, April 2022
@@ -353,11 +382,13 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfSource source to read a PDF/A1 compliant document from * @param pdfSource source to read a PDF/A1 compliant document from
*/ */
public DXExporterFromA3 load(InputStream pdfSource) throws IOException { @Override
public DXExporterFromA3 load(InputStream pdfSource) throws IOException {
return load(readAllBytes(pdfSource)); return load(readAllBytes(pdfSource));
} }
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException { @Override
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException {
return true; return true;
} }
@@ -380,23 +411,27 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* <p> * <p>
* Feel free to pass "A" as new level if you know what you are doing :-) * Feel free to pass "A" as new level if you know what you are doing :-)
*/ */
public DXExporterFromA3 setConformanceLevel(PDFAConformanceLevel newLevel) { @Override
public DXExporterFromA3 setConformanceLevel(PDFAConformanceLevel newLevel) {
conformanceLevel = newLevel; conformanceLevel = newLevel;
return this; return this;
} }
public DXExporterFromA3 setCreator(String creator) { @Override
public DXExporterFromA3 setCreator(String creator) {
this.creator = creator; this.creator = creator;
return this; return this;
} }
public DXExporterFromA3 setCreatorTool(String creatorTool) { @Override
public DXExporterFromA3 setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool; this.creatorTool = creatorTool;
return this; return this;
} }
public DXExporterFromA3 setProducer(String producer) { @Override
public DXExporterFromA3 setProducer(String producer) {
this.producer = producer; this.producer = producer;
return this; return this;
} }
@@ -415,7 +450,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
protected DXExporterFromA3 setAttachZUGFeRDHeaders(boolean attachHeaders) { @Override
protected DXExporterFromA3 setAttachZUGFeRDHeaders(boolean attachHeaders) {
this.attachZUGFeRDHeaders = attachHeaders; this.attachZUGFeRDHeaders = attachHeaders;
return this; return this;
} }
@@ -428,7 +464,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param metadata the PDFbox XMPMetadata object * @param metadata the PDFbox XMPMetadata object
*/ */
protected void addXMP(XMPMetadata metadata) { @Override
protected void addXMP(XMPMetadata metadata) {
if (attachZUGFeRDHeaders) { if (attachZUGFeRDHeaders) {
// As of late 2022 the Delivery-X standard is not yet published. See specification: // As of late 2022 the Delivery-X standard is not yet published. See specification:
@@ -459,12 +496,14 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code> * <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF * @throws IOException if anything is wrong with already loaded PDF
*/ */
public IExporter setTransaction(IExportableTransaction trans) throws IOException { @Override
public IExporter setTransaction(IExportableTransaction trans) throws IOException {
this.trans = trans; this.trans = trans;
return prepare(); return prepare();
} }
public IExporter prepare() throws IOException { @Override
public IExporter prepare() throws IOException {
prepareDocument(); prepareDocument();
xmlProvider.generateXML(trans); xmlProvider.generateXML(trans);
String filename = "cida.xml"; String filename = "cida.xml";
@@ -483,7 +522,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Reads the XMPMetadata from the PDDocument, if it exists. * Reads the XMPMetadata from the PDDocument, if it exists.
* Otherwise creates XMPMetadata. * Otherwise creates XMPMetadata.
*/ */
protected XMPMetadata getXmpMetadata() throws IOException { @Override
protected XMPMetadata getXmpMetadata() throws IOException {
PDMetadata meta = doc.getDocumentCatalog().getMetadata(); PDMetadata meta = doc.getDocumentCatalog().getMetadata();
if ((meta != null) && (meta.getLength() > 0)) { if ((meta != null) && (meta.getLength() > 0)) {
try { try {
@@ -496,7 +536,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return XMPMetadata.createXMPMetadata(); return XMPMetadata.createXMPMetadata();
} }
protected byte[] serializeXmpMetadata(XMPMetadata xmpMetadata) throws TransformerException { @Override
protected byte[] serializeXmpMetadata(XMPMetadata xmpMetadata) throws TransformerException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
new XmpSerializer().serialize(xmpMetadata, buffer, true); // see https://github.com/ZUGFeRD/mustangproject/issues/44 new XmpSerializer().serialize(xmpMetadata, buffer, true); // see https://github.com/ZUGFeRD/mustangproject/issues/44
return buffer.toByteArray(); return buffer.toByteArray();
@@ -506,7 +547,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Sets the producer if the overwrite flag is set or the producer is not already set. * Sets the producer if the overwrite flag is set or the producer is not already set.
* Sets the PDFVersion to 1.4 if the field is empty. * Sets the PDFVersion to 1.4 if the field is empty.
*/ */
protected void writeAdobePDFSchema(XMPMetadata xmp) { @Override
protected void writeAdobePDFSchema(XMPMetadata xmp) {
AdobePDFSchema pdf = getAdobePDFSchema(xmp); AdobePDFSchema pdf = getAdobePDFSchema(xmp);
if (overwrite || isEmpty(pdf.getProducer())) if (overwrite || isEmpty(pdf.getProducer()))
pdf.setProducer(producer); pdf.setProducer(producer);
@@ -516,7 +558,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Returns the AdobePDFSchema from the XMPMetadata if it exists. * Returns the AdobePDFSchema from the XMPMetadata if it exists.
* If the overwrite flag is set or no AdobePDFSchema exists in the XMPMetadata, it is created, added and returned. * If the overwrite flag is set or no AdobePDFSchema exists in the XMPMetadata, it is created, added and returned.
*/ */
protected AdobePDFSchema getAdobePDFSchema(XMPMetadata xmp) { @Override
protected AdobePDFSchema getAdobePDFSchema(XMPMetadata xmp) {
AdobePDFSchema pdf = xmp.getAdobePDFSchema(); AdobePDFSchema pdf = xmp.getAdobePDFSchema();
if (pdf != null) if (pdf != null)
if (overwrite) if (overwrite)
@@ -526,7 +569,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddAdobePDFSchema(); return xmp.createAndAddAdobePDFSchema();
} }
protected void writePDFAIdentificationSchema(XMPMetadata xmp) { @Override
protected void writePDFAIdentificationSchema(XMPMetadata xmp) {
PDFAIdentificationSchema pdfaid = getPDFAIdentificationSchema(xmp); PDFAIdentificationSchema pdfaid = getPDFAIdentificationSchema(xmp);
if (overwrite || isEmpty(pdfaid.getConformance())) { if (overwrite || isEmpty(pdfaid.getConformance())) {
try { try {
@@ -541,17 +585,19 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
pdfaid.setPart(3); pdfaid.setPart(3);
} }
protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) { @Override
PDFAIdentificationSchema pdfaid = xmp.getPDFIdentificationSchema(); protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) {
PDFAIdentificationSchema pdfaid = xmp.getPDFAIdentificationSchema();
if (pdfaid != null) if (pdfaid != null)
if (overwrite) if (overwrite)
xmp.removeSchema(pdfaid); xmp.removeSchema(pdfaid);
else else
return pdfaid; return pdfaid;
return xmp.createAndAddPFAIdentificationSchema(); return xmp.createAndAddPDFAIdentificationSchema();
} }
protected void writeDublinCoreSchema(XMPMetadata xmp) { @Override
protected void writeDublinCoreSchema(XMPMetadata xmp) {
DublinCoreSchema dc = getDublinCoreSchema(xmp); DublinCoreSchema dc = getDublinCoreSchema(xmp);
if (dc.getFormat() == null) if (dc.getFormat() == null)
dc.setFormat("application/pdf"); dc.setFormat("application/pdf");
@@ -574,7 +620,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
} }
} }
protected DublinCoreSchema getDublinCoreSchema(XMPMetadata xmp) { @Override
protected DublinCoreSchema getDublinCoreSchema(XMPMetadata xmp) {
DublinCoreSchema dc = xmp.getDublinCoreSchema(); DublinCoreSchema dc = xmp.getDublinCoreSchema();
if (dc != null) if (dc != null)
if (overwrite) if (overwrite)
@@ -584,15 +631,17 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddDublinCoreSchema(); return xmp.createAndAddDublinCoreSchema();
} }
protected void writeXMLBasicSchema(XMPMetadata xmp) { @Override
protected void writeXMLBasicSchema(XMPMetadata xmp) {
XMPBasicSchema xsb = getXmpBasicSchema(xmp); XMPBasicSchema xsb = getXmpBasicSchema(xmp);
if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool())) if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool()))
xsb.setCreatorTool(creatorTool); xsb.setCreatorTool(creatorTool);
if (overwrite || xsb.getCreateDate() == null) if (overwrite || xsb.getCreateDate() == null)
xsb.setCreateDate(GregorianCalendar.getInstance()); xsb.setCreateDate(Calendar.getInstance());
} }
protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) { @Override
protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) {
XMPBasicSchema xsb = xmp.getXMPBasicSchema(); XMPBasicSchema xsb = xmp.getXMPBasicSchema();
if (xsb != null) if (xsb != null)
if (overwrite) if (overwrite)
@@ -602,7 +651,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddXMPBasicSchema(); return xmp.createAndAddXMPBasicSchema();
} }
protected void writeDocumentInformation() { @Override
protected void writeDocumentInformation() {
String fullProducer = producer + " (via mustangproject.org " + Version.VERSION + ")"; String fullProducer = producer + " (via mustangproject.org " + Version.VERSION + ")";
PDDocumentInformation info = doc.getDocumentInformation(); PDDocumentInformation info = doc.getDocumentInformation();
if (overwrite || info.getCreationDate() == null) if (overwrite || info.getCreationDate() == null)
@@ -624,7 +674,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds an OutputIntent and the sRGB color profile if no OutputIntent exist * Adds an OutputIntent and the sRGB color profile if no OutputIntent exist
*/ */
protected void addSRGBOutputIntend() throws IOException { @Override
protected void addSRGBOutputIntend() throws IOException {
if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) { if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) {
return; return;
} }
@@ -647,7 +698,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds a MarkInfo element to the PDF if it doesn't already exist and sets it as marked. * Adds a MarkInfo element to the PDF if it doesn't already exist and sets it as marked.
*/ */
protected void setMarked() { @Override
protected void setMarked() {
PDDocumentCatalog catalog = doc.getDocumentCatalog(); PDDocumentCatalog catalog = doc.getDocumentCatalog();
if (catalog.getMarkInfo() == null) { if (catalog.getMarkInfo() == null) {
catalog.setMarkInfo(new PDMarkInfo(doc.getPages().getCOSObject())); catalog.setMarkInfo(new PDMarkInfo(doc.getPages().getCOSObject()));
@@ -658,7 +710,8 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds a StructureTreeRoot element to the PDF if it doesn't already exist. * Adds a StructureTreeRoot element to the PDF if it doesn't already exist.
*/ */
protected void addStructureTreeRoot() { @Override
protected void addStructureTreeRoot() {
if (doc.getDocumentCatalog().getStructureTreeRoot() == null) { if (doc.getDocumentCatalog().getStructureTreeRoot() == null) {
doc.getDocumentCatalog().setStructureTreeRoot(new PDStructureTreeRoot()); doc.getDocumentCatalog().setStructureTreeRoot(new PDStructureTreeRoot());
} }
@@ -668,19 +721,22 @@ public class DXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* @return if pdf file will be automatically closed after adding ZF * @return if pdf file will be automatically closed after adding ZF
*/ */
public boolean isAutoCloseDisabled() { @Override
public boolean isAutoCloseDisabled() {
return disableAutoClose; return disableAutoClose;
} }
/** /**
* @param disableAutoClose prevent PDF file from being closed after adding ZF * @param disableAutoClose prevent PDF file from being closed after adding ZF
*/ */
public DXExporterFromA3 disableAutoClose(boolean disableAutoClose) { @Override
public DXExporterFromA3 disableAutoClose(boolean disableAutoClose) {
this.disableAutoClose = disableAutoClose; this.disableAutoClose = disableAutoClose;
return this; return this;
} }
protected void setXMLProvider(IXMLProvider p) { @Override
protected void setXMLProvider(IXMLProvider p) {
this.xmlProvider = p; this.xmlProvider = p;
if (profile != null) { if (profile != null) {
xmlProvider.setProfile(profile); xmlProvider.setProfile(profile);

View File

@@ -20,31 +20,10 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.cos.*; import java.io.File;
import org.apache.pdfbox.io.IOUtils; import java.io.FileOutputStream;
import org.apache.pdfbox.pdmodel.*; import java.io.IOException;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import java.io.OutputStream;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
public class EinLieferscheinExporter implements IExporter { public class EinLieferscheinExporter implements IExporter {
IXMLProvider xmlProvider; IXMLProvider xmlProvider;

View File

@@ -140,6 +140,11 @@ public interface IExportableTransaction {
return null; return null;
} }
default IZUGFeRDCashDiscount[] getCashDiscounts() { return null; }
/***
* @return the invoice line items with the positions
*/
IZUGFeRDExportableItem[] getZFItems(); IZUGFeRDExportableItem[] getZFItems();
/** /**

View File

@@ -18,7 +18,6 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;

View File

@@ -15,10 +15,10 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
/** /**
* The interface for allowances or charges, to be used by the pullprovider * The interface for allowances or charges, to be used by the pullprovider
* @author AlexanderSchmidt * @author AlexanderSchmidt

View File

@@ -0,0 +1,18 @@
package org.mustangproject.ZUGFeRD;
public interface IZUGFeRDCashDiscount {
/***
* @return this particular cash discount as cross industry invoice XML
*/
public String getAsCII();
/***
* since EN16931 voted not to have (or even allow) cash discounts in their core invoice the german
* XRechnung CIUS defined it's own proprietary format for a freetext field
* @return this particular cash discount in proprietary xrechnung format
*/
public String getAsXRechnung();
}

View File

@@ -26,12 +26,13 @@ package org.mustangproject.ZUGFeRD;
* @author jstaerk * @author jstaerk
* */ * */
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.mustangproject.Item;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import org.mustangproject.Item;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonDeserialize(as = Item.class) @JsonDeserialize(as = Item.class)
public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
IZUGFeRDExportableProduct getProduct(); IZUGFeRDExportableProduct getProduct();
@@ -140,6 +141,6 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
*/ */
default IZUGFeRDAllowanceCharge[] getItemTotalAllowances() { default IZUGFeRDAllowanceCharge[] getItemTotalAllowances() {
return null; return null;
}; }
} }

View File

@@ -21,6 +21,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.HashMap;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
@@ -156,4 +157,12 @@ public interface IZUGFeRDExportableProduct {
} }
return null; return null;
} }
default String getCountryOfOrigin() {
return null;
}
default HashMap<String, String> getAttributes() {
return null;
}
} }

View File

@@ -20,8 +20,6 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.mustangproject.TradeParty;
/** /**
* 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
* *

View File

@@ -18,11 +18,12 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import javax.activation.DataSource;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import jakarta.activation.DataSource;
public interface IZUGFeRDExporter extends Closeable, IExporter { public interface IZUGFeRDExporter extends Closeable, IExporter {
/** /**
* factory: loads a PDF file and returns an appropriate exporter * factory: loads a PDF file and returns an appropriate exporter

View File

@@ -18,11 +18,6 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import javax.activation.DataSource;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
public interface IZUGFeRDLegalOrganisation { public interface IZUGFeRDLegalOrganisation {
/*** /***

View File

@@ -1,6 +1,7 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
/*** /***
* the linecalculator does the math within an item line, and e.g. calculates quantity*price. * the linecalculator does the math within an item line, and e.g. calculates quantity*price.
@@ -40,7 +41,7 @@ public class LineCalculator {
priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159 priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159
price = priceGross.subtract(allowance).add(charge); price = priceGross.subtract(allowance).add(charge);
itemTotalNetAmount = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity()) itemTotalNetAmount = currentItem.getQuantity().multiply(getPrice()).divide(currentItem.getBasisQuantity())
.subtract(allowanceItemTotal).setScale(2, BigDecimal.ROUND_HALF_UP); .subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator); itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);
} }

View File

@@ -19,31 +19,35 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import javax.activation.DataSource;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
public class OXExporterFromA1 extends OXExporterFromA3 implements IZUGFeRDExporter { import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.ValidationResult;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import jakarta.activation.DataSource;
public class OXExporterFromA1 extends OXExporterFromA3 {
protected boolean ignorePDFAErrors = false; protected boolean ignorePDFAErrors = false;
public OXExporterFromA1 ignorePDFAErrors() { @Override
public OXExporterFromA1 ignorePDFAErrors() {
this.ignorePDFAErrors = true; this.ignorePDFAErrors = true;
return this; return this;
} }
private static boolean isValidA1(DataSource dataSource) throws IOException { private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(new PreflightParser(dataSource)); return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser(dataSource));
} }
/*** /***
* internal helper function: get namespace for order-x * internal helper function: get namespace for order-x
* @param ver the order-x version * @param ver the order-x version
* @return the URN of the namespace * @return the URN of the namespace
*/ */
public String getNamespaceForVersion(int ver) { @Override
public String getNamespaceForVersion(int ver) {
return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#"; return "urn:factur-x:pdfa:CrossIndustryDocument:1p0#";
} }
/*** /***
@@ -51,7 +55,8 @@ public class OXExporterFromA1 extends OXExporterFromA3 implements IZUGFeRDExport
* @param ver the ox version * @param ver the ox version
* @return the namespace prefix as string, without colon * @return the namespace prefix as string, without colon
*/ */
public String getPrefixForVersion(int ver) { @Override
public String getPrefixForVersion(int ver) {
return "fx"; return "fx";
} }
@@ -61,19 +66,19 @@ public class OXExporterFromA1 extends OXExporterFromA3 implements IZUGFeRDExport
* NonSequentialParser. Some additional controls are present to check a set of * NonSequentialParser. Some additional controls are present to check a set of
* PDF/A requirements. (Stream length consistency, EOL after some Keyword...) * PDF/A requirements. (Stream length consistency, EOL after some Keyword...)
*/ */
parser.parse();// might add a Format.PDF_A1A as parameter and iterate through A1 and A3 // might add a Format.PDF_A1A as parameter and iterate through A1 and A3
try (PreflightDocument document = parser.getPreflightDocument()) { try (PreflightDocument document = (PreflightDocument) parser.parse()) {
/* /*
* Once the syntax validation is done, the parser can provide a * Once the syntax validation is done, the parser can provide a
* PreflightDocument (that inherits from PDDocument) This document process the * PreflightDocument (that inherits from PDDocument) This document process the
* end of PDF/A validation. * end of PDF/A validation.
*/ */
document.validate(); ValidationResult res = document.validate();
// Get validation result // Get validation result
return document.getResult().isValid(); return res.isValid();
} catch (ValidationException e) { } catch (ValidationException e) {
/* /*
* the parse method can throw a SyntaxValidationException if the PDF file can't * the parse method can throw a SyntaxValidationException if the PDF file can't
@@ -85,14 +90,17 @@ public class OXExporterFromA1 extends OXExporterFromA3 implements IZUGFeRDExport
} }
public OXExporterFromA1 setProfile(Profile p) { @Override
public OXExporterFromA1 setProfile(Profile p) {
return (OXExporterFromA1)super.setProfile(p); return (OXExporterFromA1)super.setProfile(p);
} }
public OXExporterFromA1 setProfile(String profileName) { @Override
public OXExporterFromA1 setProfile(String profileName) {
return (OXExporterFromA1)super.setProfile(profileName); return (OXExporterFromA1)super.setProfile(profileName);
} }
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException { @Override
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException {
if (!ignorePDFAErrors && !isValidA1(dataSource)) { if (!ignorePDFAErrors && !isValidA1(dataSource)) {
throw new IOException("File is not a valid PDF/A input file"); throw new IOException("File is not a valid PDF/A input file");
} }
@@ -104,32 +112,41 @@ public class OXExporterFromA1 extends OXExporterFromA3 implements IZUGFeRDExport
} }
public OXExporterFromA1 load(String pdfFilename) throws IOException { @Override
public OXExporterFromA1 load(String pdfFilename) throws IOException {
return (OXExporterFromA1) super.load(pdfFilename); return (OXExporterFromA1) super.load(pdfFilename);
} }
public OXExporterFromA1 load(byte[] pdfBinary) throws IOException { @Override
public OXExporterFromA1 load(byte[] pdfBinary) throws IOException {
return (OXExporterFromA1) super.load(pdfBinary); return (OXExporterFromA1) super.load(pdfBinary);
} }
public OXExporterFromA1 load(InputStream pdfSource) throws IOException{ @Override
public OXExporterFromA1 load(InputStream pdfSource) throws IOException{
return (OXExporterFromA1) super.load(pdfSource); return (OXExporterFromA1) super.load(pdfSource);
} }
public OXExporterFromA1 setCreator(String creator) { @Override
public OXExporterFromA1 setCreator(String creator) {
return (OXExporterFromA1) super.setCreator(creator); return (OXExporterFromA1) super.setCreator(creator);
} }
public OXExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) { @Override
public OXExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) {
return (OXExporterFromA1) super.setConformanceLevel(newLevel); return (OXExporterFromA1) super.setConformanceLevel(newLevel);
} }
public OXExporterFromA1 setProducer(String producer){ @Override
public OXExporterFromA1 setProducer(String producer){
return (OXExporterFromA1) super.setProducer(producer); return (OXExporterFromA1) super.setProducer(producer);
} }
public OXExporterFromA1 setZUGFeRDVersion(int version){ @Override
public OXExporterFromA1 setZUGFeRDVersion(int version){
return (OXExporterFromA1) super.setZUGFeRDVersion(version); return (OXExporterFromA1) super.setZUGFeRDVersion(version);
} }
public OXExporterFromA1 setXML(byte[] zugferdData) throws IOException{ @Override
public OXExporterFromA1 setXML(byte[] zugferdData) throws IOException{
return (OXExporterFromA1) super.setXML(zugferdData); return (OXExporterFromA1) super.setXML(zugferdData);
} }
public OXExporterFromA1 disableAutoClose(boolean disableAutoClose){ @Override
public OXExporterFromA1 disableAutoClose(boolean disableAutoClose){
return (OXExporterFromA1) super.disableAutoClose(disableAutoClose); return (OXExporterFromA1) super.disableAutoClose(disableAutoClose);
} }
public OXExporterFromA1 convertOnly() { public OXExporterFromA1 convertOnly() {

View File

@@ -20,20 +20,38 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.cos.*; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.TransformerException;
import org.apache.pdfbox.Loader;
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.io.IOUtils; import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo; import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo;
import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
import org.apache.pdfbox.pdmodel.font.PDCIDFontType2;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent; import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema; import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema; import org.apache.xmpbox.schema.DublinCoreSchema;
@@ -47,16 +65,13 @@ import org.apache.xmpbox.xml.XmpSerializer;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
import javax.activation.DataSource; import jakarta.activation.DataSource;
import javax.activation.FileDataSource; import jakarta.activation.FileDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.*;
public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 { public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE; protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE;
protected ArrayList<FileAttachment> fileAttachments = new ArrayList<FileAttachment>(); protected ArrayList<FileAttachment> fileAttachments = new ArrayList<>();
/** /**
* This flag controls whether or not the metadata is overwritten, or kind of merged. * This flag controls whether or not the metadata is overwritten, or kind of merged.
@@ -112,9 +127,6 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
protected String orderXDocumentType = "ORDER"; protected String orderXDocumentType = "ORDER";
private HashMap<String, byte[]> additionalXMLs = new HashMap<String, byte[]>();
private boolean attachZUGFeRDHeaders = true; private boolean attachZUGFeRDHeaders = true;
/** /**
@@ -123,7 +135,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfFilename filename of an PDF/A1 compliant document * @param pdfFilename filename of an PDF/A1 compliant document
*/ */
public OXExporterFromA3 load(String pdfFilename) throws IOException { @Override
public OXExporterFromA3 load(String pdfFilename) throws IOException {
ensurePDFIsValid(new FileDataSource(pdfFilename)); ensurePDFIsValid(new FileDataSource(pdfFilename));
try (FileInputStream pdf = new FileInputStream(pdfFilename)) { try (FileInputStream pdf = new FileInputStream(pdfFilename)) {
@@ -131,11 +144,13 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
} }
} }
public IXMLProvider getProvider() { @Override
public IXMLProvider getProvider() {
return xmlProvider; return xmlProvider;
} }
public OXExporterFromA3 setProfile(Profile p) { @Override
public OXExporterFromA3 setProfile(Profile p) {
this.profile = p; this.profile = p;
if (xmlProvider != null) { if (xmlProvider != null) {
xmlProvider.setProfile(p); xmlProvider.setProfile(p);
@@ -143,7 +158,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
public OXExporterFromA3 setProfile(String profilename) { @Override
public OXExporterFromA3 setProfile(String profilename) {
this.profile = Profiles.getByName(profilename); this.profile = Profiles.getByName(profilename);
if (xmlProvider != null) { if (xmlProvider != null) {
@@ -152,7 +168,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
public OXExporterFromA3 addAdditionalFile(String name, byte[] content) { @Override
public OXExporterFromA3 addAdditionalFile(String name, byte[] content) {
fileAttachments.add(new FileAttachment(name, "text/xml", "Supplement", content).setDescription("ZUGFeRD extension/additional data")); fileAttachments.add(new FileAttachment(name, "text/xml", "Supplement", content).setDescription("ZUGFeRD extension/additional data"));
return this; return this;
} }
@@ -165,9 +182,10 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfBinary binary of a PDF/A1 compliant document * @param pdfBinary binary of a PDF/A1 compliant document
*/ */
public OXExporterFromA3 load(byte[] pdfBinary) throws IOException { @Override
public OXExporterFromA3 load(byte[] pdfBinary) throws IOException {
ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary))); ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary)));
doc = PDDocument.load(pdfBinary); doc = Loader.loadPDF(pdfBinary);
return this; return this;
} }
@@ -175,11 +193,13 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
super(); super();
} }
public void attachFile(FileAttachment file) { @Override
public void attachFile(FileAttachment file) {
fileAttachments.add(file); fileAttachments.add(file);
} }
public void attachFile(String filename, byte[] data, String mimetype, String relation) { @Override
public void attachFile(String filename, byte[] data, String mimetype, String relation) {
FileAttachment fa = new FileAttachment(filename, mimetype, relation, data); FileAttachment fa = new FileAttachment(filename, mimetype, relation, data);
fileAttachments.add(fa); fileAttachments.add(fa);
} }
@@ -189,7 +209,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param ZUGFeRDfilename the pdf file name * @param ZUGFeRDfilename the pdf file name
* @throws IOException if anything is wrong in the target location * @throws IOException if anything is wrong in the target location
*/ */
public void export(String ZUGFeRDfilename) throws IOException { @Override
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -216,7 +237,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param output the OutputStream * @param output the OutputStream
* @throws IOException if anything is wrong in the OutputStream * @throws IOException if anything is wrong in the OutputStream
*/ */
public void export(OutputStream output) throws IOException { @Override
public void export(OutputStream output) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -242,7 +264,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param data the binary data of the file/attachment * @param data the binary data of the file/attachment
* @throws java.io.IOException if anything is wrong with filename * @throws java.io.IOException if anything is wrong with filename
*/ */
public void PDFAttachGenericFile(String filename, String relationship, String description, @Override
public void PDFAttachGenericFile(String filename, String relationship, String description,
String subType, byte[] data) throws IOException { String subType, byte[] data) throws IOException {
PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data); PDFAttachGenericFile(this.doc, filename, relationship, description, subType, data);
} }
@@ -259,7 +282,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param data the binary data of the file/attachment * @param data the binary data of the file/attachment
* @throws IOException if anything is wrong with filename * @throws IOException if anything is wrong with filename
*/ */
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description, @Override
public void PDFAttachGenericFile(PDDocument doc, String filename, String relationship, String description,
String subType, byte[] data) throws IOException { String subType, byte[] data) throws IOException {
fileAttached = true; fileAttached = true;
@@ -278,7 +302,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
ef.setSize(data.length); ef.setSize(data.length);
ef.setCreationDate(new GregorianCalendar()); ef.setCreationDate(new GregorianCalendar());
ef.setModDate(GregorianCalendar.getInstance()); ef.setModDate(Calendar.getInstance());
fs.setEmbeddedFile(ef); fs.setEmbeddedFile(ef);
@@ -310,7 +334,7 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
doc.getDocumentCatalog().setNames(names); doc.getDocumentCatalog().setNames(names);
// AF entry (Array) in catalog with the FileSpec // AF entry (Array) in catalog with the FileSpec
COSBase AFEntry = (COSBase) doc.getDocumentCatalog().getCOSObject().getItem("AF"); COSBase AFEntry = doc.getDocumentCatalog().getCOSObject().getItem("AF");
if ((AFEntry == null)) { if ((AFEntry == null)) {
COSArray cosArray = new COSArray(); COSArray cosArray = new COSArray();
cosArray.add(fs); cosArray.add(fs);
@@ -336,7 +360,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* @param zugferdData XML data to be set as a byte array (XML file in raw form). * @param zugferdData XML data to be set as a byte array (XML file in raw form).
* @throws IOException (should not happen) * @throws IOException (should not happen)
*/ */
public OXExporterFromA3 setXML(byte[] zugferdData) throws IOException { @Override
public OXExporterFromA3 setXML(byte[] zugferdData) throws IOException {
CustomXMLProvider cus = new CustomXMLProvider(); CustomXMLProvider cus = new CustomXMLProvider();
cus.setXML(zugferdData); cus.setXML(zugferdData);
this.setXMLProvider(cus); this.setXMLProvider(cus);
@@ -351,11 +376,13 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param pdfSource source to read a PDF/A1 compliant document from * @param pdfSource source to read a PDF/A1 compliant document from
*/ */
public OXExporterFromA3 load(InputStream pdfSource) throws IOException { @Override
public OXExporterFromA3 load(InputStream pdfSource) throws IOException {
return load(readAllBytes(pdfSource)); return load(readAllBytes(pdfSource));
} }
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException { @Override
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException {
return true; return true;
} }
@@ -378,23 +405,27 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* <p> * <p>
* Feel free to pass "A" as new level if you know what you are doing :-) * Feel free to pass "A" as new level if you know what you are doing :-)
*/ */
public OXExporterFromA3 setConformanceLevel(PDFAConformanceLevel newLevel) { @Override
public OXExporterFromA3 setConformanceLevel(PDFAConformanceLevel newLevel) {
conformanceLevel = newLevel; conformanceLevel = newLevel;
return this; return this;
} }
public OXExporterFromA3 setCreator(String creator) { @Override
public OXExporterFromA3 setCreator(String creator) {
this.creator = creator; this.creator = creator;
return this; return this;
} }
public OXExporterFromA3 setCreatorTool(String creatorTool) { @Override
public OXExporterFromA3 setCreatorTool(String creatorTool) {
this.creatorTool = creatorTool; this.creatorTool = creatorTool;
return this; return this;
} }
public OXExporterFromA3 setProducer(String producer) { @Override
public OXExporterFromA3 setProducer(String producer) {
this.producer = producer; this.producer = producer;
return this; return this;
} }
@@ -413,7 +444,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return this; return this;
} }
protected OXExporterFromA3 setAttachZUGFeRDHeaders(boolean attachHeaders) { @Override
protected OXExporterFromA3 setAttachZUGFeRDHeaders(boolean attachHeaders) {
this.attachZUGFeRDHeaders = attachHeaders; this.attachZUGFeRDHeaders = attachHeaders;
return this; return this;
} }
@@ -426,7 +458,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* *
* @param metadata the PDFbox XMPMetadata object * @param metadata the PDFbox XMPMetadata object
*/ */
protected void addXMP(XMPMetadata metadata) { @Override
protected void addXMP(XMPMetadata metadata) {
if (attachZUGFeRDHeaders) { if (attachZUGFeRDHeaders) {
XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, 1, true, xmlProvider.getProfile(), XMPSchemaZugferd zf = new XMPSchemaZugferd(metadata, 1, true, xmlProvider.getProfile(),
@@ -453,12 +486,14 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code> * <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF * @throws IOException if anything is wrong with already loaded PDF
*/ */
public IExporter setTransaction(IExportableTransaction trans) throws IOException { @Override
public IExporter setTransaction(IExportableTransaction trans) throws IOException {
this.trans = trans; this.trans = trans;
return prepare(); return prepare();
} }
public IExporter prepare() throws IOException { @Override
public IExporter prepare() throws IOException {
prepareDocument(); prepareDocument();
xmlProvider.generateXML(trans); xmlProvider.generateXML(trans);
String filename = "order-x.xml"; String filename = "order-x.xml";
@@ -477,7 +512,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Reads the XMPMetadata from the PDDocument, if it exists. * Reads the XMPMetadata from the PDDocument, if it exists.
* Otherwise creates XMPMetadata. * Otherwise creates XMPMetadata.
*/ */
protected XMPMetadata getXmpMetadata() throws IOException { @Override
protected XMPMetadata getXmpMetadata() throws IOException {
PDMetadata meta = doc.getDocumentCatalog().getMetadata(); PDMetadata meta = doc.getDocumentCatalog().getMetadata();
if ((meta != null) && (meta.getLength() > 0)) { if ((meta != null) && (meta.getLength() > 0)) {
try { try {
@@ -490,7 +526,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return XMPMetadata.createXMPMetadata(); return XMPMetadata.createXMPMetadata();
} }
protected byte[] serializeXmpMetadata(XMPMetadata xmpMetadata) throws TransformerException { @Override
protected byte[] serializeXmpMetadata(XMPMetadata xmpMetadata) throws TransformerException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream();
new XmpSerializer().serialize(xmpMetadata, buffer, true); // see https://github.com/ZUGFeRD/mustangproject/issues/44 new XmpSerializer().serialize(xmpMetadata, buffer, true); // see https://github.com/ZUGFeRD/mustangproject/issues/44
return buffer.toByteArray(); return buffer.toByteArray();
@@ -500,7 +537,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Sets the producer if the overwrite flag is set or the producer is not already set. * Sets the producer if the overwrite flag is set or the producer is not already set.
* Sets the PDFVersion to 1.4 if the field is empty. * Sets the PDFVersion to 1.4 if the field is empty.
*/ */
protected void writeAdobePDFSchema(XMPMetadata xmp) { @Override
protected void writeAdobePDFSchema(XMPMetadata xmp) {
AdobePDFSchema pdf = getAdobePDFSchema(xmp); AdobePDFSchema pdf = getAdobePDFSchema(xmp);
if (overwrite || isEmpty(pdf.getProducer())) if (overwrite || isEmpty(pdf.getProducer()))
pdf.setProducer(producer); pdf.setProducer(producer);
@@ -510,7 +548,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
* Returns the AdobePDFSchema from the XMPMetadata if it exists. * Returns the AdobePDFSchema from the XMPMetadata if it exists.
* If the overwrite flag is set or no AdobePDFSchema exists in the XMPMetadata, it is created, added and returned. * If the overwrite flag is set or no AdobePDFSchema exists in the XMPMetadata, it is created, added and returned.
*/ */
protected AdobePDFSchema getAdobePDFSchema(XMPMetadata xmp) { @Override
protected AdobePDFSchema getAdobePDFSchema(XMPMetadata xmp) {
AdobePDFSchema pdf = xmp.getAdobePDFSchema(); AdobePDFSchema pdf = xmp.getAdobePDFSchema();
if (pdf != null) if (pdf != null)
if (overwrite) if (overwrite)
@@ -520,7 +559,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddAdobePDFSchema(); return xmp.createAndAddAdobePDFSchema();
} }
protected void writePDFAIdentificationSchema(XMPMetadata xmp) { @Override
protected void writePDFAIdentificationSchema(XMPMetadata xmp) {
PDFAIdentificationSchema pdfaid = getPDFAIdentificationSchema(xmp); PDFAIdentificationSchema pdfaid = getPDFAIdentificationSchema(xmp);
if (overwrite || isEmpty(pdfaid.getConformance())) { if (overwrite || isEmpty(pdfaid.getConformance())) {
try { try {
@@ -535,17 +575,19 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
pdfaid.setPart(3); pdfaid.setPart(3);
} }
protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) { @Override
PDFAIdentificationSchema pdfaid = xmp.getPDFIdentificationSchema(); protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) {
PDFAIdentificationSchema pdfaid = xmp.getPDFAIdentificationSchema();
if (pdfaid != null) if (pdfaid != null)
if (overwrite) if (overwrite)
xmp.removeSchema(pdfaid); xmp.removeSchema(pdfaid);
else else
return pdfaid; return pdfaid;
return xmp.createAndAddPFAIdentificationSchema(); return xmp.createAndAddPDFAIdentificationSchema();
} }
protected void writeDublinCoreSchema(XMPMetadata xmp) { @Override
protected void writeDublinCoreSchema(XMPMetadata xmp) {
DublinCoreSchema dc = getDublinCoreSchema(xmp); DublinCoreSchema dc = getDublinCoreSchema(xmp);
if (dc.getFormat() == null) if (dc.getFormat() == null)
dc.setFormat("application/pdf"); dc.setFormat("application/pdf");
@@ -568,7 +610,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
} }
} }
protected DublinCoreSchema getDublinCoreSchema(XMPMetadata xmp) { @Override
protected DublinCoreSchema getDublinCoreSchema(XMPMetadata xmp) {
DublinCoreSchema dc = xmp.getDublinCoreSchema(); DublinCoreSchema dc = xmp.getDublinCoreSchema();
if (dc != null) if (dc != null)
if (overwrite) if (overwrite)
@@ -578,15 +621,17 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddDublinCoreSchema(); return xmp.createAndAddDublinCoreSchema();
} }
protected void writeXMLBasicSchema(XMPMetadata xmp) { @Override
protected void writeXMLBasicSchema(XMPMetadata xmp) {
XMPBasicSchema xsb = getXmpBasicSchema(xmp); XMPBasicSchema xsb = getXmpBasicSchema(xmp);
if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool())) if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool()))
xsb.setCreatorTool(creatorTool); xsb.setCreatorTool(creatorTool);
if (overwrite || xsb.getCreateDate() == null) if (overwrite || xsb.getCreateDate() == null)
xsb.setCreateDate(GregorianCalendar.getInstance()); xsb.setCreateDate(Calendar.getInstance());
} }
protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) { @Override
protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) {
XMPBasicSchema xsb = xmp.getXMPBasicSchema(); XMPBasicSchema xsb = xmp.getXMPBasicSchema();
if (xsb != null) if (xsb != null)
if (overwrite) if (overwrite)
@@ -596,7 +641,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
return xmp.createAndAddXMPBasicSchema(); return xmp.createAndAddXMPBasicSchema();
} }
protected void writeDocumentInformation() { @Override
protected void writeDocumentInformation() {
String fullProducer = producer + " (via mustangproject.org " + Version.VERSION + ")"; String fullProducer = producer + " (via mustangproject.org " + Version.VERSION + ")";
PDDocumentInformation info = doc.getDocumentInformation(); PDDocumentInformation info = doc.getDocumentInformation();
if (overwrite || info.getCreationDate() == null) if (overwrite || info.getCreationDate() == null)
@@ -618,7 +664,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds an OutputIntent and the sRGB color profile if no OutputIntent exist * Adds an OutputIntent and the sRGB color profile if no OutputIntent exist
*/ */
protected void addSRGBOutputIntend() throws IOException { @Override
protected void addSRGBOutputIntend() throws IOException {
if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) { if (!doc.getDocumentCatalog().getOutputIntents().isEmpty()) {
return; return;
} }
@@ -641,7 +688,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds a MarkInfo element to the PDF if it doesn't already exist and sets it as marked. * Adds a MarkInfo element to the PDF if it doesn't already exist and sets it as marked.
*/ */
protected void setMarked() { @Override
protected void setMarked() {
PDDocumentCatalog catalog = doc.getDocumentCatalog(); PDDocumentCatalog catalog = doc.getDocumentCatalog();
if (catalog.getMarkInfo() == null) { if (catalog.getMarkInfo() == null) {
catalog.setMarkInfo(new PDMarkInfo(doc.getPages().getCOSObject())); catalog.setMarkInfo(new PDMarkInfo(doc.getPages().getCOSObject()));
@@ -652,7 +700,8 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* Adds a StructureTreeRoot element to the PDF if it doesn't already exist. * Adds a StructureTreeRoot element to the PDF if it doesn't already exist.
*/ */
protected void addStructureTreeRoot() { @Override
protected void addStructureTreeRoot() {
if (doc.getDocumentCatalog().getStructureTreeRoot() == null) { if (doc.getDocumentCatalog().getStructureTreeRoot() == null) {
doc.getDocumentCatalog().setStructureTreeRoot(new PDStructureTreeRoot()); doc.getDocumentCatalog().setStructureTreeRoot(new PDStructureTreeRoot());
} }
@@ -662,19 +711,22 @@ public class OXExporterFromA3 extends ZUGFeRDExporterFromA3 {
/** /**
* @return if pdf file will be automatically closed after adding ZF * @return if pdf file will be automatically closed after adding ZF
*/ */
public boolean isAutoCloseDisabled() { @Override
public boolean isAutoCloseDisabled() {
return disableAutoClose; return disableAutoClose;
} }
/** /**
* @param disableAutoClose prevent PDF file from being closed after adding ZF * @param disableAutoClose prevent PDF file from being closed after adding ZF
*/ */
public OXExporterFromA3 disableAutoClose(boolean disableAutoClose) { @Override
public OXExporterFromA3 disableAutoClose(boolean disableAutoClose) {
this.disableAutoClose = disableAutoClose; this.disableAutoClose = disableAutoClose;
return this; return this;
} }
protected void setXMLProvider(IXMLProvider p) { @Override
protected void setXMLProvider(IXMLProvider p) {
this.xmlProvider = p; this.xmlProvider = p;
if (profile != null) { if (profile != null) {
xmlProvider.setProfile(profile); xmlProvider.setProfile(profile);

View File

@@ -22,26 +22,19 @@ package org.mustangproject.ZUGFeRD;
import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE; import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE;
import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE; import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE;
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
import org.mustangproject.IncludedNote;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider { public class OXPullProvider extends ZUGFeRD2PullProvider {
protected IExportableTransaction trans; protected IExportableTransaction trans;
protected TransactionCalculator calc; protected TransactionCalculator calc;
@@ -333,17 +326,19 @@ public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider
} }
} }
} }
if ((trans.getDocumentCode() == CORRECTEDINVOICE)/*||(trans.getDocumentCode() == DocumentCodeTypeConstants.CREDITNOTE)*/) { if (trans.getDocumentCode() != null) {
hasDueDate = false; if ((trans.getDocumentCode().equals(CORRECTEDINVOICE))/*||(trans.getDocumentCode().equals (DocumentCodeTypeConstants.CREDITNOTE))*/) {
hasDueDate = false;
}
} }
final Map<BigDecimal, VATAmount> VATPercentAmountMap = calc.getVATPercentAmountMap(); final Map<BigDecimal, VATAmount> VATPercentAmountMap = calc.getVATPercentAmountMap();
for (final BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) { for (final BigDecimal currentTaxPercent : VATPercentAmountMap.keySet()) {
final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent); final VATAmount amount = VATPercentAmountMap.get(currentTaxPercent);
if (amount != null) { if (amount != null) {
final String amountCategoryCode = amount.getCategoryCode(); /*final String amountCategoryCode = amount.getCategoryCode();
final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode); final boolean displayExemptionReason = CATEGORY_CODES_WITH_EXEMPTION_REASON.contains(amountCategoryCode);
/* xml += "<ram:ApplicableTradeTax>\n" xml += "<ram:ApplicableTradeTax>\n"
+ "<ram:CalculatedAmount>" + currencyFormat(amount.getCalculated()) + "<ram:CalculatedAmount>" + currencyFormat(amount.getCalculated())
+ "</ram:CalculatedAmount>\n" //currencyID=\"EUR\" + "</ram:CalculatedAmount>\n" //currencyID=\"EUR\"
+ "<ram:TypeCode>VAT</ram:TypeCode>\n" + "<ram:TypeCode>VAT</ram:TypeCode>\n"
@@ -479,13 +474,9 @@ public class OXPullProvider extends ZUGFeRD2PullProvider implements IXMLProvider
+ "</rsm:SCRDMCCBDACIOMessageStructure>"; + "</rsm:SCRDMCCBDACIOMessageStructure>";
final byte[] zugferdRaw; final byte[] zugferdRaw;
try { zugferdRaw = xml.getBytes(StandardCharsets.UTF_8);
zugferdRaw = xml.getBytes("UTF-8");
zugferdData = XMLTools.removeBOM(zugferdRaw); zugferdData = XMLTools.removeBOM(zugferdRaw);
} catch (final UnsupportedEncodingException e) {
Logger.getLogger(OXPullProvider.class.getName()).log(Level.SEVERE, null, e);
}
} }

View File

@@ -0,0 +1,99 @@
package org.mustangproject.ZUGFeRD;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import jakarta.activation.DataSource;
// Copied from PDFBox preflight 2.0.x
final class ByteArrayDataSource implements DataSource
{
private ByteArrayOutputStream data;
private String type = null;
private String name = null;
public ByteArrayDataSource (InputStream is) throws IOException
{
data = new ByteArrayOutputStream ();
IOUtils.copy (is, data);
IOUtils.closeQuietly (is);
}
public String getContentType ()
{
return this.type;
}
/**
* @param type
* the type to set
*/
public void setType (String type)
{
this.type = type;
}
/**
* @param name
* the name to set
*/
public void setName (String name)
{
this.name = name;
}
public InputStream getInputStream () throws IOException
{
return new ByteArrayInputStream (data.toByteArray ());
}
public String getName ()
{
return this.name;
}
public OutputStream getOutputStream () throws IOException
{
this.data = new ByteArrayOutputStream ();
return data;
}
}
// Try to create an API similar to the 2.x one
final class PreflightParserHelper
{
private static File createTmpFile (InputStream input) throws IOException
{
FileOutputStream fos = null;
try
{
File tmpFile = File.createTempFile ("mustang-pdf", ".pdf");
fos = new FileOutputStream (tmpFile);
IOUtils.copy (input, fos);
return tmpFile;
}
finally
{
IOUtils.closeQuietly (input);
IOUtils.closeQuietly (fos);
}
}
public static PreflightParser createPreflightParser (DataSource dataSource) throws IOException
{
return new PreflightParser (createTmpFile (dataSource.getInputStream ()));
}
}
final class PDFBoxUpdateMitigation
{
// Dummy for the name only
}

View File

@@ -20,12 +20,12 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.mustangproject.EStandard;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.mustangproject.EStandard;
public class Profiles { public class Profiles {
static Map<String, Profile> zf2Map = Stream.of(new Object[][]{ static Map<String, Profile> zf2Map = Stream.of(new Object[][]{
{"MINIMUM", new Profile("MINIMUM", "urn:factur-x.eu:1p0:minimum")}, {"MINIMUM", new Profile("MINIMUM", "urn:factur-x.eu:1p0:minimum")},

View File

@@ -20,33 +20,23 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment;
import org.mustangproject.TradeParty;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import java.io.IOException; import org.slf4j.LoggerFactory;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.mustangproject.ZUGFeRD.ZUGFeRDDateFormat.DATE;
import static org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants.CORRECTEDINVOICE;
import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATEGORY_CODES_WITH_EXEMPTION_REASON;
public class UBLDAPullProvider implements IXMLProvider { public class UBLDAPullProvider implements IXMLProvider {
private static final Logger LOGGER = LoggerFactory.getLogger (UBLDAPullProvider.class);
protected IExportableTransaction trans; protected IExportableTransaction trans;
protected TransactionCalculator calc; protected TransactionCalculator calc;
@@ -96,13 +86,9 @@ public class UBLDAPullProvider implements IXMLProvider {
} }
xml += "</DespatchAdvice>\n"; xml += "</DespatchAdvice>\n";
final byte[] ublRaw; final byte[] ublRaw;
try { ublRaw = xml.getBytes(StandardCharsets.UTF_8);
ublRaw = xml.getBytes("UTF-8");
ublData = XMLTools.removeBOM(ublRaw); ublData = XMLTools.removeBOM(ublRaw);
} catch (final UnsupportedEncodingException e) {
Logger.getLogger(UBLDAPullProvider.class.getName()).log(Level.SEVERE, null, e);
}
} }
public String getPartyXML(IZUGFeRDExportableTradeParty tp) { public String getPartyXML(IZUGFeRDExportableTradeParty tp) {
@@ -133,7 +119,7 @@ public class UBLDAPullProvider implements IXMLProvider {
try { try {
document = DocumentHelper.parseText(new String(ublData)); document = DocumentHelper.parseText(new String(ublData));
} catch (final DocumentException e1) { } catch (final DocumentException e1) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e1); LOGGER.error ("Failed to parse UBL", e1);
} }
try { try {
final OutputFormat format = OutputFormat.createPrettyPrint(); final OutputFormat format = OutputFormat.createPrettyPrint();
@@ -143,7 +129,7 @@ public class UBLDAPullProvider implements IXMLProvider {
res = sw.toString().getBytes(StandardCharsets.UTF_8); res = sw.toString().getBytes(StandardCharsets.UTF_8);
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to write XML", e);
} }
return res; return res;

View File

@@ -1,9 +1,20 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import javax.xml.transform.*; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import java.io.*;
/*** /***
@@ -31,9 +42,8 @@ public class XMLUpgrader {
* @return String the updated XML * @return String the updated XML
* @throws FileNotFoundException if the source could not be found * @throws FileNotFoundException if the source could not be found
* @throws TransformerException if the source could not be transformed * @throws TransformerException if the source could not be transformed
* @throws UnsupportedEncodingException if the source was not utf8
*/ */
public String migrateFromV1ToV2(String xmlFilename) throws FileNotFoundException, TransformerException, UnsupportedEncodingException { public String migrateFromV1ToV2(String xmlFilename) throws FileNotFoundException, TransformerException {
/** /**
* * * *
* http://www.unece.org/fileadmin/DAM/cefact/xml/XML-Naming-And-Design-Rules-V2_1.pdf * http://www.unece.org/fileadmin/DAM/cefact/xml/XML-Naming-And-Design-Rules-V2_1.pdf
@@ -45,7 +55,7 @@ public class XMLUpgrader {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
applySchematronXsl(new FileInputStream(xmlFilename), baos); applySchematronXsl(new FileInputStream(xmlFilename), baos);
String res = null; String res = null;
res = baos.toString("UTF-8"); res = baos.toString(StandardCharsets.UTF_8);
return res; return res;
} }

View File

@@ -30,7 +30,15 @@ package org.mustangproject.ZUGFeRD;
import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.XmpConstants; import org.apache.xmpbox.XmpConstants;
import org.apache.xmpbox.schema.PDFAExtensionSchema; import org.apache.xmpbox.schema.PDFAExtensionSchema;
import org.apache.xmpbox.type.*; import org.apache.xmpbox.type.ArrayProperty;
import org.apache.xmpbox.type.Attribute;
import org.apache.xmpbox.type.Cardinality;
import org.apache.xmpbox.type.ChoiceType;
import org.apache.xmpbox.type.DefinedStructuredType;
import org.apache.xmpbox.type.PDFAPropertyType;
import org.apache.xmpbox.type.PDFASchemaType;
import org.apache.xmpbox.type.StructuredType;
import org.apache.xmpbox.type.TextType;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
/** /**

View File

@@ -20,31 +20,10 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.cos.*; import java.io.File;
import org.apache.pdfbox.io.IOUtils; import java.io.FileOutputStream;
import org.apache.pdfbox.pdmodel.*; import java.io.IOException;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import java.io.OutputStream;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.schema.XMPBasicSchema;
import org.apache.xmpbox.type.BadFieldValueException;
import org.apache.xmpbox.xml.XmpSerializer;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
public class XRExporter implements IExporter { public class XRExporter implements IExporter {
IXMLProvider xmlProvider; IXMLProvider xmlProvider;

View File

@@ -1,21 +1,54 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.io.InputStream;
import java.util.logging.Logger; import java.nio.file.Files;
import java.nio.file.Paths;
import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XRechnungImporter extends ZUGFeRDImporter { public class XRechnungImporter extends ZUGFeRDImporter {
private static final Logger LOGGER = LoggerFactory.getLogger (XRechnungImporter.class);
public XRechnungImporter(byte[] rawXml) { public XRechnungImporter(byte[] rawXml) {
super(); super();
try { try {
setRawXML(rawXml); setRawXML(rawXml);
containsMeta = true; containsMeta = true;
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to set raw XML", e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
}
} }
}
public XRechnungImporter(String filename) {
super();
try {
setRawXML(Files.readAllBytes(Paths.get(filename)));
containsMeta = true;
} catch (final IOException e) {
LOGGER.error ("Failed to set raw XML", e);
throw new ZUGFeRDExportException(e);
}
}
public XRechnungImporter(InputStream fileinput) {
super();
try {
setRawXML(XMLTools.getBytesFromStream(fileinput));
containsMeta = true;
} catch (final IOException e) {
LOGGER.error ("Failed to set raw XML", e);
throw new ZUGFeRDExportException(e);
}
}
} }

View File

@@ -30,8 +30,6 @@ import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentException; import org.dom4j.DocumentException;
@@ -39,9 +37,11 @@ import org.dom4j.DocumentHelper;
import org.dom4j.io.OutputFormat; import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLProvider { public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider {
private static final Logger LOGGER = LoggerFactory.getLogger (ZUGFeRD1PullProvider.class);
//// MAIN CLASS //// MAIN CLASS
@@ -93,17 +93,17 @@ public class ZUGFeRD1PullProvider extends ZUGFeRD2PullProvider implements IXMLPr
try { try {
document = DocumentHelper.parseText(new String(zugferdData)); document = DocumentHelper.parseText(new String(zugferdData));
} catch (final DocumentException e1) { } catch (final DocumentException e1) {
Logger.getLogger(ZUGFeRD1PullProvider.class.getName()).log(Level.SEVERE, null, e1); LOGGER.error ("Failed to parse ZUGFeRD data", e1);
} }
try { try {
final OutputFormat format = OutputFormat.createPrettyPrint(); final OutputFormat format = OutputFormat.createPrettyPrint();
format.setTrimText(false); format.setTrimText(false);
final XMLWriter writer = new XMLWriter(sw, format); final XMLWriter writer = new XMLWriter(sw, format);
writer.write(document); writer.write(document);
res = sw.toString().getBytes("UTF-8"); res = sw.toString().getBytes(StandardCharsets.UTF_8);
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRD1PullProvider.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to write ZUGFeRD data", e);
} }
return res; return res;

View File

@@ -25,7 +25,6 @@ import static org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants.CATE
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -35,9 +34,8 @@ import java.util.Base64;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.dom4j.Document; import org.dom4j.Document;
@@ -49,8 +47,11 @@ import org.mustangproject.FileAttachment;
import org.mustangproject.IncludedNote; import org.mustangproject.IncludedNote;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ZUGFeRD2PullProvider implements IXMLProvider { public class ZUGFeRD2PullProvider implements IXMLProvider {
private static final Logger LOGGER = LoggerFactory.getLogger (ZUGFeRD2PullProvider.class);
protected byte[] zugferdData; protected byte[] zugferdData;
protected IExportableTransaction trans; protected IExportableTransaction trans;
@@ -92,7 +93,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
try { try {
document = DocumentHelper.parseText(new String(zugferdData)); document = DocumentHelper.parseText(new String(zugferdData));
} catch (final DocumentException e1) { } catch (final DocumentException e1) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e1); LOGGER.error ("Failed to parse ZUGFeRD data", e1);
} }
try { try {
final OutputFormat format = OutputFormat.createPrettyPrint(); final OutputFormat format = OutputFormat.createPrettyPrint();
@@ -102,7 +103,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
res = sw.toString().getBytes(StandardCharsets.UTF_8); res = sw.toString().getBytes(StandardCharsets.UTF_8);
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to write ZUGFeRD data", e);
} }
return res; return res;
@@ -290,11 +291,21 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
paymentTermsDescription = XMLTools.encodeXML(trans.getPaymentTermDescription()); paymentTermsDescription = XMLTools.encodeXML(trans.getPaymentTermDescription());
} }
if ((paymentTermsDescription == null) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CORRECTEDINVOICE) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE)) {
paymentTermsDescription = "Zahlbar ohne Abzug bis " + germanDateFormat.format(trans.getDueDate()); if ((profile == Profiles.getByName("XRechnung")) && (trans.getCashDiscounts() != null) && (trans.getCashDiscounts().length > 0)) {
for (IZUGFeRDCashDiscount discount : trans.getCashDiscounts()
) {
if (paymentTermsDescription == null) {
paymentTermsDescription = "";
}
paymentTermsDescription += discount.getAsXRechnung();
}
} else if ((paymentTermsDescription == null) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CORRECTEDINVOICE) && (trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE)) {
paymentTermsDescription = "Please remit until " + germanDateFormat.format(trans.getDueDate());
} }
String typecode = "380"; String typecode = "380";
if (trans.getDocumentCode() != null) { if (trans.getDocumentCode() != null) {
typecode = trans.getDocumentCode(); typecode = trans.getDocumentCode();
@@ -387,6 +398,19 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
XMLTools.encodeXML(currentItem.getProduct().getDescription()) + XMLTools.encodeXML(currentItem.getProduct().getDescription()) +
"</ram:Description>"; "</ram:Description>";
} }
if (currentItem.getProduct().getAttributes() != null) {
for ( Entry<String, String> entry : currentItem.getProduct().getAttributes().entrySet() ) {
xml += "<ram:ApplicableProductCharacteristic>" +
"<ram:Description>" + XMLTools.encodeXML(entry.getKey()) + "</ram:Description>" +
"<ram:Value>" + XMLTools.encodeXML(entry.getValue()) + "</ram:Value>" +
"</ram:ApplicableProductCharacteristic>";
}
}
if (currentItem.getProduct().getCountryOfOrigin() != null) {
xml += "<ram:OriginTradeCountry><ram:ID>" +
XMLTools.encodeXML(currentItem.getProduct().getCountryOfOrigin()) +
"</ram:ID></ram:OriginTradeCountry>";
}
xml += "</ram:SpecifiedTradeProduct>" xml += "</ram:SpecifiedTradeProduct>"
+ "<ram:SpecifiedLineTradeAgreement>"; + "<ram:SpecifiedLineTradeAgreement>";
@@ -404,19 +428,23 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ "<ram:LineID>" + XMLTools.encodeXML(currentItem.getBuyerOrderReferencedDocumentLineID()) + "</ram:LineID>" + "<ram:LineID>" + XMLTools.encodeXML(currentItem.getBuyerOrderReferencedDocumentLineID()) + "</ram:LineID>"
+ "</ram:BuyerOrderReferencedDocument>"; + "</ram:BuyerOrderReferencedDocument>";
} }
xml += "<ram:GrossPriceProductTradePrice>"
+ "<ram:ChargeAmount>" + priceFormat(lc.getPriceGross()) if (!allowanceChargeStr.isEmpty()) {
+ "</ram:ChargeAmount>" //currencyID=\"EUR\" xml += "<ram:GrossPriceProductTradePrice>"
+ "<ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + "<ram:ChargeAmount>" + priceFormat(lc.getPriceGross())
+ "\">" + quantityFormat(currentItem.getBasisQuantity()) + "</ram:BasisQuantity>" + "</ram:ChargeAmount>" //currencyID=\"EUR\"
+ allowanceChargeStr + "<ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit())
// + " <AppliedTradeAllowanceCharge>" + "\">" + quantityFormat(currentItem.getBasisQuantity()) + "</ram:BasisQuantity>"
// + " <ChargeIndicator>false</ChargeIndicator>" + allowanceChargeStr
// + " <ActualAmount currencyID=\"EUR\">0.6667</ActualAmount>" // + " <AppliedTradeAllowanceCharge>"
// + " <Reason>Rabatt</Reason>" // + " <ChargeIndicator>false</ChargeIndicator>"
// + " </AppliedTradeAllowanceCharge>" // + " <ActualAmount currencyID=\"EUR\">0.6667</ActualAmount>"
+ "</ram:GrossPriceProductTradePrice>" // + " <Reason>Rabatt</Reason>"
+ "<ram:NetPriceProductTradePrice>" // + " </AppliedTradeAllowanceCharge>"
+ "</ram:GrossPriceProductTradePrice>";
}
xml += "<ram:NetPriceProductTradePrice>"
+ "<ram:ChargeAmount>" + priceFormat(lc.getPrice()) + "<ram:ChargeAmount>" + priceFormat(lc.getPrice())
+ "</ram:ChargeAmount>" // currencyID=\"EUR\" + "</ram:ChargeAmount>" // currencyID=\"EUR\"
+ "<ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit()) + "<ram:BasisQuantity unitCode=\"" + XMLTools.encodeXML(currentItem.getProduct().getUnit())
@@ -536,10 +564,6 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "</ram:OccurrenceDateTime>"; xml += "</ram:OccurrenceDateTime>";
xml += " </ram:ActualDeliverySupplyChainEvent>"; xml += " </ram:ActualDeliverySupplyChainEvent>";
} else {
if ((trans.getDocumentCode() != DocumentCodeTypeConstants.CREDITNOTE) && (getProfile() != Profiles.getByName("Minimum"))) {
throw new IllegalStateException("No delivery date provided");
}
} }
/* /*
* + "<DeliveryNoteReferencedDocument>" + * + "<DeliveryNoteReferencedDocument>" +
@@ -685,6 +709,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
} else { } else {
xml += buildPaymentTermsXml(); xml += buildPaymentTermsXml();
} }
if ((profile == Profiles.getByName("Extended")) && (trans.getCashDiscounts() != null) && (trans.getCashDiscounts().length > 0)) {
for (IZUGFeRDCashDiscount discount : trans.getCashDiscounts()
) {
xml += discount.getAsCII();
}
}
final String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowancesForPercent(null)) + "</ram:AllowanceTotalAmount>"; final String allowanceTotalLine = "<ram:AllowanceTotalAmount>" + currencyFormat(calc.getAllowancesForPercent(null)) + "</ram:AllowanceTotalAmount>";
@@ -736,13 +766,9 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ "</rsm:CrossIndustryInvoice>"; + "</rsm:CrossIndustryInvoice>";
final byte[] zugferdRaw; final byte[] zugferdRaw;
try { zugferdRaw = xml.getBytes(StandardCharsets.UTF_8);
zugferdRaw = xml.getBytes("UTF-8");
zugferdData = XMLTools.removeBOM(zugferdRaw); zugferdData = XMLTools.removeBOM(zugferdRaw);
} catch (final UnsupportedEncodingException e) {
Logger.getLogger(ZUGFeRD2PullProvider.class.getName()).log(Level.SEVERE, null, e);
}
} }
protected String buildItemNotes(IZUGFeRDExportableItem currentItem) { protected String buildItemNotes(IZUGFeRDExportableItem currentItem) {

View File

@@ -19,25 +19,21 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.IOException;
import java.io.InputStream;
import org.apache.pdfbox.preflight.PreflightDocument; import org.apache.pdfbox.preflight.PreflightDocument;
import org.apache.pdfbox.preflight.ValidationResult;
import org.apache.pdfbox.preflight.exception.ValidationException; import org.apache.pdfbox.preflight.exception.ValidationException;
import org.apache.pdfbox.preflight.parser.PreflightParser; import org.apache.pdfbox.preflight.parser.PreflightParser;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import javax.activation.DataSource; import jakarta.activation.DataSource;
import java.io.IOException;
import java.io.InputStream;
public class ZUGFeRDExporterFromA1 extends ZUGFeRDExporterFromA3 implements IZUGFeRDExporter { public class ZUGFeRDExporterFromA1 extends ZUGFeRDExporterFromA3 {
protected boolean ignorePDFAErrors = false;
public ZUGFeRDExporterFromA1 ignorePDFAErrors() {
this.ignorePDFAErrors = true;
return this;
}
private static boolean isValidA1(DataSource dataSource) throws IOException { private static boolean isValidA1(DataSource dataSource) throws IOException {
return getPDFAParserValidationResult(new PreflightParser(dataSource)); return getPDFAParserValidationResult(PreflightParserHelper.createPreflightParser(dataSource));
} }
private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException { private static boolean getPDFAParserValidationResult(PreflightParser parser) throws IOException {
@@ -46,19 +42,19 @@ public class ZUGFeRDExporterFromA1 extends ZUGFeRDExporterFromA3 implements IZUG
* NonSequentialParser. Some additional controls are present to check a set of * NonSequentialParser. Some additional controls are present to check a set of
* PDF/A requirements. (Stream length consistency, EOL after some Keyword...) * PDF/A requirements. (Stream length consistency, EOL after some Keyword...)
*/ */
parser.parse();// might add a Format.PDF_A1A as parameter and iterate through A1 and A3 // might add a Format.PDF_A1A as parameter and iterate through A1 and A3
try (PreflightDocument document = parser.getPreflightDocument()) { try (PreflightDocument document = (PreflightDocument) parser.parse()) {
/* /*
* Once the syntax validation is done, the parser can provide a * Once the syntax validation is done, the parser can provide a
* PreflightDocument (that inherits from PDDocument) This document process the * PreflightDocument (that inherits from PDDocument) This document process the
* end of PDF/A validation. * end of PDF/A validation.
*/ */
document.validate(); ValidationResult res = document.validate();
// Get validation result // Get validation result
return document.getResult().isValid(); return res.isValid();
} catch (ValidationException e) { } catch (ValidationException e) {
/* /*
* the parse method can throw a SyntaxValidationException if the PDF file can't * the parse method can throw a SyntaxValidationException if the PDF file can't
@@ -70,14 +66,17 @@ public class ZUGFeRDExporterFromA1 extends ZUGFeRDExporterFromA3 implements IZUG
} }
public ZUGFeRDExporterFromA1 setProfile(Profile p) { @Override
public ZUGFeRDExporterFromA1 setProfile(Profile p) {
return (ZUGFeRDExporterFromA1)super.setProfile(p); return (ZUGFeRDExporterFromA1)super.setProfile(p);
} }
public ZUGFeRDExporterFromA1 setProfile(String profileName) { @Override
public ZUGFeRDExporterFromA1 setProfile(String profileName) {
return (ZUGFeRDExporterFromA1)super.setProfile(profileName); return (ZUGFeRDExporterFromA1)super.setProfile(profileName);
} }
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException { @Override
public boolean ensurePDFIsValid(final DataSource dataSource) throws IOException {
if (!ignorePDFAErrors && !isValidA1(dataSource)) { if (!ignorePDFAErrors && !isValidA1(dataSource)) {
throw new IOException("File is not a valid PDF/A-1 input file"); throw new IOException("File is not a valid PDF/A-1 input file");
} }
@@ -85,35 +84,45 @@ public class ZUGFeRDExporterFromA1 extends ZUGFeRDExporterFromA3 implements IZUG
} }
public ZUGFeRDExporterFromA1 load(String pdfFilename) throws IOException { @Override
public ZUGFeRDExporterFromA1 load(String pdfFilename) throws IOException {
return (ZUGFeRDExporterFromA1) super.load(pdfFilename); return (ZUGFeRDExporterFromA1) super.load(pdfFilename);
} }
public ZUGFeRDExporterFromA1 load(byte[] pdfBinary) throws IOException { @Override
public ZUGFeRDExporterFromA1 load(byte[] pdfBinary) throws IOException {
return (ZUGFeRDExporterFromA1) super.load(pdfBinary); return (ZUGFeRDExporterFromA1) super.load(pdfBinary);
} }
public ZUGFeRDExporterFromA1 load(InputStream pdfSource) throws IOException{ @Override
public ZUGFeRDExporterFromA1 load(InputStream pdfSource) throws IOException{
return (ZUGFeRDExporterFromA1) super.load(pdfSource); return (ZUGFeRDExporterFromA1) super.load(pdfSource);
} }
public ZUGFeRDExporterFromA1 setCreator(String creator) { @Override
public ZUGFeRDExporterFromA1 setCreator(String creator) {
return (ZUGFeRDExporterFromA1) super.setCreator(creator); return (ZUGFeRDExporterFromA1) super.setCreator(creator);
} }
public ZUGFeRDExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) { @Override
public ZUGFeRDExporterFromA1 setConformanceLevel(PDFAConformanceLevel newLevel) {
return (ZUGFeRDExporterFromA1) super.setConformanceLevel(newLevel); return (ZUGFeRDExporterFromA1) super.setConformanceLevel(newLevel);
} }
public ZUGFeRDExporterFromA1 setProducer(String producer){ @Override
public ZUGFeRDExporterFromA1 setProducer(String producer){
return (ZUGFeRDExporterFromA1) super.setProducer(producer); return (ZUGFeRDExporterFromA1) super.setProducer(producer);
} }
public ZUGFeRDExporterFromA1 setZUGFeRDVersion(EStandard est, int version){ @Override
public ZUGFeRDExporterFromA1 setZUGFeRDVersion(EStandard est, int version){
return (ZUGFeRDExporterFromA1) super.setZUGFeRDVersion(est, version); return (ZUGFeRDExporterFromA1) super.setZUGFeRDVersion(est, version);
} }
public ZUGFeRDExporterFromA1 setZUGFeRDVersion(int version){ @Override
public ZUGFeRDExporterFromA1 setZUGFeRDVersion(int version){
return (ZUGFeRDExporterFromA1) super.setZUGFeRDVersion(version); return (ZUGFeRDExporterFromA1) super.setZUGFeRDVersion(version);
} }
public ZUGFeRDExporterFromA1 setXML(byte[] zugferdData) throws IOException{ @Override
public ZUGFeRDExporterFromA1 setXML(byte[] zugferdData) throws IOException{
return (ZUGFeRDExporterFromA1) super.setXML(zugferdData); return (ZUGFeRDExporterFromA1) super.setXML(zugferdData);
} }
public ZUGFeRDExporterFromA1 disableAutoClose(boolean disableAutoClose){ @Override
public ZUGFeRDExporterFromA1 disableAutoClose(boolean disableAutoClose){
return (ZUGFeRDExporterFromA1) super.disableAutoClose(disableAutoClose); return (ZUGFeRDExporterFromA1) super.disableAutoClose(disableAutoClose);
} }
public ZUGFeRDExporterFromA1 convertOnly() { public ZUGFeRDExporterFromA1 convertOnly() {

View File

@@ -20,9 +20,35 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.cos.*; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.TransformerException;
import org.apache.pdfbox.Loader;
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.io.IOUtils; import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdfwriter.compress.CompressParameters;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
@@ -33,7 +59,6 @@ import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontDescriptor; import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent; import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
import org.apache.pdfbox.preflight.utils.ByteArrayDataSource;
import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.AdobePDFSchema; import org.apache.xmpbox.schema.AdobePDFSchema;
import org.apache.xmpbox.schema.DublinCoreSchema; import org.apache.xmpbox.schema.DublinCoreSchema;
@@ -47,19 +72,21 @@ import org.apache.xmpbox.xml.XmpSerializer;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.FileAttachment; import org.mustangproject.FileAttachment;
import javax.activation.DataSource; import jakarta.activation.DataSource;
import javax.activation.FileDataSource; import jakarta.activation.FileDataSource;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.*;
public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter, IExporter, Closeable { public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporter {
private boolean isFacturX = true; private boolean isFacturX = true;
public static final int DefaultZUGFeRDVersion = 2; public static final int DefaultZUGFeRDVersion = 2;
protected boolean ignorePDFAErrors = false;
public ZUGFeRDExporterFromA3 ignorePDFAErrors() {
this.ignorePDFAErrors = true;
return this;
}
protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE; protected PDFAConformanceLevel conformanceLevel = PDFAConformanceLevel.UNICODE;
protected ArrayList<FileAttachment> fileAttachments = new ArrayList<FileAttachment>(); protected ArrayList<FileAttachment> fileAttachments = new ArrayList<>();
/** /**
* This flag controls whether or not the metadata is overwritten, or kind of merged. * This flag controls whether or not the metadata is overwritten, or kind of merged.
@@ -111,9 +138,6 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
protected PDDocument doc; protected PDDocument doc;
private HashMap<String, byte[]> additionalXMLs = new HashMap<String, byte[]>();
protected int ZFVersion = DefaultZUGFeRDVersion; protected int ZFVersion = DefaultZUGFeRDVersion;
private boolean attachZUGFeRDHeaders = true; private boolean attachZUGFeRDHeaders = true;
@@ -220,9 +244,10 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
* Generate ZF2.1 files with filename factur-x.xml * Generate ZF2.1 files with filename factur-x.xml
* *
* @return this (fluent setter) * @return this (fluent setter)
* @deprecated * @deprecated It's now the default anyway
*/ */
public ZUGFeRDExporterFromA3 setFacturX() { @Deprecated
public ZUGFeRDExporterFromA3 setFacturX() {
isFacturX = true; isFacturX = true;
return this; return this;
} }
@@ -258,7 +283,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
*/ */
public ZUGFeRDExporterFromA3 load(byte[] pdfBinary) throws IOException { public ZUGFeRDExporterFromA3 load(byte[] pdfBinary) throws IOException {
ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary))); ensurePDFIsValid(new ByteArrayDataSource(new ByteArrayInputStream(pdfBinary)));
doc = PDDocument.load(pdfBinary); doc = Loader.loadPDF(pdfBinary);
return this; return this;
} }
@@ -282,7 +307,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
* @param ZUGFeRDfilename the pdf file name * @param ZUGFeRDfilename the pdf file name
* @throws IOException if anything is wrong in the target location * @throws IOException if anything is wrong in the target location
*/ */
public void export(String ZUGFeRDfilename) throws IOException { @Override
public void export(String ZUGFeRDfilename) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -290,7 +316,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
throw new IOException( throw new IOException(
"File must be attached (usually with setTransaction) before perfoming this operation"); "File must be attached (usually with setTransaction) before perfoming this operation");
} }
doc.save(ZUGFeRDfilename); doc.save(ZUGFeRDfilename, CompressParameters.NO_COMPRESSION);
if (!disableAutoClose) { if (!disableAutoClose) {
close(); close();
} }
@@ -309,7 +335,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
* @param output the OutputStream * @param output the OutputStream
* @throws IOException if anything is wrong in the OutputStream * @throws IOException if anything is wrong in the OutputStream
*/ */
public void export(OutputStream output) throws IOException { @Override
public void export(OutputStream output) throws IOException {
if (!documentPrepared) { if (!documentPrepared) {
prepareDocument(); prepareDocument();
} }
@@ -317,7 +344,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
throw new IOException( throw new IOException(
"File must be attached (usually with setTransaction) before perfoming this operation"); "File must be attached (usually with setTransaction) before perfoming this operation");
} }
doc.save(output); doc.save(output, CompressParameters.NO_COMPRESSION);
if (!disableAutoClose) { if (!disableAutoClose) {
close(); close();
} }
@@ -371,7 +398,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
ef.setSize(data.length); ef.setSize(data.length);
ef.setCreationDate(new GregorianCalendar()); ef.setCreationDate(new GregorianCalendar());
ef.setModDate(GregorianCalendar.getInstance()); ef.setModDate(Calendar.getInstance());
fs.setEmbeddedFile(ef); fs.setEmbeddedFile(ef);
@@ -403,7 +430,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
doc.getDocumentCatalog().setNames(names); doc.getDocumentCatalog().setNames(names);
// AF entry (Array) in catalog with the FileSpec // AF entry (Array) in catalog with the FileSpec
COSBase AFEntry = (COSBase) doc.getDocumentCatalog().getCOSObject().getItem("AF"); COSBase AFEntry = doc.getDocumentCatalog().getCOSObject().getItem("AF");
if ((AFEntry == null)) { if ((AFEntry == null)) {
COSArray cosArray = new COSArray(); COSArray cosArray = new COSArray();
cosArray.add(fs); cosArray.add(fs);
@@ -529,7 +556,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
metadata.addSchema(pdfaex); metadata.addSchema(pdfaex);
} }
private void removeCidSet(PDDocumentCatalog catalog, PDDocument doc) private void removeCidSet(PDDocument doc)
throws IOException throws IOException
{ {
// https://github.com/ZUGFeRD/mustangproject/issues/249 // https://github.com/ZUGFeRD/mustangproject/issues/249
@@ -550,7 +577,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
PDType0Font typedFont = (PDType0Font) pdFont; PDType0Font typedFont = (PDType0Font) pdFont;
if (typedFont.getDescendantFont() instanceof PDCIDFontType2) { if (typedFont.getDescendantFont() instanceof PDCIDFontType2) {
PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont(); @SuppressWarnings ("unused")
PDCIDFontType2 f = (PDCIDFontType2) typedFont.getDescendantFont();
PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor(); PDFontDescriptor fontDescriptor = pdFont.getFontDescriptor();
fontDescriptor.getCOSObject().removeItem(cidSet); fontDescriptor.getCOSObject().removeItem(cidSet);
@@ -571,7 +599,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
metadata = new PDMetadata(doc); metadata = new PDMetadata(doc);
cat.setMetadata(metadata); cat.setMetadata(metadata);
removeCidSet(cat, doc); removeCidSet(doc);
xmp = getXmpMetadata(); xmp = getXmpMetadata();
writeAdobePDFSchema(xmp); writeAdobePDFSchema(xmp);
writePDFAIdentificationSchema(xmp); writePDFAIdentificationSchema(xmp);
@@ -608,7 +636,8 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
* <code>setZUGFeRDXMLData(byte[] zugferdData)</code> * <code>setZUGFeRDXMLData(byte[] zugferdData)</code>
* @throws IOException if anything is wrong with already loaded PDF * @throws IOException if anything is wrong with already loaded PDF
*/ */
public IExporter setTransaction(IExportableTransaction trans) throws IOException { @Override
public IExporter setTransaction(IExportableTransaction trans) throws IOException {
this.trans = trans; this.trans = trans;
return prepare(); return prepare();
} }
@@ -713,13 +742,13 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
} }
protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) { protected PDFAIdentificationSchema getPDFAIdentificationSchema(XMPMetadata xmp) {
PDFAIdentificationSchema pdfaid = xmp.getPDFIdentificationSchema(); PDFAIdentificationSchema pdfaid = xmp.getPDFAIdentificationSchema();
if (pdfaid != null) if (pdfaid != null)
if (overwrite) if (overwrite)
xmp.removeSchema(pdfaid); xmp.removeSchema(pdfaid);
else else
return pdfaid; return pdfaid;
return xmp.createAndAddPFAIdentificationSchema(); return xmp.createAndAddPDFAIdentificationSchema();
} }
protected void writeDublinCoreSchema(XMPMetadata xmp) { protected void writeDublinCoreSchema(XMPMetadata xmp) {
@@ -760,7 +789,7 @@ public class ZUGFeRDExporterFromA3 extends XRExporter implements IZUGFeRDExporte
if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool())) if (overwrite || isEmpty(xsb.getCreatorTool()) || "UnknownApplication".equals(xsb.getCreatorTool()))
xsb.setCreatorTool(creatorTool); xsb.setCreatorTool(creatorTool);
if (overwrite || xsb.getCreateDate() == null) if (overwrite || xsb.getCreateDate() == null)
xsb.setCreateDate(GregorianCalendar.getInstance()); xsb.setCreateDate(Calendar.getInstance());
} }
protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) { protected XMPBasicSchema getXmpBasicSchema(XMPMetadata xmp) {

View File

@@ -20,6 +20,13 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
@@ -30,8 +37,7 @@ import org.apache.xmpbox.xml.XmpParsingException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.activation.DataSource; import jakarta.activation.DataSource;
import java.io.*;
/*** /***
* Auto-detects the source PDF-A-Version and acts accordingly * Auto-detects the source PDF-A-Version and acts accordingly
@@ -43,14 +49,28 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter {
protected IZUGFeRDExporter theExporter; protected IZUGFeRDExporter theExporter;
protected boolean ignorePDFAErrors = false;
public ZUGFeRDExporterFromPDFA ignorePDFAErrors() {
this.ignorePDFAErrors = true;
return this;
}
protected void determineAndSetExporter(int PDFAVersion) { protected void determineAndSetExporter(int PDFAVersion) {
if (PDFAVersion == 3) { if (PDFAVersion == 3) {
theExporter = new ZUGFeRDExporterFromA3(); theExporter = new ZUGFeRDExporterFromA3();
if (ignorePDFAErrors) {
((ZUGFeRDExporterFromA3)theExporter).ignorePDFAErrors();
}
} else if (PDFAVersion == 1) { } else if (PDFAVersion == 1) {
theExporter = new ZUGFeRDExporterFromA1(); theExporter = new ZUGFeRDExporterFromA1();
if (ignorePDFAErrors) {
((ZUGFeRDExporterFromA1)theExporter).ignorePDFAErrors();
}
} else { } else {
throw new IllegalArgumentException("PDF-A version not supported"); throw new IllegalArgumentException("PDF-A version not supported");
} }
} }
protected IZUGFeRDExporter getExporter() { protected IZUGFeRDExporter getExporter() {
if (theExporter==null) { if (theExporter==null) {
@@ -79,7 +99,7 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter {
* @throws IOException * @throws IOException
*/ */
private int getPDFAVersion(byte[] byteArrayInputStream) throws IOException { private int getPDFAVersion(byte[] byteArrayInputStream) throws IOException {
PDDocument document = PDDocument.load(byteArrayInputStream); PDDocument document = Loader.loadPDF(byteArrayInputStream);
PDDocumentCatalog catalog = document.getDocumentCatalog(); PDDocumentCatalog catalog = document.getDocumentCatalog();
PDMetadata metadata = catalog.getMetadata(); PDMetadata metadata = catalog.getMetadata();
// the PDF version we could get through the document but we want the PDF-A version, // the PDF version we could get through the document but we want the PDF-A version,
@@ -89,7 +109,7 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter {
DomXmpParser xmpParser = new DomXmpParser(); DomXmpParser xmpParser = new DomXmpParser();
XMPMetadata xmp = xmpParser.parse(metadata.createInputStream()); XMPMetadata xmp = xmpParser.parse(metadata.createInputStream());
PDFAIdentificationSchema pdfaSchema = xmp.getPDFIdentificationSchema(); PDFAIdentificationSchema pdfaSchema = xmp.getPDFAIdentificationSchema();
if (pdfaSchema != null) { if (pdfaSchema != null) {
return pdfaSchema.getPart(); return pdfaSchema.getPart();
} }
@@ -147,16 +167,16 @@ public class ZUGFeRDExporterFromPDFA implements IZUGFeRDExporter {
return getExporter().setCreator(creator); return getExporter().setCreator(creator);
} }
public ZUGFeRDExporterFromPDFA setProfile(Profile p) { public IZUGFeRDExporter setProfile(Profile p) {
return (ZUGFeRDExporterFromPDFA) getExporter().setProfile(p); return getExporter().setProfile(p);
} }
public ZUGFeRDExporterFromPDFA setProfile(String profileName) { public IZUGFeRDExporter setProfile(String profileName) {
Profile p = Profiles.getByName(profileName); Profile p = Profiles.getByName(profileName);
if (p==null) { if (p==null) {
throw new RuntimeException("Profile not found."); throw new RuntimeException("Profile not found.");
} }
return (ZUGFeRDExporterFromPDFA) getExporter().setProfile(p); return getExporter().setProfile(p);
} }
public IZUGFeRDExporter setConformanceLevel(PDFAConformanceLevel newLevel) { public IZUGFeRDExporter setConformanceLevel(PDFAConformanceLevel newLevel) {

View File

@@ -14,22 +14,19 @@ package org.mustangproject.ZUGFeRD;
* @author jstaerk * @author jstaerk
*/ */
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
@@ -40,6 +37,8 @@ import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary;
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode;
@@ -49,12 +48,16 @@ import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.Item; import org.mustangproject.Item;
import org.mustangproject.Product; import org.mustangproject.Product;
import org.mustangproject.XMLTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
public class ZUGFeRDImporter { public class ZUGFeRDImporter {
private static final Logger LOGGER = LoggerFactory.getLogger (ZUGFeRDImporter.class);
/** /**
* if metadata has been found * if metadata has been found
@@ -80,14 +83,14 @@ public class ZUGFeRDImporter {
protected ZUGFeRDImporter() { protected ZUGFeRDImporter() {
//constructor for extending classes //constructor for extending classes
} }
public ZUGFeRDImporter(String pdfFilename) { public ZUGFeRDImporter(String pdfFilename) {
try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) { try (InputStream bis = Files.newInputStream(Paths.get(pdfFilename), StandardOpenOption.READ)) {
extractLowLevel(bis); extractLowLevel(bis);
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to extract ZUGFeRD data", e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
} }
@@ -97,7 +100,7 @@ public class ZUGFeRDImporter {
try { try {
extractLowLevel(pdfStream); extractLowLevel(pdfStream);
} catch (final IOException e) { } catch (final IOException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to extract ZUGFeRD data", e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
} }
@@ -106,41 +109,59 @@ public class ZUGFeRDImporter {
/** /**
* Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling. * Extracts a ZUGFeRD invoice from a PDF document represented by an input stream. Errors are reported via exception handling.
* *
* @param pdfStream a inputstream of a pdf file * @param inStream a inputstream of a pdf file
*/ */
private void extractLowLevel(InputStream pdfStream) throws IOException { private void extractLowLevel(InputStream inStream) throws IOException {
try (PDDocument doc = PDDocument.load(pdfStream)) { BufferedInputStream pdfStream = new BufferedInputStream(inStream);
// PDDocumentInformation info = doc.getDocumentInformation(); byte[] pad = new byte[4];
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog()); pdfStream.mark(0);
//start pdfStream.read(pad);
pdfStream.reset();
byte[] pdfSignature = {'%', 'P', 'D', 'F'};
if (Arrays.equals(pad, pdfSignature)) { // we have a pdf
if (doc.getDocumentCatalog() == null || doc.getDocumentCatalog().getMetadata() == null) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.INFO, "no-xmlpart");
return;
}
final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata(); try (PDDocument doc = Loader.loadPDF(IOUtils.toByteArray (pdfStream))) {
xmpString = convertStreamToString(XMP); // PDDocumentInformation info = doc.getDocumentInformation();
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(doc.getDocumentCatalog());
//start
final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles(); if (doc.getDocumentCatalog() == null || doc.getDocumentCatalog().getMetadata() == null) {
if (etn == null) { LOGGER.info("no-xmlpart");
return; return;
} }
final Map<String, PDComplexFileSpecification> efMap = etn.getNames(); final InputStream XMP = doc.getDocumentCatalog().getMetadata().exportXMPMetadata();
// String filePath = "/tmp/"; xmpString = convertStreamToString(XMP);
if (efMap != null) { final PDEmbeddedFilesNameTreeNode etn = names.getEmbeddedFiles();
extractFiles(efMap); // see if (etn == null) {
// https://memorynotfound.com/apache-pdfbox-extract-embedded-file-pdf-document/ return;
} else { }
final List<PDNameTreeNode<PDComplexFileSpecification>> kids = etn.getKids(); final Map<String, PDComplexFileSpecification> efMap = etn.getNames();
for (final PDNameTreeNode<PDComplexFileSpecification> node : kids) { // String filePath = "/tmp/";
final Map<String, PDComplexFileSpecification> namesL = node.getNames();
extractFiles(namesL); if (efMap != null) {
extractFiles(efMap); // see
// https://memorynotfound.com/apache-pdfbox-extract-embedded-file-pdf-document/
} else {
final List<PDNameTreeNode<PDComplexFileSpecification>> kids = etn.getKids();
if (kids == null) {
return;
}
for (final PDNameTreeNode<PDComplexFileSpecification> node : kids) {
final Map<String, PDComplexFileSpecification> namesL = node.getNames();
extractFiles(namesL);
}
} }
} }
} else {
// no PDF probably XML
containsMeta = true;
setRawXML(XMLTools.getBytesFromStream(pdfStream));
} }
} }
@@ -187,7 +208,7 @@ public class ZUGFeRDImporter {
xmlFact.setNamespaceAware(true); xmlFact.setNamespaceAware(true);
final DocumentBuilder builder = xmlFact.newDocumentBuilder(); final DocumentBuilder builder = xmlFact.newDocumentBuilder();
final ByteArrayInputStream is = new ByteArrayInputStream(rawXML); final ByteArrayInputStream is = new ByteArrayInputStream(rawXML);
/// is.skip(guessBOMSize(is)); /// is.skip(guessBOMSize(is));
document = builder.parse(is); document = builder.parse(is);
} }
@@ -198,14 +219,12 @@ public class ZUGFeRDImporter {
try { try {
setDocument(); setDocument();
} catch (ParserConfigurationException | SAXException e) { } catch (ParserConfigurationException | SAXException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to parse XML", e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
} }
protected String extractString(String xpathStr) { protected String extractString(String xpathStr) {
if (!containsMeta) { if (!containsMeta) {
throw new ZUGFeRDExportException("No suitable data/ZUGFeRD file could be found."); throw new ZUGFeRDExportException("No suitable data/ZUGFeRD file could be found.");
@@ -217,7 +236,7 @@ public class ZUGFeRDImporter {
final XPath xpath = xpathFact.newXPath(); final XPath xpath = xpathFact.newXPath();
result = xpath.evaluate(xpathStr, document); result = xpath.evaluate(xpathStr, document);
} catch (final XPathExpressionException e) { } catch (final XPathExpressionException e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error ("Failed to evaluate XPath", e);
throw new ZUGFeRDExportException(e); throw new ZUGFeRDExportException(e);
} }
return result; return result;
@@ -248,7 +267,11 @@ public class ZUGFeRDImporter {
* @return the ZUGFeRD Profile * @return the ZUGFeRD Profile
*/ */
public String getZUGFeRDProfil() { public String getZUGFeRDProfil() {
switch (extractString("//*[local-name() = 'GuidelineSpecifiedDocumentContextParameter']//*[local-name() = 'ID']")) { String guideline = extractString("//*[local-name() = 'GuidelineSpecifiedDocumentContextParameter']//*[local-name() = 'ID']");
if(guideline.contains("xrechnung")) {
return "XRECHNUNG";
}
switch (guideline) {
case "urn:cen.eu:en16931:2017": case "urn:cen.eu:en16931:2017":
case "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort": case "urn:ferd:CrossIndustryDocument:invoice:1p0:comfort":
return "COMFORT"; return "COMFORT";
@@ -262,9 +285,9 @@ public class ZUGFeRDImporter {
case "urn:ferd:CrossIndustryDocument:invoice:1p0:extended": case "urn:ferd:CrossIndustryDocument:invoice:1p0:extended":
case "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended": case "urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended":
return "EXTENDED"; return "EXTENDED";
default: default:
return ""; return "";
} }
} }
/** /**
@@ -278,8 +301,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']"); return extractString("//*[local-name() = 'ApplicableHeaderTradeSettlement']//*[local-name() = 'InvoiceCurrencyCode']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -298,7 +320,7 @@ public class ZUGFeRDImporter {
return extractIssuerAssignedID("SellerOrderReferencedDocument"); return extractIssuerAssignedID("SellerOrderReferencedDocument");
} }
/** /**
* @return the IssuerAssigned ID * @return the IssuerAssigned ID
*/ */
public String getContractOrderReferencedDocumentIssuerAssignedID() { public String getContractOrderReferencedDocumentIssuerAssignedID() {
@@ -313,7 +335,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']"); return extractString("//*[local-name() = '" + propertyName + "']//*[local-name() = 'IssuerAssignedID']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -336,25 +358,26 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']"); return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IssueDateTime']//*[local-name() = 'DateTimeString']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
public Date getDetailedDeliveryPeriodFrom(){ public Date getDetailedDeliveryPeriodFrom() {
final String toParse = extractString( final String toParse = extractString(
"//*[local-name() = 'ApplicableHeaderTradeSettlement']" + "//*[local-name() = 'ApplicableHeaderTradeSettlement']" +
"//*[local-name() = 'BillingSpecifiedPeriod']" + "//*[local-name() = 'BillingSpecifiedPeriod']" +
"//*[local-name() = 'StartDateTime']//*[local-name() = 'DateTimeString']"); "//*[local-name() = 'StartDateTime']//*[local-name() = 'DateTimeString']");
return tryDate(toParse); return tryDate(toParse);
} }
public Date getDetailedDeliveryPeriodTo(){
final String toParse = extractString( public Date getDetailedDeliveryPeriodTo() {
"//*[local-name() = 'ApplicableHeaderTradeSettlement']" + final String toParse = extractString(
"//*[local-name() = 'BillingSpecifiedPeriod']" + "//*[local-name() = 'ApplicableHeaderTradeSettlement']" +
"//*[local-name() = 'EndDateTime']//*[local-name() = 'DateTimeString']"); "//*[local-name() = 'BillingSpecifiedPeriod']" +
return tryDate(toParse); "//*[local-name() = 'EndDateTime']//*[local-name() = 'DateTimeString']");
} return tryDate(toParse);
}
/** /**
* @return the TaxBasisTotalAmount * @return the TaxBasisTotalAmount
@@ -367,7 +390,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TaxBasisTotalAmount']"); return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TaxBasisTotalAmount']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -383,7 +406,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TaxTotalAmount']"); return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TaxTotalAmount']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -399,7 +422,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'RoundingAmount']"); return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'RoundingAmount']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -415,7 +438,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TotalPrepaidAmount']"); return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'TotalPrepaidAmount']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -453,7 +476,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IncludedNote']"); return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'IncludedNote']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -463,7 +486,9 @@ public class ZUGFeRDImporter {
*/ */
public String getBuyerTradePartyName() { public String getBuyerTradePartyName() {
return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'Name']"); return extractString("//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'Name']");
} /** }
/**
* @return the BuyerTradeParty Name * @return the BuyerTradeParty Name
*/ */
public String getDeliveryTradePartyName() { public String getDeliveryTradePartyName() {
@@ -471,7 +496,6 @@ public class ZUGFeRDImporter {
} }
/** /**
* @return the line Total Amount * @return the line Total Amount
*/ */
@@ -483,7 +507,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'LineTotalAmount']"); return extractString("//*[local-name() = 'SpecifiedTradeSettlementHeaderMonetarySummation']//*[local-name() = 'LineTotalAmount']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -506,7 +530,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ActualDeliverySupplyChainEvent']//*[local-name() = 'OccurrenceDateTime']//*[local-name() = 'DateTimeString']"); return extractString("//*[local-name() = 'ActualDeliverySupplyChainEvent']//*[local-name() = 'OccurrenceDateTime']//*[local-name() = 'DateTimeString']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -522,13 +546,12 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'ID']"); return extractString("//*[local-name() = 'ExchangedDocument']//*[local-name() = 'ID']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
/** /**
* @return the document code * @return the document code
*/ */
@@ -540,7 +563,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ExchangedDocument']/*[local-name() = 'TypeCode']"); return extractString("//*[local-name() = 'ExchangedDocument']/*[local-name() = 'TypeCode']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -557,7 +580,7 @@ public class ZUGFeRDImporter {
return extractString("//*[local-name() = 'ApplicableHeaderTradeAgreement']/*[local-name() = 'BuyerReference']"); return extractString("//*[local-name() = 'ApplicableHeaderTradeAgreement']/*[local-name() = 'BuyerReference']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return ""; return "";
} }
} }
@@ -600,7 +623,7 @@ public class ZUGFeRDImporter {
if (result == null || result.isEmpty()) { if (result == null || result.isEmpty()) {
/* fx/zf would be SpecifiedTradeSettlementMonetarySummation /* fx/zf would be SpecifiedTradeSettlementMonetarySummation
* but ox is SpecifiedTradeSettlementHeaderMonetarySummation...*/ * but ox is SpecifiedTradeSettlementHeaderMonetarySummation...*/
result = extractString("//*[local-name() = 'GrandTotalAmount']"); result = extractString("//*[local-name() = 'GrandTotalAmount']");
} }
return result; return result;
@@ -664,7 +687,7 @@ public class ZUGFeRDImporter {
throw new Exception("Not yet parsed"); throw new Exception("Not yet parsed");
} }
final String head = getUTF8(); final String head = getUTF8();
String rootNode=extractString("local-name(/*)"); String rootNode = extractString("local-name(/*)");
if (rootNode.equals("CrossIndustryDocument")) { if (rootNode.equals("CrossIndustryDocument")) {
return EStandard.zugferd; return EStandard.zugferd;
} else if (rootNode.equals("Invoice")) { } else if (rootNode.equals("Invoice")) {
@@ -680,26 +703,26 @@ public class ZUGFeRDImporter {
throw new Exception("ZUGFeRD version could not be determined"); throw new Exception("ZUGFeRD version could not be determined");
} }
public int getVersion() throws Exception { public int getVersion() throws Exception {
if (!containsMeta) { if (!containsMeta) {
throw new Exception("Not yet parsed"); throw new Exception("Not yet parsed");
} }
if (version != null) { if (version != null) {
return version; return version;
} }
final String head = getUTF8(); final String head = getUTF8();
if (head.contains("<rsm:CrossIndustryDocument") // if (head.contains("<rsm:CrossIndustryDocument") //
|| head.contains("<CrossIndustryDocument") // || head.contains("<CrossIndustryDocument") //
|| head.contains("<SCRDMCCBDACIDAMessageStructure") // || head.contains("<SCRDMCCBDACIDAMessageStructure") //
|| head.contains("<rsm:SCRDMCCBDACIOMessageStructure")) { // || head.contains("<rsm:SCRDMCCBDACIOMessageStructure")) { //
version = 1; version = 1;
} else if (head.contains("<rsm:CrossIndustryInvoice")) { } else if (head.contains("<rsm:CrossIndustryInvoice")) {
version = 2; version = 2;
} else {
throw new Exception("ZUGFeRD version could not be determined");
} }
else {
throw new Exception("ZUGFeRD version could not be determined");
}
return version; return version;
} }
@@ -719,12 +742,12 @@ public class ZUGFeRDImporter {
final byte[] bomlessData; final byte[] bomlessData;
if ((rawXML[0] == (byte) 0xEF) if ((rawXML[0] == (byte) 0xEF)
&& (rawXML[1] == (byte) 0xBB) && (rawXML[1] == (byte) 0xBB)
&& (rawXML[2] == (byte) 0xBF)) { && (rawXML[2] == (byte) 0xBF)) {
// I don't like BOMs, lets remove it // I don't like BOMs, lets remove it
bomlessData = new byte[rawXML.length - 3]; bomlessData = new byte[rawXML.length - 3];
System.arraycopy(rawXML, 3, bomlessData, 0, System.arraycopy(rawXML, 3, bomlessData, 0,
rawXML.length - 3); rawXML.length - 3);
} else { } else {
bomlessData = rawXML; bomlessData = rawXML;
} }
@@ -754,19 +777,21 @@ public class ZUGFeRDImporter {
// indication if zugferd is present - better than just invoice // indication if zugferd is present - better than just invoice
final String meta = getMeta(); final String meta = getMeta();
return (meta != null) && (meta.length() > 0) && ((meta.contains("SpecifiedExchangedDocumentContext") return (meta != null) && (meta.length() > 0) && ((meta.contains("SpecifiedExchangedDocumentContext")
/* ZF1 */ || meta.contains("ExchangedDocumentContext") /* ZF2 */)); /* ZF1 */ || meta.contains("ExchangedDocumentContext") /* ZF2 */));
} }
static String convertStreamToString(java.io.InputStream is) { 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 try {
// https://community.oracle.com/blogs/pat/2004/10/23/stupid-scanner-tricks return IOUtils.toString(is, StandardCharsets.UTF_8);
final Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A"); } catch (IOException e) {
return s.hasNext() ? s.next() : ""; throw new UncheckedIOException(e);
}
} }
/** /**
* returns an instance of PostalTradeAddress for SellerTradeParty section * returns an instance of PostalTradeAddress for SellerTradeParty section
*
* @return an instance of PostalTradeAddress * @return an instance of PostalTradeAddress
*/ */
public PostalTradeAddress getBuyerTradePartyAddress() { public PostalTradeAddress getBuyerTradePartyAddress() {
@@ -780,7 +805,7 @@ public class ZUGFeRDImporter {
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']"); nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'BuyerTradeParty']//*[local-name() = 'PostalTradeAddress']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return null; return null;
} }
@@ -789,6 +814,7 @@ public class ZUGFeRDImporter {
/** /**
* returns an instance of PostalTradeAddress for SellerTradeParty section * returns an instance of PostalTradeAddress for SellerTradeParty section
*
* @return an instance of PostalTradeAddress * @return an instance of PostalTradeAddress
*/ */
public PostalTradeAddress getSellerTradePartyAddress() { public PostalTradeAddress getSellerTradePartyAddress() {
@@ -801,15 +827,16 @@ public class ZUGFeRDImporter {
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'SellerTradeParty']//*[local-name() = 'PostalTradeAddress']"); nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeAgreement']//*[local-name() = 'SellerTradeParty']//*[local-name() = 'PostalTradeAddress']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return null; return null;
} }
return getAddressFromNodeList(nl); return getAddressFromNodeList(nl);
} }
/** /**
* returns an instance of PostalTradeAddress for ShipToTradeParty section * returns an instance of PostalTradeAddress for ShipToTradeParty section
*
* @return an instance of PostalTradeAddress * @return an instance of PostalTradeAddress
*/ */
public PostalTradeAddress getDeliveryTradePartyAddress() { public PostalTradeAddress getDeliveryTradePartyAddress() {
@@ -822,7 +849,7 @@ public class ZUGFeRDImporter {
nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeDelivery']//*[local-name() = 'ShipToTradeParty']//*[local-name() = 'PostalTradeAddress']"); nl = getNodeListByPath("//*[local-name() = 'CrossIndustryInvoice']//*[local-name() = 'SupplyChainTradeTransaction']//*[local-name() = 'ApplicableHeaderTradeDelivery']//*[local-name() = 'ShipToTradeParty']//*[local-name() = 'PostalTradeAddress']");
} }
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
return null; return null;
} }
@@ -839,7 +866,7 @@ public class ZUGFeRDImporter {
for (int j = 0; j < nodes.getLength(); j++) { for (int j = 0; j < nodes.getLength(); j++) {
n = nodes.item(j); n = nodes.item(j);
final short nodeType = n.getNodeType(); final short nodeType = n.getNodeType();
if ((nodeType==Node.ELEMENT_NODE)&&(n.getLocalName()!=null)){ if ((nodeType == Node.ELEMENT_NODE) && (n.getLocalName() != null)) {
switch (n.getLocalName()) { switch (n.getLocalName()) {
case "PostcodeCode": case "PostcodeCode":
address.setPostCodeCode(""); address.setPostCodeCode("");
@@ -893,21 +920,22 @@ public class ZUGFeRDImporter {
/** /**
* returns a list of LineItems * returns a list of LineItems
*
* @return a List of LineItem instances * @return a List of LineItem instances
*/ */
public List<Item> getLineItemList() { public List<Item> getLineItemList() {
final List<Node> nodeList = getLineItemNodes(); final List<Node> nodeList = getLineItemNodes();
final List<Item> lineItemList = new ArrayList<>(); final List<Item> lineItemList = new ArrayList<>();
for (final Node n: nodeList) { for (final Node n : nodeList) {
final Item lineItem = new Item(null, null, null); final Item lineItem = new Item(null, null, null);
lineItem.setProduct(new Product(null,null,null,null)); lineItem.setProduct(new Product(null, null, null, null));
final NodeList nl = n.getChildNodes(); final NodeList nl = n.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) { for (int i = 0; i < nl.getLength(); i++) {
final Node nn = nl.item(i); final Node nn = nl.item(i);
Node node = null; Node node = null;
if (nn.getLocalName()!=null) { if (nn.getLocalName() != null) {
switch (nn.getLocalName()) { switch (nn.getLocalName()) {
case "SpecifiedLineTradeAgreement": case "SpecifiedLineTradeAgreement":
case "SpecifiedSupplyChainTradeAgreement": case "SpecifiedSupplyChainTradeAgreement":
@@ -975,17 +1003,17 @@ public class ZUGFeRDImporter {
node = getNodeByName(nn.getChildNodes(), "BillingSpecifiedPeriod"); node = getNodeByName(nn.getChildNodes(), "BillingSpecifiedPeriod");
if (node != null) { if (node != null) {
final Node start = getNodeByName(node.getChildNodes(), "StartDateTime"); final Node start = getNodeByName(node.getChildNodes(), "StartDateTime");
Node dateTimeStart = null; Node dateTimeStart = null;
if(start != null) { if (start != null) {
dateTimeStart = getNodeByName(start.getChildNodes(), "DateTimeString"); dateTimeStart = getNodeByName(start.getChildNodes(), "DateTimeString");
} }
final Node end = getNodeByName(node.getChildNodes(), "EndDateTime"); final Node end = getNodeByName(node.getChildNodes(), "EndDateTime");
Node dateTimeEnd = null; Node dateTimeEnd = null;
if(end != null) { if (end != null) {
dateTimeEnd = getNodeByName(end.getChildNodes(), "DateTimeString"); dateTimeEnd = getNodeByName(end.getChildNodes(), "DateTimeString");
} }
lineItem.setDetailedDeliveryPeriod(tryDate(dateTimeStart), tryDate(dateTimeEnd)); lineItem.setDetailedDeliveryPeriod(tryDate(dateTimeStart), tryDate(dateTimeEnd));
} }
node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementLineMonetarySummation"); node = getNodeByName(nn.getChildNodes(), "SpecifiedTradeSettlementLineMonetarySummation");
if (node != null) { if (node != null) {
@@ -1024,16 +1052,17 @@ public class ZUGFeRDImporter {
/** /**
* returns a List of LineItem Nodes from ZUGFeRD XML * returns a List of LineItem Nodes from ZUGFeRD XML
*
* @return a List of Node instances * @return a List of Node instances
*/ */
public List<Node> getLineItemNodes() { public List<Node> getLineItemNodes() {
final List<Node> lineItemNodes = new ArrayList<>(); final List<Node> lineItemNodes = new ArrayList<>();
NodeList nl = null; NodeList nl = null;
try { try {
nl = getNodeListByPath("//*[local-name() = 'IncludedSupplyChainTradeLineItem']"); nl = getNodeListByPath("//*[local-name() = 'IncludedSupplyChainTradeLineItem']");
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); // Exception was already logged
} }
for (int i = 0; i < nl.getLength(); i++) { for (int i = 0; i < nl.getLength(); i++) {
@@ -1045,13 +1074,14 @@ public class ZUGFeRDImporter {
/** /**
* Returns a node, found by name. If more nodes with the same name are present, the first occurence will be returned * Returns a node, found by name. If more nodes with the same name are present, the first occurence will be returned
* @param nl - A NodeList which may contains the searched node *
* @param nl - A NodeList which may contains the searched node
* @param name The nodes name * @param name The nodes name
* @return a Node or null, if nothing is found * @return a Node or null, if nothing is found
*/ */
private Node getNodeByName(NodeList nl, String name) { private Node getNodeByName(NodeList nl, String name) {
for (int i = 0; i < nl.getLength(); i++) { for (int i = 0; i < nl.getLength(); i++) {
if ((nl.item(i).getLocalName()!=null)&&(nl.item(i).getLocalName().equals(name))) { if ((nl.item(i).getLocalName() != null) && (nl.item(i).getLocalName().equals(name))) {
return nl.item(i); return nl.item(i);
} else if (nl.item(i).getChildNodes().getLength() > 0) { } else if (nl.item(i).getChildNodes().getLength() > 0) {
final Node node = getNodeByName(nl.item(i).getChildNodes(), name); final Node node = getNodeByName(nl.item(i).getChildNodes(), name);
@@ -1065,6 +1095,7 @@ public class ZUGFeRDImporter {
/** /**
* Get a NodeList by providing an path * Get a NodeList by providing an path
*
* @param path a compliable Path * @param path a compliable Path
* @return a Nodelist or null, if an error occurs * @return a Nodelist or null, if an error occurs
*/ */
@@ -1078,25 +1109,27 @@ public class ZUGFeRDImporter {
final XPathExpression xpr = xPath.compile(s); final XPathExpression xpr = xPath.compile(s);
return (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); return (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
} catch (final Exception e) { } catch (final Exception e) {
Logger.getLogger(ZUGFeRDImporter.class.getName()).log(Level.SEVERE, null, e); LOGGER.error("Failed to evaluate XPath", e);
return null; return null;
} }
} }
/** /**
* returns the value of an node * returns the value of an node
*
* @param node the Node to get the value from * @param node the Node to get the value from
* @return A String or empty String, if no value was found * @return A String or empty String, if no value was found
*/ */
private String getNodeValue(Node node) { private String getNodeValue(Node node) {
if (node != null && node.getFirstChild() != null) { if (node != null && node.getFirstChild() != null) {
return node.getFirstChild().getNodeValue(); return node.getFirstChild().getNodeValue();
} }
return ""; return "";
} }
/** /**
* tries to convert an String to BigDecimal. * tries to convert an String to BigDecimal.
*
* @param nodeValue The value as String * @param nodeValue The value as String
* @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs * @return a BigDecimal with the value provides as String or a BigDecimal with value 0.00 if an error occurs
*/ */
@@ -1111,20 +1144,21 @@ public class ZUGFeRDImporter {
} }
} }
} }
private Date tryDate(Node node) {
final String nodeValue = getNodeValue(node);
if (nodeValue.isEmpty()) {
return null;
}
return tryDate(nodeValue);
}
private static Date tryDate(String toParse) { private Date tryDate(Node node) {
final SimpleDateFormat formatter = ZUGFeRDDateFormat.DATE.getFormatter(); final String nodeValue = getNodeValue(node);
try { if (nodeValue.isEmpty()) {
return formatter.parse(toParse); return null;
} catch (final Exception e) {
return null;
} }
} return tryDate(nodeValue);
}
private static Date tryDate(String toParse) {
final SimpleDateFormat formatter = ZUGFeRDDateFormat.DATE.getFormatter();
try {
return formatter.parse(toParse);
} catch (final Exception e) {
return null;
}
}
} }

View File

@@ -3,12 +3,12 @@ package org.mustangproject.ZUGFeRD;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List;
import javax.xml.xpath.XPath; import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathConstants;
@@ -17,13 +17,11 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFactory;
import org.mustangproject.Allowance; import org.mustangproject.Allowance;
import org.mustangproject.BankDetails;
import org.mustangproject.Charge; import org.mustangproject.Charge;
import org.mustangproject.EStandard; import org.mustangproject.EStandard;
import org.mustangproject.Invoice; import org.mustangproject.Invoice;
import org.mustangproject.Item; import org.mustangproject.Item;
import org.mustangproject.Product;
import org.mustangproject.ReferencedDocument;
import org.mustangproject.SchemedID;
import org.mustangproject.TradeParty; import org.mustangproject.TradeParty;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -75,10 +73,10 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
*/ */
XPathFactory xpathFact = XPathFactory.newInstance(); XPathFactory xpathFact = XPathFactory.newInstance();
XPath xpath = xpathFact.newXPath(); XPath xpath = xpathFact.newXPath();
XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]"); XPathExpression xpr = xpath.compile("//*[local-name()=\"SellerTradeParty\"]|//*[local-name()=\"AccountingSupplierParty\"]/*");
NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList SellerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]"); xpr = xpath.compile("//*[local-name()=\"BuyerTradeParty\"]|//*[local-name()=\"AccountingCustomerParty\"]/*");
NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList BuyerNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]"); xpr = xpath.compile("//*[local-name()=\"ExchangedDocument\"]|//*[local-name()=\"HeaderExchangedDocument\"]");
NodeList ExchangedDocumentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList ExchangedDocumentNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
@@ -118,17 +116,22 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
} }
} }
} }
String rootNode=extractString("local-name(/*)"); String rootNode = extractString("local-name(/*)");
if (rootNode.equals("Invoice")) if (rootNode.equals("Invoice")) {
{ // UBL...
// UBL...
number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim(); number = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"ID\"]").trim();
issueDate=new SimpleDateFormat("yyyy-MM-dd") issueDate = new SimpleDateFormat("yyyy-MM-dd")
.parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim()); .parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"IssueDate\"]").trim());
dueDate=new SimpleDateFormat("yyyy-MM-dd") String dueDt = extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim();
.parse(extractString("//*[local-name()=\"Invoice\"]/*[local-name()=\"DueDate\"]").trim()); if (dueDt.length() > 0) {
deliveryDate=new SimpleDateFormat("yyyy-MM-dd") dueDate = new SimpleDateFormat("yyyy-MM-dd")
.parse(extractString("//*[local-name()=\"Delivery\"]/*[local-name()=\"ActualDeliveryDate\"]").trim()); .parse(dueDt);
}
String deliveryDt = extractString("//*[local-name()=\"Delivery\"]/*[local-name()=\"ActualDeliveryDate\"]").trim();
if (deliveryDt.length() > 0) {
deliveryDate = new SimpleDateFormat("yyyy-MM-dd")
.parse(deliveryDt);
}
} }
xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeDelivery\"]"); xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeDelivery\"]");
NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
@@ -208,6 +211,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]"); xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]|//*[local-name()=\"ApplicableSupplyChainTradeSettlement\"]");
NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList headerTradeSettlementNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
List<BankDetails> bankDetails = new ArrayList<>();
for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) { for (int i = 0; i < headerTradeSettlementNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) { // nodes.item(i).getTextContent())) {
@@ -235,11 +239,81 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
} }
} }
} }
if ((headerTradeSettlementChilds.item(settlementChildIndex).getLocalName() != null)
&& (headerTradeSettlementChilds.item(settlementChildIndex).getLocalName()
.equals("SpecifiedTradeSettlementPaymentMeans"))) {
NodeList paymentMeansChilds = headerTradeSettlementChilds.item(settlementChildIndex).getChildNodes();
for (int paymentMeansChildIndex = 0; paymentMeansChildIndex < paymentMeansChilds
.getLength(); paymentMeansChildIndex++) {
String IBAN = null, BIC = null;
if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds
.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialAccount"))) {
NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes();
for (int accountChildIndex = 0; accountChildIndex < accountChilds
.getLength(); accountChildIndex++) {
if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds
.item(accountChildIndex).getLocalName().equals("IBANID"))) {//CII
IBAN = accountChilds.item(accountChildIndex).getTextContent();
}
}
}
if ((paymentMeansChilds.item(paymentMeansChildIndex).getLocalName() != null) && (paymentMeansChilds
.item(paymentMeansChildIndex).getLocalName().equals("PayeePartyCreditorFinancialInstitution"))) {
NodeList accountChilds = paymentMeansChilds.item(paymentMeansChildIndex).getChildNodes();
for (int accountChildIndex = 0; accountChildIndex < accountChilds
.getLength(); accountChildIndex++) {
if ((accountChilds.item(accountChildIndex).getLocalName() != null) && (accountChilds
.item(accountChildIndex).getLocalName().equals("BICID"))) {//CII
BIC = accountChilds.item(accountChildIndex).getTextContent();
}
}
}
if (IBAN != null) {
BankDetails bd = new BankDetails(IBAN);
if (BIC != null) {
bd.setBIC(BIC);
}
bankDetails.add(bd);
}
}
}
}
}
xpr = xpath.compile("//*[local-name()=\"PaymentMeans\"]"); //UBL only
NodeList paymentMeansNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
for (int i = 0; i < paymentMeansNodes.getLength(); i++) {
// nodes.item(i).getTextContent())) {
Node paymentMeansNode = paymentMeansNodes.item(i);
NodeList paymentMeansChilds = paymentMeansNode.getChildNodes();
for (int meansChildIndex = 0; meansChildIndex < paymentMeansChilds
.getLength(); meansChildIndex++) {
if ((paymentMeansChilds.item(meansChildIndex).getLocalName() != null)
&& (paymentMeansChilds.item(meansChildIndex).getLocalName()
.equals("PayeeFinancialAccount"))) {
NodeList paymentTermChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes();
for (int paymentTermChildIndex = 0; paymentTermChildIndex < paymentTermChilds
.getLength(); paymentTermChildIndex++) {
if ((paymentTermChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds
.item(paymentTermChildIndex).getLocalName().equals("ID"))) {
String IBAN = paymentTermChilds.item(paymentTermChildIndex).getTextContent();
if (IBAN != null) {
BankDetails bd = new BankDetails(IBAN);
bankDetails.add(bd);
}
}
}
}
} }
} }
zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate) zpp.setDueDate(dueDate).setDeliveryDate(deliveryDate).setIssueDate(issueDate)
.setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number); .setSender(new TradeParty(SellerNodes)).setRecipient(new TradeParty(BuyerNodes)).setNumber(number);
bankDetails.forEach(bankDetail -> zpp.getSender().addBankDetails(bankDetail));
if (buyerOrderIssuerAssignedID != null) { if (buyerOrderIssuerAssignedID != null) {
zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID); zpp.setBuyerOrderReferencedDocumentID(buyerOrderIssuerAssignedID);
} }
@@ -263,8 +337,7 @@ public class ZUGFeRDInvoiceImporter extends ZUGFeRDImporter {
xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]"); xpr = xpath.compile("//*[local-name()=\"IncludedSupplyChainTradeLineItem\"]|//*[local-name()=\"InvoiceLine\"]");
NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET); NodeList nodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (nodes.getLength() == 0) { if (nodes.getLength() != 0) {
} else {
for (int i = 0; i < nodes.getLength(); i++) { for (int i = 0; i < nodes.getLength(); i++) {
Node currentItemNode = nodes.item(i); Node currentItemNode = nodes.item(i);

View File

@@ -20,28 +20,56 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.fop.apps.*; import java.io.BufferedOutputStream;
import org.apache.fop.configuration.Configuration; import java.io.ByteArrayInputStream;
import org.apache.fop.configuration.ConfigurationException; import java.io.ByteArrayOutputStream;
import org.apache.fop.configuration.DefaultConfigurationBuilder; import java.io.File;
import org.apache.xmlgraphics.util.MimeConstants; import java.io.FileInputStream;
import org.mustangproject.CII.CIIToUBL; import java.io.FileNotFoundException;
import org.mustangproject.ClasspathResolverURIAdapter; import java.io.FileOutputStream;
import org.xml.sax.SAXException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.xml.transform.*; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXResult; import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import java.io.*; import org.apache.fop.apps.FOPException;
import java.net.URI; import org.apache.fop.apps.FOUserAgent;
import java.net.URISyntaxException; import org.apache.fop.apps.Fop;
import java.nio.charset.StandardCharsets; import org.apache.fop.apps.FopFactory;
import java.nio.file.Files; import org.apache.fop.apps.FopFactoryBuilder;
import java.nio.file.Paths; import org.apache.fop.apps.io.ResourceResolverFactory;
import java.util.logging.Level; import org.apache.fop.configuration.Configuration;
import java.util.logging.Logger; import org.apache.fop.configuration.ConfigurationException;
import org.apache.fop.configuration.DefaultConfigurationBuilder;
import org.apache.xmlgraphics.util.MimeConstants;
import org.mustangproject.ClasspathResolverURIAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.helger.commons.io.stream.StreamHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class ZUGFeRDVisualizer { public class ZUGFeRDVisualizer {
@@ -53,7 +81,7 @@ public class ZUGFeRDVisualizer {
static final ClassLoader CLASS_LOADER = ZUGFeRDVisualizer.class.getClassLoader(); static final ClassLoader CLASS_LOADER = ZUGFeRDVisualizer.class.getClassLoader();
private static final String RESOURCE_PATH = ""; private static final String RESOURCE_PATH = "";
private static final Logger LOG = Logger.getLogger(ZUGFeRDVisualizer.class.getName()); private static final Logger LOGGER = LoggerFactory.getLogger(ZUGFeRDVisualizer.class);
// private static File createTempFileResult(final Transformer transformer, final // private static File createTempFileResult(final Transformer transformer, final
// StreamSource toTransform, // StreamSource toTransform,
// final String suffix) throws TransformerException, IOException { // final String suffix) throws TransformerException, IOException {
@@ -79,27 +107,27 @@ public class ZUGFeRDVisualizer {
} }
public String visualize(String xmlFilename, Language lang) public String visualize(String xmlFilename, Language lang)
throws FileNotFoundException, TransformerException, UnsupportedEncodingException { throws FileNotFoundException, TransformerException, IOException, SAXException, ParserConfigurationException {
try { try {
if (mXsltXRTemplate == null) { if (mXsltXRTemplate == null) {
mXsltXRTemplate = mFactory.newTemplates( mXsltXRTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
} }
if (mXsltPDFTemplate == null) { if (mXsltPDFTemplate == null) {
mXsltPDFTemplate = mFactory.newTemplates( mXsltPDFTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xr-pdf.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xr-pdf.xsl")));
} }
if (mXsltHTMLTemplate == null) { if (mXsltHTMLTemplate == null) {
mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource( mXsltHTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html." + lang.name().toLowerCase() + ".xsl"))); CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xrechnung-html." + lang.name().toLowerCase() + ".xsl")));
} }
if (mXsltZF1HTMLTemplate == null) { if (mXsltZF1HTMLTemplate == null) {
mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource( mXsltZF1HTMLTemplate = mFactory.newTemplates(new StreamSource(
CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt"))); CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ZUGFeRD_1p0_c1p0_s1p0.xslt")));
} }
} catch (TransformerConfigurationException ex) { } catch (TransformerConfigurationException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to init XSLT templates", ex);
} }
/** /**
@@ -113,35 +141,40 @@ public class ZUGFeRDVisualizer {
try { try {
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8); fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8);
} catch (IOException e2) { } catch (IOException e2) {
LOG.log(Level.SEVERE, null, e2); LOGGER.error("Failed to read file content", e2);
} }
ByteArrayOutputStream iaos = new ByteArrayOutputStream(); ByteArrayOutputStream iaos = new ByteArrayOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
String zf1Signature = "rsm:CrossIndustryDocument"; String zf1Signature = "CrossIndustryDocument";
String zf2Signature = "rsm:CrossIndustryInvoice"; String zf2Signature = "CrossIndustryInvoice";
String ublSignature = "ubl:Invoice"; String ublSignature = "Invoice";
String ublCreditNoteSignature = "ubl:CreditNote"; String ublCreditNoteSignature = "CreditNote";
boolean doPostProcessing = false; boolean doPostProcessing = false;
if (fileContent.contains(zf1Signature)) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(fis));
Element root = doc.getDocumentElement();
fis = new FileInputStream(xmlFilename); // fis wont reset() so re-read from beginning
if (root.getLocalName().equals(zf1Signature)) {
applyZF1XSLT(fis, baos); applyZF1XSLT(fis, baos);
} else if (root.getLocalName().equals(zf2Signature)) {
} else if (fileContent.contains(zf2Signature)) {
//zf2 or fx //zf2 or fx
applyZF2XSLT(fis, iaos); applyZF2XSLT(fis, iaos);
doPostProcessing = true; doPostProcessing = true;
} else if (fileContent.contains(ublSignature)) { } else if (root.getLocalName().equals(ublSignature)) {
//zf2 or fx //zf2 or fx
applyUBL2XSLT(fis, iaos); applyUBL2XSLT(fis, iaos);
doPostProcessing = true; doPostProcessing = true;
} else if (root.getLocalName().equals(ublCreditNoteSignature)) {
} else if (fileContent.contains(ublCreditNoteSignature)) {
//zf2 or fx //zf2 or fx
applyUBLCreditNote2XSLT(fis, iaos); applyUBLCreditNote2XSLT(fis, iaos);
doPostProcessing = true; doPostProcessing = true;
} else {
throw new IllegalArgumentException("File does not look like CII or UBL");
} }
if (doPostProcessing) { if (doPostProcessing) {
// take the copy of the stream and re-write it to an InputStream // take the copy of the stream and re-write it to an InputStream
@@ -158,54 +191,46 @@ public class ZUGFeRDVisualizer {
// ByteArrayOutputStream // ByteArrayOutputStream
iaos.writeTo(out); iaos.writeTo(out);
} catch (IOException e) { } catch (IOException e) {
LOG.log(Level.SEVERE, null, e); LOGGER.error("Failed to write to stream", e);
} finally { } finally {
// close the PipedOutputStream here because we're done writing data // close the PipedOutputStream here because we're done writing data
// once this thread has completed its run // once this thread has completed its run
if (out != null) { StreamHelper.close(out);
// close the PipedOutputStream cleanly
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE, null, e);
}
}
} }
} }
}).start(); }).start();
applyXSLTToHTML(in, baos); applyXSLTToHTML(in, baos);
} catch (IOException e1) { } catch (IOException e1) {
LOG.log(Level.SEVERE, null, e1); LOGGER.error("Failed to create HTML", e1);
} }
} }
return baos.toString("UTF-8"); return baos.toString(StandardCharsets.UTF_8);
} }
protected String toFOP(String xmlFilename) protected String toFOP(String xmlFilename)
throws FileNotFoundException, TransformerException, UnsupportedEncodingException { throws FileNotFoundException, TransformerException {
try { try {
if (mXsltXRTemplate == null) { if (mXsltXRTemplate == null) {
mXsltXRTemplate = mFactory.newTemplates( mXsltXRTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/cii-xr.xsl")));
} }
if (mXsltPDFTemplate == null) { if (mXsltPDFTemplate == null) {
mXsltPDFTemplate = mFactory.newTemplates( mXsltPDFTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xr-pdf.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/xr-pdf.xsl")));
} }
} catch (TransformerConfigurationException ex) { } catch (TransformerConfigurationException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to init XSLT templates", ex);
} }
FileInputStream fis = new FileInputStream(xmlFilename); FileInputStream fis = new FileInputStream(xmlFilename);
String fileContent = "";
try { try {
fileContent = new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8); // TODO why are we reading the whole file here and discarding the content?
new String(Files.readAllBytes(Paths.get(xmlFilename)), StandardCharsets.UTF_8);
} catch (IOException e2) { } catch (IOException e2) {
LOG.log(Level.SEVERE, null, e2); LOGGER.error("Failed to read file", e2);
} }
ByteArrayOutputStream iaos = new ByteArrayOutputStream(); ByteArrayOutputStream iaos = new ByteArrayOutputStream();
@@ -227,39 +252,28 @@ public class ZUGFeRDVisualizer {
// ByteArrayOutputStream // ByteArrayOutputStream
iaos.writeTo(out); iaos.writeTo(out);
} catch (IOException e) { } catch (IOException e) {
LOG.log(Level.SEVERE, null, e); LOGGER.error("Failed to write to stream", e);
} finally { } finally {
// close the PipedOutputStream here because we're done writing data // close the PipedOutputStream here because we're done writing data
// once this thread has completed its run // once this thread has completed its run
if (out != null) { StreamHelper.close(out);
// close the PipedOutputStream cleanly
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE, null, e);
}
}
} }
} }
}).start(); }).start();
applyXSLTToPDF(in, baos); applyXSLTToPDF(in, baos);
} catch (IOException e1) { } catch (IOException e1) {
LOG.log(Level.SEVERE, null, e1); LOGGER.error("Failed to create PDF", e1);
} }
return baos.toString("UTF-8"); return baos.toString(StandardCharsets.UTF_8);
} }
public void toPDF(String xmlFilename, String pdfFilename) { public void toPDF(String xmlFilename, String pdfFilename) {
// the writing part // the writing part
CIIToUBL c2u = new CIIToUBL();
String sourceFilename = "factur-x.xml";
File CIIinputFile = new File(xmlFilename); File CIIinputFile = new File(xmlFilename);
String expected = null;
String result = null; String result = null;
ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer(); ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
@@ -268,12 +282,8 @@ public class ZUGFeRDVisualizer {
*/ */
try { try {
result = zvi.toFOP(CIIinputFile.getAbsolutePath()); result = zvi.toFOP(CIIinputFile.getAbsolutePath());
} catch (FileNotFoundException e) { } catch (FileNotFoundException | TransformerException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e); LOGGER.error("Failed to apply FOP", e);
} catch (TransformerException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e);
} catch (UnsupportedEncodingException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e);
} }
/* /*
FopConfParser parser = null; FopConfParser parser = null;
@@ -304,6 +314,11 @@ public class ZUGFeRDVisualizer {
FopFactory fopFactory = builder.build();//FopFactory.newInstance(new File("c:\\Users\\jstaerk\\temp\\fop-config.xconf")); FopFactory fopFactory = builder.build();//FopFactory.newInstance(new File("c:\\Users\\jstaerk\\temp\\fop-config.xconf"));
fopFactory.getFontManager().setResourceResolver(
ResourceResolverFactory.createInternalResourceResolver(
new File(".").toURI(),
new ClasspathResolverURIAdapter()));
FOUserAgent userAgent = fopFactory.newFOUserAgent(); FOUserAgent userAgent = fopFactory.newFOUserAgent();
userAgent.getRendererOptions().put("pdf-a-mode", "PDF/A-1b"); userAgent.getRendererOptions().put("pdf-a-mode", "PDF/A-1b");
@@ -332,31 +347,23 @@ public class ZUGFeRDVisualizer {
//Files.write(Paths.get("C:\\Users\\jstaerk\\temp\\fop.pdf"), res.toString().getBytes(StandardCharsets.UTF_8)); //Files.write(Paths.get("C:\\Users\\jstaerk\\temp\\fop.pdf"), res.toString().getBytes(StandardCharsets.UTF_8));
} catch (FOPException e) { } catch (FOPException | IOException | TransformerException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e); LOGGER.error("Failed to create PDF", e);
} catch (TransformerConfigurationException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e);
} catch (IOException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e);
} catch (TransformerException e) {
Logger.getLogger(ZUGFeRDVisualizer.class.getName()).log(Level.SEVERE, null, e);
} }
} }
protected void applyZF2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyZF2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException { throws TransformerException {
Transformer transformer = mXsltXRTemplate.newTransformer(); Transformer transformer = mXsltXRTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
} }
protected void applyUBL2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyUBL2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException { throws TransformerException {
if (mXsltUBLTemplate == null) { if (mXsltUBLTemplate == null) {
mXsltUBLTemplate = mFactory.newTemplates( mXsltUBLTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ubl-invoice-xr.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ubl-invoice-xr.xsl")));
} }
Transformer transformer = mXsltUBLTemplate.newTransformer(); Transformer transformer = mXsltUBLTemplate.newTransformer();
@@ -364,10 +371,10 @@ public class ZUGFeRDVisualizer {
} }
protected void applyUBLCreditNote2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyUBLCreditNote2XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException { throws TransformerException {
if (mXsltUBLTemplate == null) { if (mXsltUBLTemplate == null) {
mXsltUBLTemplate = mFactory.newTemplates( mXsltUBLTemplate = mFactory.newTemplates(
new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ubl-creditnote-xr.xsl"))); new StreamSource(CLASS_LOADER.getResourceAsStream(RESOURCE_PATH + "stylesheets/ubl-creditnote-xr.xsl")));
} }
Transformer transformer = mXsltUBLTemplate.newTransformer(); Transformer transformer = mXsltUBLTemplate.newTransformer();
@@ -375,21 +382,21 @@ public class ZUGFeRDVisualizer {
} }
protected void applyZF1XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyZF1XSLT(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException { throws TransformerException {
Transformer transformer = mXsltZF1HTMLTemplate.newTransformer(); Transformer transformer = mXsltZF1HTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
} }
protected void applyXSLTToHTML(final InputStream xmlFile, final OutputStream HTMLOutstream) protected void applyXSLTToHTML(final InputStream xmlFile, final OutputStream HTMLOutstream)
throws TransformerException { throws TransformerException {
Transformer transformer = mXsltHTMLTemplate.newTransformer(); Transformer transformer = mXsltHTMLTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(HTMLOutstream));
} }
protected void applyXSLTToPDF(final InputStream xmlFile, final OutputStream PDFOutstream) protected void applyXSLTToPDF(final InputStream xmlFile, final OutputStream PDFOutstream)
throws TransformerException { throws TransformerException {
Transformer transformer = mXsltPDFTemplate.newTransformer(); Transformer transformer = mXsltPDFTemplate.newTransformer();
transformer.transform(new StreamSource(xmlFile), new StreamResult(PDFOutstream)); transformer.transform(new StreamSource(xmlFile), new StreamResult(PDFOutstream));

View File

@@ -25,7 +25,7 @@
<renderer mime="application/pdf"> <renderer mime="application/pdf">
<fonts><!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed --> <fonts><!-- https://xmlgraphics.apache.org/fop/1.1/fonts.html auto-embed -->
<auto-detect/> <auto-detect/>
<font kerning="no" embed-url="FreeSans.ttf" embedding-mode="subset"> <font kerning="no" embed-url="classpath:FreeSans.ttf" embedding-mode="subset">
<font-triplet name="SourceSerifPro" style="normal" weight="normal" /> <font-triplet name="SourceSerifPro" style="normal" weight="normal" />
<font-triplet name="Times-Bold" style="normal" weight="normal" /> <font-triplet name="Times-Bold" style="normal" weight="normal" />
</font> </font>

View File

@@ -22,6 +22,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
@@ -65,7 +66,7 @@ public class BackwardCompatibilityTest extends TestCase implements IExportableTr
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
// check for pdf-a schema extension // check for pdf-a schema extension
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1); // assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
@@ -105,7 +106,7 @@ public class BackwardCompatibilityTest extends TestCase implements IExportableTr
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
// check for pdf-a schema extension // check for pdf-a schema extension
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1); // assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
@@ -143,7 +144,7 @@ public class BackwardCompatibilityTest extends TestCase implements IExportableTr
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
// check for pdf-a schema extension // check for pdf-a schema extension
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1); // assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
@@ -316,7 +317,7 @@ public class BackwardCompatibilityTest extends TestCase implements IExportableTr
// //
public IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment() { public IZUGFeRDTradeSettlementPayment[] getTradeSettlementPayment() {
Payment P = new Payment(); Payment P = new Payment();
IZUGFeRDTradeSettlementPayment[] allP = new Payment[1]; IZUGFeRDTradeSettlementPayment[] allP = new Payment[1];
allP[0] = P; allP[0] = P;

View File

@@ -22,16 +22,7 @@ import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.mustangproject.XMLTools; import org.mustangproject.XMLTools;
public class BaseTest extends TestCase { public class BaseTest extends TestCase {

View File

@@ -6,17 +6,18 @@ import static org.junit.Assert.assertEquals;
import org.junit.Test; import org.junit.Test;
import org.mustangproject.*; import org.mustangproject.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
/*** /***
* tests the linecalculator and transactioncalculator classes * tests the linecalculator and transactioncalculator classes
* *
*/ */
public class CalculationTest { public class CalculationTest {
private static final Logger LOGGER = LoggerFactory.getLogger (CalculationTest.class);
@Test @Test
public void testLineCalculator_simpleAmounts_resultInValidVATAmount() { public void testLineCalculator_simpleAmounts_resultInValidVATAmount() {
@@ -92,7 +93,7 @@ public class CalculationTest {
invoice.setDeliveryDate(sqlDate.parse("2020-12-31")); invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15")); invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(CalculationTest.class.getName()).log(Level.SEVERE, null, e); LOGGER.error("Failed to set dates", e);
} }

View File

@@ -45,7 +45,7 @@ import java.util.GregorianCalendar;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DXTest extends MustangReaderTestCase implements IExportableTransaction { public class DXTest extends MustangReaderTestCase {
final String TARGET_PDF = "./target/testout-DX.pdf"; final String TARGET_PDF = "./target/testout-DX.pdf";
final String TARGET_XML = "./target/testout-DX.xml"; final String TARGET_XML = "./target/testout-DX.xml";

View File

@@ -21,12 +21,9 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.assertj.core.util.Lists;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.mustangproject.*; import org.mustangproject.*;

View File

@@ -256,13 +256,13 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
"</ram:ApplicableHeaderTradeSettlement>\n" + "</ram:ApplicableHeaderTradeSettlement>\n" +
"</rsm:SupplyChainTradeTransaction>\n" + "</rsm:SupplyChainTradeTransaction>\n" +
"</rsm:CrossIndustryInvoice>"; "</rsm:CrossIndustryInvoice>";
zea1.setXML(ownZUGFeRDXML.getBytes("UTF-8")); zea1.setXML(ownZUGFeRDXML.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
zea1.disableAutoClose(true); zea1.disableAutoClose(true);
zea1.export(TARGET_PDF); zea1.export(TARGET_PDF);
zea1.export(baos); zea1.export(baos);
zea1.close(); zea1.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
assertFalse(pdfContent.indexOf("<fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>") == -1); assertFalse(pdfContent.indexOf("<fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>") == -1);
@@ -437,14 +437,14 @@ public class MustangReaderWriterCustomXMLTest extends TestCase {
+ "<ram:Name>Heiße Luft pro Liter</ram:Name>\n" + "<ram:Description/>\n" + "<ram:Name>Heiße Luft pro Liter</ram:Name>\n" + "<ram:Description/>\n"
+ "</ram:SpecifiedTradeProduct>\n" + "</ram:IncludedSupplyChainTradeLineItem>\n" + "</ram:SpecifiedTradeProduct>\n" + "</ram:IncludedSupplyChainTradeLineItem>\n"
+ "</rsm:SpecifiedSupplyChainTradeTransaction>\n" + "</rsm:CrossIndustryDocument>"; + "</rsm:SpecifiedSupplyChainTradeTransaction>\n" + "</rsm:CrossIndustryDocument>";
zea1.setXML(ownZUGFeRDXML.getBytes("UTF-8")); zea1.setXML(ownZUGFeRDXML.getBytes(StandardCharsets.UTF_8));
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
zea1.disableAutoClose(true); zea1.disableAutoClose(true);
zea1.export(TARGET_PDF); zea1.export(TARGET_PDF);
zea1.export(baos); zea1.export(baos);
zea1.close(); zea1.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>BASIC</zf:ConformanceLevel>") == -1); assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>BASIC</zf:ConformanceLevel>") == -1);

View File

@@ -21,6 +21,7 @@ package org.mustangproject.ZUGFeRD;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.xmpbox.XMPMetadata; import org.apache.xmpbox.XMPMetadata;
@@ -42,7 +43,7 @@ import java.util.GregorianCalendar;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class MustangReaderWriterEdgeTest extends MustangReaderTestCase { public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
protected class EasyRecipientTradeParty extends RecipientTradeParty implements IZUGFeRDExportableTradeParty { protected class EasyRecipientTradeParty extends RecipientTradeParty {
// Not testing extended profile here, lineThree not possible // Not testing extended profile here, lineThree not possible
@Override @Override
public String getAdditionalAddressExtension() { public String getAdditionalAddressExtension() {
@@ -252,7 +253,7 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
setupPdfUnderTest(SOURCE_PDF, TARGET_PDF); setupPdfUnderTest(SOURCE_PDF, TARGET_PDF);
// now check the contents // now check the contents
PDDocument doc = PDDocument.load(new File(TARGET_PDF)); PDDocument doc = Loader.loadPDF(new File(TARGET_PDF));
PDMetadata meta = doc.getDocumentCatalog().getMetadata(); PDMetadata meta = doc.getDocumentCatalog().getMetadata();
assertNotNull("The pdf must contain XMPMetadata", meta); assertNotNull("The pdf must contain XMPMetadata", meta);
@@ -262,7 +263,7 @@ public class MustangReaderWriterEdgeTest extends MustangReaderTestCase {
DomXmpParser xmpParser = new DomXmpParser(); DomXmpParser xmpParser = new DomXmpParser();
XMPMetadata xmp = xmpParser.parse(xmpBytes); XMPMetadata xmp = xmpParser.parse(xmpBytes);
PDFAIdentificationSchema pdfai = xmp.getPDFIdentificationSchema(); PDFAIdentificationSchema pdfai = xmp.getPDFAIdentificationSchema();
assertNotNull("The pdf must contain a PDFIdentificationSchema", pdfai); assertNotNull("The pdf must contain a PDFIdentificationSchema", pdfai);
assertTrue("The PDF/A conformance must be A, B or U", assertTrue("The PDF/A conformance must be A, B or U",

View File

@@ -21,6 +21,7 @@ package org.mustangproject.ZUGFeRD;
import junit.framework.Test; import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDMetadata;
import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException; import org.apache.pdfbox.pdmodel.encryption.InvalidPasswordException;
@@ -38,6 +39,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
@@ -328,13 +330,13 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
} }
private void checkPdfA3B(File tempFile) throws IOException, InvalidPasswordException { private void checkPdfA3B(File tempFile) throws IOException, InvalidPasswordException {
try (PDDocument doc = PDDocument.load(tempFile)) { try (PDDocument doc = Loader.loadPDF(tempFile)) {
PDMetadata metadata = doc.getDocumentCatalog().getMetadata(); PDMetadata metadata = doc.getDocumentCatalog().getMetadata();
InputStream exportXMPMetadata = metadata.exportXMPMetadata(); InputStream exportXMPMetadata = metadata.exportXMPMetadata();
byte[] xmpBytes = new byte[exportXMPMetadata.available()]; byte[] xmpBytes = new byte[exportXMPMetadata.available()];
exportXMPMetadata.read(xmpBytes); exportXMPMetadata.read(xmpBytes);
final XMPMetadata xmp = new DomXmpParser().parse(xmpBytes); final XMPMetadata xmp = new DomXmpParser().parse(xmpBytes);
PDFAIdentificationSchema pdfaid = xmp.getPDFIdentificationSchema(); PDFAIdentificationSchema pdfaid = xmp.getPDFAIdentificationSchema();
assertEquals(pdfaid.getPart().intValue(), 3); assertEquals(pdfaid.getPart().intValue(), 3);
assertEquals(pdfaid.getConformance(), "U"); assertEquals(pdfaid.getConformance(), "U");
} catch (XmpParsingException e) { } catch (XmpParsingException e) {
@@ -367,7 +369,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
// check for pdf-a schema extension // check for pdf-a schema extension
// assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1); // assertFalse(pdfContent.indexOf("<zf:ConformanceLevel>EN 16931</zf:ConformanceLevel>") == -1);
@@ -411,7 +413,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.BASIC) >= 0); assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.BASIC) >= 0);
assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.EXTENDED) >= 0); assertFalse(pdfContent.indexOf(DocumentContextParameterTypeConstants.EXTENDED) >= 0);
assertTrue(pdfContent.indexOf(DocumentContextParameterTypeConstants.COMFORT) >= 0); assertTrue(pdfContent.indexOf(DocumentContextParameterTypeConstants.COMFORT) >= 0);
@@ -447,7 +449,7 @@ public class MustangReaderWriterTest extends MustangReaderTestCase {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
ze.export(baos); ze.export(baos);
ze.close(); ze.close();
String pdfContent = baos.toString("UTF-8"); String pdfContent = baos.toString(StandardCharsets.UTF_8);
assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1); assertFalse(pdfContent.indexOf("(via mustangproject.org") == -1);
// check for pdf-a schema extension // check for pdf-a schema extension
assertFalse(pdfContent.indexOf("<fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>") == -1); assertFalse(pdfContent.indexOf("<fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>") == -1);

View File

@@ -28,7 +28,6 @@ import org.junit.runners.MethodSorters;
import org.mustangproject.*; import org.mustangproject.*;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -42,7 +41,7 @@ import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class OXTest extends MustangReaderTestCase implements IExportableTransaction { public class OXTest extends MustangReaderTestCase {
final String TARGET_PDF = "./target/testout-OX.pdf"; final String TARGET_PDF = "./target/testout-OX.pdf";
final String TARGET_PDF_EDGE = "./target/testout-OX-edge.pdf"; final String TARGET_PDF_EDGE = "./target/testout-OX-edge.pdf";
final String TARGET_XML = "./target/testout-OX.xml"; final String TARGET_XML = "./target/testout-OX.xml";

View File

@@ -0,0 +1,87 @@
/**
* *********************************************************************
* <p>
* Copyright 2019 Jochen Staerk
* <p>
* Use is subject to license terms.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* <p>
* See the License for the specific language governing permissions and
* limitations under the License.
* <p>
* **********************************************************************
*/
package org.mustangproject.ZUGFeRD;
import junit.framework.TestCase;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
import org.mustangproject.*;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class PreValidationTest extends TestCase {
final String TARGET_PDF = "./target/testout-Preval.pdf";
final String SOURCE_PDF = "/veraPDFtestsuite6-7-11-t01-fail-a.pdf";
public void testFailIgnore() {
// the writing part
boolean hasEx = false;
try (InputStream source = this.getClass()
.getResourceAsStream(SOURCE_PDF)) {
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2)
.load(source);
ze.setTransaction(createInvoice());
ze.export(TARGET_PDF);
} catch (IOException e) {
hasEx = true;
}
assertTrue(hasEx);
hasEx = false;
try (InputStream source = this.getClass()
.getResourceAsStream(SOURCE_PDF)) {
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
ze.ignorePDFAErrors().load(source);
ze.setProducer("My Application").setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).
setTransaction(createInvoice());
ze.export(TARGET_PDF);
} catch (IOException e) {
hasEx = true;
}
assertFalse(hasEx);
}
private Invoice createInvoice() {
String orgname = "Test company";
String number = "123";
String amountStr = "1.00";
BigDecimal amount = new BigDecimal(amountStr);
return new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("DE4711").addVATID("DE0815").setEmail("info@example.org").setContact(new Contact("Hans Test", "+49123456789", "test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890", "COBADEFXXX")))
.setRecipient(new TradeParty("Theodor Est", "Bahnstr. 42", "88802", "Spielkreis", "DE"))
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)));
}
}

View File

@@ -23,13 +23,10 @@ package org.mustangproject.ZUGFeRD;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.mustangproject.*; import org.mustangproject.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar;
/*** /***
* This is a test to confirm the minimum steps to implement a interface are still sufficient * This is a test to confirm the minimum steps to implement a interface are still sufficient

View File

@@ -1,6 +1,8 @@
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.junit.Ignore;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -9,6 +11,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@Ignore
public class ResourceCase extends TestCase { public class ResourceCase extends TestCase {
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceCase.class.getCanonicalName()); // log output is private static final Logger LOGGER = LoggerFactory.getLogger(ResourceCase.class.getCanonicalName()); // log output is

View File

@@ -22,15 +22,16 @@ import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* This test utility class is providing usability functions to test file resources * This test utility class is providing usability functions to test file resources
*/ */
public class ResourceUtilities { public class ResourceUtilities {
private static final Logger LOG = Logger.getLogger(ResourceUtilities.class.getName()); private static final Logger LOGGER = LoggerFactory.getLogger (ResourceUtilities.class);
/** /**
* Loading a File into a String using a certain encoding and file path * Loading a File into a String using a certain encoding and file path
@@ -73,7 +74,7 @@ public class ResourceUtilities {
uri = ResourceUtilities.class.getClassLoader().getResource(relativeFilePath).toURI(); uri = ResourceUtilities.class.getClassLoader().getResource(relativeFilePath).toURI();
uri = new URI(toExternalForm(uri)); uri = new URI(toExternalForm(uri));
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to parse URI", ex);
} }
if (uri == null) { if (uri == null) {
throw new FileNotFoundException("Could not find the file '" + relativeFilePath + "'!"); throw new FileNotFoundException("Could not find the file '" + relativeFilePath + "'!");
@@ -129,7 +130,7 @@ public class ResourceUtilities {
try { try {
filepath = ResourceUtilities.class.getClassLoader().getResource("").toURI().getPath() + relativeFilePath; filepath = ResourceUtilities.class.getClassLoader().getResource("").toURI().getPath() + relativeFilePath;
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to parse URI", ex);
} }
return new File(filepath); return new File(filepath);
} }
@@ -142,7 +143,7 @@ public class ResourceUtilities {
try { try {
testFolder = ResourceUtilities.class.getClassLoader().getResource("").toURI().getPath(); testFolder = ResourceUtilities.class.getClassLoader().getResource("").toURI().getPath();
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to parse URI", ex);
} }
return testFolder; return testFolder;
} }
@@ -206,7 +207,7 @@ public class ResourceUtilities {
try { try {
ret = new URI(sb.toString()).toASCIIString(); ret = new URI(sb.toString()).toASCIIString();
} catch (URISyntaxException ex) { } catch (URISyntaxException ex) {
LOG.log(Level.SEVERE, null, ex); LOGGER.error("Failed to parse URI", ex);
} }
return ret; return ret;
} }

View File

@@ -21,13 +21,12 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.ByteArrayOutputStream; import java.io.*;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.ParseException;
import java.util.Date; import java.util.Date;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
@@ -41,6 +40,8 @@ import org.mustangproject.Item;
import org.mustangproject.Product; import org.mustangproject.Product;
import org.mustangproject.TradeParty; import org.mustangproject.TradeParty;
import javax.xml.xpath.XPathExpressionException;
public class UBLTest extends ResourceCase { public class UBLTest extends ResourceCase {
final String TARGET_XML = "./target/testout-1Lieferschein.xml"; final String TARGET_XML = "./target/testout-1Lieferschein.xml";
@@ -89,7 +90,7 @@ public class UBLTest extends ResourceCase {
final ByteArrayOutputStream baos=new ByteArrayOutputStream(); final ByteArrayOutputStream baos=new ByteArrayOutputStream();
oe.export(baos); oe.export(baos);
final String theXML = baos.toString("UTF-8"); final String theXML = baos.toString(StandardCharsets.UTF_8);
assertTrue(theXML.contains("<DespatchAdvice")); assertTrue(theXML.contains("<DespatchAdvice"));
Files.write(Paths.get(TARGET_XML), theXML.getBytes(StandardCharsets.UTF_8)); Files.write(Paths.get(TARGET_XML), theXML.getBytes(StandardCharsets.UTF_8));
} catch (final IOException e) { } catch (final IOException e) {
@@ -98,4 +99,76 @@ public class UBLTest extends ResourceCase {
} }
public void testEdgeInvoiceImportUBL() {
File UBLinputFile = getResourceAsFile("ubl/01.01a-INVOICE.ubl.xml");
boolean hasExceptions = false;
ZUGFeRDInvoiceImporter zii = null;
Invoice invoice = null;
try {
zii = new ZUGFeRDInvoiceImporter(new FileInputStream(UBLinputFile));
invoice = zii.extractInvoice();
} catch (XPathExpressionException | ParseException | FileNotFoundException e) {
hasExceptions = true;
}
assertFalse(hasExceptions);
// Reading ZUGFeRD
assertEquals(new BigDecimal("288.79"), invoice.getZFItems()[0].getPrice());
assertEquals("04011000-12345-03", invoice.getReferenceNumber());
assertEquals("seller@email.de", invoice.getSender().getContact().getEMail());
assertEquals("12345", invoice.getRecipient().getZIP());
assertEquals("DE75512108001245126199", invoice.getSender().getBankDetails().get(0).getIBAN());
}
/* public void testInvoiceImportOtherUBL() {
File UBLinputFile = getResourceAsFile("ubl/EN16931_1_Teilrechnung.ubl.xml");
// File UBLinputFile = getResourceAsFile("ubl/EN16931_2_Teilrechnung.ubl.xml");
/* File UBLinputFile = getResourceAsFile("ubl/EN16931_AbweichenderZahlungsempf.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Betriebskostenabrechnung.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Einfach.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Einfach_DueDate.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Einfach_negativePaymentDue.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Elektron.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_ElektronischeAdresse.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Gutschrift.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Haftpflichtversicherung_Versicherungssteuer.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Innergemeinschaftliche_Lieferungen.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Kraftfahrversicherung_Bruttopreise.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Miete.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_OEPNV.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Physiotherapeut.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Rabatte.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Rechnungskorrektur.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_RechnungsUebertragung.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Reisekostenabrechnung.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_Sachversicherung_berechneter_Steuersatz.ubl.xml");
File UBLinputFile = getResourceAsFile("ubl/EN16931_SEPA_Prenotification.ubl.xml");
boolean hasExceptions = false;
ZUGFeRDInvoiceImporter zii = null;
Invoice invoice = null;
try {
zii = new ZUGFeRDInvoiceImporter(new FileInputStream(UBLinputFile));
invoice = zii.extractInvoice();
} catch (XPathExpressionException | ParseException | FileNotFoundException e) {
hasExceptions = true;
}
assertFalse(hasExceptions);
// Reading ZUGFeRD
assertEquals(new BigDecimal("288.79"), invoice.getZFItems()[0].getPrice());
assertEquals("04011000-12345-03", invoice.getReferenceNumber());
assertEquals("seller@email.de", invoice.getSender().getContact().getEMail());
assertEquals("12345", invoice.getRecipient().getZIP());
assertEquals("DE75512108001245126199", invoice.getSender().getBankDetails().get(0).getIBAN());
}*/
} }

View File

@@ -19,24 +19,9 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import junit.framework.Test;
import junit.framework.TestCase; import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.mustangproject.Contact;
import org.mustangproject.Invoice;
import org.mustangproject.Item;
import org.mustangproject.Product;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UXTest extends TestCase { public class UXTest extends TestCase {

View File

@@ -19,16 +19,12 @@
*********************************************************************** */ *********************************************************************** */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.fop.apps.*;
import org.apache.xmlgraphics.util.MimeConstants;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters; import org.junit.runners.MethodSorters;
import org.mustangproject.CII.CIIToUBL;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*; import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@@ -63,19 +59,20 @@ public class VisualizationTest extends ResourceCase {
fail("TransformerException should not happen: "+e.getMessage()); fail("TransformerException should not happen: "+e.getMessage());
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not happen: "+e.getMessage()); fail("IOException should not happen: "+e.getMessage());
} } catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: "+e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: "+e.getMessage());
}
assertNotNull(result);
assertNotNull(result);
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals(expected, result); assertEquals(expected, result);
} }
public void testUBLCreditNoteVisualizationBasic() { public void testUBLCreditNoteVisualizationBasic() {
// the writing part // the writing part
CIIToUBL c2u = new CIIToUBL();
String sourceFilename = "factur-x.xml";
File UBLinputFile = getResourceAsFile("ubl-creditnote.xml"); File UBLinputFile = getResourceAsFile("ubl-creditnote.xml");
String expected = null; String expected = null;
@@ -85,10 +82,10 @@ public class VisualizationTest extends ResourceCase {
/* remove file endings so that tests can also pass after checking /* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes) out from git with arbitrary options (which may include CSRF changes)
*/ */
result = zvi.visualize(UBLinputFile.getAbsolutePath(),ZUGFeRDVisualizer.Language.EN).replace("\r","").replace("\n",""); result = zvi.visualize(UBLinputFile.getAbsolutePath(),ZUGFeRDVisualizer.Language.EN).replace("\r","").replace("\n","").replace(" ","").replace("\t","");
File expectedResult=getResourceAsFile("factur-x-vis-ubl-creditnote.en.html"); File expectedResult=getResourceAsFile("factur-x-vis-ubl-creditnote.en.html");
expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r","").replace("\n",""); expected = new String(Files.readAllBytes(expectedResult.toPath()), StandardCharsets.UTF_8).replace("\r","").replace("\n","").replace(" ","").replace("\t","");
// remove linebreaks as well... // remove linebreaks as well...
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
@@ -99,20 +96,21 @@ public class VisualizationTest extends ResourceCase {
fail("TransformerException should not happen: "+e.getMessage()); fail("TransformerException should not happen: "+e.getMessage());
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not happen: "+e.getMessage()); fail("IOException should not happen: "+e.getMessage());
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: "+e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: "+e.getMessage());
} }
assertNotNull(result);
assertNotNull(result);
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals(expected, result); assertEquals(expected, result);
} }
public void testUBLVisualizationBasic() { public void testUBLVisualizationBasic() {
// the writing part // the writing part
CIIToUBL c2u = new CIIToUBL(); File UBLinputFile = getResourceAsFile("ubl/01.01a-INVOICE.ubl.xml");
String sourceFilename = "factur-x.xml";
File UBLinputFile = getResourceAsFile("01.01a-INVOICE_ubl.xml");
String expected = null; String expected = null;
String result = null; String result = null;
@@ -135,11 +133,14 @@ public class VisualizationTest extends ResourceCase {
fail("TransformerException should not happen: "+e.getMessage()); fail("TransformerException should not happen: "+e.getMessage());
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not happen: "+e.getMessage()); fail("IOException should not happen: "+e.getMessage());
} } catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: "+e.getMessage());
} catch (SAXException e) {
fail("SAXException should not happen: "+e.getMessage());
}
assertNotNull(result);
assertNotNull(result);
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals(expected, result); assertEquals(expected, result);
} }

View File

@@ -32,9 +32,6 @@ import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date; import java.util.Date;
import static org.xmlunit.assertj.XmlAssert.assertThat; import static org.xmlunit.assertj.XmlAssert.assertThat;
@@ -88,10 +85,11 @@ public class XRTest extends TestCase {
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").setEmail("sender@example.com").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX"))) .setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").setEmail("sender@example.com").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("recipient@sample.org")) .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setEmail("recipient@sample.org"))
.addCashDiscount(new CashDiscount(new BigDecimal(2),7))
.addCashDiscount(new CashDiscount(new BigDecimal(3),14))
.setReferenceNumber("991-01484-64")//leitweg-id .setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods: // not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).setPaymentTermDescription("#SKONTO#TAGE=14#PROZENT=2.25#\n" + .setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
"#SKONTO#TAGE=28#PROZENT=1.00#\n").addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
.embedFileInXML(fe1); .embedFileInXML(fe1);
@@ -101,6 +99,7 @@ public class XRTest extends TestCase {
zf2p.generateXML(i); zf2p.generateXML(i);
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8); String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertTrue(theXML.contains("#SKONTO#"));
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])") assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt() .asInt()
.isEqualTo(1); //2 errors are OK because there is a known bug .isEqualTo(1); //2 errors are OK because there is a known bug

View File

@@ -37,7 +37,7 @@ import junit.framework.Test;
import junit.framework.TestSuite; import junit.framework.TestSuite;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ZF2EdgeTest extends MustangReaderTestCase implements IExportableTransaction { public class ZF2EdgeTest extends MustangReaderTestCase {
final String TARGET_PDF = "./target/testout-ZF2newEdge.pdf"; final String TARGET_PDF = "./target/testout-ZF2newEdge.pdf";
protected class EdgeProduct implements IZUGFeRDExportableProduct { protected class EdgeProduct implements IZUGFeRDExportableProduct {
@@ -293,12 +293,15 @@ public class ZF2EdgeTest extends MustangReaderTestCase implements IExportableTra
// the writing part // the writing part
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();ze.ignorePDFAErrors();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("Extended")).ignorePDFAErrors() ze.load(SOURCE_PDF);
.load(SOURCE_PDF)) { ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("Extended"));
ze.setTransaction(this); ze.setTransaction(this);
String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8); String theXML = new String(ze.getProvider().getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
@@ -322,7 +325,7 @@ public class ZF2EdgeTest extends MustangReaderTestCase implements IExportableTra
assertTrue(resultXML.contains("<ram:IssuerAssignedID>123</ram:IssuerAssignedID>")); assertTrue(resultXML.contains("<ram:IssuerAssignedID>123</ram:IssuerAssignedID>"));
// Reading ZUGFeRD // Reading ZUGFeRD
assertEquals("337.60", zi.getAmount());; assertEquals("337.60", zi.getAmount());
assertEquals(zi.getHolder(), getOwnOrganisationName()); assertEquals(zi.getHolder(), getOwnOrganisationName());
assertEquals(zi.getForeignReference(), getNumber()); assertEquals(zi.getForeignReference(), getNumber());
try { try {

View File

@@ -21,9 +21,11 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
@@ -35,9 +37,6 @@ import org.junit.runners.MethodSorters;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants; import org.mustangproject.ZUGFeRD.model.EventTimeCodeTypeConstants;
import org.xmlunit.builder.Input;
import org.xmlunit.xpath.JAXPXPathEngine;
import org.xmlunit.xpath.XPathEngine;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
@@ -54,6 +53,7 @@ public class ZF2PushTest extends TestCase {
final String TARGET_CHARGESALLOWANCESPDF = "./target/testout-ZF2PushChargesAllowances.pdf"; final String TARGET_CHARGESALLOWANCESPDF = "./target/testout-ZF2PushChargesAllowances.pdf";
final String TARGET_RELATIVECHARGESALLOWANCESPDF = "./target/testout-ZF2PushRelativeChargesAllowances.pdf"; final String TARGET_RELATIVECHARGESALLOWANCESPDF = "./target/testout-ZF2PushRelativeChargesAllowances.pdf";
final String TARGET_ATTACHMENTSPDF = "./target/testout-ZF2PushAttachments.pdf"; final String TARGET_ATTACHMENTSPDF = "./target/testout-ZF2PushAttachments.pdf";
final String TARGET_BANKPDF = "./target/testout-ZF2PushBank.pdf";
final String TARGET_PUSHEDGE = "./target/testout-ZF2PushEdge.pdf"; final String TARGET_PUSHEDGE = "./target/testout-ZF2PushEdge.pdf";
final String TARGET_INTRACOMMUNITYSUPPLYPDF = "./target/testout-ZF2PushIntraCommunitySupply.pdf"; final String TARGET_INTRACOMMUNITYSUPPLYPDF = "./target/testout-ZF2PushIntraCommunitySupply.pdf";
final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf"; final String TARGET_REVERSECHARGEPDF = "./target/testout-ZF2PushReverseCharge.pdf";
@@ -68,28 +68,29 @@ public class ZF2PushTest extends TestCase {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20201121_508blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20201121_508blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors() ze.ignorePDFAErrors();
.load(SOURCE_PDF)) { ze.load(SOURCE_PDF);
ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2);
ze.setTransaction(new Invoice().setDueDate(sdf.parse("2020-12-12")).setIssueDate(sdf.parse("2020-11-21")).setDeliveryDate(sdf.parse("2020-11-10")) ze.setTransaction(new Invoice().setDueDate(sdf.parse("2020-12-12")).setIssueDate(sdf.parse("2020-11-21")).setDeliveryDate(sdf.parse("2020-11-10"))
.setSender(new TradeParty(orgname, "Ecke 12", "12345", "Stadthausen", "DE").addBankDetails(new BankDetails("DE88200800000970375700", "COBADEFFXXX").setAccountName("Max Mustermann")).addVATID("DE136695976")) .setSender(new TradeParty(orgname, "Ecke 12", "12345", "Stadthausen", "DE").addBankDetails(new BankDetails("DE88200800000970375700", "COBADEFFXXX").setAccountName("Max Mustermann")).addVATID("DE136695976"))
.setRecipient(new TradeParty("Theodor Est", "Bahnstr. 42", "88802", "Spielkreis", "DE") .setRecipient(new TradeParty("Theodor Est", "Bahnstr. 42", "88802", "Spielkreis", "DE")
.setContact(new Contact("Ingmar N. Fo", "(555) 23 78-23", "info@localhost.local")).setID("2")) .setContact(new Contact("Ingmar N. Fo", "(555) 23 78-23", "info@localhost.local")).setID("2"))
.setNumber(number) .setNumber(number)
.setReferenceNumber("AB321") .setReferenceNumber("AB321")
.addItem(new Item(new Product("Design (hours)", "Of a sample invoice", "HUR", new BigDecimal(7)), price, new BigDecimal(1.0))) .addItem(new Item(new Product("Design (hours)", "Of a sample invoice", "HUR", new BigDecimal(7)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Ballons", "various colors, ~2000ml", "H87", new BigDecimal(19)), new BigDecimal("0.79"), new BigDecimal(400.0))) .addItem(new Item(new Product("Ballons", "various colors, ~2000ml", "H87", new BigDecimal(19)), new BigDecimal("0.79"), new BigDecimal(400.0)))
.addItem(new Item(new Product("Hot air „heiße Luft“ (litres)", "", "LTR", new BigDecimal(19)), new BigDecimal("0.025"), new BigDecimal(800.0))) .addItem(new Item(new Product("Hot air „heiße Luft“ (litres)", "", "LTR", new BigDecimal(19)), new BigDecimal("0.025"), new BigDecimal(800.0)))
); );
ze.export(TARGET_PDF); ze.export(TARGET_PDF);
} catch (IOException | ParseException e) { } catch (IOException | ParseException e) {
fail("Exception should not be raised in testPushExport"); fail("Exception should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -121,24 +122,26 @@ public class ZF2PushTest extends TestCase {
String priceStr = "1.00"; String priceStr = "1.00";
String taxID = "9990815"; String taxID = "9990815";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors() ze.ignorePDFAErrors();
.load(SOURCE_PDF)) { ze.load(SOURCE_PDF);
ze.setProducer("My Application").setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2);
byte[] b = {12, 13}; byte[] b = {12, 13};
ze.attachFile("one.pdf", b, "application/pdf", "Alternative"); ze.attachFile("one.pdf", b, "application/pdf", "Alternative");
ze.attachFile("two.pdf", b, "application/pdf", "Alternative"); ze.attachFile("two.pdf", b, "application/pdf", "Alternative");
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID)).setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0))) ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_ATTACHMENTSPDF); ze.export(TARGET_ATTACHMENTSPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -158,6 +161,47 @@ public class ZF2PushTest extends TestCase {
} }
}
public void testBankTransferExport() {
String orgname = "Test company";
String number = "123";
String priceStr = "1.00";
String taxID = "9990815";
BigDecimal price = new BigDecimal(priceStr);
try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
ze.ignorePDFAErrors().load(SOURCE_PDF);
ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2);
String IBAN = "DE999888777";
String BIC = "COBADEFXXX";
BankDetails bd = new BankDetails(IBAN, BIC);
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addBankDetails(bd).addTaxID(taxID).addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE"))).setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
);
String theXML = new String(ze.getProvider().getXML());
Invoice read = new Invoice();
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new ByteArrayInputStream(theXML.getBytes(StandardCharsets.UTF_8)));
zii.extractInto(read);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
//assertEquals(IBAN,read.getSender().getBankDetails().get(0).getIBAN());
ze.export(TARGET_BANKPDF);
} catch (IOException e) {
fail("IOException should not be raised");
} catch (XPathExpressionException e) {
fail("XPathException should not be raised");
} catch (ParseException e) {
fail("ParseException should not be raised");
}
} }
public void testItemChargesAllowancesExport() { public void testItemChargesAllowancesExport() {
@@ -166,21 +210,23 @@ public class ZF2PushTest extends TestCase {
String number = "123"; String number = "123";
String amountStr = "3.00"; String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr); BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors() ze.ignorePDFAErrors().load(SOURCE_PDF);
.load(SOURCE_PDF)) { ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended");
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) // ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))); // .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number) ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1")))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1"))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1")))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1"))))
); );
@@ -188,7 +234,7 @@ public class ZF2PushTest extends TestCase {
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_ITEMCHARGESALLOWANCESPDF); ze.export(TARGET_ITEMCHARGESALLOWANCESPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -217,32 +263,34 @@ public class ZF2PushTest extends TestCase {
String number = "123"; String number = "123";
String amountStr = "3.00"; String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr); BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors() ze.ignorePDFAErrors().load(SOURCE_PDF);
.load(SOURCE_PDF)) { ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended");
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) // ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))); // .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711") .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815").addTaxID ("4711"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))) .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")))
.setDeliveryAddress(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816")) .setDeliveryAddress(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816"))
.setNumber(number) .setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(2.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(2.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setIntraCommunitySupply(), amount, new BigDecimal(1.0)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF); ze.export(TARGET_INTRACOMMUNITYSUPPLYPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -270,28 +318,31 @@ public class ZF2PushTest extends TestCase {
String number = "123"; String number = "123";
String amountStr = "3.00"; String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr); BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended").ignorePDFAErrors() ze.ignorePDFAErrors().load(SOURCE_PDF);
.load(SOURCE_PDF)) { ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile("extended");
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) // ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))); // .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number) ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815").addTaxID("4711")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(2.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(2.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).setReverseCharge(), amount, new BigDecimal(1.0)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_REVERSECHARGEPDF); ze.export(TARGET_REVERSECHARGEPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -312,32 +363,36 @@ public class ZF2PushTest extends TestCase {
} }
public void testChargesAllowancesExport() { public void testChargesAllowancesExport() {
String orgname = "Test company"; String orgname = "Test company";
String number = "123"; String number = "123";
String amountStr = "3.00"; String amountStr = "3.00";
BigDecimal amount = new BigDecimal(amountStr); BigDecimal amount = new BigDecimal(amountStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("en16931")).ignorePDFAErrors() ze.ignorePDFAErrors()
.load(SOURCE_PDF)) { .load(SOURCE_PDF);
ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("en16931"));
ze.setTransaction(new Invoice().setCurrency("CHF").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) ze.setTransaction(new Invoice().setCurrency("CHF").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)))
.addCharge(new Charge(new BigDecimal(0.5)).setTaxPercent(new BigDecimal(19))) .addCharge(new Charge(new BigDecimal(0.5)).setTaxPercent(new BigDecimal(19)))
.addAllowance(new Allowance(new BigDecimal(0.2)).setTaxPercent(new BigDecimal(19))) .addAllowance(new Allowance(new BigDecimal(0.2)).setTaxPercent(new BigDecimal(19)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_CHARGESALLOWANCESPDF); ze.export(TARGET_CHARGESALLOWANCESPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -371,29 +426,34 @@ public class ZF2PushTest extends TestCase {
String priceStr = "1.00"; String priceStr = "1.00";
String taxID = "9990815"; String taxID = "9990815";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors() ze.ignorePDFAErrors()
.load(SOURCE_PDF)) { .load(SOURCE_PDF);
ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("extended"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try { try {
SchemedID gtin=new SchemedID("0160","2001015001325"); SchemedID gtin = new SchemedID("0160", "2001015001325");
SchemedID gln=new SchemedID("0088","4304171000002"); SchemedID gln = new SchemedID("0088", "4304171000002");
ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()) ze.setTransaction(new Invoice().setCurrency("CHF").addNote("document level 1/2").addNote("document level 2/2").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSellerOrderReferencedDocumentID("9384").setBuyerOrderReferencedDocumentID("28934") .setSellerOrderReferencedDocumentID("9384").setBuyerOrderReferencedDocumentID("28934")
.setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo)) .setDetailedDeliveryPeriod(new SimpleDateFormat("yyyyMMdd").parse(occurrenceFrom), new SimpleDateFormat("yyyyMMdd").parse(occurrenceTo))
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).setEmail("sender@test.org").setID("0009845")) .setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID(taxID).setEmail("sender@test.org").setID("0009845").addVATID("DE0815"))
.setDeliveryAddress(new TradeParty("just the other side of the street", "teststr.12a", "55232", "Entenhausen", "DE").addVATID("DE47110")) .setDeliveryAddress(new TradeParty("just the other side of the street", "teststr.12a", "55232", "Entenhausen", "DE").addVATID("DE47110"))
.setContractReferencedDocument(contractID) .setContractReferencedDocument(contractID)
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addGlobalID(gln).setEmail("recipient@test.org").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")).setAdditionalAddress("Hinterhaus 3")) .setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addGlobalID(gln).setEmail("recipient@test.org").addVATID("DE4711").setContact(new Contact("Franz Müller", "01779999999", "franz@mueller.de", "teststr. 12", "55232", "Entenhausen", "DE").setFax("++49555123456")).setAdditionalAddress("Hinterhaus 3"))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15"))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(16)).addGlobalID(gtin).setSellerAssignedID("4711"), price, new BigDecimal(1.0)).addReferencedLineID("xxx").addNote("item level 1/1").addAllowance(new Allowance(new BigDecimal(0.02)).setReason("item discount").setTaxPercent(new BigDecimal(16))).setDetailedDeliveryPeriod(sdf.parse("2020-01-13"), sdf.parse("2020-01-15")))
.addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16))) .addCharge(new Charge(new BigDecimal(0.5)).setReason("quick delivery charge").setTaxPercent(new BigDecimal(16)))
.addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16))) .addAllowance(new Allowance(new BigDecimal(0.2)).setReason("discount").setTaxPercent(new BigDecimal(16)))
.setDeliveryDate(sdf.parse("2020-11-02")).setOwnVATID("DE0815").setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE) .addCashDiscount(new CashDiscount(new BigDecimal(2), 14))
.setDeliveryDate(sdf.parse("2020-11-02")).setNumber(number).setVATDueDateTypeCode(EventTimeCodeTypeConstants.PAYMENT_DATE)
); );
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
@@ -403,7 +463,7 @@ public class ZF2PushTest extends TestCase {
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_PUSHEDGE); ze.export(TARGET_PUSHEDGE);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -412,9 +472,9 @@ public class ZF2PushTest extends TestCase {
assertFalse(zi.getUTF8().contains("<ram:IBANID>")); // maybe add a direct debit mandate to the class in the future then this would fail assertFalse(zi.getUTF8().contains("<ram:IBANID>")); // maybe add a direct debit mandate to the class in the future then this would fail
assertTrue(zi.getUTF8().contains("Hinterhaus")); assertTrue(zi.getUTF8().contains("Hinterhaus"));
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='BuyerTradeParty']/*[local-name()='GlobalID'][@schemeID=0088]").asString() assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='BuyerTradeParty']/*[local-name()='GlobalID'][@schemeID=0088]").asString()
.isEqualTo("4304171000002"); .isEqualTo("4304171000002");
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='SpecifiedTradeProduct']/*[local-name()='GlobalID'][@schemeID=0160]").asString() assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='SpecifiedTradeProduct']/*[local-name()='GlobalID'][@schemeID=0160]").asString()
.isEqualTo("2001015001325"); .isEqualTo("2001015001325");
assertTrue(zi.getUTF8().contains("2001015001325")); assertTrue(zi.getUTF8().contains("2001015001325"));
assertTrue(zi.getUTF8().contains("4304171000002")); assertTrue(zi.getUTF8().contains("4304171000002"));
assertTrue(zi.getUTF8().contains("0088")); assertTrue(zi.getUTF8().contains("0088"));
@@ -431,9 +491,10 @@ public class ZF2PushTest extends TestCase {
assertTrue(zi.getUTF8().contains("DE4711")); // the VAT ID should be there... assertTrue(zi.getUTF8().contains("DE4711")); // the VAT ID should be there...
assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty assertFalse(zi.getUTF8().contains("DE47110")); // but not the VAT ID of the shiptotradeparty
assertTrue(zi.getUTF8().contains("document level 2/2")); assertTrue(zi.getUTF8().contains("document level 2/2"));
assertFalse(zi.getUTF8().contains("++49555123456")); // in profile EN16931 contact fax number is not allowed assertTrue(zi.getUTF8().contains("++49555123456"));
assertTrue(zi.getUTF8().contains("Cash Discount")); // default description for cash discounts
assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString() assertThat(zi.getUTF8()).valueByXPath("//*[local-name()='ApplicableTradeTax']/*[local-name()='DueDateTypeCode']").asString()
.isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE); .isEqualTo(EventTimeCodeTypeConstants.PAYMENT_DATE);
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(TARGET_PUSHEDGE);
try { try {
@@ -444,15 +505,14 @@ public class ZF2PushTest extends TestCase {
} catch (XPathExpressionException e) { } catch (XPathExpressionException e) {
fail("XPathExpressionException should not be raised in testEdgeExport"); fail("XPathExpressionException should not be raised");
} catch (ParseException e) { } catch (ParseException e) {
fail("ParseException should not be raised in testEdgeExport"); fail("ParseException should not be raised");
/* a parseException would also be fired if the calculated grand total does not /* a parseException would also be fired if the calculated grand total does not
match the read grand total */ match the read grand total */
} }
} }
public void testAllowancesExport() { public void testAllowancesExport() {
@@ -460,23 +520,27 @@ public class ZF2PushTest extends TestCase {
String orgname = "Test company"; String orgname = "Test company";
String number = "123"; String number = "123";
BigDecimal qty = new BigDecimal("20"); BigDecimal qty = new BigDecimal("20");
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("en16931")).ignorePDFAErrors() ze.ignorePDFAErrors().load(SOURCE_PDF);
.load(SOURCE_PDF)) {
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) ze.setProducer("My Application")
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19)))) .setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("en16931"));
.addAllowance(new Allowance(new BigDecimal(600)).setTaxPercent(new BigDecimal(19)))
ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance(new BigDecimal(600)).setTaxPercent(new BigDecimal(19)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_ALLOWANCESPDF); ze.export(TARGET_ALLOWANCESPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -503,25 +567,29 @@ public class ZF2PushTest extends TestCase {
String number = "123"; String number = "123";
String priceStr = "3.00"; String priceStr = "3.00";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("extended")).ignorePDFAErrors() ze.ignorePDFAErrors()
.load(SOURCE_PDF)) { .load(SOURCE_PDF);
ze.setTransaction(new Invoice().setCurrency("CHF").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number) ze.setProducer("My Application")
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0))) .setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).setProfile(Profiles.getByName("extended"));
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)))) ze.setTransaction(new Invoice().setCurrency("CHF").setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19))) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)))
); );
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_RELATIVECHARGESALLOWANCESPDF); ze.export(TARGET_RELATIVECHARGESALLOWANCESPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -555,23 +623,26 @@ public class ZF2PushTest extends TestCase {
String priceStr = "1.00"; String priceStr = "1.00";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
BigDecimal qty = new BigDecimal(-1.0); BigDecimal qty = new BigDecimal(-1.0);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors() ze.ignorePDFAErrors()
.load(SOURCE_PDF)) { .load(SOURCE_PDF);
ze.setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2);
// no due date, since we are not expecting money // no due date, since we are not expecting money
Invoice i = new Invoice().setIssueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number) Invoice i = new Invoice().setIssueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815")).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)).setCorrection("0815"); .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)).setCorrection("0815");
ze.setTransaction(i); ze.setTransaction(i);
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_CORRECTIONPDF); ze.export(TARGET_CORRECTIONPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)
@@ -605,22 +676,26 @@ public class ZF2PushTest extends TestCase {
String priceStr = "1.00"; String priceStr = "1.00";
BigDecimal price = new BigDecimal(priceStr); BigDecimal price = new BigDecimal(priceStr);
BigDecimal qty = new BigDecimal(1.0); BigDecimal qty = new BigDecimal(1.0);
try (InputStream SOURCE_PDF = this.getClass() try {
InputStream SOURCE_PDF = this.getClass()
.getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf"); .getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application") ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1();
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2).ignorePDFAErrors() ze.ignorePDFAErrors()
.load(SOURCE_PDF)) { .load(SOURCE_PDF);
ze.
setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(2);
Invoice i = new Invoice().setIssueDate(new Date()).setDueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815").addBankDetails(new BankDetails("DE88200800000970375700", "COBADEFFXXX"))).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number) Invoice i = new Invoice().setIssueDate(new Date()).setDueDate(new Date()).setDetailedDeliveryPeriod(new Date(), new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815").addBankDetails(new BankDetails("DE88200800000970375700", "COBADEFFXXX"))).setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0815")).setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)) .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)).setCreditNote(); .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, qty)).setCreditNote();
ze.setTransaction(i); ze.setTransaction(i);
String theXML = new String(ze.getProvider().getXML()); String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice")); assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
ze.export(TARGET_CREDITNOTEPDF); ze.export(TARGET_CREDITNOTEPDF);
} catch (IOException e) { } catch (IOException e) {
fail("IOException should not be raised in testEdgeExport"); fail("IOException should not be raised");
} }
// now check the contents (like MustangReaderTest) // now check the contents (like MustangReaderTest)

View File

@@ -229,15 +229,14 @@ public class ZF2Test extends MustangReaderTestCase {
assertEquals(zi.getSellerTradePartyAddress().getCityName(), "Stadthausen"); assertEquals(zi.getSellerTradePartyAddress().getCityName(), "Stadthausen");
final List<org.mustangproject.Item> li = zi.getLineItemList(); final List<org.mustangproject.Item> li = zi.getLineItemList();
assertEquals(zi.getLineItemList().get(0).getId().toString(), "1"); assertEquals(li.get(0).getId().toString(), "1");
assertEquals(zi.getLineItemList().get(0).getProduct().getBuyerAssignedID(), ""); assertEquals(li.get(0).getProduct().getBuyerAssignedID(), "");
assertEquals(zi.getLineItemList().get(0).getProduct().getSellerAssignedID(), ""); assertEquals(li.get(0).getProduct().getSellerAssignedID(), "");
assertEquals(zi.getLineItemList().get(0).getLineTotalAmount().toString(), "160.00"); assertEquals(li.get(0).getLineTotalAmount().toString(), "160.00");
assertEquals(zi.getLineItemList().get(0).getQuantity().toString(), "1.0000"); assertEquals(li.get(0).getQuantity().toString(), "1.0000");
assertEquals(zi.getLineItemList().get(0).getGrossPrice().toString(), "160.0000"); assertEquals(li.get(0).getProduct().getVATPercent().toString(), "7.00");
assertEquals(zi.getLineItemList().get(0).getProduct().getVATPercent().toString(), "7.00"); assertEquals(li.get(0).getProduct().getName(), "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung");
assertEquals(zi.getLineItemList().get(0).getProduct().getName(), "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung"); assertEquals(li.get(0).getProduct().getDescription(), "");
assertEquals(zi.getLineItemList().get(0).getProduct().getDescription(), "");
try { try {
assertEquals(zi.getVersion(), 2); assertEquals(zi.getVersion(), 2);

View File

@@ -21,16 +21,8 @@
*/ */
package org.mustangproject.ZUGFeRD; package org.mustangproject.ZUGFeRD;
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile;
import org.mustangproject.Invoice; import org.mustangproject.Invoice;
import junit.framework.TestCase;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathExpressionException;
import java.io.*; import java.io.*;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -39,7 +31,6 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Scanner;
/*** /***
@@ -175,6 +166,7 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
} }
public void testZF1Import() { public void testZF1Import() {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-MustangGnuaccountingBeispielRE-20171118_506zf1.pdf"); ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-MustangGnuaccountingBeispielRE-20171118_506zf1.pdf");

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>471102</cbc:ID>
<cbc:IssueDate>2018-03-05</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>Rechnung gemäß Bestellung vom 01.03.2018.</cbc:Note>
<cbc:Note>#REG#Lieferant GmbH
Lieferantenstraße 20
80333 München
Deutschland
Geschäftsführer: Hans Muster
Handelsregisternummer: H A 123
</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>04011000-12345-34</cbc:BuyerReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID schemeID="0088">4000001123452</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Lieferant GmbH</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Lieferantenstraße 20</cbc:StreetName>
<cbc:CityName>München</cbc:CityName>
<cbc:PostalZone>80333</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>201/113/40209</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>FC</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE123456789</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Lieferant GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
<cac:Contact>
<cbc:Name>Max Mustermann</cbc:Name>
<cbc:Telephone>+49891234567</cbc:Telephone>
<cbc:ElectronicMail>Max@Mustermann.de</cbc:ElectronicMail>
</cac:Contact>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>GE2020211</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Kunden AG Mitte</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Kundenstraße 15</cbc:StreetName>
<cbc:CityName>Frankfurt</cbc:CityName>
<cbc:PostalZone>69876</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Kunden AG Mitte</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2018-03-05</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Zahlung per SEPA Überweisung.">58</cbc:PaymentMeansCode>
<cac:PayeeFinancialAccount>
<cbc:ID>DE02120300000000202051</cbc:ID>
<cbc:Name>Kunden AG</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>BYLADEM1001</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Zahlbar innerhalb 30 Tagen netto bis 04.04.2018, 3% Skonto innerhalb 10 Tagen bis 15.03.2018</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">56.87</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">275</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">19.25</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">198</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">37.62</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">473</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">473</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">529.87</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">529.87</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">20</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">198</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Trennblätter A4</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>TB100A4</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4012345001235</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">9.9</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">50</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">275</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Joghurt Banane</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>ARNR2</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4000050986428</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">5.5</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
<html xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xr="urn:ce.eu:en16931:2017:xoev-de:kosit:standard:xrechnung-1" xmlns:xrv="http://www.example.org/XRechnung-Viewer" lang="de"> <!DOCTYPE HTML>
<html xmlns:xr="urn:ce.eu:en16931:2017:xoev-de:kosit:standard:xrechnung-1" xmlns:xrv="http://www.example.org/XRechnung-Viewer" xmlns:xs="http://www.w3.org/2001/XMLSchema" lang="de">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="UTF-8">
<title>XRechnung</title> <title>XRechnung</title>
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0"> <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0">
<style> <style>
@@ -1555,7 +1555,7 @@
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende ">Description:</div> <div class="boxdaten legende ">Description:</div>
<div id="BT-154" title="BT-154" class="boxdaten wert">Processor: Intel Core 2 Duo SU9400 LV (1.4GHz). RAM: <div id="BT-154" title="BT-154" class="boxdaten wert">Processor: Intel Core 2 Duo SU9400 LV (1.4GHz). RAM:
3MB. Screen 1440x900</div> 3MB. Screen 1440x900</div>
</div> </div>
<div class="boxzeile"> <div class="boxzeile">
<div class="boxdaten legende ">Part number:</div> <div class="boxdaten legende ">Part number:</div>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,168 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><Invoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"> <cbc:ID>RE-20201121/508</cbc:ID>
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID> <cbc:IssueDate>2020-11-21</cbc:IssueDate>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID> <cbc:DueDate>2020-12-12</cbc:DueDate>
<cbc:ID>RE-20201121/508</cbc:ID> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:IssueDate>2020-11-21</cbc:IssueDate> <cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:DueDate>2020-12-12</cbc:DueDate> <cbc:BuyerReference>AB321</cbc:BuyerReference>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cac:AccountingSupplierParty>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <cac:Party>
<cbc:BuyerReference>AB321</cbc:BuyerReference> <cac:PostalAddress>
<cac:AccountingSupplierParty> <cbc:StreetName>Ecke 12</cbc:StreetName>
<cac:Party> <cbc:CityName>Stadthausen</cbc:CityName>
<cac:PartyName> <cbc:PostalZone>12345</cbc:PostalZone>
<cbc:Name>Bei Spiel GmbH</cbc:Name> <cac:Country>
</cac:PartyName> <cbc:IdentificationCode>DE</cbc:IdentificationCode>
<cac:PostalAddress> </cac:Country>
<cbc:StreetName>Ecke 12</cbc:StreetName> </cac:PostalAddress>
<cbc:CityName>Stadthausen</cbc:CityName> <cac:PartyTaxScheme>
<cbc:PostalZone>12345</cbc:PostalZone> <cbc:CompanyID>DE136695976</cbc:CompanyID>
<cac:Country> <cac:TaxScheme>
<cbc:IdentificationCode>DE</cbc:IdentificationCode> <cbc:ID>VAT</cbc:ID>
</cac:Country> </cac:TaxScheme>
</cac:PostalAddress> </cac:PartyTaxScheme>
<cac:PartyTaxScheme> <cac:PartyLegalEntity>
<cbc:CompanyID>DE136695976</cbc:CompanyID> <cbc:RegistrationName>Bei Spiel GmbH</cbc:RegistrationName>
<cac:TaxScheme> </cac:PartyLegalEntity>
<cbc:ID>VAT</cbc:ID> </cac:Party>
</cac:TaxScheme> </cac:AccountingSupplierParty>
</cac:PartyTaxScheme> <cac:AccountingCustomerParty>
<cac:PartyLegalEntity> <cac:Party>
<cbc:RegistrationName>Bei Spiel GmbH</cbc:RegistrationName> <cac:PartyIdentification>
</cac:PartyLegalEntity> <cbc:ID>2</cbc:ID>
</cac:Party> </cac:PartyIdentification>
</cac:AccountingSupplierParty> <cac:PostalAddress>
<cac:AccountingCustomerParty> <cbc:StreetName>Bahnstr. 42</cbc:StreetName>
<cac:Party> <cbc:CityName>Spielkreis</cbc:CityName>
<cac:PartyIdentification> <cbc:PostalZone>88802</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Theodor Est</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2020-11-10</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Bank transfer">42</cbc:PaymentMeansCode>
<cbc:PaymentID>RE-20201121/508</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>DE88200800000970375700</cbc:ID>
<cbc:Name>Max Mustermann</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>COBADEFFXXX</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Zahlbar ohne Abzug bis 12.12.2020</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">75.04</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">160</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">11.2</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">336</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">63.84</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">496</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">496</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">571.04</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">571.04</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">160</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Of a sample invoice</cbc:Description>
<cbc:Name>Design (hours)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">160</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="HUR">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">160</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID> <cbc:ID>2</cbc:ID>
</cac:PartyIdentification> <cbc:InvoicedQuantity unitCode="H87">400</cbc:InvoicedQuantity>
<cac:PartyName> <cbc:LineExtensionAmount currencyID="EUR">316</cbc:LineExtensionAmount>
<cbc:Name>Theodor Est</cbc:Name> <cac:Item>
</cac:PartyName> <cbc:Description>various colors, ~2000ml</cbc:Description>
<cac:PostalAddress> <cbc:Name>Ballons</cbc:Name>
<cbc:StreetName>Bahnstr. 42</cbc:StreetName> <cac:ClassifiedTaxCategory>
<cbc:CityName>Spielkreis</cbc:CityName> <cbc:ID>S</cbc:ID>
<cbc:PostalZone>88802</cbc:PostalZone> <cbc:Percent>19</cbc:Percent>
<cac:Country> <cac:TaxScheme>
<cbc:IdentificationCode>DE</cbc:IdentificationCode> <cbc:ID>VAT</cbc:ID>
</cac:Country> </cac:TaxScheme>
</cac:PostalAddress> </cac:ClassifiedTaxCategory>
<cac:PartyLegalEntity> </cac:Item>
<cbc:RegistrationName>Theodor Est</cbc:RegistrationName> <cac:Price>
</cac:PartyLegalEntity> <cbc:PriceAmount currencyID="EUR">0.79</cbc:PriceAmount>
</cac:Party> <cbc:BaseQuantity unitCode="H87">1</cbc:BaseQuantity>
</cac:AccountingCustomerParty> <cac:AllowanceCharge>
<cac:Delivery> <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:ActualDeliveryDate>2020-11-10</cbc:ActualDeliveryDate> <cbc:Amount currencyID="EUR">0</cbc:Amount>
</cac:Delivery> <cbc:BaseAmount currencyID="EUR">0.79</cbc:BaseAmount>
<cac:PaymentMeans> </cac:AllowanceCharge>
<cbc:PaymentMeansCode name="Bank transfer">42</cbc:PaymentMeansCode> </cac:Price>
<cbc:PaymentID>RE-20201121/508</cbc:PaymentID> </cac:InvoiceLine>
<cac:PayeeFinancialAccount> <cac:InvoiceLine>
<cbc:ID>DE88200800000970375700</cbc:ID> <cbc:ID>3</cbc:ID>
<cbc:Name>Max Mustermann</cbc:Name> <cbc:InvoicedQuantity unitCode="LTR">800</cbc:InvoicedQuantity>
<cac:FinancialInstitutionBranch> <cbc:LineExtensionAmount currencyID="EUR">20</cbc:LineExtensionAmount>
<cbc:ID>COBADEFFXXX</cbc:ID> <cac:Item>
</cac:FinancialInstitutionBranch> <cbc:Name>Hot air „heiße Luft“ (litres)</cbc:Name>
</cac:PayeeFinancialAccount> <cac:ClassifiedTaxCategory>
</cac:PaymentMeans> <cbc:ID>S</cbc:ID>
<cac:PaymentTerms> <cbc:Percent>19</cbc:Percent>
<cbc:Note>Zahlbar ohne Abzug bis 12.12.2020</cbc:Note> <cac:TaxScheme>
</cac:PaymentTerms> <cbc:ID>VAT</cbc:ID>
<cac:TaxTotal> </cac:TaxScheme>
<cbc:TaxAmount currencyID="EUR">75.04</cbc:TaxAmount> </cac:ClassifiedTaxCategory>
<cac:TaxSubtotal> </cac:Item>
<cbc:TaxableAmount currencyID="EUR">160</cbc:TaxableAmount> <cac:Price>
<cbc:TaxAmount currencyID="EUR">11.2</cbc:TaxAmount> <cbc:PriceAmount currencyID="EUR">0.025</cbc:PriceAmount>
<cac:TaxCategory> <cbc:BaseQuantity unitCode="LTR">1</cbc:BaseQuantity>
<cbc:ID>S</cbc:ID> <cac:AllowanceCharge>
<cbc:Percent>7</cbc:Percent> <cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cac:TaxScheme> <cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:ID>VAT</cbc:ID> <cbc:BaseAmount currencyID="EUR">0.025</cbc:BaseAmount>
</cac:TaxScheme> </cac:AllowanceCharge>
</cac:TaxCategory> </cac:Price>
</cac:TaxSubtotal> </cac:InvoiceLine>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">336</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">63.84</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">496</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">496</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">571.04</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">571.04</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">160</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Of a sample invoice</cbc:Description>
<cbc:Name>Design (hours)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">160</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="HUR">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">400</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">316</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>various colors, ~2000ml</cbc:Description>
<cbc:Name>Ballons</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.79</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="H87">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:InvoicedQuantity unitCode="LTR">800</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">20</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Hot air „heiße Luft“ (litres)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.025</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="LTR">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
</Invoice> </Invoice>

View File

@@ -2,7 +2,8 @@
<ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" <ubl:Invoice xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"> xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3</cbc:CustomizationID> <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>123456XX</cbc:ID> <cbc:ID>123456XX</cbc:ID>
<cbc:IssueDate>2016-04-04</cbc:IssueDate> <cbc:IssueDate>2016-04-04</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
@@ -11,6 +12,7 @@
<cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference> <cbc:BuyerReference>04011000-12345-03</cbc:BuyerReference>
<cac:AccountingSupplierParty> <cac:AccountingSupplierParty>
<cac:Party> <cac:Party>
<cbc:EndpointID schemeID="EM">seller@email.de</cbc:EndpointID>
<cac:PartyName> <cac:PartyName>
<cbc:Name>[Seller trading name]</cbc:Name> <cbc:Name>[Seller trading name]</cbc:Name>
</cac:PartyName> </cac:PartyName>
@@ -42,6 +44,7 @@
</cac:AccountingSupplierParty> </cac:AccountingSupplierParty>
<cac:AccountingCustomerParty> <cac:AccountingCustomerParty>
<cac:Party> <cac:Party>
<cbc:EndpointID schemeID="EM">buyer@info.de</cbc:EndpointID>
<cac:PartyIdentification> <cac:PartyIdentification>
<cbc:ID>[Buyer identifier]</cbc:ID> <cbc:ID>[Buyer identifier]</cbc:ID>
</cac:PartyIdentification> </cac:PartyIdentification>

View File

@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>471102</cbc:ID>
<cbc:IssueDate>2018-06-05</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>Rechnung gemäß Bestellung Nr. 2018-471331 vom 01.03.2018.</cbc:Note>
<cbc:Note>#AAK#Es bestehen Rabatt- und Bonusvereinbarungen.</cbc:Note>
<cbc:Note>#REG#Lieferant GmbH
Lieferantenstraße 20
80333 München
Deutschland
Geschäftsführer: Hans Muster
Handelsregisternummer: H A 123
</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:OrderReference>
<cbc:ID>2018-471331</cbc:ID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID schemeID="0088">4000001123452</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Lieferant GmbH</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Lieferantenstraße 20</cbc:StreetName>
<cbc:CityName>München</cbc:CityName>
<cbc:PostalZone>80333</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>201/113/40209</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>FC</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE123456789</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Lieferant GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>GE2020211</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Kunden AG Mitte</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Kundenstraße 15</cbc:StreetName>
<cbc:CityName>Frankfurt</cbc:CityName>
<cbc:PostalZone>69876</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Kunden AG Mitte</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2018-06-03</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentTerms>
<cbc:Note>Zahlbar innerhalb 30 Tagen netto bis 04.07.2018, 3% Skonto innerhalb 10 Tagen bis 15.06.2018</cbc:Note>
</cac:PaymentTerms>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReason>Sondernachlass</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="EUR">1</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">10</cbc:BaseAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:AllowanceCharge>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:AllowanceChargeReason>Sondernachlass</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="EUR">8.07</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">115.3</cbc:BaseAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:AllowanceCharge>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
<cbc:AllowanceChargeReason>Versandkosten</cbc:AllowanceChargeReason>
<cbc:Amount currencyID="EUR">5.8</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">115.3</cbc:BaseAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:AllowanceCharge>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">20.16</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">113.03</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">7.91</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">64.46</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">12.25</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">180.76</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">177.49</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">197.65</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">9.07</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">5.8</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">50</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">147.65</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note>Wir erlauben uns Ihnen folgende Positionen aus der Lieferung Nr. 2018-51112 in Rechnung zu stellen:</cbc:Note>
<cbc:InvoicedQuantity unitCode="MTK">3</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">10</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>300cm x 100 cm</cbc:Description>
<cbc:Name>Kunstrasen grün 3m breit</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>KR3M</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4012345001235</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">3.3333</cbc:PriceAmount>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0.6667</cbc:Amount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID>
<cbc:Note>Bestellt wurden 5 kg Schweinesteak. Mit dieser Rechnung werden nur die bereits gelieferten Steaks berechnet. Die noch offenen 4 kg Schweinesteak werden separat geliefert und berechnet.</cbc:Note>
<cbc:InvoicedQuantity unitCode="KGM">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">5.5</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Schweinesteak aus Deutschland</cbc:Description>
<cbc:Name>Schweinesteak</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>SFK5</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4000050986428</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">5.5</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">20</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">109.8</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Mineralwasser Medium
12 x 1,0l PET
</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>GTRWA5</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4000001234561</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">5.49</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>4</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">20</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">55.46</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Pfand</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>PFA5</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4000001234578</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">2.77</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>471113</cbc:ID>
<cbc:IssueDate>2018-06-13</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:Note>
Rechnung gemäß Bestellung Nr. 2018-471331 vom 01.03.2018.
</cbc:Note>
<cbc:Note>#AAK#
Es bestehen Rabatt- und Bonusvereinbarungen.
</cbc:Note>
<cbc:Note>#REG#Lieferant GmbH
Lieferantenstraße 20
80333 München
Deutschland
Geschäftsführer: Hans Muster
Handelsregisternummer: H A 123
</cbc:Note>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:OrderReference>
<cbc:ID>2018-471331</cbc:ID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID schemeID="0088">4000001123452</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Lieferant GmbH</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Lieferantenstraße 20</cbc:StreetName>
<cbc:CityName>München</cbc:CityName>
<cbc:PostalZone>80333</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>201/113/40209</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>FC</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE123456789</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Lieferant GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>GE2020211</cbc:ID>
</cac:PartyIdentification>
<cac:PartyName>
<cbc:Name>Kunden AG Mitte</cbc:Name>
</cac:PartyName>
<cac:PostalAddress>
<cbc:StreetName>Kundenstraße 15</cbc:StreetName>
<cbc:CityName>Frankfurt</cbc:CityName>
<cbc:PostalZone>69876</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Kunden AG Mitte</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2018-06-03</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentTerms>
<cbc:Note>Zahlbar innerhalb 30 Tagen netto bis 04.07.2018, 3% Skonto innerhalb 10 Tagen bis 15.06.2018</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">1.54</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">22</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">1.54</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">22</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">22</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">23.54</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">23.54</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note>Bestellt wurden 5 kg Schweinesteak. Mit dieser Rechnung werden die nachgelieferten 4 kg Steaks berechnet.</cbc:Note>
<cbc:InvoicedQuantity unitCode="KGM">4</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">22</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Schweinesteak aus Deutschland</cbc:Description>
<cbc:Name>Schweinesteak</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID>SFK5</cbc:ID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeID="0160">4000050986428</cbc:ID>
</cac:StandardItemIdentification>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">5.5</cbc:PriceAmount>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

Some files were not shown because too many files have changed in this diff Show More