Using the Mustang interfaces
Set up
For a Factur-X/ZUGFeRD invoice you will have to provide PDF/A. You could also provide custom XML.
But the advantage of generated XML from java application logic is that it is more sustainable:
you can switch between different, code that uses
the invoice class and interfaces does not need to be changed no matter if you want to export
for XRechnung, ZUGFeRD 1 or 2/Factur-X, most likely also not for future versions and probably with few if any changes if e.g. UBL
would be added later. On the downside, please make sure the calculations you do for the PDF matches those Mustang does for the XML.
So you can generate the XML, using the invoice class is often the easiest.
The invoice class, however, is based on the interface itself,
and if you have already classes doing other application logic and persistence
you might as well just implement the Mustang interfaces.
Getting started
To get started, you will need to implement a IExportableTransaction, which will require you to specify a
invoice recipient (as IZUGFeRDExportableTradeParty) and invoice lines, i.e. items, as IZUGFeRDExportableItem.
In your main method you will need something like
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();
}
}
}
The class is called YourInvoice in this example to avoid confusion with the Mustang invoice class.
This code requires a PDF/A-1 file MustangGnuaccountingBeispielRE-20190610_507blanko.pdf
in your project root.
A first version of YourInvoice could look like
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";
}
}
Further custom classes
Your contact and tradeparty class consist of much more properties, this is just a illustrative and minimalistic
example what needed to be added where.
In case you wanted to use the interface also on those classes
remove the imports of org.mustangproject.Item, org.mustangproject.Product and org.mustangproject.TradeParty
The relevant parts of YourItem could look like this (any very much like Mustang’s item).
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;
}
}
and a product consists of
description, name, unit code and VAT percentage
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;
}
}
a company(=tradeparty) consists of name, city (location), postcode, street address, VAT ID and country code,
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;
}
}
This should already give you a valid invoice.
Adding even further details
You can confirm that the calculations done by Mustang (which should be in line with EN16931)
match the ones you put in your PDF/A e.g. by checking with the TransactionCalculator in your Main:
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");
Again, this is just an example, of couse you would put your amount in 571.04,
and most likely you would not want a RuntimeException.
If you want to specify your bank account, which works by providing IZUGFeRDTradeSettlementPayments (IBAN, BIC and a PaymentInfoText)
in the getTradeSettlement of the IExportableTransaction, i.e. by adding
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;
}
If you want a XRechnung you also need the Leitweg-ID in
@Override
public String getReferenceNumber() {
return "leitweg-id";
}
and your Sender TradeParty is supposed to return a IZUGFeRDExportableContact contact with name phone and email like
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";
}
}
added to the TradeParty by changing
public IZUGFeRDExportableTradeParty getSender() {
to
return new YourTradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711").setContact(new SenderContact());
In the Main class you can additionally call ze.setProfile like in the second line of the following listing to get a amended guideline ID (the parts after the hash are optional anyway).
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);
The complete example now looks like this: 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";
}
}
Reading
We recommend using the invoice class for parsing , which also supports UBL input, but if that is no option there is a simplified ZUGFeRD reader:
import org.mustangproject.ZUGFeRD.ZUGFeRDImporter;
//...
ZUGFeRDImporter zi=new ZUGFeRDImporter("factur-x.pdf");
System.out.println(zi.getAmount());//Returns DuePayableAmount, or, if not present, GrandTotalAmount
Upgrade Mustang 1.x to 2.x
This paragraph covers the update from Mustang 1 to 2, please note that in org.mustangproject.ZUGFeRD.XMLUpgrader there is also a (highly experimental) XML upgrade from ZUGFeRD 1 to 2.
Since there was no invoice class before, the only code that may need upgrades from Mustang 1.x is the interface class:
What | Old value | New value |
---|---|---|
Repository | github.com/ZUGFeRD… | remove (maven central) |
Group id | org.mustangproject.zugferd | org.mustangproject |
Artifact id | mustang | library |
Version | 1.7.8 | 2.1.1 |
If you want you can also embed the validator in your software using validator
as artifact ID. “validator” includes the library functionality but is >20 MB
bigger due to it’s dependencies. ZF2 was possible with Mustang 1 but it is default in Mustang 2, so
you will need to
.setZUGFeRDVersion(1)
if you don’t want ZUGFeRD 2 files.
In the commandline, all actions will have to be invoked via –action=<theaction>, so
–combine changes to –action=combine.</theaction>
PDFattachZugferdFile
is now calle
setTransaction
and instead of a
ZUGFeRDExporterFromA1Factory
the
ZUGFeRDExporterFromA1
will now return a
a class implementing
IZUGFeRDExporter
instead of a
ZUGFeRDExporter
Instead of
ZUGFeRDConformanceLevel
we now talk of
Profile
So
ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.COMFORT).load(SOURCE_PDF)) {
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setZUGFeRDVersion(1).setProfile(Profiles.getByName("EN16931")).load(SOURCE_PDF)) {
Instead of
Profile.EXTENDED
use
Profiles.getByName("Extended")
If you want to use Profiles from older versions, please specify the version like
Profiles.getByName("Extended", 1)
for an Extended profile of ZF1.
setZUGFeRDXMLData
has been replaced with
setXML
The old Contact class has been corrected to TradeParty. The TradeParty class
can now refer to a (human) Contact from the new Contact() class.