Read and write electronic invoices

License

Mustangproject is licensed under the permissive Apache 2 license and can be used for free in commercial and noncommercial projects.

Source code

The git repository of the Mustangproject source code lives on Github.

Documentation

New project

Feel free to use any IDE but for the keyboard shortcuts this example assumes you use Eclipse as IDE.

Start Eclipse, create a new Maven project, e.g. „MustangSample“, as “simple project”, group ID “org.mustangproject”, artifact ID “mustangtest”.

You will need a

  • ZUGFeRD PDF/A-3 sample file for the reader example. Save it in the same directory where your pom.xml file lives.
  • and a PDF/A-1 without ZUGFeRD data for the writer which you can
    • either download
    • or create yourself using Libreoffice on this source: Just say File|Export as PDF, set the checkbox PDF/A-1a in the export options and save the PDF file as MustangGnuaccountingBeispielRE-20190610_507blanko.pdf.
    • Please also save this PDF in the same directory where your pom.xml file resides.

With Maven

Open your pom.xml and add the following repository


<repositories>
    <repository>
        <id>mustang-mvn-repo</id>
        <url>https://raw.github.com/ZUGFeRD/mustangproject/mvn-repo/</url>
    </repository>
</repositories>

                        

as well as the following


<dependencies>
    <dependency>
        <groupId>org.mustangproject.ZUGFeRD</groupId>
        <artifactId>mustang</artifactId>
        <version>1.7.4</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.1</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>2.2.5</version>
    </dependency>
</dependencies>

                        

We will need Maven to download Mustangproject and add it in the Eclipse project properties: In the pom.xml right click and say Run as| Maven build. Specify eclipse:eclipse as goal and run. Right-click on the project and click refresh.

Read ZUGFeRD data

  1. Create a new class in the src/main/java folder, called Reader. Check the “Public static void main()” checkbox.
  2. Create a run configuration: Run|Run configurations Java Application, click on the “new” icon.
  3. Within the main method, enter ZUGFeRDImporter zu=new ZUGFeRDImporter(); and
  4. add the imports by pressing CTRL+SHIFT+O (Windows) or CMD+SHIFT+O (Mac).
  5. use zu.extract("MustangGnuaccountingBeispielRE-20190610_507.pdf")
  6. and zu.parse() you can access the getter-Methods like getAmount()
  7. There are only getters for few properties but additional ones can be added easily. Which data is available can be seen in the ZUGFeRD invoice.xml file embedded any ZUGFeRD compliant PDF
  8. Complete example
    
    ZUGFeRDImporter zu=new ZUGFeRDImporter();
    zu.extract("MustangGnuaccountingBeispielRE-20190610_507.pdf");
    zu.parse();
    System.out.println("Total amount: "+zu.getAmount());                                    
    
                                    

Write custom XML

The idea is that Mustangprojects creates the XML for you. However, you can also specify your own XML.

Create a class XMLWriter with a static main method. Use


