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
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromA1;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
System.out.println("Writing invoice...");
YourInvoice yi=new YourInvoice();
try {
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application").setCreator(System.getProperty("user.name")).ignorePDFAErrors().load("MustangBlanko.pdf");
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 MustangBlanko like this
in your project root.
A first version of YourInvoice could look like
import org.mustangproject.ZUGFeRD.IExportableTransaction;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
import java.util.Date;
public class YourInvoice implements IExportableTransaction {
@Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFCharges() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return null;
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
return new IZUGFeRDExportableItem[0];
}
@Override
public IZUGFeRDExportableTradeParty getRecipient() {
return null;
}
@Override
public Date getDeliveryDate() {
return null;
}
}
Mixing the classes
As you see you need items for getZFItems.
An item consists of a price, a quantity and a product.
You can mix what is called the “invoice class” and what really consists of the
classes invoice, item, product, tradeparty and contact.
In that case your new YourInvoice can really work by
importing org.mustangproject.Item, org.mustangproject.Product, org.mustangproject.TradeParty as well as
java.math.BigDecimal, java.util.Calendar, java.util.Date and java.util.GregorianCalendar;
Now we specify some dates, the invoice number, currency, sender and recipient as well as the items:
import org.mustangproject.ZUGFeRD.IExportableTransaction;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
import org.mustangproject.Item;
import org.mustangproject.Product;
import org.mustangproject.TradeParty;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class YourInvoice implements IExportableTransaction {
@Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFCharges() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return null;
}
@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 TradeParty("Theodor Est", "Bahnstr. 42", "88802","Spielkreis", "DE").setVATID("DE0815");
}
@Override
public IZUGFeRDExportableTradeParty getSender() {
return new TradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711");
}
@Override
public IZUGFeRDExportableItem[] getZFItems() {
Item[] allItems = new Item[3];
Product designProduct = new Product("", "Design (hours): Of a sample invoice", "HUR",
new BigDecimal("7.000000"));
Product balloonProduct = new Product("", "Ballons: various colors, ~2000ml", "C62", new BigDecimal("19.000000"));
Product airProduct = new Product("", "Hot air „heiße Luft“ (litres)", "LTR", new BigDecimal("19.000000"));
allItems[0] = new Item(designProduct, new BigDecimal("160"), new BigDecimal("1") );
allItems[1] = new Item(balloonProduct, new BigDecimal("0.79"), new BigDecimal("400"));
allItems[2] = new Item(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. The notices are neglictible but also tackled in the next paragraph.
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:
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.
Further, you might 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";
}
}
@Override
public IZUGFeRDTradeSettlement[] getTradeSettlement() {
Payment[] payments = new Payment[1];
payments[0] = new Payment();
return payments;
}
If you want a XRechnung you also need the Leitwegs-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).
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application").setCreator(System.getProperty("user.name")).ignorePDFAErrors().load("MustangBlanko.pdf");
ze.setProfile(Profiles.getByName("XRechnung"));
ze.setTransaction(yi);
The complete example now looks like this: build.gradle
plugins {
id 'groovy'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.mustangproject:library:2.1.1'
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()
}
src/main/java/Main.java
import org.mustangproject.ZUGFeRD.Profiles;
import org.mustangproject.ZUGFeRD.TransactionCalculator;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromA1;
import java.io.IOException;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
System.out.println("Writing invoice...");
YourInvoice yi = new YourInvoice();
try {
ZUGFeRDExporterFromA1 ze = new ZUGFeRDExporterFromA1().setProducer("My Application").setCreator(System.getProperty("user.name")).ignorePDFAErrors().load("MustangBlanko.pdf");
ze.setProfile(Profiles.getByName("XRechnung"));
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");
System.out.println("Invoice written."+ti.getGrandTotal());
} catch (IOException e) {
e.printStackTrace();
}
}
}
and src/main/java/YourInvoice.java
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;
private String addReference=null;
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 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";
}
}
@Override
public IZUGFeRDTradeSettlement[] getTradeSettlement() {
Payment[] payments = new Payment[1];
payments[0] = new Payment();
return payments;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFCharges() {
return null;
}
@Override
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
return null;
}
@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");
}
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";
}
}
@Override
public IZUGFeRDExportableTradeParty getSender() {
return new YourTradeParty("Bei Spiel GmbH", "Ecke 12","12345", "Stadthausen", "DE").setVATID("DE4711").setContact(new SenderContact());
}
@Override
public String getReferenceNumber() {
return "99 0 00 000 01818 16";
}
@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";
}
}
Upgrade Mustang 1.x to 2.x
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.