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