Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 49 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Gallop 🐎 XRechnung Library
# Gallop 🐎 E-Invoice Library

[![Tests](https://github.com/codebarista-de/gallop/actions/workflows/test.yml/badge.svg)](https://github.com/codebarista-de/gallop/actions/workflows/test.yml)

Gallop is a Java library for creating electronic invoices (E-Invoices) compliant to
the [XRechnung standard](https://xeinkauf.de/dokumente/).
Gallop is a Java library for creating electronic invoices (E-Invoices) in the Cross Industry Invoice (CII) syntax:
[XRechnung](https://xeinkauf.de/dokumente/), ZUGFeRD and Factur-X.

Specification: Standard XRechnung
Version: XRechnung 3.0.2
Expand All @@ -13,27 +13,49 @@ It does not yet implement the whole specification. Contributions are welcome!

## Why another e-invoice library?

Despite the existence of several mature Java libraries for electronic invoice creation,
none provided what we needed: a permissive license combined with complete control over the output.
Gallop was built to fill this gap.
Despite the existence of several mature Java libraries for electronic invoice creation, none provided what we needed: a
permissive license combined with complete control over the output. Gallop was built to fill this gap.

### Gallop does not impose

Gallop does not manipulate your data. It writes the exact values you provide into the XRechnung XML,
with no calculations or transformations (aside from necessary XML escaping).
Gallop does not manipulate your data. It writes the exact values you provide into the XML, with no
calculations or transformations (aside from necessary XML escaping).

This preservation of your original values eliminates rounding discrepancies between source data and the final invoice.
This is ideal, when creating an e-invoice that must match an existing PDF invoice.

### Gallop does not judge

Gallop does not validate the e-invoices it generates.
It will happily accept any input and do its best to create a valid e-invoice,
but will not notice or complain when the result does not meet all the rules specified in the XRechnung standard.
Gallop does not validate the e-invoices it generates. It will happily accept any input and do its best to create a valid
e-invoice, but will not notice or complain when the result does not meet all the rules specified in the
choosen e-invoice format.

There are other tools like the [KOSIT Validator](https://github.com/itplr-kosit/validator)
which verify that the generated XML is a valid X-Rechnung.

## Supported formats

`CIIXMLEInvoiceWriter` writes XRechnung 3.0 as well as the EN16931 ("COMFORT") conformance level of ZUGFeRD (Germany)
and Factur-X (France). Which one you get is decided by the `EInvoiceProfile` you pass:

```java
byte[] xml = CIIXMLEInvoiceWriter.generateXML(invoice, EInvoiceProfile.EN16931_CII);
```

XRechnung, ZUGFeRD and Factur-X all share the same Cross Industry Invoice (CII) syntax and the same EN16931 semantic
data model, so the same `Invoice` object works for all three. Only the document context identifiers differ, and Gallop
takes care of that based on the `EInvoiceProfile`.

ZUGFeRD and Factur-X aligned their specifications at the EN16931 level, so a single profile,
`EInvoiceProfile.EN16931_CII`, covers both. The XML Gallop writes is the same document.
The constants `ZUGFERD_EN16931` and `FACTURX_EN16931` are aliases of `EN16931_CII`.

Note that ZUGFeRD and Factur-X are hybrid formats combining a PDF/A-3 document with embedded XML; Gallop only produces
the XML part, embedding it into a PDF/A-3 document is up to you.

Also note that unit codes conventionally differ by format: XRechnung examples use `XPP` for "piece",
while ZUGFeRD examples use `H87` (see `UnitCode.java`). Pick the unit code your target format/validator expects.

## Usage

Add Gallop to your project via [Maven Central](https://central.sonatype.com/artifact/de.codebarista/gallop):
Expand All @@ -42,7 +64,7 @@ Add Gallop to your project via [Maven Central](https://central.sonatype.com/arti

```groovy
dependencies {
implementation 'de.codebarista:gallop:2.2.0'
implementation 'de.codebarista:gallop:3.0.0'
}
```

Expand All @@ -53,7 +75,7 @@ dependencies {
<dependency>
<groupId>de.codebarista</groupId>
<artifactId>gallop</artifactId>
<version>2.2.0</version>
<version>3.0.0</version>
</dependency>
```

Expand All @@ -64,7 +86,7 @@ You find this code in the `BuildInvoiceTest` class.
```java
public class InvoiceGenerator {

public String generateInvoice() throws XRechnungWriterException {
public String generateInvoice() throws EInvoiceWriterException {
Invoice invoice = Invoice.create()
.documentTypeCode(InvoiceType.COMMERCIAL_INVOICE.getValue()) // Define invoice type
.documentId("INV-2025-1001") // Unique invoice identifier
Expand Down Expand Up @@ -161,20 +183,27 @@ public class InvoiceGenerator {
.salesOrderReference("SO-98765");

// Generate the XRechnung XML from the invoice
byte[] xRechnungXML = XRechnungWriter.generateXRechnungXML(invoice);
byte[] xRechnungXML = CIIXMLEInvoiceWriter.generateXML(invoice, EInvoiceProfile.XRECHNUNG);
return new String(xRechnungXML);
}
}
```

### Changelog

- 3.0.0: Add ZUGFeRD and Factur-X (both covered by the single shared profile `EN16931_CII`) support alongside
XRechnung via the new `EInvoiceProfile` parameter of `CIIXMLEInvoiceWriter`. **Breaking:** the packages were
reorganized: the model classes moved from
`de.codebarista.gallop.xrechnung.model` to `de.codebarista.gallop.model`, and
`XRechnungWriterException` became `de.codebarista.gallop.EInvoiceWriterException`;
`XRechnungUtils` moved to `de.codebarista.gallop.GallopUtils` and
`XmlDocumentBuilder` to `de.codebarista.gallop.XmlDocumentBuilder`.
`de.codebarista.gallop.xrechnung.XRechnungWriter` keeps its package and its API, and now delegates to
`CIIXMLEInvoiceWriter`.
- 2.2.0: Add BT-114 (Rounding amount)
- 2.1.0: Add BT-30/BT-47 (Seller/Buyer legal registration identifier),
BT-32 (Seller tax registration identifier),
BT-33 (Seller additional legal information),
BT-113 (Paid amount)
and `NetAmount#getVatCategory`
- 2.1.0: Add BT-30/BT-47 (Seller/Buyer legal registration identifier), BT-32 (Seller tax registration identifier), BT-33
(Seller additional legal information), BT-113 (Paid amount)
and `NetAmount#getVatCategory`
- 2.0.0: Gallop no longer relies on lombok, introduce fluent api
- 1.0.1: Add action to publish to maven central
- 1.0.0: Initial version
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = 'de.codebarista'
version = '2.2.0'
version = '3.0.0'

java {
toolchain {
Expand Down Expand Up @@ -40,7 +40,7 @@ jacocoTestReport {
mavenPublishing {
pom {
name.set("Gallop")
description.set("Java library for creating electronic invoices compliant to the XRechnung standard")
description.set("Java library for creating XRechnung, ZUGFeRD and Factur-X electronic invoices")
url.set("https://github.com/codebarista-de/gallop")

licenses {
Expand Down
112 changes: 112 additions & 0 deletions src/main/java/de/codebarista/gallop/EInvoiceProfile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package de.codebarista.gallop;

import java.util.Objects;

/**
* Formats of e-invoices.
* <p>
* The constants defined here cover the formats Gallop ships with; you can supply profiles Gallop does not define yet.
*/
public class EInvoiceProfile {
/**
* XRechnung 3.0.
*/
public static final EInvoiceProfile XRECHNUNG = new EInvoiceProfile(
"XRECHNUNG",
"urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0",
"urn:fdc:peppol.eu:2017:poacc:billing:01:1.0");

/**
* EN16931 conformance level of the CII syntax, as used by both ZUGFeRD 2.x and
* Factur-X 1.0.7x (where the level is called "COMFORT").
* <p>
* FeRD/AWV and FNFE-MPE aligned the two specifications at this level, so they share the same
* guideline URN and neither requires a business-process URN: at this level the XML Gallop
* writes is the same document for both formats. {@link #ZUGFERD_EN16931} and
* {@link #FACTURX_EN16931} are aliases of this profile.
* <p>
* Note that ZUGFeRD and Factur-X are hybrid formats combining a PDF/A-3 document with embedded XML;
* Gallop only produces the XML part. The two differ in how that XML is embedded.
*/
public static final EInvoiceProfile EN16931_CII = new EInvoiceProfile(
"EN16931_CII",
"urn:cen.eu:en16931:2017",
null);

/**
* ZUGFeRD 2.x, EN16931 conformance level.
* <p>
* An alias of {@link #EN16931_CII}: at this conformance level ZUGFeRD and Factur-X produce the same XML.
*/
public static final EInvoiceProfile ZUGFERD_EN16931 = EN16931_CII;

/**
* Factur-X 1.0.7x, EN16931 ("COMFORT") conformance level.
* <p>
* An alias of {@link #EN16931_CII}: at this conformance level Factur-X and ZUGFeRD produce the same XML.
*/
public static final EInvoiceProfile FACTURX_EN16931 = EN16931_CII;

private final String name;
private final String guidelineUrn;
private final String businessProcessUrn;

/**
* Defines a profile. Use the constants of this class for the formats Gallop already covers.
*
* @param name the name of the profile, used for {@link #toString()}
* @param guidelineUrn the URN written as {@code GuidelineSpecifiedDocumentContextParameter},
* identifying the specification the invoice claims to follow
* @param businessProcessUrn the URN written as {@code BusinessProcessSpecifiedDocumentContextParameter},
* or {@code null} if the format does not require one
*/
public EInvoiceProfile(String name, String guidelineUrn, String businessProcessUrn) {
this.name = Objects.requireNonNull(name);
this.guidelineUrn = Objects.requireNonNull(guidelineUrn);
this.businessProcessUrn = businessProcessUrn;
}

/**
* @return the name of this profile, e.g. {@code "EN16931_CII"}, never {@code null}
*/
public String getName() {
return name;
}

/**
* @return the URN for the {@code GuidelineSpecifiedDocumentContextParameter}, never {@code null}
*/
public String getGuidelineUrn() {
return guidelineUrn;
}

/**
* @return the URN for the {@code BusinessProcessSpecifiedDocumentContextParameter},
* or {@code null} if this profile does not declare one
*/
public String getBusinessProcessUrn() {
return businessProcessUrn;
}

@Override
public String toString() {
return name;
}

@Override
public final boolean equals(Object o) {
if (!(o instanceof EInvoiceProfile that)) return false;

return name.equals(that.name)
&& guidelineUrn.equals(that.guidelineUrn)
&& Objects.equals(businessProcessUrn, that.businessProcessUrn);
}

@Override
public final int hashCode() {
int result = name.hashCode();
result = 31 * result + guidelineUrn.hashCode();
result = 31 * result + Objects.hashCode(businessProcessUrn);
return result;
}
}
10 changes: 10 additions & 0 deletions src/main/java/de/codebarista/gallop/EInvoiceWriterException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.codebarista.gallop;

/**
* Exception class for all errors that occur when writing an e-invoice.
*/
public class EInvoiceWriterException extends RuntimeException {
public EInvoiceWriterException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package de.codebarista.gallop.xrechnung;
package de.codebarista.gallop;

/**
* Utility class for handling XRechnung-related operations.
* Internal helper methods shared across Gallop.
*/
public final class XRechnungUtils {
private XRechnungUtils() {
public final class GallopUtils {
private GallopUtils() {
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.codebarista.gallop.xrechnung;
package de.codebarista.gallop;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand Down
Loading
Loading