ZUGFeRDExporter ze;
try {
    System.out.println("Converting to PDF/A-3u");

    /*
    * Add .setZUGFeRDVersion and .setZUGFeRDConformanceLevel
    * in the next lines to set the ZUGFeRD version respective profile of the XML
    * you are inserting.
    */
    ze = new ZUGFeRDExporterFromA1Factory().setProducer("My Application")
    .setCreator(System.getProperty("user.name"))
    .load("./MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
    System.out.println("Attaching ZUGFeRD-Data");

    /*
    * Mustangproject checks if the input PDF/A file looks halfway valid and the XML
    * data contains „<rsm:CrossIndustry“ which is the case for both ZF1
    * (CrossIndustryDocument) and ZF2 files (CrossIndustryInvoice).
    * Insert your (validated) XML here.
    */
    String ownZUGFeRDXML = "<rsm:CrossIndustryDocument></rsm:CrossIndustryDocument>";
    ze.setZUGFeRDXMLData(ownZUGFeRDXML.getBytes());
    System.out.println("Writing ZUGFeRD-PDF");
    ze.export("./Target.pdf");
} catch (IOException e) {
    e.printStackTrace();
}
        



                

and import classes (CTRL+SHIFT+O on Windows or CMD+SHIFT+O on the Mac).
Create a run configuration: Run|Run configurations Java Application, click on the “new” icon.

Writing EN16931 XML starts with the

Schema

The EN16931 schema file for UN/CEFACT is the “SCRDM uncoupled” from 2016 (“16B”) version
available from https://www.unece.org/cefact/xml_schemas/ .
The schema file which comes with the ZUGFeRD “info packet” should be pretty much equivalent.

CIUS

ZUGFeRD/Factur-X profile-wise you can e.g. just use EN16931, CIUS wise you can use XRechnung as far as possible.

Sample

You might have a look at the XML of the official Mustangproject sample.

The reason why it fails in the XRechnung validator is interesting:

Validators

You can download the official XRechnung validator using something like


mkdir xr
cd xr
wget https://github.com/itplr-kosit/validator/releases/download/v1.0.2/validationtool-1.0.2-full.zip
wget https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/release-2018-12-19/validator-configuration-xrechnung_1.2.0_2018-12-19.zip
unzip validationtool-1.0.2-full.zip
unzip validator-configuration-xrechnung_1.2.0_2018-12-19.zip
mkdir test/instances

then copy your test files into test/instances and you can get your XRechnung report HTMLs into test/reports
using


java -jar validationtool-1.0.2-standalone.jar -s scenarios.xml -o test/reports -h test/instances/*.xml

Now the EN16931 ZF profile requests the RAM:ID element to be exactly
urn:cen.eu:en16931:2017 while XRechnung requires urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_1.2 .

So feel free to add #compliant#urn:xoev-de:kosit:standard:xrechnung_1.2 to the Mustangproject sample in order to have the XRechnung validator pick it up. It should not complain about anything else.

Or remove the fragment from the XRechnung and throw it through the ZUGFeRD Validator.

The output of


wget https://github.com/ZUGFeRD/ZUV/releases/download/v0.8.0/ZUV-0.8.0.jar

java -jar ZUV-0.8.0.jar -x 

should be “valid”.

Please note that the schema file may tell you which elements you can use.
But it may not be sufficient to check your XML, the schematron files
are something completely different: a schematron uses XPath and can compare and calculate.
E.g. a schematron can check if the invoice date is not before the delivery date as required by
the specs or if the total amount matches the sum of the items.
The validators will also run against schematron and you are on the safe side only if both schema and schematron validates.

Rules

If you are in question how many
decimals you may use, or how the calculation works at all, please note that that
is covered in the first part of the standard, EN16931-1.

Codelists

But you might sell articles not by
piece but e.g. in five packs. Or you don’t have a german VAT ID. Or you want to identify
organizations differently, send an invoice to Malaysia etc..

In that case you would need to use different identifying attributes, “codes” in your file.

The CEF has a very handy summary of all “codelists” you can use in EN16931.

Write a ZUGFeRD-PDF file

  1. Create a new class in the src-folder, e.g. MustangWriter with the usual „Public static void main()“ .
  2. Create a run configuration: Run|Run configurations Java Application, click on the “new” icon.
  3. let the main class implement IZUGFeRDExportableTransaction like this
    public class MustangWriter implements IZUGFeRDExportableTransaction {
  4. Even before that, but after package mustangtest;, add the following classes in in the same file:
    
    class Contact implements IZUGFeRDExportableContact {}
    class Item implements IZUGFeRDExportableItem {
        private BigDecimal price, quantity;
        private Product product;
    }
    class Product implements IZUGFeRDExportableProduct {
        private String description, name, unit;
        private BigDecimal VATPercent;
    }                                
                                
  5. add the imports by pressing CTRL+SHIFT+O (Windows) or CMD+SHIFT+O (Mac).
  6. Left click left on the main class MustangWriter and press ALT+SHIFT+S
    (Windows)/ALT+CMD+S (Mac), select “Override/Implement Methods” and press return.
  7. Left click on the class name Contact and press ALT+SHIFT+S
    (Windows)/ALT+CMD+S (Mac), select “Override/Implement Methods” and press return.
  8. Click Item, press ALT+SHIFT+S or ALT+CMD+S, this time select „Generate Getters and Setters“. Mark all members and press return.
  9. Stay on Item, refactor again (ALT+SHIFT+S or ALT+CMD+S) and select „Generate Constructor using Fields“. Choose again all member variables and press return.
  10. Item also needs other methods besides the getter/setters, refactor again (ALT+SHIFT+S or ALT+CMD+S) and choose Override/Implement Methods
  11. „Generate Getters and Setters“ for the class (not the member „Product“: Click Product, (ALT+SHIFT+S or ALT+CMD+S) and select „Generate Getters and Setters“. Choose all members and confirm
  12. Click on Product again, press ALT+SHIFT+S and select „Generate Constructor using Fields“. Choose all members again and press return.
  13. Your skeleton should now look like this
  14. The following methods of Contact should now return the following strings:
    • getCountry(): “DE”
    • getLocation(): “Spielkreis”
    • getName(): “Theodor Est”
    • getStreet(): “Bahnstr. 42”
    • getVATID(): “DE999999999”
    • getZIP(): “88802”;
  15. The following methods of the main class should return the following:
    • getCurrency(): “EUR”
    • getDeliveryDate(): newGregorianCalendar(2017,Calendar.NOVEMBER,17).getTime();
    • Pressing CTRLs+SHIFT+O (Windows) or CMD+SHIFT+O (Mac) will import the
      necessary
      GregorianCalendar and Calendar class
    • getDueDate(): newGregorianCalendar(2017,Calendar.DECEMBER,9).getTime();
    • getIssueDate(): newGregorianCalendar(2017,Calendar.NOVEMBER,18).getTime();
    • getNumber(): “RE-20171118/506”
    • getOwnBIC(): “COBADEFFXXX”
    • getOwnBankName(): “Commerzbank”
    • getOwnCountry() “DE”
    • getOwnIBAN(): “DE88 2008 0000 0970 3757 00”
    • getOwnLocation() “Stadthausen”
    • getOwnOrganisationFullPlaintextInfo(): “Bei Spiel GmbH\n”+
      “Ecke 12\n”+
      “12345 Stadthausen\n”+
      “Geschäftsführer: Max Mustermann”
    • getOwnOrganisationName(): “Bei Spiel GmbH”
    • getOwnStreet() “Ecke 12”
    • getOwnTaxID(): “22/815/0815/4”
    • getOwnVATID(): “DE136695976”
    • getOwnZIP() “12345”
    • getRecipient(): new Contact();
  16. getZFItems() of the main class can now create products and return them as a arrays of items:
    
    Item[] allItems=new Item[3];
    Product designProduct=new Product("", "Künstlerische Gestaltung (Stunde): Einer Beispielrechnung",
    "HUR", new BigDecimal("7.000000"));
    Product balloonProduct=new Product("", "Luftballon: Bunt, ca. 500ml", "C62", new
    BigDecimal("19.000000"));
    Product airProduct=new Product("", "Heiße Luft pro Liter", "LTR", new BigDecimal("19.000000"));
    allItems[0]=new Item(new BigDecimal("160"), new BigDecimal("1"), designProduct);
    allItems[1]=new Item(new BigDecimal("0.79"), new BigDecimal("400"), balloonProduct);
    allItems[2]=new Item(new BigDecimal("0.10"), new BigDecimal("200"), airProduct);
    return allItems;                        
                        
  17. Now create a “private void” method called apply in the MustangWriter class
  18. Please instantiate this main MustangWriter class in the main method and invoke the apply() function.
  19. In the apply-method you can now
    • create a new ZUGFeRDExporterFromA1Factory, run
    • setProducer and setCreator on it (e.g. ZUGFeRDExporter ze=newZUGFeRDExporterFromA1Factory().setProducer("string").setCreator("string")) and get
      the ZUGFeRDExporter from the factories load("./MustangGnuaccountingBeispielRE-20171118_506new.pdf")
      method.
    • Feel free to use your own PDF/A-1 invoice file.
    • In this chain (.setProducer.setCreator…) you can also insert setZUGFeRDVersion(2)
    • use the PDFattachZugferdFile-method (with the IZUGFeRDExportableTransation, i.e. „this“ as parameter) on the ZUGFeRDExporter and
    • use export() to save the PDF/A-3 file.
    • Finally, add the imports by pressing CTRL+SHIFT+O (Windows) or CMD+SHIFT+O (Mac).

    The apply-method then looks – with according try/catch-blocks- as follows:

    
    private void apply() {
        try {
            System.out.println("Reading Blanko-PDF");
            ZUGFeRDExporter ze = new ZUGFeRDExporterFromA1Factory().setProducer("My Application")
            .setCreator(System.getProperty("user.name"))
            .load("./MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");
            System.out.println("Generating and attaching ZUGFeRD-Data");
            ze.PDFattachZugferdFile(this);
            System.out.println("Writing ZUGFeRD-PDF");
            ze.export("./MustangGnuaccountingBeispielRE-20190610_507new.pdf");
            System.out.println("Done.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    
    }                    
                    
  20. „My Application“ and System.getProperty(“user.name”) are stored in the meta data as „Producer“ (producing application) respectively „Creator“ (author). Please adjust accordingly.
  21. Instanciate a MustangWriter in public static void main and call it’s apply() method.
  22. Start it to write the ZUGFeRD invoice to the file specified in export.
  23. To be APL compliant: Adjust the NOTICE-File for and add it to your application.

Your MustangWriter.java should now look like this.

Project setup without Maven

  1. Download
    1. Mustang
      1. the JAR file http://mustangproject.org/deploy/mustang-1.7.4.jar
      2. the notice file http://mustangproject.org/deploy/NOTICE
  2. In your IDE: Add the downloaded JAR files to your project (right click on project name, Properties) add as „external Jar“ to the „Build Path“ in the „libraries“ tab. If you anyway embed PDFBox (pdfbox, fontbox, preflight, xmpbox as well as their dependencies apache-commons-io and apache-commons-logging) you can also download the much lighter http://mustangproject.org/deploy/original-mustang-1.7.4.jar

Supplementary functions

new ZUGFeRDExporterFromA1Factory().setZUGFeRDVersion(2).setZUGFeRDConformanceLevel(ZUGFeRDConformanceLevel.EN16931).load(SOURCE_PDF)
Returns a ZUGFeRDExporter in Factur-X mode
ZUGFeRDImporter.getXMP()
Returns the XMP metadata of the according PDF
ZUGFeRDExporter.setTest()
sets a attribute in the XML structure used to identify test invoices.
ZUGFeRDExporter.ignoreA1Errors()
skips the check of the input file whether it’s valid PDF/A-1
ZUGFeRDMigrator().migrateFromV1ToV2(zugferdInvoice)
A first attempt to migrate from ZF1 to ZF2 can be done with something like String facturx=new
ZUGFeRDMigrator().migrateFromV1ToV2(zugferdInvoice);
ZUGFeRDImpoter().getMeta()
Extract raw XML from PDF file.
ZUGFeRDImpoter().getUTF8()
Extracts XML from PDF file but also removes a potential UTF8 BOM

Commandline

After downloading Mustang, besides integrating into your software you can also use it on the command line:

Extract XML

➜ target git:(master) ✗ java -jar mustang-1.7.4.jar -e

Source PDF (default: invoice.pdf):
Ouput PDF (default: invoice.ZUGFeRD.pdf):
Written to invoice.ZUGFeRD.pdf
        

Migrating ZUGFeRD 1 to 2

java -jar mustang-1.7.4.jar -u
ZUGFeRD 1.0 XML
        source (default: ZUGFeRD-invoice.xml):
        ZUGFeRD 2.0 XML target (default: factur-x.xml):
    Written to factur-x.xml

To migrate XML from ZF1 to Version 2 Mustangproject internally uses a self written (read: incomplete) XSLT-Transformation. Corrections and amendments are much appreciated.

Combining PDF and XML

In this case the source PDF has to be a PDF/A-1 like our “blanko

java -jar mustang-1.7.4.jar -c
Source PDF (default: invoice.pdf):
ZUGFeRD XML (default: ZUGFeRD-invoice.xml):
Ouput PDF (default: invoice.ZUGFeRD.pdf):
ZUGFeRD version (1 or 2) (default: 1):
ZUGFeRD profile b)asic, c)omfort or e)xtended (default: e):
Written to invoice.ZUGFeRD.pdf

Convert PDF/A-1 to PDF/A-3

java -jar mustang-1.7.4.jar -a

Migrates a PDF/A-1 into a PDF/A-3 file without adding ZUGFeRD metadata hinzuzufügen. The same contents will be labelled the new version (which is valid because the format is backward-compatible), additional features like the PDF/A-2 JPG2000-compression of images will not be used.

Metrics

The original purpose for the command line tool was finding out, how many ZUGFeRD invoices already existed in a directory structure of PDFs.
Use

java -jar mustang-1.7.4.jar -d <Directory name>

E.g.

java -jar mustang-1.7.4.jar -d /tmp/XMLExamples
.................................................................................................................................................
===================================================================
Files: 145 Dirs: 15 PDF: 18 ZUGFeRD: 14

Each dot is a checked file.
The software works recursively, in this example 18 PDF files were found in 15 directories. 14 of those 18 files were identified as ZUGFeRD 1 or 2.Usually only files ending in .pdf, .PDF etc. are checked. By additionally specifying -i it is possible to check independently from file name extension.

java -jar mustang-1.7.4.jar -l

With -l it is possible to pass a list of files to be checked for ZUGFeRDIty via standard input. A empty last line starts the check.
Example:

java -jar mustang-1.7.4.jar -l
./MustangGnuaccountingBeispielRE-20170509_505PDFA3.pdf
./MustangGnuaccountingBeispielRE-20171118_506blanko.pdf
./MustangGnuaccountingBeispielRE-20170509_505PDF14.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Sachversicherung_berechneter_Steuersatz.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Einfach.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Rechnungskorrektur.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_EXTENDED_Warenrechnung.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Kraftfahrversicherung_Bruttopreise.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_SEPA_Prenotification.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_EXTENDED_Kostenrechnung.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_EXTENDED_Rechnungskorrektur.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_BASIC_Rechnungskorrektur.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_BASIC_Einfach.pdf
./ZUGFerdExamples/xslt/factur-x.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Haftpflichtversicherung_Versicherungssteuer.pdf
./ZUGFerdExamples/ZUGFeRD_1p0_COMFORT_Rabatte.pdf
./MustangGnuaccountingBeispielRE-20170509_505.pdf
./MustangGnuaccountingBeispielRE-20170509_505blanko.pdf
./zf2test.pdf
...................
===================================================================
Files: 19 Dirs: 0 PDF: 19 ZUGFeRD: 15

Verbose parameters

If you want to use mustang non-interactively, e.g. in a script, you may, depending on the operation, use the following parameters to specify details:

–source <filename>
set input PDF file
–source-xml <filename>
set input XML file
–out <filename>
set output PDF file
–format <fx|zf>
enable factur-x or ZUGFeRD
–version <1|2>
set ZUGFeRD version
–profile <…>
set ZUGFeRD profile

  • For ZUGFeRD v1: <b>ASIC, <c>OMFORT or <e>XTENDED
  • For ZUGFeRD v2: <m>INIMUM, BASIC <w>L, <b>ASIC, <c>IUS, <e>N16931, E<x>TENDED