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.

Architecture

You can use Mustang from the command line application, or in your java software by referring (via Maven or Gradle) to or downloading the according jars.
You can combine, extract and/or validate PDF+XML. On the command line, to write Factur-X,
you have to provide a PDF/A-1 file and XML, when using in your software and writing Factur-X you (have to provide at least a PDF/A-file and you)
can provide XML on your own as well, or use the invoice classes which use the invoice interfaces, or use the
invoice interfaces directly. Parsing Factur-X is currently only available in “library mode” where you can use the
invoice class reader,
or the basic ZUGFeRDImporter.
Using a different artifact you can also embed the validator in your software.

In the commandline tool you can also get some statistics on directories with Factur-X files,
upgrade ZUGFeRD 1 to 2, migrate from PDF/A-1 to PDF/A-3,
or visualize xml which are details omitted from this diagram:
Mustang architectural diagram

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


<dependencies>
    <dependency>
        <groupId>org.mustangproject</groupId>
        <artifactId>validator</artifactId>
        <version>2.15.0</version>
        <classifier>shaded</classifier>
    </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.

With Gradle

Create a new gradle project for java. In the groovy DSL you can refer to the according gradle dependencies in the mavenCentral() repository like this:


    implementation 'org.mustangproject:validator:2.12.0:shaded'
 

The complete build.gradle could look like


 plugins {
    id 'groovy'
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {

    implementation 'org.mustangproject:validator:2.15.0:shaded'

    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()
}
 

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("MustangGnuaccountingBeispielRE-20190610_507.pdf"); and
  4. add the imports by pressing CTRL+SHIFT+O (Windows) or CMD+SHIFT+O (Mac).
  5. you should now be able to access the getter-Methods like getAmount()
  6. 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
  7. Complete example
    
    ZUGFeRDImporter zu=new ZUGFeRDImporter("MustangGnuaccountingBeispielRE-20190610_507.pdf"); 
    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


 try {
            System.out.println("Converting to PDF/A-3u");
            ZUGFeRDExporterFromA1 ze=new ZUGFeRDExporterFromA1().load("./MustangGnuaccountingBeispielRE-20190610_507blanko.pdf");

            /*
             * Add .setZUGFeRDVersion and .setZUGFeRDConformanceLevel
             * in the next lines to set the ZUGFeRD version respective profile of the
             * XML you are inserting.
             */
            ;
            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 = "";
            ze.setXML(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


java -jar Mustang-CLI-2.15.0.jar --action validate --source yourFile.xml

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.

Project setup without Maven

  1. Download
    1. Mustang
      1. the JAR file https://www.mustangproject.org/deploy/library-2.15.0-shaded.jar (with or without dependencies) or the oder full download including validator with or without dependencies
      2. the notice file https://www.mustangproject.org/deploy/NOTICE
  2. In your IDE: Add the downloaded JAR files to your project (right click on project name, Properties) and add http://mustangproject.org/deploy/library-2.15.0-shaded.jar as „external Jar“ to the „Build Path“ in the „libraries“ tab.

Write ZUGFeRD PDFs

You can implement own invoices without the need to write XML yourself using ordinary fluent style setter classes or using interfaces.

Order-X

You can create Order-X using the IExportableTransaction-Interface or, accordingly, the invoice-class. To import you can use ZUGFeRDImporter as well as ZUGFeRDInvoiceImporter. For the export OXExporterFromA1 (respectively OXExporterFromA3) is used instead of ZUGFeRDExporterFromA1/A3. OXExporter… internallly uses OXPullProvider. The Order-X-version (1) is still set with setZUGFeRDVersion. If you choose a different Order-X-profile you can obtain the according object if you explicitly mention the Order-X-standard:


import org.mustangproject.EStandard;
import org.mustangproject.ZUGFeRD.Profiles;
...
zfConformanceLevelProfile = Profiles.getByName(EStandard.orderx, "EXTENDED", 1);

Merging PDF and XML accordingly works like this:


import org.mustangproject.ZUGFeRD.OXExporterFromA1;
import org.mustangproject.*;
...

OXExporterFromA1 oe = new OXExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name")).setZUGFeRDVersion(1).ignorePDFAErrors()
.load("blanko.pdf"));
oe.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addVATID("DE0815")).setOwnTaxID("4711").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE").addVATID("DE0816").setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562"))).setNumber(number)
					.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)));
oe.export("order-x.pdf");

XRechnung XML

