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.0.3'
    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.0.0

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.