Die Interfaces benutzen
Einrichtung
Für eine Factur-X/ZUGFeRD-Rechnung brauchen Sie eine PDF/A-Datei. Sie können auch eigenes XML angeben, aber der Vorteil von generiertem XML ist die Nachhaltigkeit: Der Java-Code bleibt in dem Fall derselbe, egal ob Sie für XRechnung, ZUGFeRD 1 oder Factur-X exportieren. Als Nachteil sollten Sie Sicherstellen, dass die Berechnungen, die Sie ins PDF schreiben, denen entsprechen, die Mustang im XML macht.
Der einfachste Weg zum Generieren von XML ist die Rechnungsklasse.
Diese basiert selbst wiederum auf den Interfaces und wenn Sie bereits Klassen mit Anwendungslogik und Persistenz haben können Sie die interfaces auch gleich direkt implementieren.
Anfangen
Sie brauchen eine IExportableTransaction, die einen Empfänger (als IZUGFeRDExportableTradeParty) und Rechnungspositionen als IZUGFeRDExportableItem erwartet.
In Ihrer Main-Klasse brauchen Sie etwas wie
package org.example;
import org.mustangproject.ZUGFeRD.IZUGFeRDExporter;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromPDFA;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
System.out.println("Writing invoice...");
YourInvoice yi=new YourInvoice();
try {
IZUGFeRDExporter ze = new ZUGFeRDExporterFromPDFA().load("MustangGnuaccountingBeispielRE-20190610_507blanko.pdf").setProducer("My Application").setCreator(System.getProperty("user.name"));
ze.setTransaction(yi);
ze.export("factur-x.pdf");
System.out.println("Invoice written.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Im Beispiel heißt die Klasse YourInvoice damit sie sich von der Mustang-Rechnungsklasse “invoice” abgrenzt. Weiterhin brauchen Sie eine MustangGnuaccountingBeispielRE-20190610_507blanko.pdf PDF/A-1-Datei wie diese hier im Projektstammverzeichnis.
Eine erste Version von YourInvoice könnte wie folgt aussehen
package org.example;
import org.mustangproject.ZUGFeRD.*;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class YourInvoice implements IExportableTransaction {
protected class YourItem implements IZUGFeRDExportableItem {
private BigDecimal price, quantity;
private IZUGFeRDExportableProduct product;
public YourItem( IZUGFeRDExportableProduct product, BigDecimal price, BigDecimal quantity) {
super();
this.price = price;
this.quantity = quantity;
this.product = product;
}
@Override
public BigDecimal getPrice() {
return price;
}
@Override
public BigDecimal getQuantity() {
return quantity;
}
@Override
public IZUGFeRDExportableProduct getProduct() {
return product;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemCharges() {
return null;
}
}
protected class YourProduct implements IZUGFeRDExportableProduct {
private String description, name, unit;
private BigDecimal VATPercent;
public YourProduct(String description, String name, String unit, BigDecimal VATPercent) {
super();
this.description = description;
this.name = name;
this.unit = unit;
this.VATPercent = VATPercent;
}
@Override
public String getDescription() {
return description;
}
@Override
public String getName() {
return name;
}
@Override
public String getUnit() {
return unit;
}
@Override
public BigDecimal getVATPercent() {
return VATPercent;
}
}
protected class YourTradeParty implements IZUGFeRDExportableTradeParty {
protected String country, location, name, street, VATID, ZIP;
protected IZUGFeRDExportableContact senderContact=null;
public YourTradeParty( String name, String street,String ZIP,String location,String country) {
this.country = country;
this.location = location;
this.name = name;
this.street = street;
this.ZIP = ZIP;
}
@Override
public String getCountry() {
return country;
}
@Override
public String getLocation() {
return location;
}
@Override
public String getName() {
return name;
}
@Override
public String getStreet() {
return street;
}
public YourTradeParty setVATID(String id) {
VATID=id;
return this;
}
@Override
public String getVATID() {
return VATID;
}
@Override
public String getZIP() {
return ZIP;
}
public YourTradeParty setContact(IZUGFeRDExportableContact senderContact) {
this.senderContact=senderContact;
return this;
}
@Override
public IZUGFeRDExportableContact getContact() {
return senderContact;
}
}
@Override
public Date getDeliveryDate() {
return new GregorianCalendar(2019, Calendar.JUNE, 10).getTime();
}
@Override
public Date getDueDate() {
return new GregorianCalendar(2019, Calendar.JULY, 1).getTime();
}
@Override
public Date getIssueDate() {
return new GregorianCalendar(2019, Calendar.JUNE, 10).getTime();
}
@Override
public String getNumber() {
return "RE-20190610/507";
}
@Override
public IZUGFeRDExportableTradeParty getRecipient() {
return new YourTradeParty("Theodor Est", "Bahnstr. 42", "88802","Spielkreis", "DE").setVATID("DE0815");
}
@Override
public IZUGFeRDExportableTradeParty getSender() {
return new YourTradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711");
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
YourItem[] allItems = new YourItem[3];
YourProduct designProduct = new YourProduct("", "Design (hours): Of a sample invoice", "HUR",
new BigDecimal("7.000000"));
YourProduct balloonProduct = new YourProduct("", "Ballons: various colors, ~2000ml", "C62", new BigDecimal("19.000000"));
YourProduct airProduct = new YourProduct("", "Hot air „heiße Luft“ (litres)", "LTR", new BigDecimal("19.000000"));
allItems[0] = new YourItem(designProduct, new BigDecimal("160"), new BigDecimal("1") );
allItems[1] = new YourItem(balloonProduct, new BigDecimal("0.79"), new BigDecimal("400"));
allItems[2] = new YourItem(airProduct, new BigDecimal("0.025"), new BigDecimal("800"));
return allItems;
}
@Override
public String getCurrency() {
return "EUR";
}
}
Weitere angepasste Klassen
Ihre Kontakt- und Tradeparty-Klassen werden natürlich aus mehr Eigenschaften und Methoden bestehen, hier nur ein minimalistisches Beispiel.
Für eigene Klassen entfernen Sie die Importe von org.mustangproject.Item, org.mustangproject.Product und org.mustangproject.TradeParty
Die YourItem-Klasse könnte beispielsweise wie folgt aussehen (und wäre dann sehr ähnlich der Item-Klasse von Mustang).
protected class YourItem implements IZUGFeRDExportableItem {
private BigDecimal price, quantity;
private IZUGFeRDExportableProduct product;
public YourItem( IZUGFeRDExportableProduct product, BigDecimal price, BigDecimal quantity) {
super();
this.price = price;
this.quantity = quantity;
this.product = product;
}
@Override
public BigDecimal getPrice() {
return price;
}
@Override
public BigDecimal getQuantity() {
return quantity;
}
@Override
public IZUGFeRDExportableProduct getProduct() {
return product;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemCharges() {
return null;
}
}
Ein Produkt besteht aus Name, Beschreibung, Mengeneinheitscode und Umsatzsteuerprozentsatz
protected class YourProduct implements IZUGFeRDExportableProduct {
private String description, name, unit;
private BigDecimal VATPercent;
public YourProduct(String description, String name, String unit, BigDecimal VATPercent) {
super();
this.description = description;
this.name = name;
this.unit = unit;
this.VATPercent = VATPercent;
}
@Override
public String getDescription() {
return description;
}
@Override
public String getName() {
return name;
}
@Override
public String getUnit() {
return unit;
}
@Override
public BigDecimal getVATPercent() {
return VATPercent;
}
}
Eine Firma(=Tradeparty) besteht aus Name, Stadt (location), Postleitzahl, Straße (+Hausnummer), USt.-ID und Ländercode
protected class YourTradeParty implements IZUGFeRDExportableTradeParty {
protected String country, location, name, street, VATID, ZIP;
protected IZUGFeRDExportableContact senderContact=null;
public YourTradeParty( String name, String street,String ZIP,String location,String country) {
this.country = country;
this.location = location;
this.name = name;
this.street = street;
this.ZIP = ZIP;
}
@Override
public String getCountry() {
return country;
}
@Override
public String getLocation() {
return location;
}
@Override
public String getName() {
return name;
}
@Override
public String getStreet() {
return street;
}
public YourTradeParty setVATID(String id) {
VATID=id;
return this;
}
@Override
public String getVATID() {
return VATID;
}
@Override
public String getZIP() {
return ZIP;
}
public YourTradeParty setContact(IZUGFeRDExportableContact senderContact) {
this.senderContact=senderContact;
return this;
}
@Override
public IZUGFeRDExportableContact getContact() {
return senderContact;
}
}
Das sollte bereits eine gültige Rechnung erzeugen.
Und noch mehr Details
Mit dem TransactionCalculator können Sie sicherstellen, dass die Berechnungen durch Mustang (die EN16931-1 entsprechen sollten) denen entsprechen die Sie in Ihre PDF-Datei geschrieben haben:
import org.mustangproject.ZUGFeRD.TransactionCalculator;
import java.math.BigDecimal;
...
ze.setTransaction(yi);
// added
TransactionCalculator ti = new TransactionCalculator(yi);
if (!ti.getGrandTotal().equals(new BigDecimal("571.04"))) {
throw new RuntimeException("Calculation mismatch");
}
// continued
ze.export("factur-x.pdf");
Natürlich handelt es sich nur um ein Beispiel, Sie würden Ihren wert in die 571.04 schreiben und Fehler vermutlich nicht mit einer RuntimeException behandeln.
Weiterhin können Sie Ihr bankkonto angeben, indem Sie IZUGFeRDTradeSettlementPayments (IBAN, BIC und ein PaymentInfoText) implementieren und in getTradeSettlement der IExportableTransaction zurückgeben.
protected class Payment implements IZUGFeRDTradeSettlementPayment {
@Override
public String getOwnBIC() {
return "COBADEFFXXX";
}
@Override
public String getOwnPaymentInfoText() {
return "Überweisung";
}
@Override
public String getOwnIBAN() {
return "DE88 2008 0000 0970 3757 00";
}
}
//... and then back in YourInvoice
@Override
public IZUGFeRDTradeSettlement[] getTradeSettlement() {
Payment[] payments = new Payment[1];
payments[0] = new Payment();
return payments;
}
Für eine XRechnung brauchen Sie zusätzlich die Leitwegs-ID
@Override
public String getReferenceNumber() {
return "leitweg-id";
}
und die TradeParty des Absenders muss einen IZUGFeRDExportableContact contact mit Name, Telefonnummer und Emailaddresse zurückgeben.
protected class SenderContact implements IZUGFeRDExportableContact {
@Override
public String getName() {
return "Ingmar N. Fo";
}
@Override
public String getPhone() {
return "++49(0)237823";
}
@Override
public String getEMail() {
return "info@localhost.local";
}
}
Dies kann der TradeParty wie folgt hinzugefügt werden
public IZUGFeRDExportableTradeParty getSender() {
to
return new YourTradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711").setContact(new SenderContact());
In der Main class können Sie zusätzlich ze.setProfile wie in der zweiten Zeile des folgenden Listings für eine korrigierte Guideline-ID angeben
import org.mustangproject.ZUGFeRD.Profiles;
//...
IZUGFeRDExporter ze = new ZUGFeRDExporterFromPDFA().load("MustangGnuaccountingBeispielRE-20190610_507blanko.pdf").setProducer("My Application").setCreator(System.getProperty("user.name"));
ze.setProfile(Profiles.getByName("XRechnung"));
ze.setTransaction(yi);
Das komplette Beispiel sieht jetzt wie folgt aus: build.gradle
plugins {
id 'java'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation group: 'org.mustangproject', name: 'validator', version: '2.9.0'
}
test {
useJUnitPlatform()
}
src/main/java/org/example/Main.java
package org.example;
import org.mustangproject.ZUGFeRD.IZUGFeRDExporter;
import org.mustangproject.ZUGFeRD.Profiles;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromPDFA;
import java.io.IOException;
import org.mustangproject.ZUGFeRD.TransactionCalculator;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
System.out.println("Writing invoice...");
YourInvoice yi=new YourInvoice();
try {
IZUGFeRDExporter ze = new ZUGFeRDExporterFromPDFA().load("MustangGnuaccountingBeispielRE-20190610_507blanko.pdf").setProducer("My Application").setCreator(System.getProperty("user.name"));
ze.setTransaction(yi);
TransactionCalculator ti = new TransactionCalculator(yi);
if (!ti.getGrandTotal().equals(new BigDecimal("571.04"))) {
throw new RuntimeException("Calculation mismatch");
}
ze.export("factur-x.pdf");
ze.setProfile(Profiles.getByName("XRechnung"));
System.out.println("Invoice written.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
and src/main/java/org/example/YourInvoice.java
package org.example;
import org.mustangproject.ZUGFeRD.*;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class YourInvoice implements IExportableTransaction {
protected class Payment implements IZUGFeRDTradeSettlementPayment {
@Override
public String getOwnBIC() {
return "COBADEFFXXX";
}
@Override
public String getOwnPaymentInfoText() {
return "Überweisung";
}
@Override
public String getOwnIBAN() {
return "DE88 2008 0000 0970 3757 00";
}
}
protected class YourItem implements IZUGFeRDExportableItem {
private BigDecimal price, quantity;
private IZUGFeRDExportableProduct product;
public YourItem( IZUGFeRDExportableProduct product, BigDecimal price, BigDecimal quantity) {
super();
this.price = price;
this.quantity = quantity;
this.product = product;
}
@Override
public BigDecimal getPrice() {
return price;
}
@Override
public BigDecimal getQuantity() {
return quantity;
}
@Override
public IZUGFeRDExportableProduct getProduct() {
return product;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getItemCharges() {
return null;
}
}
protected class YourProduct implements IZUGFeRDExportableProduct {
private String description, name, unit;
private BigDecimal VATPercent;
public YourProduct(String description, String name, String unit, BigDecimal VATPercent) {
super();
this.description = description;
this.name = name;
this.unit = unit;
this.VATPercent = VATPercent;
}
@Override
public String getDescription() {
return description;
}
@Override
public String getName() {
return name;
}
@Override
public String getUnit() {
return unit;
}
@Override
public BigDecimal getVATPercent() {
return VATPercent;
}
}
protected class YourTradeParty implements IZUGFeRDExportableTradeParty {
protected String country, location, name, street, VATID, ZIP;
protected IZUGFeRDExportableContact senderContact=null;
public YourTradeParty( String name, String street,String ZIP,String location,String country) {
this.country = country;
this.location = location;
this.name = name;
this.street = street;
this.ZIP = ZIP;
}
@Override
public String getCountry() {
return country;
}
@Override
public String getLocation() {
return location;
}
@Override
public String getName() {
return name;
}
@Override
public String getStreet() {
return street;
}
public YourTradeParty setVATID(String id) {
VATID=id;
return this;
}
@Override
public String getVATID() {
return VATID;
}
@Override
public String getZIP() {
return ZIP;
}
public YourTradeParty setContact(IZUGFeRDExportableContact senderContact) {
this.senderContact=senderContact;
return this;
}
@Override
public IZUGFeRDExportableContact getContact() {
return senderContact;
}
}
protected class SenderContact implements IZUGFeRDExportableContact {
@Override
public String getName() {
return "Ingmar N. Fo";
}
@Override
public String getPhone() {
return "++49(0)237823";
}
@Override
public String getEMail() {
return "info@localhost.local";
}
}
// YourInvoice continued
@Override
public IZUGFeRDTradeSettlement[] getTradeSettlement() {
Payment[] payments = new Payment[1];
payments[0] = new Payment();
return payments;
}
@Override
public String getReferenceNumber() {
return "leitweg-id";
}
@Override
public Date getDeliveryDate() {
return new GregorianCalendar(2019, Calendar.JUNE, 10).getTime();
}
@Override
public Date getDueDate() {
return new GregorianCalendar(2019, Calendar.JULY, 1).getTime();
}
@Override
public Date getIssueDate() {
return new GregorianCalendar(2019, Calendar.JUNE, 10).getTime();
}
@Override
public String getNumber() {
return "RE-20190610/507";
}
@Override
public IZUGFeRDExportableTradeParty getRecipient() {
return new YourTradeParty("Theodor Est", "Bahnstr. 42", "88802","Spielkreis", "DE").setVATID("DE0815");
}
@Override
public IZUGFeRDExportableTradeParty getSender() {
return new YourTradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711").setContact(new SenderContact());
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
YourItem[] allItems = new YourItem[3];
YourProduct designProduct = new YourProduct("", "Design (hours): Of a sample invoice", "HUR",
new BigDecimal("7.000000"));
YourProduct balloonProduct = new YourProduct("", "Ballons: various colors, ~2000ml", "C62", new BigDecimal("19.000000"));
YourProduct airProduct = new YourProduct("", "Hot air „heiße Luft“ (litres)", "LTR", new BigDecimal("19.000000"));
allItems[0] = new YourItem(designProduct, new BigDecimal("160"), new BigDecimal("1") );
allItems[1] = new YourItem(balloonProduct, new BigDecimal("0.79"), new BigDecimal("400"));
allItems[2] = new YourItem(airProduct, new BigDecimal("0.025"), new BigDecimal("800"));
return allItems;
}
@Override
public String getCurrency() {
return "EUR";
}
}
Lesen
Zum Lesen empfehlen wir die Rechnungsklasse die auch UBL-Eingabe unterstützt, falls das keine Option ist gibt es auch einen vereinfachten ZUGFeRD-Parser:
import org.mustangproject.ZUGFeRD.ZUGFeRDImporter;
//...
ZUGFeRDImporter zi=new ZUGFeRDImporter("factur-x.pdf");
System.out.println(zi.getAmount());//Returns DuePayableAmount, or, if not present, GrandTotalAmount
Mustang 1.x aktualisieren auf 2.x
Dieser Abschnitt behandelt das Update von Mustang 1 auf 2, bitte beachten Sie, dass es in org.mustangproject.ZUGFeRD.XMLUpgrader auch ein (experimentelles) Feature zum XML-upgrade von ZUGFeRD 1 auf 2 gibt.
Da es die Rechnungsklasse erst seit Mustang 2 gibt gibt es nur für die Interfaces Bedarf einer Portierung von Mustang 1.x auf Mustang 2.
Alter Wert | Neuer Wert | |
---|---|---|
Repository | github.com/ZUGFeRD… | entfernen (verfügbar über Maven Central) |
Group id | org.mustangproject.zugferd | org.mustangproject |
Artifact id | mustang | library |
Version | 1.7.8 | 2.1.1 |
Wenn Sie den Validierer einbetten wollen verwenden Sie validator als Artifact ID. Er beinhaltet die Funktionalität der library, ist aber durch seine Abhängigkeiten >20 MB
größer. ZF2 war mit Mustang 1 schon möglich, ist aber default in Mustang 2, also brauchen Sie ein
.setZUGFeRDVersion(1)
wenn Sie kein ZUGFeRD 2 wünschen.
In der Kommandozeile müssen jetzt alle Actions über –action=<dieAktion> ausgeführt werden, das heißt
–combine ändert sich zu –action=combine.
PDFattachZugferdFile
heißt jetzt
setTransaction
und anstelle von
ZUGFeRDExporterFromA1Factory
wird
ZUGFeRDExporterFromA1
jetzt eine Klasse zurückliefern die
IZUGFeRDExporter
statt
ZUGFeRDExporter
implementiert.
Aus
ZUGFeRDConformanceLevel
wird
Profile
Aus
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.COMFORT).load(SOURCE_PDF)) {
wird daher
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setZUGFeRDVersion(1).setProfile(Profiles.getByName("EN16931")).load(SOURCE_PDF)) {
Anstelle von
Profile.EXTENDED
verwenden Sie bitte
Profiles.getByName("Extended")
Um Profile älterer ZUGFeRD-Versionen zu nutzen geben Sie die Version bitte wie folgt an:
Profiles.getByName("Extended", 1)
für ein Extended-Profil von ZF1.
Die alte Contact-Klasse heißt jetzt TradeParty. Die TradeParty (=Firma) kann Ansprechpartner beinhalten die in der neuen Contact-Klasse definiert werden können.
Aus
setZUGFeRDXMLData
wurde
setXML