The ZUGFeRD2PullProvider can be used directly (zf2p.generateXML(i); and zf2p.getXML()) to get a UN/CEFACT CII XRechnung, provided you set the XRechnung profile, provide a sender contact and a leitwegs-ID.
A file like this can be created using this:


		Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
				.setSender(new TradeParty("Test company","teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
				.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
				.setReferenceNumber("991-01484-64")//leitweg-id
				.setNumber("123").addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), /*price*/ new BigDecimal("1.0"),  /*qty*/ new BigDecimal("1.0")));

		ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
		zf2p.setProfile(Profiles.getByName("XRechnung"));
		zf2p.generateXML(i);
		String theXML = new String(zf2p.getXML());
			try {
			BufferedWriter writer = new BufferedWriter(new FileWriter("xrechnung.xml"));
			writer.write(theXML);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

Additionally you can add files to be base64-coded into the XML:


		FileAttachment fe1=new FileAttachment("one.pdf", "application/pdf", "Alternative", b);
		FileAttachment fe2=new FileAttachment("two.pdf", "application/pdf", "Alternative", b);
		Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
				.setSender(new TradeParty(orgname,"teststr","55232","teststadt","DE").addTaxID("DE4711").addVATID("DE0815").setContact(new Contact("Hans Test","+49123456789","test@example.org")).addBankDetails(new BankDetails("DE12500105170648489890","COBADEFXXX")))
				.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
				.setReferenceNumber("991-01484-64")//leitweg-id
				// not using any VAT, this is also a test of zero-rated goods:
				.setNumber(number).addItem(new Item(new Product("Testprodukt", "", "C62", BigDecimal.ZERO), amount, new BigDecimal(1.0)))
				.embedFileInXML(fe1).embedFileInXML(fe2);

Supplementary functions

Apart from disabling the inbound PDF/A check or get the raw XML from an importer you can e.g. convert ZUGFeRD 1 into Version 2, Factur-X/ZUGFeRD 2 into HTML or Factur-X/ZUGFeRD 2 into UBL.

Experimentally: Convert ZUGFeRD v1 XML to Fatur-X/ZUGFeRD 2

import org.mustangproject.ZUGFeRD.XMLUpgrader;
...
		XMLUpgrader zmi = new XMLUpgrader();
		String xml = zmi.migrateFromV1ToV2("ZUGFeRD-invoice.xml");
		Files.write(Paths.get("factur-x.xml"), xml.getBytes());
		System.out.println("Written to " + outName);

Internally the (incomplete) XSLT-Transformation is used.

Visualisation: Convert Factur-X/ZUGFeRD 2 into HTML

import org.mustangproject.ZUGFeRD.ZUGFeRDVisualizer;

	/**
	 * Export a resource embedded into a Jar file to the local file path.
	 *
	 * @param resourceName ie.: "/SmartLibrary.dll"
	 * @return The path to the exported resource
	 * @throws Exception e.g. if the specified resource does not exist at the specified location
	 */
	static public String ExportResource(String resourceName) throws Exception {
		InputStream stream = null;
		OutputStream resStreamOut = null;
		String jarFolder;
		try {
			stream = Main.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
			if(stream == null) {
				throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
			}

			int readBytes;
			byte[] buffer = new byte[4096];
			jarFolder = System.getProperty("user.dir");
			resStreamOut = new FileOutputStream(jarFolder + resourceName);
			while ((readBytes = stream.read(buffer)) > 0) {
				resStreamOut.write(buffer, 0, readBytes);
			}
		} catch (Exception ex) {
			throw ex;
		} finally {
			stream.close();
			resStreamOut.close();
		}

		return jarFolder + resourceName;
	}

...
		ZUGFeRDVisualizer zvi = new ZUGFeRDVisualizer();
		String xml = null;
		try {
			xml = zvi.visualize(sourceName);
			Files.write(Paths.get("out.html"), xml.getBytes());
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Written to out.html");

		try {
			ExportResource("/xrechnung-viewer.css");
			ExportResource("/xrechnung-viewer.js");

			System.out.println("xrechnung-viewer.css and xrechnung-viewer.js written as well (to local working dir)");
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		}
Convert Factur-X/ZUGFeRD 2 CII into UBL

import org.mustangproject.CII.CIIToUBL;
...
		CIIToUBL c2u = new CIIToUBL();
		c2u.convert(new File("factur-x.xml"), new File("ubl.xml"));

Sets a attribute in the XML structure used to identify test invoices
ZUGFeRDExporter.setTest()
skips the check of the input file whether it’s valid PDF/A-1
ZUGFeRDExporter.ignoreA1Errors()
Returns PDF Metadata
ZUGFeRDImpoter().getMeta()
Extracts XML from PDF file but also removes a potential UTF8 BOM
ZUGFeRDImpoter().getUTF8()

Embedding the validator

If you use the validator as dependency in your pom.xml


<dependency>
  <groupId>org.mustangproject</groupId>
  <artifactId>validator</artifactId>
  <classifier>shaded</classifier>
  <version>2.15.0</version>
</dependency>

you can still use all Mustang library functions but additionally invoke the validator as follows:


ZUGFeRDValidator zfv=new ZUGFeRDValidator();
System.out.println(zfv.validate("factur-x.pdf"));