Elektronische Rechnungen schreiben und lesen
Lizenz
Mustangproject liegt unter der liberalen Apache Public License 2 vor und darf kostenlos in kommerziellen und nichtkommerziellen Produkten genutzt werden.
Quelltext
Das git repository des Mustangproject-Quelltext liegt auf Github.
Architektur
Sie können Mustang von der Kommandozeile aufrufen oder in Ihre Java-Software einbinden, entweder indem Sie (mit Maven oder Gradle) auf Mustang verweisen oder indem Sie die entsprechenden Dateien herunterladen.
Sie können PDF und XML zusammenfügen, trennen und/oder validieren. Auf der Kommandozeile müssen Sie um Factur-X zu schreiben immer eine PDF/A-1 Datei und eine XML-Datei angeben, mit der Bibliothek müssen Sie mindestens eine PDF/A-1-Datei angeben und können eigenes XML einbinden oder die Rechnungsklassen benutzen die die Interfaces benutzen, oder direkt per
interfaces kommunizieren. Factur-X lesen geht derzeit nur im “Bibliotheksmodus” wo Sie in ein
Rechnungsobjekt parsen können,
oder einfach nur per ZUGFeRDImporter Basisdaten auslesen.
Mit einem anderen Artefakt können Sie auch den Validierer in Ihre Software einbetten.
In der Kommandozeile gibt es auch kleinere Statistiken für Verzeichnisse mit Factur-X-Dateien und Sie können
versuchsweise XML von ZUGFeRD Version 1 auf 2 übersetzen, PDF/A-1 nach PDF/A-3 konvertieren, oder XML visualisieren, was in diesem Diagram fehlt:
Neues Projekt
Die Tastenkürzel dieses Beispiels geht von Eclipse als IDE aus.
Starten Sie Eclipse und erstellen ein neues Maven-Projekt, beispielsweise „MustangSample“ als “simple project”, Gruppen-ID “org.mustangproject”, Artifakt “mustangtest”.
Sie werden
- eine ZUGFeRD PDF/A-3 Beispieldatei für das Lesebeispiel benötigen. Speichern Sie es im selben Verzeichnis wie Ihre pom.xml.
- eine PDF/A-1-Datei ohne ZUGFeRD-Daten für das Schreibbeispiel die Sie sich
- direkt herunterladen
- oder selbst mit Libreoffice und der entsprechenden Quelldatei: Benutzen Sie Datei|Export als PDF und setzen Sie den Haken für PDF/A-1a in den Export-Option.
- Speichern Sie das Resultat als MustangGnuaccountingBeispielRE-20190610_507blanko.pdf, bitte ebenfalls im selben Verzeichnis wie ihre POM.xml.
Mit Maven
Öffnen Sie Ihre pom.xml und fügen Sie die Abhängigkeiten
<dependencies>
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<classifier>shaded</classifier>
<version>2.15.2</version>
</dependency>
</dependencies>
hinzu.
Mit Gradle
Erstellen Sie ein neues Java-Gradle-Projekt. Mit der groovy DSL können Sie die Abhängigkeiten mavenCentral() wie folgt einfügen:
implementation 'org.mustangproject:validator:2.15.2:shaded'
Die komplette build.gradle kann dann wie folgt aussehen
plugins {
id 'groovy'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.mustangproject:library:2.15.2'
implementation 'org.xmlunit:xmlunit-assertj:2.8.2'
compile 'org.codehaus.groovy:groovy-all:2.3.11'
testCompile group: 'junit', name: 'junit', version: '4.12'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
ZUGFeRD-Daten lesen
- Erstellen Sie eine neue Klasse im src/main/java-Verzeichnis und nennen Sie es beispielsweise Reader. Wählen Sie dabei die “Public static void main()”-Option.
- Erstellen Sie eine “run configuration”: klicken Sie in Run|Run configurations auf Java Application,und dort auf das Icon für eine neue Konfiguration.
- Geben Sie innerhalb der Main-Methode
ZUGFeRDImporter zu=new ZUGFeRDImporter("MustangGnuaccountingBeispielRE-20190610_507.pdf");
ein und - fügen Sie die imports durch drücken von
STRG
+SHIFT
+O
(Windows) oderCMD
+SHIFT
+O
(Mac) hinzu. - prüfen Sie gegebenenfalls mit
if (zu.canParse())
ob es sich um eine geeignete ZUGFeRD-Datei handelt - und
zu.parse()
um in der Folge die getter-Methoden wie getAmount() nutzen zu können - verwenden Sie schließlich beispielsweise
zu.getAmount()
. Es gibt nur für einige Attribute getter aber weitere können einfach ergänzt werden. Welche Daten verfügbar sind ersehen Sie beispielsweise der ZUGFeRD-invoice.xml die in jede ZUGFeRD-Datei eingebettet ist. - Vollständiges Beispiel
ZUGFeRDImporter zu=new ZUGFeRDImporter("MustangGnuaccountingBeispielRE-20190610_507.pdf"); System.out.println("Endbetrag: "+zu.getAmount());
Eigenes XML schreiben
Mustangprojects erstellt das nötige XML für Sie, alternativ können Sie auch eigenes XML angeben.
Erstellen Sie dazu eine Klasse XMLWriter mit einer statischen main-Methode. Benutzen Sie
try {
System.out.println("Converting to PDF/A-3u");
ZUGFeRDExporterFromA1 ze=new ZUGFeRDExporterFromA1().load("./MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
/*
* Add .setZUGFeRDVersion and .setZUGFeRDConformanceLevel
* in the next lines to set the ZUGFeRD version respective profile of the
* XML you are inserting.
*/
;
System.out.println("Attaching ZUGFeRD-Data");
/*
* Mustangproject checks if the input PDF/A file looks halfway valid and
* the XML data contains <rsm:CrossIndustry which is the case for both
* ZF1 (CrossIndustryDocument) and ZF2 files (CrossIndustryInvoice).
* Insert your (validated) XML here.
*/
String ownZUGFeRDXML = " ";
ze.setXML(ownZUGFeRDXML.getBytes());
System.out.println("Writing ZUGFeRD-PDF");
ze.export("./Target.pdf");
} catch (IOException e) {
e.printStackTrace();
}
und importieren Sie die Klassen (STRG
+SHIFT
+O
in Windows oder CMD
+SHIFT
+O
auf dem Mac).Erstellen Sie eine Run-Configuration durch Klick auf Run|Run configurations, dann auf Java Application und dann Klick auf das Icon für “neu”.
Um das eigene EN16931 XML zu schreiben sei erwähnt:
Schema
Die EN16931 Schemadatei für UN/CEFACT ist das “SCRDM uncoupled” der 2016 (“16B”) Version
erhältlich von https://www.unece.org/cefact/xml_schemas/ .
Die Schemadatei des ZUGFeRD “info packet” sollte äquivalent sein.
CIUS
Als ZUGFeRD/Factur-X Profil können Sie bspw. einfach EN16931 nutzen. Als CIUS kommt XRechnung soweit als möglich in Frage.
Beispiel
Das XML der Mustangprojects Beispieldatei kann online eingesehen werden.
Der Grund warum diese Datei im XRechnungs-Validierer nicht funktioniert ist interessant:
Validatoren
Sie können sich den offiziellen XRechnung-Validator wie folgt herunterladen, konfigurieren und verwenden:
mkdir xr
cd xr
wget https://github.com/itplr-kosit/validator/releases/download/v1.0.2/validationtool-1.0.2-full.zip
wget https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/release-2018-12-19/validator-configuration-xrechnung_1.2.0_2018-12-19.zip
unzip validationtool-1.0.2-full.zip
unzip validator-configuration-xrechnung_1.2.0_2018-12-19.zip
mkdir test/instances
Ihre Testdateien können Sie dann in test/instances ablegen. Validierungsberichte in HTML in test/reports erhalten Sie dann mit
java -jar validationtool-1.0.2-standalone.jar -s scenarios.xml -o test/reports -h test/instances/*.xml
Das EN16931 ZF Profile erwartet genau urn:cen.eu:en16931:2017 im RAM:ID-Element während die XRechnung urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_1.2 erwartet.
Fügen Sie im Beispiel der RAM:ID #compliant#urn:xoev-de:kosit:standard:xrechnung_1.2 hinzu sollte es den XRechnungs-Validierer erfolgreich durchlaufen.
Die Ausgabe von
java -jar Mustang-CLI-2.15.2.jar --action validate --source ihreDatei.xml
sollte bei Ihren Dateien “valid” sein.
Es ist leider nicht ausreichend, lediglich mit den Schemadateien zu prüfen: Die Schematron-Datei prüft nämlich zusätzlich noch andere Elemente läßt aber auch einiges außer Acht was in der Schemadatei steht.
Schematron kann beispielsweise prüfen ob das Lieferdatum vor dem Rechnungsdatum liegt und ob die Gesamtsumme der Summe der Posten entspricht.
Validierer prüfen in der Regel sowohl gegen Schema als auch Schematron und Sie sollten einen benutzen oder diese Doppelprüfung auch vornehmen.
Rechenregeln
Wenn Sie wissen möchten wie viele Stellen hinter dem Komma Sie bei Beträgen angeben müssen oder wie richtig gerechnet wird: Das ist Teil des ersten Teils des Standards.
Codelisten
Wenn Sie Artikel in Fünferpacks verkaufen oder keine deutsche USt-ID angeben möchten, oder Firmen anders identifizieren oder eine Rechnung nach Malaysia schicken möchten brauchen Sie andere Codes.
Das CEF hat eine sehr praktische Liste aller möglichen Codes in EN16931 veröffentlicht.
XML generieren und schreiben
Sie können Rechnungen schreiben ohne das XML selbst schreiben zu müssen indem sie die normalen setter verwenden oder die Interfaces implementieren.
Project setup ohne Maven
- Download
- Mustang
- die JAR-Datei https://www.mustangproject.org/deploy/library-2.15.2-shaded.jar mit oder ohne Abhängigkeiten oder das Gesamtpaket mit Validierer mit oder ohne Abhängigkeiten
- die notice-Datei https://www.mustangproject.org/deploy/NOTICE
- Mustang
- In Ihrer Entwicklungsumgebung: Fügen Sie die http://mustangproject.org/deploy/mustang-2.15.2.jar den JAR-Dateien Ihres Projekts hinzu (Rechtsklick auf Projektname, Eigenschaften), hinzufügen als „external Jar“ zum „Build Path“ im Reiter „libraries“.
XRechnung
Der ZUGFeRD2PullProvider kann direkt (zf2p.generateXML(i);
und zf2p.getXML()
)
genutzt werden um UN/CEFACT CII XRechnungen zu erzeugen, unter der Voraussetzung, dass das XRechnung-Profil gewählt wurde, ein Absender-Ansprechpartner
und eine Leitwegs-ID angegeben wurde.
Eine XRechnung wie diese kann so erzeugt werden:
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty("Test company","teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setReferenceNumber("991-01484-64")//leitweg-id
.setNumber("123").addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), /*price*/ new BigDecimal("1.0"), /*qty*/ new BigDecimal("1.0")));
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML());
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("xrechnung.xml"));
writer.write(theXML);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
Es ist auch möglich, Dateianhänge Base64-codiert ins XML einzubringen:
FileAttachment fe1=new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
FileAttachment fe2=new FileAttachment("two.pdf", "application/pdf", "Alternative", b);
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
.embedFileInXML(fe1).embedFileInXML(fe2);
Order-X
Order-X erzeugen Sie mit der normalen IExportableTransaction-Interface oder dementsprechend der Invoice-Klasse. Zum Import kann sowohl der normale ZUGFeRDImporter als auch der ZUGFeRDInvoiceImporter genutzt werden. Es wird allerdings der OXExporterFromA1 (bzw. OXExporterFromA3) statt ZUGFeRDExporterFromA1/A3 genutzt der intern den OXPullProvider verwendet. Fälschlicherweise wird die Order-X-Version (1) noch mit setZUGFeRDVersion gesetzt. Möchten Sie ein anderes Profil verwenden können Sie dies unter Angabe des Order-X-Standards erhalten:
import org.mustangproject.EStandard;
import org.mustangproject.ZUGFeRD.Profiles;
...
zfConformanceLevelProfile = Profiles.getByName(EStandard.orderx, "EXTENDED", 1);
Das Zusammenführen erfolgt dann mit
import org.mustangproject.ZUGFeRD.OXExporterFromA1;
import org.mustangproject.*;
...
OXExporterFromA1 oe = new OXExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(1).ignorePDFAErrors()
.load("blanko.pdf"));
oe.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)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)));
oe.export("order-x.pdf");
Zusatzfunktionen
Neben dem Abschalten der Eingangsprüfung oder dem Auslesen des rohen XMLs können Sie beispielsweise ZUGFeRD 1 in Version 2, Factur-X/ZUGFeRD 2 in HTML oder Factur-X/ZUGFeRD 2 in UBL umwandeln.
- Experimentell: Umwandeln von ZUGFeRD v1 XML nach Fatur-X/ZUGFeRD 2
-
import org.mustangproject.ZUGFeRD.XMLUpgrader; ... XMLUpgrader zmi = new XMLUpgrader(); String xml = zmi.migrateFromV1ToV2("ZUGFeRD-invoice.xml"); Files.write(Paths.get("factur-x.xml"), xml.getBytes()); System.out.println("Written to " + outName);
Intern werden dazu die (noch unvollständigen) XSLT-Transformationen genutzt.
- Visualisierung: Umwandeln von ZUGFeRD 2 in HTML
-
import org.mustangproject.ZUGFeRD.ZUGFeRDVisualizer; /** * Export a resource embedded into a Jar file to the local file path. * * @param resourceName ie.: "/SmartLibrary.dll" * @return The path to the exported resource * @throws Exception e.g. if the specified resource does not exist at the specified location */ static public String ExportResource(String resourceName) throws Exception { InputStream stream = null; OutputStream resStreamOut = null; String jarFolder; try { 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) { throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); } int readBytes; byte[] buffer = new byte[4096]; jarFolder = System.getProperty("user.dir"); resStreamOut = new FileOutputStream(jarFolder + resourceName); while ((readBytes = stream.read(buffer)) > 0) { resStreamOut.write(buffer, 0, readBytes); } } catch (Exception ex) { throw ex; } finally { stream.close(); resStreamOut.close(); } return jarFolder + resourceName; } ... ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer(); String xml = null; try { xml = zvi.visualize(sourceName); Files.write(Paths.get("out.html"), xml.getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Written to out.html"); try { ExportResource("/xrechnung-viewer.css"); ExportResource("/xrechnung-viewer.js"); System.out.println("xrechnung-viewer.css and xrechnung-viewer.js written as well (to local working dir)"); } catch (Exception e) { LOGGER.error(e.getMessage(), e); }
- Visualisierung: Umwandeln von Factur-X/ZUGFeRD 2 CII in UBL
-
import org.mustangproject.CII.CIIToUBL; ... CIIToUBL c2u = new CIIToUBL(); c2u.convert(new File("factur-x.xml"), new File("ubl.xml"));
- Überspringt die Eingangsprüfung auf valides PDF/A-1
-
ZUGFeRDExporter.ignoreA1Errors()
- Liefert die PDF XMP Metadaten
-
ZUGFeRDImporter().getMeta()
- Liefert ebenfalls das XML der ZUGFeRD-PDF-Datei und entfernt gleichzeigt mögliche UTF8 BOMs
-
ZUGFeRDImporter().getUTF8()
Einbetten des Validierers
Verwenden Sie als Abhängigkeit die Validiererbibliothek statt der Library in Ihrer pom.xml
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<classifier>shaded</classifier>
<version>2.15.2</version>
</dependency>
können Sie weiterhin alle Mustang-Funktionen nutzen und zusätzlich den Validierer wie folgt aufrufen:
ZUGFeRDValidator zfv=new ZUGFeRDValidator();
System.out.println(zfv.validate("factur-x.pdf"));