Merge branch 'master' into issue-830

This commit is contained in:
jayddaniels
2025-06-25 12:46:59 +02:00
committed by GitHub
43 changed files with 3973 additions and 2939 deletions

View File

@@ -1,8 +1,20 @@
#820/801/815
#822
#843
add getCalculation on calculatedInvoice
Transactioncalculator getTaxDetails to include correct percentage, calculated amounts
- support parsing of BT-90 CreditorReferenceID
- #870
- #871 sec
- #868
2.17.0
=======
2025-06-11
- Breaking change: #764 Item allowances not to be multiplied by quantity
(there are now product level quantities for that)
- #820/801/815 Fix invoiceImporter: empty NodeList, empty Strings
- #822 Wrapping Message Text in Validation Result PDF
- #843 Re-Initialize the HTML-template on language change
- #854 allow multiple item charges/allowances when visualizing to html
- #786 Fix Invoices with Category Code "O" #786
- add getCalculation on calculatedInvoice
- Transactioncalculator getTaxDetails to include correct percentage, calculated amounts
2.16.5
=======

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
@@ -12,7 +12,7 @@
should also work for XRechnung/CII.
</name>
<packaging>jar</packaging>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
@@ -23,7 +23,7 @@
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
<!-- prototypes of new mustangproject versions can be installed by referring to them and installed to the local repo from a jar file with
mvn install:install-file -Dfile=mustang-1.5.4-SNAPSHOT.jar -DgroupId=org.mustangproject.ZUGFeRD -DartifactId=mustang -Dversion=1.5.4 -Dpackaging=jar -DgeneratePom=true
-->

View File

@@ -34,7 +34,7 @@ If you set up a Maven project, you can reference the mustang artifact like this:
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<version>2.15.0</version>
<version>2.17.0</version>
<classifier>shaded</classifier>
</dependency>
```

View File

@@ -1,277 +0,0 @@
package mustangtest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import org.mustangproject.ZUGFeRD.IExportableTransaction;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableItem;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableProduct;
import org.mustangproject.ZUGFeRD.IZUGFeRDExportableTradeParty;
import org.mustangproject.ZUGFeRD.IZUGFeRDExporter;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromA1;
class Contact implements IZUGFeRDExportableTradeParty {
public String getCountry() {
return "DE";
}
public String getLocation() {
return "Spielkreis";
}
public String getName() {
return "Theodor Est";
}
public String getStreet() {
return "Bahnstr. 42";
}
public String getVATID() {
return "DE999999999";
}
public String getZIP() {
return "88802";
}
}
class Item implements IZUGFeRDExportableItem {
public Item(BigDecimal price, BigDecimal quantity, Product product) {
super();
this.price = price;
this.quantity = quantity;
this.product = product;
}
private BigDecimal price, quantity;
private Product product;
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getQuantity() {
return quantity;
}
public void setQuantity(BigDecimal quantity) {
this.quantity = quantity;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public IZUGFeRDAllowanceCharge[] getItemAllowances() {
// TODO Auto-generated method stub
return null;
}
public IZUGFeRDAllowanceCharge[] getItemCharges() {
// TODO Auto-generated method stub
return null;
}
}
class Product implements IZUGFeRDExportableProduct {
public Product(String description, String name, String unit, BigDecimal vATPercent) {
super();
this.description = description;
this.name = name;
this.unit = unit;
VATPercent = vATPercent;
}
private String description, name, unit;
private BigDecimal VATPercent;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public BigDecimal getVATPercent() {
return VATPercent;
}
public void setVATPercent(BigDecimal vATPercent) {
VATPercent = vATPercent;
}
}
public class MustangWriter implements IExportableTransaction {
private void apply() {
try {
System.out.println("Reading Blanko-PDF");
IZUGFeRDExporter ze = new ZUGFeRDExporterFromA1().setProducer("My Application")
.setCreator(System.getProperty("user.name"))
.load("./MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
System.out.println("Generating and attaching ZUGFeRD-Data");
ze.setTransaction(this);
System.out.println("Writing ZUGFeRD-PDF");
ze.export("./MustangGnuaccountingBeispielRE-20170509_505new.pdf");
System.out.println("Done.");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MustangWriter mw=new MustangWriter();
mw.apply();
}
public String getCurrency() {
return "EUR";
}
public Date getDeliveryDate() {
return new GregorianCalendar(2017, Calendar.NOVEMBER, 17).getTime();
}
public Date getDueDate() {
return new GregorianCalendar(2017, Calendar.DECEMBER, 9).getTime();
}
public Date getIssueDate() {
return new GregorianCalendar(2017, Calendar.NOVEMBER, 18).getTime();
}
public String getNumber() {
// TODO Auto-generated method stub
return "RE-20171118/506";
}
public String getOwnBIC() {
// TODO Auto-generated method stub
return "COBADEFFXXX";
}
public String getOwnBankName() {
// TODO Auto-generated method stub
return "Commerzbank";
}
public String getOwnCountry() {
// TODO Auto-generated method stub
return "DE";
}
public String getOwnIBAN() {
// TODO Auto-generated method stub
return "DE88 2008 0000 0970 3757 00";
}
public String getOwnLocation() {
// TODO Auto-generated method stub
return "Stadthausen";
}
public String getOwnOrganisationFullPlaintextInfo() {
// TODO Auto-generated method stub
return "Bei Spiel GmbH\n" + "Ecke 12\n" + "12345 Stadthausen\n" + "Geschäftsführer: Max Mustermann";
}
public String getOwnOrganisationName() {
// TODO Auto-generated method stub
return "Bei Spiel GmbH";
}
public String getOwnPaymentInfoText() {
// TODO Auto-generated method stub
return null;
}
public String getOwnStreet() {
return "Ecke 12";
}
public String getOwnTaxID() {
return "22/815/0815/4";
}
public String getOwnVATID() {
return "DE136695976";
}
public String getOwnZIP() {
return "12345";
}
public String getPaymentTermDescription() {
// TODO Auto-generated method stub
return null;
}
public IZUGFeRDExportableTradeParty getRecipient() {
return new Contact();
}
public String getReferenceNumber() {
// TODO Auto-generated method stub
return null;
}
public IZUGFeRDAllowanceCharge[] getZFAllowances() {
// TODO Auto-generated method stub
return null;
}
public IZUGFeRDAllowanceCharge[] getZFCharges() {
// TODO Auto-generated method stub
return null;
}
public IZUGFeRDExportableItem[] getZFItems() {
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;
}
public IZUGFeRDAllowanceCharge[] getZFLogisticsServiceCharges() {
// TODO Auto-generated method stub
return null;
}
}

446
doc/ZugferdDev.de.adoc Normal file
View File

@@ -0,0 +1,446 @@
== [#anchor]####Mustang Project Nutzerdokumentation
Jochen Stärk
zu Mustangproject 1.5.1, 13.01.2018
http://www.mustangproject.org/[http://www.mustangproject.org]
== [#anchor-1]####Über Mustangproject
Mustangproject ist eine Java-Bibliothek zur Unterstützung von
erweiterten („ZUGFeRD“-)Metadaten in PDF-Rechnungsdateien. Sie verwendet
als Eingabe PDF/A-Dateien, benötigt die Apache PDFBox-Bibliothek und
unterliegt wie diese der APL-Lizenz. Sie ist daher, entsprechend den
Richtlinien der Apache Public License, unter Einbettung einer
entsprechenden „Notice“-Datei kostenlos einsetzbar in kommerziellen und
nichtkommerziellen Projekten.
== [#anchor-2]####Übersicht von ZUGFeRD-Lösungen
[cols=",,,,,,,,,,,",]
|===
| |Plattform |Lizenz |ZF Versionen |Funktionsumfang |Geeignet für |Preis
| | | | |
|Lesen |XML erzeugen |PDF Schreiben |PDF/A-Umwandlung |Kommerz. Software
|Freeware |Open Source | | | | |
|intarsys |Java |proprietär |1 |Ja |Ja |Ja |Ja |Ja |Ja |Nein |a.A.
|Konik |Java |AGPL |1 |Ja |Ja |Ja |Nein |Nein |Nein |Ja |0 €
|Mustang |Java |APL |1 |Ja |Ja |Ja |Nein |Ja |Ja |Ja |0 €
|https://github.com/akretion/factur-x |Python |BSD |1,2 |Nein |Nein |Ja
|Nein |Ja |Ja |Ja |0€
|https://github.com/stephanstapel/ZUGFeRD-csharp |C# |APL |1 |Ja |Ja
|Nein |Nein |Ja |Ja |Ja |0 €
|===
== [#anchor-3]####Download/Projekteinrichtung
=== [#anchor-4]####Source code
Heimat der Mustangprojekt-Quelltexte ist
https://github.com/ZUGFeRD/mustangproject/
=== [#anchor-5]####Projekteinrichtung ohne Maven
Mit installiertem OpenOffice.org oder LibreOffice und Eclipse for Java.
[arabic]
. Starten Sie Eclipse, Neues Java-Eclipse-Projekt erstellen,
beispielsweise „MustangSample“.
. Wechseln Sie in der Shell in den erstellten Ordner.
. Download von
[arabic]
.. Mustang
[arabic]
... Downloaden Sie
http://mustangproject.org/deploy/mustang-1.5.1.jarfootnote:[Falls Sie
ohnehin PDFBox verwenden (pdfbox, fontbox, preflight, xmpbox sowie deren
Abhängigkeiten apache-commons-io und apache-commons-logging) können Sie
auch die wesentlich kleinere
http://mustangproject.org/deploy/original-mustang-1.5.1.jar verwenden]
... Downloaden Sie http://mustangproject.org/deploy/NOTICE
.. Laden Sie
[arabic]
... Direkt eine PDF-A/1-Musterrechnung noch ohne
ZUGFeRD-Mhttp://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-2017]etadaten
von
http://www.mustangproject.org/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[1118]http://www.mustangproject.org/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[_50]http://www.mustangproject.org/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[6]http://www.mustangproject.org/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[blanko.pdf]
herunter.
... oder erzeugen sich alternativ
[arabic]
.... ein eigenes Beispiel-PDF durch Download der Quelldatei im
OpenOffice.org-Format
http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506.odt[http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-]http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506.odt[20171118_506.odt]
und
[arabic]
..... Öffnen Sie die Datei in OpenOffice.org
..... Datei|Exportieren als PDF: Wichtig ist hier, dass Sie die Checkbox
PDF/A-1a setzen
..... Speichern Sie die PDF-Datei beispielsweise als
„http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[MustangGnuaccountingBeispiel]http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506blanko.pdf[RE-20171118_506blanko.pdf]“
im MustangSample-Ordner
. Wechseln Sie zurück zu Eclipse. Fügen Sie durch Rechtsklick auf das
Projekt („Eigenschaften“) die heruntergeladene JAR-Dateien
Projekteigenschaften als „externe Jars“ zum „Java Build Path“ Reiter
„Bibliotheken“ hinzu.
=== [#anchor-6]####Mit Maven
Mit folgendem Repository
<repositories>
<repository>
<id>mustang-mvn-repo</id>
<url>https://raw.github.com/ZUGFeRD/mustangproject/mvn-repo/</url>
</repository>
</repositories>
und folgender Dependency
<dependency>
<groupId>org.mustangproject.ZUGFeRD</groupId>
<artifactId>mustang</artifactId>
<version>1.5.1</version>
</dependency>
Bei der Gelegenheit kann man auch gleich Apache Commons Logging und JAXB
importieren:
<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>
== [#anchor-7]####Lesen von ZUGFeRD-Daten
[arabic]
. Laden Sie sich eine PDF-Datei mit ZUGFeRD-Daten herunter,
beispielsweise
http://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20171118_506.pdf
. Erstellen Sie eine neue Java-Klasse unterhalb von src, beispielsweise
MustangReader. inklusive „Public static void main()“
. Geben Sie innerhalb von Main „ZUGFeRDImporter zi=**new**
ZUGFeRDImporter();“ ein und lassen Sie den Import durch STRG+SHIFT+O
ergänzen
. verwenden Sie zi.extract(PDF-Dateiname) und ggf. canParse() um
festzustellen ob es sich um ZUGFeRD-Daten handelt.
. Nach zi.parse() haben Sie Zugriff auf die getter wie getAmount()
. Welche Daten enthalten sind, können Sie der XML-Datei entnehmen die im
ZUGFeRD-Beispiel-PDF eingebettet ist
=== [#anchor-8]####Komplettes Lesebeispiel
[arabic, start=7]
.
*package* sample;
*import* org.mustangproject.ZUGFeRD.ZUGFeRDImporter;
*public* *class* Read \{
*public* *static* *void* main(String[] args) \{
ZUGFeRDImporter zi=**new** ZUGFeRDImporter();
zi.extract("./MustangGnuaccountingBeispielRE-20171118_506.pdf");
System._out_.println("Lese ZUGFeRD");
*if* (zi.canParse()) \{
zi.parse();
System._out_.println("Fälliger Betrag:"+zi.getAmount());
System._out_.println("BIC:"+zi.getBIC());
System._out_.println("IBAN:"+zi.getIBAN());
System._out_.println("Kontoinhaber:"+zi.getHolder());
System._out_.println("Rechnungsnr:"+zi.getForeignReference());
}
}
}
== [#anchor-9]####Schreiben einer ZUGFeRD-PDF-Datei
Ein Beispielprogramm zum Schreiben ist deshalb umfangreicher, weil
erstens mehr Daten in einer differenzierteren Struktur geschrieben
werden als derzeit beim Lesen benötigt und zweitens dem Exporter die
Daten per Interface zur Verfügung gestellt werden müssen.
[arabic]
. Erstellen Sie eine neue Klasse unterhalb von src, beispielsweise
MustangWriter inklusive des obligatorischen „Public static void main()“
.
. Ändern Sie __*public* *class* MustangWriter __in__ *public* *class*
MustangWriter *implements* IZUGFeRDExportableTransaction__
. Fügen Sie innerhalb der Klasse MustangWriter folgende Klassen hinzu
[arabic]
.. *class* _Contact_ *implements* IZUGFeRDExportableContact \{}
.. *class* _Item_ *implements* IZUGFeRDExportableItem \{
[arabic]
... _*private* BigDecimal price, quantity;_
*private* Product product;
[arabic, start=4]
. {blank}
[arabic]
.. }
.. *class* Product *implements* IZUGFeRDExportableProduct \{
[arabic]
... *private* String description, name, unit;
*private* BigDecimal VATPercent;
}
[arabic, start=5]
. Generieren Sie die Imports durch Drücken von STRG+SHIFT+O
. Markieren Sie den Klassennamen MustangWriter und drücken Sie
ALT+SHIFT+S, wählen Sie Override/Implement Methods und drücken Return.
. Klicken Sie auf Contact und Wiederholen Sie den letzten Schritt.
. Klicken Sie auf Item, markieren Sie die Variablen und wählen Sie
zuerst „_Generate Getters and Setters_“ nach drücken von ALT+SHIFT+S.
Wählen Sie alle Member aus und drücken Sie Return.
. Klicken Sie erneut auf Item, drücken von ALT+SHIFT+S und wählen
„Generate Constructor using Fields“. Wählen Sie erneut alle Member aus
und drücken Sie Return.
. Auf Item muss der Quick Fix „add unimplemented methods“ ausgeführt
werden, die zwei generierten Funktionen (getItemAllowances und
getItemCharges) dürfen null zurück geben.
. Wenden Sie die beiden letzten Schritte auch auf Product an: Klicken
Sie auf Product, markieren Sie die Variablen und wählen Sie „Generate
Getters and Setters“ nach drücken von ALT+SHIFT+S. Wählen Sie alle
Member aus und drücken Sie Return.
. Item benötigt neben den getter/setter auch noch andere Methode, wählen
Sie Item aus, drücken Sie ALT+SHIFT+S, wählen Sie Override/Implement
Methods
. Klicken Sie erneut auf Product, drücken von ALT+SHIFT+S und wählen
„Generate Constructor using Fields“. Wählen Sie erneut alle Member aus
und drücken Sie Return.
. Folgende Methoden von Contact sollten Folgendes zurückgeben:
[arabic]
.. getCountry(): "DE"
.. getLocation(): "Spielkreis"
.. getName(): "Theodor Est"
.. getStreet(): "Bahnstr. 42"
.. getVATID(): "DE999999999"
.. getZIP(): "88802";
. Folgende Methoden der Hauptklasse -MustangWriter- sollten folgendes
zurückgeben:
[arabic]
.. getDeliveryDate(): *new*
GregorianCalendar(2017,Calendar._NOVEMBER_,17).getTime()
.. Zweimaliges CTRL+SHIFT+O importiert die dazu nötige GregorianCalendar
und Calendar Klasse
.. getDueDate(): *new*
GregorianCalendar(2017,Calendar._DECEMBER_,9).getTime()
.. getIssueDate(): *new*
GregorianCalendar(2017,Calendar._NOVEMBER_,18).getTime()
.. __getNumber(): "RE-2017__1118__/50__6__"__
.. _getOwnBIC(): "COBADEFFXXX"_
.. getOwnBankName(): "Commerzbank"
.. getOwnCountry() "DE"
.. getOwnIBAN(): "DE88 2008 0000 0970 3757 00"
.. getOwnLocation() "Stadthausen"
.. _getOwnOrganisationName(): "Bei Spiel GmbH"_
.. getOwnStreet() "Ecke 12"
.. getOwnTaxID(): "22/815/0815/4"
.. getOwnVATID(): "DE136695976"
.. getOwnZIP(): "12345"
.. getOwnOrganisationFullPlaintextInfo(): "Bei Spiel GmbH\n"+
+
"Ecke 12\n"+
+
"12345 Stadthausen\n"+
+
"Geschäftsführer: Max Mustermann"
.. _getRecipient(): *new* Contact()_
+
.. getZFItems() der Hauptklasse kann jetzt Produkte anlegen und diese
als Array von Posten (Items) zurückliefern:
+
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;
[arabic]
.
[arabic]
. {blank}
[arabic]
.. Legen Sie eine neue Funktion, beispielsweise „apply“ an (private
reicht).
.. In der Main-Methode der Hauptklasse instantiiert man jetzt die Klasse
und rufen Sie dort apply() auf.
.. In der apply-Methode kann man jetzt
[arabic]
... eine ZUGFeRDExporterFactory instantiieren,
... darauf Producer und Creator setzen (bspw. ZUGFeRDExporter ze=new
ZUGFeRDExporterFromA1Factory().setProducer(_"string"_).setCreator(_"string"_))
und über load die PDF-A/1-Datei angeben und über den Rückgabewert den
ZUGFeRDExporter holen. Die ZUGFeRD-Version kann man in der
setProducer.setCreator-Kette mit setZUGFeRDVersion(2) angeben.
... Auf dem Exporter ruft man diePDFattachZugferdFile-Methode (mit this
als IZUGFeRDExportableTransaction) auf und
... benutzt schlussendlich die export-Funktion. Die apply-Methode sieht
dann mit entsprechenden try/catch-Blöcken- beispielsweise so aus:
* try* \{
System._out_.println("Lese Blanko-PDF");
ZUGFeRDExporter ze = *new*
ZUGFeRDExporterFromA1Factory().setProducer("My
Application").setCreator(System._getProperty_("user.name")).load("./MustangGnuaccountingBeispielRE-20171118_506blanko.pdf");
System._out_.println("Generiere ZUGFeRD-Daten");
ze.PDFattachZugferdFile(*this*);
System._out_.println("Schreibe ZUGFeRD-PDF");
ze.export("./MustangGnuaccountingBeispielRE-20171118_506new.pdf");
System._out_.println("Fertig.");
} *catch* (IOException e) \{
e.printStackTrace();
}
[arabic, start=2]
. {blank}
[arabic]
.. Ein CTRL+SHIFT+O hilft wieder beim Hinzufügen der nötigen Imports
.. „My Application“ und __System.getProperty("user.name") __werden in
den Metadaten als „Producer“ (in etwa: erstellende Anwendung)
beziehungsweise „Creator“ (in etwa: Autor) gespeichert. Bitte passen Sie
die Werte entsprechend Ihrer Anwendung an.
.. Starten Sie, es sollte eine valide ZUFeRD-Rechnung in
_./MustangGnuaccountingBeispielRE-20171118_506new.pdf_
+
erstellt werden.
.. Passen Sie ggf. die NOTICE-Datei an und fügen Sie Ihrer Anwendung
hinzu.
.. Stellen Sie sicher, dass die XML-Daten in der dem PDF eingebetteten
ZUGFeRD-invoice.xml immer den menschenlesbaren Daten im PDF entsprechen.
Die erzeugte Datei beinhaltet ZUGFeRD-invoice.xml statt factur-x.xml im
offiziellen Beispiel so lange die ZUGFeRD-Version in der Factory nicht
auf 2 gesetzt wurde.
[arabic]
.
=== [#anchor-10]####Komplettes Schreibbeispiel
Siehe MustangWriter.java in diesem Verzeichnis.
=== [#anchor-11]####Schreiben eigener XML-Daten
Sollten Sie eine eigene Implementierung verwenden um ZUGFeRD-XML-Daten
zu erzeugen können Sie diese mit setZUGFeRDXMLData schreiben:
ZUGFeRDExporter ze;
*try* \{
System.*_out_*.println("Konvertiere zu PDF/A-3u");
ze = new
ZUGFeRDExporterFromA1Factory().setProducer("My
Application").setCreator(System._getProperty_("user.name")).load("./MustangGnuaccountingBeispielRE-20171118_506blanko.pdf");
System.*_out_*.println("Hänge ZUGFeRD-Datei an");
String ownZUGFeRDXML =
"<rsm:CrossIndustryDocument></rsm:CrossIndustryDocument>";
ze.setZUGFeRDXMLData(ownZUGFeRDXML.getBytes());
System.*_out_*.println("Schreibe ZUGFeRD-PDF");
ze.export("./Target.pdf");
System.*_out_*.println("Fertig.");
} *catch* (IOException e) \{
e.printStackTrace();
}
Mustangproject prüft, die Eingabedatei auf einigermaßen korrektes PDF/A
ist und dass die XML-Daten mindestens <rsm:CrossIndustry enthalten was
auf ZF1- (CrossIndustryDocument) und ZF2-Daten (CrossIndustryInvoice)
zutrifft.
=== [#anchor-12]####Zusatzfunktionen
* ZUGFeRDExporter.setTest() setzt ein Attribut im ZUGFeRD-XML, das
benutzt wird um eine Testrechnung auszuzeichnen.
* ZUGFeRDExporter.ignoreA1Errors() überspringt die Überprüfung der
Eingangsdatei auf PDF/A-1-Fehler
* Ein erster Versuch bestehendes ZUGFeRD1-XML in 2 zu konvertieren kann
mit String facturx=new
ZUGFeRDMigrator().migrateFromV1ToV2(zugferdInvoice); begonnen werden.

182
doc/ZugferdDev.en.adoc Normal file
View File

@@ -0,0 +1,182 @@
== Mustang project user documentation
Jochen Stärk
For Mustangproject 2.17.0, 2025-05-28
http://www.mustangproject.org/[http://www.mustangproject.org]
== About Mustangproject
Mustangproject is a Java-Library for invoice („Factur-X/ZUGFeRD“-)metadata in
PDF-invoices as well as XRechnung (CII, or on the reading side also UBL).
It can read, write and validate those files uses PDF/A files as
input, underlies the Apache Public License and can
be used for free in commercial and noncommercial projects as long as
the license conditions are met.
On link:https://zugferd.org/[zugferd.org] we list other libraries also for other
languages
== Download/Project setup
=== Source code
Home of the Mustangproject source code is
https://github.com/ZUGFeRD/mustangproject/
=== With Maven
E.g. using link:https://www.jetbrains.com/idea/download/?section=windows[IntelliJ].
Add the following dependency to your project
----
<dependencies>
<dependency>
<groupId>org.mustangproject</groupId>
<artifactId>validator</artifactId>
<classifier>shaded</classifier>
<version>2.17.0</version>
</dependency>
</dependencies>
----
Open the Maven Window and click Sync all Maven projects.
== Reading Factur-X/ZUGFeRD data
[arabic]
. Download a factur-x sample with metadata like
https://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20201121_508.pdf[https://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20201121_508.pdf]
. Create a new Maven project. Add the jar in the pom.xml as described above. In
----
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
----
use
----
ZUGFeRDInvoiceImporter zii=new ZUGFeRDInvoiceImporter("MustangGnuaccountingBeispielRE-20201121_508.pdf");
CalculatedInvoice ci=new CalculatedInvoice();
try {
zii.extractInto(ci);
System.out.println("Pay: "+ci.getDuePayable());
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
----
To find out how much you have to pay.
=== Complete sample source code for reading ZUGFeRD data
----
package de.usegroup;
import org.mustangproject.CalculatedInvoice;
import org.mustangproject.ZUGFeRD.ZUGFeRDInvoiceImporter;
import javax.xml.xpath.XPathExpressionException;
import java.text.ParseException;
public class Main {
public static void main(String[] args) {
ZUGFeRDInvoiceImporter zii=new ZUGFeRDInvoiceImporter("MustangGnuaccountingBeispielRE-20201121_508.pdf");
CalculatedInvoice ci=new CalculatedInvoice();
try {
zii.extractInto(ci);
System.out.println("Pay: "+ci.getDuePayable());
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
----
NOTE: You can additionally validate the file with two additional lines
----
ZUGFeRDValidator zv=new ZUGFeRDValidator();
System.out.println(zv.validate("MustangGnuaccountingBeispielRE-20201121_508.pdf"));
----
== Writing a ZUGFeRD-PDF file
A sample for writing Factur-X/ZUGFeRD PDFs requires a PDF/A file to attach the XML to,
saved
e.g. as link:https://www.mustangproject.org/files/MustangGnuaccountingBeispielRE-20190610_507blanko.pdf[blanko.pdf]
The core is creating an exporter, an invoice and send the invoice through the exporter:
----
IZUGFeRDExporter ze = new ZUGFeRDExporterFromPDFA().load(sourcePDF).setProducer("My Application").
setCreator(System.getProperty("user.name"));
Invoice i=new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty("ACME co", "teststr", "55232", "teststadt", "DE")
.addBankDetails(new BankDetails("777666555", "DE4321"))).setOwnTaxID("4711").setOwnVATID("DE19990815")
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")
.setContact(new Contact("nameRep", "phoneRep", "emailRep@test.com"))).setNumber("X12")
.addItem(new Item(new Product("Testproduct", "", "H87", new BigDecimal(19)),
new BigDecimal(2.5), new BigDecimal(1.0)));
ze.setTransaction(i);
ze.export("factur-x.pdf");
----
Additionally, you should confirm that Mustangs calculated total matches that of your PDF/A file, for which you can use
the TransactionCalculator (which is also used internally in the exporter):
----
TransactionCalculator tc=new TransactionCalculator(i);
System.out.println("Confirm "+tc.getDuePayable()+ " is also the final amount in your PDF");
----
NOTE: We're using BigDecimals to avoid rounding errors.
So the complete write example looks like this:
----
package de.usegroup;
import org.mustangproject.*;
import org.mustangproject.ZUGFeRD.IZUGFeRDExporter;
import org.mustangproject.ZUGFeRD.TransactionCalculator;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromPDFA;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
public class Main {
public static void main(String[] args) {
String sourcePDF="MustangGnuaccountingBeispielRE-20201121_508.pdf";
try {
IZUGFeRDExporter ze = new ZUGFeRDExporterFromPDFA().load(sourcePDF).setProducer("My Application").
setCreator(System.getProperty("user.name"));
Invoice i=new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date())
.setSender(new TradeParty("ACME co", "teststr", "55232", "teststadt", "DE")
.addBankDetails(new BankDetails("777666555", "DE4321"))).setOwnTaxID("4711").setOwnVATID("DE19990815")
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")
.setContact(new Contact("nameRep", "phoneRep", "emailRep@test.com"))).setNumber("X12")
.addItem(new Item(new Product("Testproduct", "", "H87", new BigDecimal(19)),
new BigDecimal(2.5), new BigDecimal(1.0)));
ze.setTransaction(i);
ze.export("factur-x.pdf");
TransactionCalculator tc=new TransactionCalculator(i);
System.out.println("Confirm "+tc.getDuePayable()+ " is also the final amount in your PDF");
} catch (IOException e) {
e.printStackTrace();
}
}
}
----

Binary file not shown.

View File

@@ -3,13 +3,13 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>library</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Library to write, read and validate e-invoices (Factur-X, ZUGFeRD, Order-X, XRechnung/CII)</name>
<description>FOSS Java library to read, write and validate european electronic invoices and orders in the UN/CEFACT
@@ -133,7 +133,7 @@
<dependency>
<groupId>com.helger</groupId>
<artifactId>en16931-cii2ubl</artifactId>
<version>2.2.4</version>
<version>2.3.0</version>
</dependency>
<!-- test dependencies -->

View File

@@ -3,8 +3,10 @@ package org.mustangproject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import java.math.BigDecimal;
import java.math.RoundingMode;
/***
* (absolute) allowances on item and document level
@@ -29,6 +31,22 @@ public class Allowance extends Charge {
}
@Override
public BigDecimal getTotalAmount(IAbsoluteValueProvider currentItem) {
if(totalAmount != null) {
return totalAmount;
} else if (percent!=null) {
BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100))), 18, RoundingMode.HALF_UP);
// BigDecimal singlePrice=currentItem.getValue().multiply(BigDecimal.ONE.subtract(getPercent().divide(new BigDecimal(100))));
BigDecimal singlePriceDiff=currentItem.getValue().subtract(singlePrice);
return singlePriceDiff;
} else {
throw new RuntimeException("percent must be set");
}
}
/***
* Always to return false for IZUGFeRDAllowanceCharge
* @return false since its not supposed to be calculated negatively

View File

@@ -23,6 +23,14 @@ public class BankDetails implements IZUGFeRDTradeSettlementPayment {
* the "name" of the bank account (holder)
*/
protected String accountName = null;
/**
* payment means code
*/
protected String paymentMeansCode = "58";
/**
* payment means information
*/
protected String paymentMeansInformation = "SEPA credit transfer";
/***
* bean constructor
@@ -123,5 +131,31 @@ public class BankDetails implements IZUGFeRDTradeSettlementPayment {
return accountName;
}
/**
* set payment means code
*
* @param paymentMeansCode the payment means code
* @return fluent setter
*/
public BankDetails setPaymentMeansCode(String paymentMeansCode) {
this.paymentMeansCode = paymentMeansCode;
return this;
}
@Override
public String getPaymentMeansCode() { return paymentMeansCode; }
/**
* set payment means information
*
* @param paymentMeansInformation the payment mean information
* @return fluent setter
*/
public BankDetails setPaymentMeansInformation(String paymentMeansInformation) {
this.paymentMeansInformation = paymentMeansInformation;
return this;
}
@Override
public String getPaymentMeansInformation() { return paymentMeansInformation; }
}

View File

@@ -103,7 +103,6 @@ public class CalculatedInvoice extends Invoice implements Serializable {
* @return a updated transactioncalulator
*/
public TransactionCalculator getCalculation() {
calculate();
return tc;
}

View File

@@ -7,6 +7,7 @@ import org.mustangproject.ZUGFeRD.IAbsoluteValueProvider;
import org.mustangproject.ZUGFeRD.IZUGFeRDAllowanceCharge;
import java.math.BigDecimal;
import java.math.RoundingMode;
/***
* Absolute and relative charges for document and item level
@@ -23,6 +24,10 @@ public class Charge implements IZUGFeRDAllowanceCharge {
* the absolute value if not percentage
*/
protected BigDecimal totalAmount;
/**
* the value the percentage is applied upon
*/
protected BigDecimal basisAmount;
/**
* the tax rate the charge belongs to
*/
@@ -103,6 +108,22 @@ public class Charge implements IZUGFeRDAllowanceCharge {
}
@Override
public BigDecimal getBasisAmount() {
return basisAmount;
}
/***
* sets a potential basis for the potential percentage
* @param basis the basis amount
* @return fluid setter
*/
public Charge setBasisAmount(BigDecimal basis) {
this.basisAmount = basis;
return this;
}
@Override
public String getReasonCode() {
return reasonCode;
@@ -121,10 +142,13 @@ public class Charge implements IZUGFeRDAllowanceCharge {
@Override
public BigDecimal getTotalAmount(IAbsoluteValueProvider currentItem) {
if (percent!=null) {
return currentItem.getValue().multiply(getPercent().divide(new BigDecimal(100)));
} else if(totalAmount != null) {
if(totalAmount != null) {
return totalAmount;
} else if (percent!=null) {
BigDecimal singlePrice=currentItem.getValue().divide(BigDecimal.ONE.add(getPercent().divide(new BigDecimal(100))), 18, RoundingMode.HALF_UP);
// BigDecimal singlePrice=currentItem.getValue().multiply(BigDecimal.ONE.subtract(getPercent().divide(new BigDecimal(100))));
BigDecimal singlePriceDiff=currentItem.getValue().add(singlePrice);
return singlePriceDiff;
} else {
throw new RuntimeException("percent must be set");
}

View File

@@ -72,8 +72,6 @@ public class Item implements IZUGFeRDExportableItem {
//we need: name description unitcode
//and we additionally have vat%
// Bharti's homework 20241126:Streams https://www.youtube.com/watch?v=Lf01cBzmuXw
// and Lambdas https://www.youtube.com/watch?v=HCyx31NW8xg
setProduct(new Product(itemMap.getNode("Item").get()));
icnm.getAsString("Name").ifPresent(product::setName);
icnm.getAsString("Description").ifPresent(product::setDescription);
@@ -86,17 +84,6 @@ public class Item implements IZUGFeRDExportableItem {
BuyersItemIdentification.getAsString("ID").ifPresent(product::setBuyerAssignedID);
});
// String name = icnm.getAsStringOrNull("Name");
// String val = icnm.getAsStringOrNull("Value");
// if (name != null && val != null) {
// if (attributes == null) {
// attributes = new HashMap<>();
// }
// product.attributes.put(name, val);
// }
//icnm.getNode("AdditionalItemProperty").flatMap(n ->n.getAttributes()).ifPresent(product::setAttributes);
icnm.getAsNodeMap("ClassifiedTaxCategory").flatMap(m -> m.getAsBigDecimal("Percent"))
.ifPresent(product::setVATPercent);
});
@@ -175,11 +162,12 @@ public class Item implements IZUGFeRDExportableItem {
.ifPresent(product::setTaxExemptionReason);
icnm.getAllNodes("SpecifiedTradeAllowanceCharge").map(NodeMap::new).forEach(stac -> {
stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> { //CII
stac.getAsNodeMap("ChargeIndicator").ifPresent(ci -> {
String isChargeString = ci.getAsString("Indicator").get();
String percentString = stac.getAsStringOrNull("CalculationPercent");//MultiplierFactorNumeric
String amountString = stac.getAsStringOrNull("ActualAmount");//Amount
String reason = stac.getAsStringOrNull("Reason");//AllowanceChargeReason
String percentString = stac.getAsStringOrNull("CalculationPercent");
String amountString = stac.getAsStringOrNull("ActualAmount");
String basisAmountString = stac.getAsStringOrNull("BasisAmount");
String reason = stac.getAsStringOrNull("Reason");
Charge izac = new Charge();
if (isChargeString.equalsIgnoreCase("false")) {
izac = new Allowance();
@@ -188,6 +176,12 @@ public class Item implements IZUGFeRDExportableItem {
}
if (amountString != null) {
izac.setTotalAmount(new BigDecimal(amountString));
if (percentString!=null&&(percentString!="0")) {
izac.setTotalAmount(new BigDecimal(amountString).divide(getQuantity()));
}
}
if (basisAmountString != null) {
izac.setBasisAmount(new BigDecimal(basisAmountString));
}
if (percentString != null) {
izac.setPercent(new BigDecimal(percentString));
@@ -288,6 +282,16 @@ public class Item implements IZUGFeRDExportableItem {
return this;
}
@Override public IZUGFeRDAllowanceCharge[] getAllowances() {
IZUGFeRDAllowanceCharge[] izac=new IZUGFeRDAllowanceCharge[Allowances.size()];
return Allowances.toArray(izac);
}
@Override public IZUGFeRDAllowanceCharge[] getCharges() {
IZUGFeRDAllowanceCharge[] izac=new IZUGFeRDAllowanceCharge[Charges.size()];
return Charges.toArray(izac);
}
/***
* BT 132 (issue https://github.com/ZUGFeRD/mustangproject/issues/247)
* @return the line ID of the order (BT132)
@@ -461,6 +465,7 @@ public class Item implements IZUGFeRDExportableItem {
* @return fluent setter
*/
public Item addAllowance(IZUGFeRDAllowanceCharge izac) {
Allowances.add(izac);
return this;
}

View File

@@ -32,6 +32,8 @@ public class Product implements IZUGFeRDExportableProduct {
protected boolean isIntraCommunitySupply = false;
protected SchemedID globalId = null;
protected String countryOfOrigin = null;
protected ArrayList<Charge> charges=new ArrayList<>();
protected ArrayList<Allowance> allowances=new ArrayList<>();
protected HashMap<String, String> attributes = new HashMap<>();
protected List<IDesignatedProductClassification> classifications = new ArrayList<>();
@@ -393,4 +395,42 @@ public class Product implements IZUGFeRDExportableProduct {
this.classifications.add(classification);
return this;
}
public Product addCharge(Charge e) {
charges.add(e);
return this;
}
public Product addAllowance(Allowance a) {
allowances.add(a);
return this;
}
/***
* returns the AppliedTradeAllowanceCharges of this product which are actually Charges
* @return array of or null, if none
*/
@Override
public Charge[] getCharges() {
if (charges.size()==0) {
return null;
}
Charge[] chargeArr = new Charge[charges.size()];
return charges.toArray(chargeArr);
}
@Override
/***
* returns the AppliedTradeAllowanceCharges of this product which are actually Allowances
* @return array of or null, if none
*/
public Allowance[] getAllowances() {
if (allowances.size()==0) {
return null;
}
Allowance[] allowanceArr = new Allowance[allowances.size()];
return allowances.toArray(allowanceArr);
}
}

View File

@@ -38,6 +38,12 @@ public interface IZUGFeRDAllowanceCharge {
*/
default BigDecimal getPercent() {return null;}
/***
* returns a basis the precentage is calculated from
* @return null or the basis
*/
default BigDecimal getBasisAmount() {return null;}
/***
* get a description for the allowance/charge
* @return the description

View File

@@ -43,7 +43,11 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
* item level discounts
* @return array of the discounts on a single item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getItemAllowances() {
if (getProduct()!=null) {
return getProduct().getAllowances();
}
return null;
}
@@ -51,10 +55,27 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
* item level price additions
* @return array of the additional charges on the item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getItemCharges() {
if (getProduct()!=null) {
return getProduct().getCharges();
}
return null;
}
@Deprecated
default IZUGFeRDAllowanceCharge[] getAllowances() {
return null;
}
/**
* item level price additions
* @return array of the additional charges on the item
*/
@Deprecated
default IZUGFeRDAllowanceCharge[] getCharges() {
return null;
}
/***
* BT 132 (issue https://github.com/ZUGFeRD/mustangproject/issues/247)
@@ -147,9 +168,9 @@ public interface IZUGFeRDExportableItem extends IAbsoluteValueProvider{
}
/***
* specify allowances amount for the line item total
* get all (allowances and charges) SpecifiedTradeAllowanceCharges
*
* @return the sum of allowances for this item
* @return the real item lecel SpecifiedTradeAllowanceCharges
*/
default IZUGFeRDAllowanceCharge[] getItemTotalAllowances() {
return null;

View File

@@ -166,6 +166,25 @@ public interface IZUGFeRDExportableProduct {
return null;
}
/**
* product level discounts (AppliedTradeAllowanceCharge, will change net price)
* @return array of the discounts on a single product
*/
default IZUGFeRDAllowanceCharge[] getAllowances() {
return null;
}
/**
* product level charges (AppliedTradeAllowanceCharge, will change net price)
* @return array of the additional charges on the product
*/
default IZUGFeRDAllowanceCharge[] getCharges() {
return null;
}
/**
* Detailed information about the product
*

View File

@@ -59,6 +59,16 @@ public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement
*/
default String getAccountName() { return null; }
/***
* @return payment means code (BT-81 / UNTDID 4461)
*/
default String getPaymentMeansCode() { return null; };
/***
* @return payment means description (BT-82) (optional)
*/
default String getPaymentMeansInformation() { return null; };
@Override
@JsonIgnore
@@ -70,12 +80,14 @@ public interface IZUGFeRDTradeSettlementPayment extends IZUGFeRDTradeSettlement
}
String xml = "<ram:SpecifiedTradeSettlementPaymentMeans>"
+ "<ram:TypeCode>58</ram:TypeCode>"
+ "<ram:Information>SEPA credit transfer</ram:Information>"
+ "<ram:PayeePartyCreditorFinancialAccount>"
+ "<ram:IBANID>" + XMLTools.encodeXML(getOwnIBAN()) + "</ram:IBANID>";
xml+= accountNameStr;
xml+= "</ram:PayeePartyCreditorFinancialAccount>";
+ "<ram:TypeCode>" + XMLTools.encodeXML(getPaymentMeansCode()) + "</ram:TypeCode>"
+ "<ram:Information>" + XMLTools.encodeXML(getPaymentMeansInformation()) + "</ram:Information>";
if (getOwnIBAN() != null) {
xml += "<ram:PayeePartyCreditorFinancialAccount>"
+ "<ram:IBANID>" + XMLTools.encodeXML(getOwnIBAN()) + "</ram:IBANID>"
+ accountNameStr
+ "</ram:PayeePartyCreditorFinancialAccount>";
}
if (getOwnBIC()!=null) {
xml+= "<ram:PayeeSpecifiedCreditorFinancialInstitution>"
+ "<ram:BICID>" + XMLTools.encodeXML(getOwnBIC()) + "</ram:BICID>"

View File

@@ -2,30 +2,49 @@ package org.mustangproject.ZUGFeRD;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
/***
* the linecalculator does the math within an item line, and e.g. calculates quantity*price.
* @see TransactionCalculator
*/
public class LineCalculator {
private final BigDecimal price;
private final BigDecimal priceGross;
private final BigDecimal itemTotalNetAmount;
private final BigDecimal itemTotalVATAmount;
private BigDecimal allowance = BigDecimal.ZERO;
private BigDecimal charge = BigDecimal.ZERO;
private BigDecimal allowanceItemTotal = BigDecimal.ZERO;
protected BigDecimal price;
protected BigDecimal priceGross;
protected BigDecimal itemTotalNetAmount;
protected BigDecimal itemTotalVATAmount;
protected BigDecimal lineAllowance = BigDecimal.ZERO;
protected BigDecimal lineCharge = BigDecimal.ZERO;
protected BigDecimal itemAllowance = BigDecimal.ZERO;
protected BigDecimal itemCharge = BigDecimal.ZERO;
protected BigDecimal allowanceItemTotal = BigDecimal.ZERO;
public LineCalculator(IZUGFeRDExportableItem currentItem) {
if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) {
for (IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) {
addAllowance(allowance.getTotalAmount(currentItem));
BigDecimal factor=BigDecimal.ONE;
BigDecimal singleAllowance=allowance.getTotalAmount(currentItem);
addItemAllowance(singleAllowance);
if ((allowance.getPercent()!=null)&&(allowance.getPercent().compareTo(BigDecimal.ZERO)!=0)) {
factor=currentItem.getQuantity();
}
addAllowanceItemTotal(singleAllowance.multiply(factor));
}
}
if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) {
for (IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) {
addCharge(charge.getTotalAmount(currentItem));
BigDecimal factor=BigDecimal.ONE;
BigDecimal singleCharge=charge.getTotalAmount(currentItem);
addItemCharge(singleCharge);
if ((charge.getPercent()!=null)&&(charge.getPercent().compareTo(BigDecimal.ZERO)!=0)) {
factor=currentItem.getQuantity();
}
subtractAllowanceItemTotal(singleCharge.multiply(factor));
}
}
if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) {
@@ -42,22 +61,41 @@ public class LineCalculator {
vatPercent = BigDecimal.ZERO;
}
BigDecimal multiplicator = vatPercent.divide(BigDecimal.valueOf(100));
priceGross = currentItem.getPrice(); // see https://github.com/ZUGFeRD/mustangproject/issues/159
price = priceGross.subtract(allowance).add(charge);
BigDecimal quantity=BigDecimal.ZERO;
if ((currentItem!=null)&&(currentItem.getQuantity()!=null)) {
quantity=currentItem.getQuantity();
}
price=currentItem.getPrice();
priceGross=price;
// price=price.subtract(itemAllowance).add(itemCharge);
// BigDecimal delta=charge.subtract(allowanceItemTotal).subtract(allowance);
// delta=delta.divide(currentItem.getQuantity(), 18, RoundingMode.HALF_UP);
BigDecimal delta=BigDecimal.ZERO;
if(currentItem.getProduct()!=null){
if (currentItem.getProduct().getAllowances()!=null) {
for (IZUGFeRDAllowanceCharge ccaf:currentItem.getProduct().getAllowances()) {
delta=delta.subtract(ccaf.getTotalAmount(currentItem));
}
}
if (currentItem.getProduct().getCharges()!=null) {
for (IZUGFeRDAllowanceCharge ccaf : currentItem.getProduct().getCharges()) {
delta = delta.subtract(ccaf.getTotalAmount(currentItem));
}
}
}
price=price.add(delta);
// Division/Zero occurred here.
// Used the setScale only because that's also done in getBasisQuantity
BigDecimal basisQuantity = currentItem.getBasisQuantity().compareTo(BigDecimal.ZERO) == 0
? BigDecimal.ONE.setScale(4)
: currentItem.getBasisQuantity();
itemTotalNetAmount = quantity.multiply(price).divide(basisQuantity, 18, RoundingMode.HALF_UP)
.subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);
.add(lineCharge).subtract(lineAllowance).subtract(allowanceItemTotal).setScale(2, RoundingMode.HALF_UP);
itemTotalVATAmount = itemTotalNetAmount.multiply(multiplicator);//.setScale(2, RoundingMode.HALF_UP);
}
public BigDecimal getPrice() {
@@ -80,16 +118,27 @@ public class LineCalculator {
return priceGross;
}
public void addAllowance(BigDecimal b) {
allowance = allowance.add(b);
public void addLineAllowance(BigDecimal b) {
lineAllowance = lineAllowance.add(b);
}
public void addCharge(BigDecimal b) {
charge = charge.add(b);
public void addLineCharge(BigDecimal b) {
lineCharge = lineCharge.add(b);
}
public void addItemAllowance(BigDecimal b) {
itemAllowance = itemAllowance.add(b);
}
public void addItemCharge(BigDecimal b) {
itemCharge = itemCharge.add(b);
}
public void addAllowanceItemTotal(BigDecimal b) {
allowanceItemTotal = allowanceItemTotal.add(b);
}
public void subtractAllowanceItemTotal(BigDecimal b) {
allowanceItemTotal = allowanceItemTotal.subtract(b);
}
}

View File

@@ -145,7 +145,7 @@ public class VATAmount {
}
public VATAmount add(VATAmount v) {
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode, this.dueDateTypeCode).setVatExemptionReasonText(v.getVatExemptionReasonText());
return new VATAmount(basis.add(v.getBasis()), calculated.add(v.getCalculated()), this.categoryCode, this.dueDateTypeCode).setVatExemptionReasonText(v.getVatExemptionReasonText() != null ? v.getVatExemptionReasonText(): this.vatExemptionReasonText);
}
public VATAmount subtract(VATAmount v) {

View File

@@ -48,6 +48,7 @@ import org.mustangproject.IncludedNote;
import org.mustangproject.ReferencedDocument;
import org.mustangproject.XMLTools;
import org.mustangproject.ZUGFeRD.model.DocumentCodeTypeConstants;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -152,12 +153,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
if (profile == Profiles.getByName("Minimum")) {
xml += "<ram:ID>" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getID()) + "</ram:ID>";
} else {
String schemeAttribute="";
if ((party.getLegalOrganisation().getSchemedID().getScheme()!=null)&&(party.getLegalOrganisation().getSchemedID().getScheme().length()>0)) {
schemeAttribute="schemeID=\"" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getScheme())+"\"";
String schemeAttribute = "";
if ((party.getLegalOrganisation().getSchemedID().getScheme() != null) && (party.getLegalOrganisation().getSchemedID().getScheme().length() > 0)) {
schemeAttribute = "schemeID=\"" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getScheme()) + "\"";
}
xml += "<ram:ID "+schemeAttribute+">" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getID()) + "</ram:ID>";
xml += "<ram:ID " + schemeAttribute + ">" + XMLTools.encodeXML(party.getLegalOrganisation().getSchemedID().getID()) + "</ram:ID>";
}
}
if (party.getLegalOrganisation().getTradingBusinessName() != null) {
@@ -320,11 +321,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
String reason = "";
if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || profile == Profiles.getByName("XRechnung"))) {
boolean isEN16931=(profile == Profiles.getByName("XRechnung")) || (profile == Profiles.getByName("EN16931"));
if ((allowance.getReason() != null) && (profile == Profiles.getByName("Extended") || isEN16931)) {
reason = "<ram:Reason>" + XMLTools.encodeXML(allowance.getReason()) + "</ram:Reason>";
}
String reasonCode = "";
if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("XRechnung"))) {
if ((allowance.getReasonCode() != null) && (profile == Profiles.getByName("Extended") || isEN16931)) {
// only in XRechnung profile
reasonCode = "<ram:ReasonCode>" + allowance.getReasonCode() + "</ram:ReasonCode>";
}
@@ -395,20 +397,20 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ "</rsm:ExchangedDocumentContext>"
+ "<rsm:ExchangedDocument>"
+ "<ram:ID>" + XMLTools.encodeXML(trans.getNumber()) + "</ram:ID>";
if (profile == Profiles.getByName("Extended") && trans.getDocumentName() != null) {
xml += "<ram:Name>" + XMLTools.encodeXML(trans.getDocumentName()) + "</ram:Name>";
}
xml += "<ram:TypeCode>" + typecode + "</ram:TypeCode>"
+ "<ram:IssueDateTime>" + DATE.udtFormat(trans.getIssueDate()) + "</ram:IssueDateTime>" // date
+ buildNotes(trans)
+ "</rsm:ExchangedDocument>"
+ "<rsm:SupplyChainTradeTransaction>";
if (profile == Profiles.getByName("Extended") && trans.getDocumentName() != null) {
xml += "<ram:Name>" + XMLTools.encodeXML(trans.getDocumentName()) + "</ram:Name>";
}
xml += "<ram:TypeCode>" + typecode + "</ram:TypeCode>"
+ "<ram:IssueDateTime>" + DATE.udtFormat(trans.getIssueDate()) + "</ram:IssueDateTime>" // date
+ buildNotes(trans)
+ "</rsm:ExchangedDocument>"
+ "<rsm:SupplyChainTradeTransaction>";
int lineID = 0;
for (final IZUGFeRDExportableItem currentItem : trans.getZFItems()) {
lineID++;
String lineIDStr = Integer.toString(lineID);
if (currentItem.getId()!=null) {
lineIDStr=currentItem.getId();
if (currentItem.getId() != null) {
lineIDStr = currentItem.getId();
}
final LineCalculator lc = new LineCalculator(currentItem);
if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BasicWL"))) {
@@ -432,24 +434,29 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ XMLTools.encodeXML(currentItem.getProduct().getBuyerAssignedID()) + "</ram:BuyerAssignedID>";
}
String allowanceChargeStr = "";
if (currentItem.getItemAllowances() != null && currentItem.getItemAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge allowance : currentItem.getItemAllowances()) {
if (currentItem.getProduct().getAllowances() != null && currentItem.getProduct().getAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge allowance : currentItem.getProduct().getAllowances()) {
allowanceChargeStr += getAllowanceChargeStr(allowance, currentItem);
}
}
if (currentItem.getItemCharges() != null && currentItem.getItemCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge charge : currentItem.getItemCharges()) {
if (currentItem.getProduct().getCharges() != null && currentItem.getProduct().getCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge charge : currentItem.getProduct().getCharges()) {
allowanceChargeStr += getAllowanceChargeStr(charge, currentItem);
}
}
String itemTotalAllowanceChargeStr = "";
if (currentItem.getItemTotalAllowances() != null && currentItem.getItemTotalAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalAllowance : currentItem.getItemTotalAllowances()) {
if (currentItem.getAllowances() != null && currentItem.getAllowances().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalAllowance : currentItem.getAllowances()) {
itemTotalAllowanceChargeStr += getItemTotalAllowanceChargeStr(itemTotalAllowance, currentItem);
}
}
if (currentItem.getCharges() != null && currentItem.getCharges().length > 0) {
for (final IZUGFeRDAllowanceCharge itemTotalCharges : currentItem.getCharges()) {
itemTotalAllowanceChargeStr += getItemTotalAllowanceChargeStr(itemTotalCharges, currentItem);
}
}
xml += "<ram:Name>" + XMLTools.encodeXML(currentItem.getProduct().getName()) + "</ram:Name>";
if (currentItem.getProduct().getDescription() != null && currentItem.getProduct().getDescription().length() > 0) {
@@ -531,15 +538,16 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ "<ram:SpecifiedLineTradeSettlement>"
+ "<ram:ApplicableTradeTax>"
+ "<ram:TypeCode>VAT</ram:TypeCode>";
if (currentItem.getProduct().getTaxExemptionReason() != null) {
xml += "<ram:ExemptionReason>" + XMLTools.encodeXML(currentItem.getProduct().getTaxExemptionReason()) + "</ram:ExemptionReason>";
}
xml += "<ram:CategoryCode>" + currentItem.getProduct().getTaxCategoryCode() + "</ram:CategoryCode>"
+ "<ram:RateApplicablePercent>"
+ vatFormat(currentItem.getProduct().getVATPercent()) + "</ram:RateApplicablePercent>"
+ "</ram:ApplicableTradeTax>";
xml += "<ram:CategoryCode>" + currentItem.getProduct().getTaxCategoryCode() + "</ram:CategoryCode>";
if (!currentItem.getProduct().getTaxCategoryCode().equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) {
xml += "<ram:RateApplicablePercent>"
+ vatFormat(currentItem.getProduct().getVATPercent()) + "</ram:RateApplicablePercent>";
}
xml += "</ram:ApplicableTradeTax>";
if ((currentItem.getDetailedDeliveryPeriodFrom() != null) || (currentItem.getDetailedDeliveryPeriodTo() != null)) {
xml += "<ram:BillingSpecifiedPeriod>";
if (currentItem.getDetailedDeliveryPeriodFrom() != null) {
@@ -551,7 +559,10 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "</ram:BillingSpecifiedPeriod>";
}
xml += itemTotalAllowanceChargeStr;
// item charges/allowances
if (!itemTotalAllowanceChargeStr.isEmpty()) {
xml += itemTotalAllowanceChargeStr ;
}
xml += "<ram:SpecifiedTradeSettlementLineMonetarySummation>"
+ "<ram:LineTotalAmount>" + currencyFormat(lc.getItemTotalNetAmount())
@@ -725,9 +736,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
+ exemptionReasonTextXML
+ "<ram:BasisAmount>" + currencyFormat(amount.getBasis()) + "</ram:BasisAmount>" // currencyID=\"EUR\"
+ "<ram:CategoryCode>" + amountCategoryCode + "</ram:CategoryCode>"
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "")
+ "<ram:RateApplicablePercent>"
+ vatFormat(amount.getApplicablePercent()) + "</ram:RateApplicablePercent></ram:ApplicableTradeTax>";
+ (amountDueDateTypeCode != null ? "<ram:DueDateTypeCode>" + amountDueDateTypeCode + "</ram:DueDateTypeCode>" : "");
if (!amountCategoryCode.equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) {
xml += "<ram:RateApplicablePercent>"
+ vatFormat(amount.getApplicablePercent()) + "</ram:RateApplicablePercent>";
}
xml += "</ram:ApplicableTradeTax>";
}
}
}
@@ -743,7 +757,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
}
if ((trans.getZFCharges() != null) && (trans.getZFCharges().length > 0)) {
if ((profile == Profiles.getByName("XRechnung")) || (profile == Profiles.getByName("EN16931")) || (profile == Profiles.getByName("EXTENDED"))) {
if ((profile == Profiles.getByName("XRechnung")) || (profile == Profiles.getByName("EN16931")) || (profile == Profiles.getByName("EXTENDED"))) {
for (IZUGFeRDAllowanceCharge charge : trans.getZFCharges()) {
xml += "<ram:SpecifiedTradeAllowanceCharge>" +
"<ram:ChargeIndicator>" +
@@ -759,7 +773,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
xml += "<ram:CategoryTradeTax>" +
"<ram:TypeCode>VAT</ram:TypeCode>" +
"<ram:CategoryCode>" + charge.getCategoryCode() + "</ram:CategoryCode>";
if (charge.getTaxPercent() != null) {
if (charge.getTaxPercent() != null && !charge.getCategoryCode().equals(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE)) {
xml += "<ram:RateApplicablePercent>" + vatFormat(charge.getTaxPercent()) + "</ram:RateApplicablePercent>";
}
xml += "</ram:CategoryTradeTax>" +
@@ -842,7 +856,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
if (paymentTermsDescription != null) {
xml += "<ram:Description>" + paymentTermsDescription + "</ram:Description>";
}
if (trans.getDueDate() != null) {
xml += "<ram:DueDateDateTime>" // $NON-NLS-2$
+ DATE.udtFormat(trans.getDueDate())
@@ -874,7 +888,7 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
final String chargesTotalLine = "<ram:ChargeTotalAmount>" + currencyFormat(calc.getChargesForPercent(null)) + "</ram:ChargeTotalAmount>";
xml += "<ram:SpecifiedTradeSettlementHeaderMonetarySummation>";
if ((getProfile() != Profiles.getByName("Minimum")) && (getProfile() != Profiles.getByName("BASICWL"))) {
if ((getProfile() != Profiles.getByName("Minimum"))) {
xml += "<ram:LineTotalAmount>" + currencyFormat(calc.getTotal()) + "</ram:LineTotalAmount>";
xml += chargesTotalLine
+ allowanceTotalLine;
@@ -910,12 +924,12 @@ public class ZUGFeRD2PullProvider implements IXMLProvider {
if (trans.getInvoiceReferencedDocuments() != null) {
for (ReferencedDocument doc : trans.getInvoiceReferencedDocuments()) {
xml += "<ram:InvoiceReferencedDocument>"
+ "<ram:IssuerAssignedID>"
+ XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>";
+ "<ram:IssuerAssignedID>"
+ XMLTools.encodeXML(doc.getIssuerAssignedID()) + "</ram:IssuerAssignedID>";
if (doc.getFormattedIssueDateTime() != null) {
xml += "<ram:FormattedIssueDateTime>"
+ DATE.qdtFormat(doc.getFormattedIssueDateTime())
+ "</ram:FormattedIssueDateTime>";
+ DATE.qdtFormat(doc.getFormattedIssueDateTime())
+ "</ram:FormattedIssueDateTime>";
}
xml += "</ram:InvoiceReferencedDocument>";
}

View File

@@ -383,29 +383,7 @@ public class ZUGFeRDInvoiceImporter {
});
});
String street, name, additionalStreet, city, postal, countrySubentity, line, country = null;
/*
String idx = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"ID\"]");
street = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name()=\"Address\"]/*[local-name()=\"StreetName\"]");
additionalStreet = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"AdditionalStreetName\"]");
city = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"CityName\"]");
postal = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"PostalZone\"]");
countrySubentity = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]/*[local-name() = \"CountrySubentity\"]");
line = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]//*[local-name() = \"AddressLine\"]/*[local-name() = \"Line\"]");
country = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"Address\"]//*[local-name() = \"Country\"]/*[local-name() = \"IdentificationCode\"]");
name = extractString("//*[local-name()=\"DeliveryLocation\"]/*[local-name() = \"DeliveryParty\"]//*[local-name() = \"PartyName\"]/*[local-name() = \"Name\"]");
*/
zpp.setDeliveryAddress(delivery);
/*
zpp.setDeliveryAddress(new TradeParty()
.setStreet(street)
.setAdditionalAddress(additionalStreet)
.setLocation(city)
.setZIP(postal)
.setAdditionalAddressExtension(line)
.setCountry(country)
.setName(name)
);
*/
}
@@ -420,14 +398,6 @@ public class ZUGFeRDInvoiceImporter {
XPathExpression shipPayee = xpath.compile("//*[local-name()=\"PayeeParty\"]/*");
NodeList ublPayeeNodes = (NodeList) shipPayee.evaluate(getDocument(), XPathConstants.NODESET);
// if(ublPayeeNodes != null) {
// TradeParty payee = new TradeParty();
// NodeMap nodeMap = new NodeMap(ublPayeeNodes).getAsNodeMap("PayeeParty").get();
// nodeMap.getNode("ID").ifPresent(s -> {
// SchemedID sID = new SchemedID().setScheme(s.getAttributes().getNamedItem("schemeID").getTextContent()).setId(s.getTextContent());
// payee.addGlobalID(sID);
// });
// }
//NodeList UBLpayeeNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
if (ublPayeeNodes.getLength() > 0) {
zpp.setPayee(new TradeParty(ublPayeeNodes));
@@ -598,6 +568,15 @@ public class ZUGFeRDInvoiceImporter {
}
}
String creditorReferenceID = extractString("//*[local-name()=\"ApplicableHeaderTradeSettlement\"]/*[local-name()=\"CreditorReferenceID\"]").trim();//BT-90
if ((creditorReferenceID == null)||(creditorReferenceID.length()==0)) {
//maybe it's there in UBL?
creditorReferenceID = extractString("//*[local-name()=\"AccountingSupplierParty\"]/*[local-name()=\"Party\"]/*[local-name()=\"PartyIdentification\"]/*[local-name()=\"ID\"]").trim();
}
if ((creditorReferenceID != null)&&(creditorReferenceID.length()>0)) {
zpp.setCreditorReferenceID(creditorReferenceID);
}
xpr = xpath.compile("//*[local-name()=\"ApplicableHeaderTradeDelivery\"]|//*[local-name()=\"Delivery\"]");
NodeList headerTradeDeliveryNodes = (NodeList) xpr.evaluate(getDocument(), XPathConstants.NODESET);
@@ -836,9 +815,6 @@ public class ZUGFeRDInvoiceImporter {
}
}
}
// if ((paymentMeansChilds.item(paymentTermChildIndex).getLocalName() != null) && (paymentTermChilds.item(paymentTermChildIndex).getLocalName().equals("DirectDebitMandateID"))) {
// directDebitMandateID = paymentTermChilds.item(paymentTermChildIndex).getTextContent();
// }
if ((paymentMeansChilds.item(meansChildIndex).getLocalName() != null)
&& (paymentMeansChilds.item(meansChildIndex).getLocalName().equals("PaymentMandate"))) {
NodeList paymentMandateChilds = paymentMeansChilds.item(meansChildIndex).getChildNodes();
@@ -974,6 +950,7 @@ public class ZUGFeRDInvoiceImporter {
NodeList chargeNodeChilds = chargeNodes.item(i).getChildNodes();
boolean isCharge = true;
String chargeAmount = null;
String basisAmount = null;
String reason = null;
String reasonCode = null;
String taxPercent = null;
@@ -1001,6 +978,8 @@ public class ZUGFeRDInvoiceImporter {
} else if (chargeChildName.equals("ActualAmount") || chargeChildName.equals("Amount")) {
chargeAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("BasisAmount")) {
basisAmount = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("Reason") || chargeChildName.equals("AllowanceChargeReason")) {
reason = XMLTools.trimOrNull(chargeNodeChilds.item(chargeChildIndex));
} else if (chargeChildName.equals("ReasonCode") || chargeChildName.equals("AllowanceChargeReasonCode")) {
@@ -1028,6 +1007,9 @@ public class ZUGFeRDInvoiceImporter {
if (taxPercent != null) {
c.setTaxPercent(new BigDecimal(taxPercent));
}
if (basisAmount != null) {
c.setBasisAmount(new BigDecimal(basisAmount));
}
zpp.addCharge(c);
} else {
Allowance a = new Allowance(new BigDecimal(chargeAmount));
@@ -1040,6 +1022,9 @@ public class ZUGFeRDInvoiceImporter {
if (taxPercent != null) {
a.setTaxPercent(new BigDecimal(taxPercent));
}
if (basisAmount != null) {
a.setBasisAmount(new BigDecimal(basisAmount));
}
zpp.addAllowance(a);
}

View File

@@ -1,21 +1,23 @@
/** **********************************************************************
*
/**
* *********************************************************************
* <p>
* Copyright 2018 Jochen Staerk
*
* <p>
* Use is subject to license terms.
*
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0.
*
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* <p>
* See the License for the specific language governing permissions and
* limitations under the License.
*
*********************************************************************** */
* <p>
* **********************************************************************
*/
package org.mustangproject.ZUGFeRD.model;
import java.util.Set;
@@ -31,5 +33,5 @@ public class TaxCategoryCodeTypeConstants {
public static final String INTRACOMMUNITY = "K";
public static final String FREEEXPORT = "G";
public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT, FREEEXPORT).collect(Collectors.toSet());
public static Set<String> CATEGORY_CODES_WITH_EXEMPTION_REASON = Stream.of(INTRACOMMUNITY, REVERSECHARGE, TAXEXEMPT, FREEEXPORT, UNTAXEDSERVICE).collect(Collectors.toSet());
}

View File

@@ -136,7 +136,7 @@
<entry key="xr:Tender_or_lot_reference" id="BT-17">Vergabenummer</entry>
<entry key="xr:Receiving_advice_reference" id="BT-15">Kennung der Empfangsbestätigung</entry>
<entry key="xr:Despatch_advice_reference" id="BT-16">Kennung der Versandanzeige</entry>
<entry key="xr:Business_process_type_identifier" id="BT-23">Prozesskennung</entry>
<entry key="xr:Business_process_type" id="BT-23">Prozesskennung</entry>
<entry key="xr:Specification_identifier" id="BT-24">Spezifikationskennung</entry>
<entry key="xr:Invoiced_object_identifier" id="BT-18">Objektkennung</entry>
<entry key="xr:Invoiced_object_identifier/@scheme_identifier" id="BT-18_scheme">Schema der Objektkennung</entry>

View File

@@ -99,11 +99,11 @@
<xsl:attribute name="xr:id" select="'BG-2'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:if test="./cbc:ProfileID">
<xr:Business_process_type_identifier>
<xr:Business_process_type>
<xsl:attribute name="xr:id" select="'BT-23'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:value-of select="./cbc:ProfileID"/>
</xr:Business_process_type_identifier>
</xr:Business_process_type>
</xsl:if>
<xr:Specification_identifier>
<xsl:attribute name="xr:id" select="'BT-24'"/>

View File

@@ -98,11 +98,11 @@
<xsl:attribute name="xr:id" select="'BG-2'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:if test="./cbc:ProfileID">
<xr:Business_process_type_identifier>
<xr:Business_process_type>
<xsl:attribute name="xr:id" select="'BT-23'"/>
<xsl:attribute name="xr:src" select="xr:src-path(.)"/>
<xsl:value-of select="./cbc:ProfileID"/>
</xr:Business_process_type_identifier>
</xr:Business_process_type>
</xsl:if>
<xr:Specification_identifier>
<xsl:attribute name="xr:id" select="'BT-24'"/>

View File

@@ -818,7 +818,6 @@
<xsl:apply-templates mode="list-entry" select="xr:Despatch_advice_reference"/>
<xsl:apply-templates mode="list-entry" select="xr:PROCESS_CONTROL/xr:Business_process_type"/>
<xsl:apply-templates mode="list-entry" select="xr:PROCESS_CONTROL/xr:Specification_identifier"/>
<xsl:apply-templates mode="list-entry" select="xr:PROCESS_CONTROL/xr:Business_process_type_identifier"/>
<xsl:apply-templates mode="list-entry" select="xr:Invoiced_object_identifier"/>
<xsl:apply-templates mode="list-entry" select="xr:Invoiced_object_identifier/@scheme_identifier">
<xsl:with-param name="field-mapping-identifier" select="'xr:Invoiced_object_identifier/@scheme_identifier'"/>

View File

@@ -1797,7 +1797,7 @@ function downloadData (element_id) {
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende "><xsl:value-of select="$i18n.bt147"/>:</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"><xsl:value-of select="format-number(xr:PRICE_DETAILS/xr:Item_price_discount,'###.##0,00','decimal')"/></div>
<div id="BT-147" title="BT-147" class="boxdaten wert"><xsl:value-of select="format-number(sum(xr:PRICE_DETAILS/xr:Item_price_discount),'###.##0,00','decimal')"/></div>
</div>
<div class="boxzeile">
<div class="boxdaten legende "><xsl:value-of select="$i18n.bt148"/>:</div>

View File

@@ -54,9 +54,9 @@ public class CalculationTest extends ResourceCase {
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(133.857).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1606.28).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(257.0048).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1769.89).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(283.1824).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
@@ -74,17 +74,20 @@ public class CalculationTest extends ResourceCase {
final LineCalculator calculator = new LineCalculator(currentItem);
assertEquals(valueOf(163.603).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1963.24).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(314.1184).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
assertEquals(valueOf(148.73).stripTrailingZeros(), calculator.getPrice().stripTrailingZeros());
assertEquals(valueOf(1799.63).stripTrailingZeros(), calculator.getItemTotalNetAmount().stripTrailingZeros());
assertEquals(valueOf(287.9408).stripTrailingZeros(), calculator.getItemTotalVATAmount().stripTrailingZeros());
}
@Test
public void testLineCalculatorForeignCurrencyExample() {
/*
/*** xml of official fx sample with allowances and charges
* 10x100 with 10% and 50€ item discount =850€
* +8,75 charges on document level=858,75, +19%VAT=1021,91
* prepaid 500->due payable=521,91
*/
File inputCII = getResourceAsFile("Extended_fremdwaehrung.xml");
inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250407\\fremdwaehrung.xml");
ZUGFeRDInvoiceImporter zii=new ZUGFeRDInvoiceImporter();
Invoice invoice=null;
zii.doIgnoreCalculationErrors();
@@ -104,9 +107,9 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250
final TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(valueOf(521.91).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
assertEquals(valueOf(521.91).stripTrailingZeros(), calculator.getDuePayable().stripTrailingZeros());
*/
}
@@ -221,9 +224,94 @@ inputCII=new File("C:\\Users\\jstaerk\\workspace\\XMLExamples\\zfdiverses\\20250
invoice.addAllowance(new Allowance().setPercent(total_discount_percent).setTaxPercent(sales_tax_percent1).setReasonCode("95").setReason("Rabatte"));
}
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(valueOf(101.86).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
assertEquals(valueOf(307.18).stripTrailingZeros(), calculator.getGrandTotal().stripTrailingZeros());
}
public void testSimpleItemPercentAllowance() {
/***
* a product with net 1.10 and qty 5 and relative item allowance of 10% should return 5 as line and grand total
*/
SimpleDateFormat sqlDate = new SimpleDateFormat("yyyy-MM-dd");
Invoice invoice = new Invoice();
invoice.setDocumentName("Rechnung");
invoice.setNumber("777777");
try {
invoice.setIssueDate(sqlDate.parse("2020-12-31"));
invoice.setDetailedDeliveryPeriod(sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[0]), sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[1]));
invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) {
LOGGER.error("Failed to set dates", e);
}
TradeParty sender = new TradeParty("Maier GmbH", "Musterweg 5", "11111", "Testung", "DE");
sender.addVATID("DE2222222222");
invoice.setSender(sender);
/* trade party (recipient) */
TradeParty recipient = new TradeParty("Teston GmbH" + " " + "Zentrale" + " " + "", "Testweg 5", "11111", "Testung", "DE");
recipient.setID("111111");
recipient.addVATID("DE111111111");
invoice.setRecipient(recipient);
/* item */
Product product;
Item item;
product = new Product("AAA", "", "H84", BigDecimal.ZERO);
item = new Item(product, new BigDecimal("1.10"), new BigDecimal(5.00));
item.addAllowance(new Allowance().setPercent(new BigDecimal(10)).setTaxPercent(BigDecimal.ZERO));
invoice.addItem(item);
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(new BigDecimal(5), calculator.getGrandTotal().stripTrailingZeros());
}
public void testSimpleItemTotalAllowance() {
/***
* a product with net 1 and qty 5 and absolute _item_ allowance of 1 should return 4 as line total, and grand total
*/
SimpleDateFormat sqlDate = new SimpleDateFormat("yyyy-MM-dd");
Invoice invoice = new Invoice();
invoice.setDocumentName("Rechnung");
invoice.setNumber("777777");
try {
invoice.setIssueDate(sqlDate.parse("2020-12-31"));
invoice.setDetailedDeliveryPeriod(sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[0]), sqlDate.parse("2020-12-01 - 2020-12-31".split(" - ")[1]));
invoice.setDeliveryDate(sqlDate.parse("2020-12-31"));
invoice.setDueDate(sqlDate.parse("2021-01-15"));
} catch (Exception e) {
LOGGER.error("Failed to set dates", e);
}
TradeParty sender = new TradeParty("Maier GmbH", "Musterweg 5", "11111", "Testung", "DE");
sender.addVATID("DE2222222222");
invoice.setSender(sender);
/* trade party (recipient) */
TradeParty recipient = new TradeParty("Teston GmbH" + " " + "Zentrale" + " " + "", "Testweg 5", "11111", "Testung", "DE");
recipient.setID("111111");
recipient.addVATID("DE111111111");
invoice.setRecipient(recipient);
/* item */
Product product;
Item item;
product = new Product("AAA", "", "H84", BigDecimal.ZERO);
item = new Item(product, new BigDecimal("1.00"), new BigDecimal(5.00));
item.addAllowance(new Allowance(new BigDecimal(1)).setTaxPercent(BigDecimal.ZERO));
invoice.addItem(item);
TransactionCalculator calculator = new TransactionCalculator(invoice);
assertEquals(new BigDecimal(4), calculator.getGrandTotal().stripTrailingZeros());
}
/**
* LineCalculator should not throw an exception when calculating a non-terminating decimal expansion
* */

View File

@@ -395,7 +395,7 @@ public class DeSerializationTest extends ResourceCase {
try {
Invoice newInvoiceFromJSON = mapper.readValue(json, Invoice.class);
TransactionCalculator tc=new TransactionCalculator(newInvoiceFromJSON);
assertEquals(new BigDecimal("19.52"),tc.getGrandTotal());
assertEquals(new BigDecimal("18.92"),tc.getGrandTotal());
} catch (JsonProcessingException e) {
throw new RuntimeException(e);

View File

@@ -32,6 +32,8 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class VisualizationTest extends ResourceCase {
@@ -80,7 +82,7 @@ public class VisualizationTest extends ResourceCase {
} catch (ParserConfigurationException e) {
fail("ParserConfigurationException should not happen: " + e.getMessage());
}
assertNotNull(result);
/* remove file endings so that tests can also pass after checking
out from git with arbitrary options (which may include CSRF changes)
@@ -94,6 +96,7 @@ public class VisualizationTest extends ResourceCase {
.replace(" ", ""));
}
public void testPDFVisualizationCII() {
File CIIinputFile = getResourceAsFile("cii/01.01a-INVOICE.cii.xml");

View File

@@ -34,6 +34,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import org.mustangproject.ZUGFeRD.model.TaxCategoryCodeTypeConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.BufferedWriter;
@@ -61,8 +62,8 @@ public class XRTest extends TestCase {
TradeParty recipient = new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE");
recipient.setEmail("quack@ducktown.org");
Invoice i = createInvoice(recipient);
String legalOrgID="aCustomSellerLegalOrgId";
String sellerID="aSellerTradePartyID";
String legalOrgID = "aCustomSellerLegalOrgId";
String sellerID = "aSellerTradePartyID";
i.getSender().setLegalOrganisation(new LegalOrganisation(legalOrgID));
i.getSender().setID(sellerID);
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
@@ -70,8 +71,8 @@ public class XRTest extends TestCase {
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
assertTrue(theXML.contains("<ram:ID>"+sellerID+"</ram:ID>"));// must be possible without scheme #
assertTrue(theXML.contains("<ram:ID>"+legalOrgID+"</ram:ID>"));// must be possible without scheme #
assertTrue(theXML.contains("<ram:ID>" + sellerID + "</ram:ID>"));// must be possible without scheme #
assertTrue(theXML.contains("<ram:ID>" + legalOrgID + "</ram:ID>"));// must be possible without scheme #
assertThat(theXML).valueByXPath("count(//*[local-name()='IncludedSupplyChainTradeLineItem'])")
.asInt()
.isEqualTo(1); //2 errors are OK because there is a known bug
@@ -90,6 +91,7 @@ public class XRTest extends TestCase {
}
public void testXREdgeExport() {
// the writing part
@@ -109,7 +111,7 @@ public class XRTest extends TestCase {
.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).setTaxExemptionReason("Kleinunternehmer"), amount, new BigDecimal(1.0)))
.setPayee( new TradeParty().setName("VR Factoring GmbH").setID("DE813838785").setLegalOrganisation(new LegalOrganisation("391200LDDFJDMIPPMZ54", "0199")))
.setPayee(new TradeParty().setName("VR Factoring GmbH").setID("DE813838785").setLegalOrganisation(new LegalOrganisation("391200LDDFJDMIPPMZ54", "0199")))
.embedFileInXML(fe1);
@@ -149,9 +151,9 @@ public class XRTest extends TestCase {
fail("ParseException not expected");
} catch (IOException e) {
fail("IOException not expected");
}
FileAttachment[] attachedFiles=readInvoice.getAdditionalReferencedDocuments();
assertNotNull(attachedFiles);
}
FileAttachment[] attachedFiles = readInvoice.getAdditionalReferencedDocuments();
assertNotNull(attachedFiles);
assertEquals(attachedFiles.length, 1);
assertTrue(Arrays.equals(attachedFiles[0].getData(), b));
@@ -289,6 +291,36 @@ public class XRTest extends TestCase {
}
public void testApplicablePercentInUntaxedService() {
// the writing part
TradeParty recipient = new TradeParty("Franz Müller", null, "55232", "Entenhausen", "DE");
String orgname = "Test company";
String number = "123";
String amountStr = "1.00";
BigDecimal amount = new BigDecimal(amountStr);
var i = new Invoice().setDueDate(new java.util.Date()).setIssueDate(new java.util.Date()).setDeliveryDate(new java.util.Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("DE4711").addVATID("DE0815").setEmail("info@example.org").setContact(new org.mustangproject.Contact("Hans Test", "+49123456789", "test@example.org")).addBankDetails(new org.mustangproject.BankDetails("DE12500105170648489890", "COBADEFXXX")))
.setRecipient(recipient)
.setReferenceNumber("991-01484-64")//leitweg-id
// not using any VAT, this is also a test of zero-rated goods:
.setNumber(number).addItem(new org.mustangproject.Item(new org.mustangproject.Product("Testprodukt", "", "C62", java.math.BigDecimal.ZERO).setTaxCategoryCode(TaxCategoryCodeTypeConstants.UNTAXEDSERVICE).setTaxExemptionReason("Expemtion reason"), amount, new java.math.BigDecimal(1.0)));
ZUGFeRD2PullProvider zf2p = new ZUGFeRD2PullProvider();
zf2p.setProfile(Profiles.getByName("XRechnung"));
zf2p.generateXML(i);
String theXML = new String(zf2p.getXML(), StandardCharsets.UTF_8);
// Untaxed services don't have the field RateApplicablePercent, since it would be always 0. An error is thrown on validation, if 0 is set.
assertThat(theXML).valueByXPath("count(//*[local-name()='RateApplicablePercent'])")
.asInt()
.isEqualTo(0);
//Exemption reason needs to be set if TaxCategoryCode == "O", reason should be at the product and in the ApplicableTradeTax
assertThat(theXML).valueByXPath("count(//*[local-name()='ExemptionReason'])")
.asInt()
.isEqualTo(2);
}
private org.mustangproject.Invoice createInvoice(TradeParty recipient) {
String orgname = "Test company";
String number = "123";

View File

@@ -141,7 +141,7 @@ public class ZF2PushTest extends TestCase {
String senderDescription = "Kleinunternehmer";
String taxID = "9990815";
String theNote="oh lala";
String theNote = "oh lala";
BigDecimal price = new BigDecimal(priceStr);
try {
InputStream SOURCE_PDF = this.getClass().getResourceAsStream("/MustangGnuaccountingBeispielRE-20170509_505blanko.pdf");
@@ -177,7 +177,7 @@ public class ZF2PushTest extends TestCase {
throw new RuntimeException(e);
}
assertEquals(i.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(),theNote);
assertEquals(i.getZFItems()[0].getNotesWithSubjectCode().get(0).getContent(), theNote);
assertEquals(senderDescription, i.getSender().getDescription());
// now check the contents (like MustangReaderTest)
@@ -255,16 +255,16 @@ public class ZF2PushTest extends TestCase {
// ze.setTransaction(new Invoice().setDueDate(new Date()).setIssueDate(new Date()).setDeliveryDate(new Date()).setSender(new TradeParty(orgname,"teststr", "55232","teststadt","DE")).setOwnTaxID("4711").setOwnVATID("DE0815").setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")).setNumber(number)
// .addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)))));
Invoice i=new Invoice().setDueDate(new Date()).setIssueDate(new Date())
Invoice i = new Invoice().setDueDate(new Date()).setIssueDate(new Date())
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE")
.setContact(new Contact("contact testname", "123456", "contact.testemail@example.org").setFax("0911623562")))
.setNumber(number)
.addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AReason").setTaxPercent(new BigDecimal(19)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1"))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance(new BigDecimal("0.1")).setReasonCode("95")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addAllowance(new Allowance().setPercent(new BigDecimal(50)).setReason("In love with salesperson")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(2.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("AnotherReason")))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1))).addAllowance(new Allowance(new BigDecimal("1"))));
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), amount, new BigDecimal(1.0)).addCharge(new Charge(new BigDecimal(1)).setReasonCode("ABK").setReason("Yet another reason")).addAllowance(new Allowance(new BigDecimal("1")).setReason("Something completely strange")));
ze.setTransaction(i);
@@ -275,21 +275,20 @@ public class ZF2PushTest extends TestCase {
fail("IOException should not be raised");
}
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_ITEMCHARGESALLOWANCESPDF);
assertEquals("EUR", zi.getInvoiceCurrencyCode());
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
assertTrue(zi.getUTF8().contains("ABK"));
// Reading ZUGFeRD
assertEquals("19.52", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {
// now check the contents (like MustangReaderTest)
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_ITEMCHARGESALLOWANCESPDF);
assertEquals("EUR", zi.getInvoiceCurrencyCode());
assertTrue(zi.getUTF8().contains("0911623562")); // fax number
assertTrue(zi.getUTF8().contains("ABK"));
// Reading ZUGFeRD
assertEquals("18.92", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
assertEquals(zi.getVersion(), 2);
} catch (Exception e) {
e.printStackTrace();
fail("Exception should not be raised");
}
}
@@ -595,7 +594,7 @@ public class ZF2PushTest extends TestCase {
assertEquals(1, i.getInvoiceReferencedDocuments().size());
assertEquals("abcd1234", i.getInvoiceReferencedDocuments().get(0).getIssuerAssignedID());
assertEquals("4304171000002", i.getRecipient().getGlobalID());
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(occurrenceFrom, sdf.format(i.getDetailedDeliveryPeriodFrom()));
assertEquals(occurrenceTo, sdf.format(i.getDetailedDeliveryPeriodTo()));
assertEquals("2001015001325", i.getZFItems()[0].getProduct().getGlobalID());
@@ -629,7 +628,7 @@ public class ZF2PushTest extends TestCase {
.setSender(new TradeParty(orgname, "teststr", "55232", "teststadt", "DE").addTaxID("4711").addVATID("DE0815"))
.setRecipient(new TradeParty("Franz Müller", "teststr.12", "55232", "Entenhausen", "DE"))
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19))))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)).addAllowance(new Allowance(BigDecimal.ONE)), new BigDecimal(500.0), qty).addAllowance(new Allowance(new BigDecimal(300)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance(new BigDecimal(600)).setTaxPercent(new BigDecimal(19)))
);
String theXML = new String(ze.getProvider().getXML());
@@ -645,7 +644,7 @@ public class ZF2PushTest extends TestCase {
assertEquals("EUR", zi.getInvoiceCurrencyCode());
// Reading ZUGFeRD
assertEquals("4046.00", zi.getAmount());
assertEquals("10805.20", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {
@@ -675,8 +674,8 @@ public class ZF2PushTest extends TestCase {
.setNumber(number)
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19))))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)))
.addItem(new Item(new Product("Testprodukt", "", "C62", new BigDecimal(19)), price, new BigDecimal(1.0)).addCharge(new Charge().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReasonCode("ABK")))
.addAllowance(new Allowance().setPercent(new BigDecimal(50)).setTaxPercent(new BigDecimal(19)).setReason("Mengenrabatt"))
);
String theXML = new String(ze.getProvider().getXML());
assertTrue(theXML.contains("<rsm:CrossIndustryInvoice"));
@@ -689,7 +688,7 @@ public class ZF2PushTest extends TestCase {
ZUGFeRDImporter zi = new ZUGFeRDImporter(TARGET_RELATIVECHARGESALLOWANCESPDF);
assertEquals("CHF", zi.getInvoiceCurrencyCode());
assertEquals("6.25", zi.getAmount());
assertEquals("11.10", zi.getAmount());
assertEquals(orgname, zi.getHolder());
assertEquals(number, zi.getForeignReference());
try {

View File

@@ -34,12 +34,14 @@ import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
@@ -262,9 +264,27 @@ public class ZF2ZInvoiceImporterTest extends ResourceCase {
}
assertFalse(hasExceptions);
TransactionCalculator tc = new TransactionCalculator(invoice);
assertEquals(new BigDecimal("19.52"), tc.getGrandTotal());
assertEquals(new BigDecimal("18.92"), tc.getGrandTotal());
}
public void testIBANImport() {
File CIIinputFile = getResourceAsFile("cii/lastschrift_iban_bug.xml");
try {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter(new FileInputStream(CIIinputFile));
CalculatedInvoice invoice = new CalculatedInvoice();
zii.extractInto(invoice);
assertEquals("DE11111111111111111111", invoice.getCreditorReferenceID());
assertEquals("DE22222222222222222222", invoice.getSender().getBankDetails().stream().map(BankDetails::getIBAN).collect(Collectors.joining(",")));
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
public void testBasisQuantityImport() {
ZUGFeRDInvoiceImporter zii = new ZUGFeRDInvoiceImporter("./target/testout-ZF2newEdge.pdf");

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:a="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:10" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>123456</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20250426</udt:DateTimeString>
</ram:IssueDateTime>
<ram:IncludedNote>
<ram:Content>Jahresbeitrag</ram:Content>
</ram:IncludedNote>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Product: Plus</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>239.00</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">1.00</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>239.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:IncludedSupplyChainTradeLineItem>
<ram:AssociatedDocumentLineDocument>
<ram:LineID>1</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>Getting Started-Rabatt</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:NetPriceProductTradePrice>
<ram:ChargeAmount>120.00</ram:ChargeAmount>
</ram:NetPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="H87">-1.00</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>-120.00</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:BuyerReference>7777777</ram:BuyerReference>
<ram:SellerTradeParty>
<ram:Name>---</ram:Name>
<ram:DefinedTradeContact>
<ram:PersonName>---</ram:PersonName>
<ram:DepartmentName>Geschäftsführer</ram:DepartmentName>
<ram:TelephoneUniversalCommunication>
<ram:CompleteNumber>---</ram:CompleteNumber>
</ram:TelephoneUniversalCommunication>
<ram:EmailURIUniversalCommunication>
<ram:URIID>q@q.com</ram:URIID>
</ram:EmailURIUniversalCommunication>
</ram:DefinedTradeContact>
<ram:PostalTradeAddress>
<ram:PostcodeCode>12345</ram:PostcodeCode>
<ram:LineOne>Str. 11</ram:LineOne>
<ram:CityName>---</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:URIUniversalCommunication>
<ram:URIID schemeID="EM">a@a.com</ram:URIID>
</ram:URIUniversalCommunication>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE111111111</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>a &amp; b GmbH</ram:Name>
<ram:PostalTradeAddress>
<ram:PostcodeCode>123456</ram:PostcodeCode>
<ram:LineOne>--</ram:LineOne>
<ram:LineTwo>Str. 17</ram:LineTwo>
<ram:CityName>--</ram:CityName>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery/>
<ram:ApplicableHeaderTradeSettlement>
<ram:CreditorReferenceID>DE11111111111111111111</ram:CreditorReferenceID>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>59</ram:TypeCode>
<ram:PayerPartyDebtorFinancialAccount>
<ram:IBANID>DE22222222222222222222</ram:IBANID>
</ram:PayerPartyDebtorFinancialAccount>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>22.61</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>119.00</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:BillingSpecifiedPeriod>
<ram:StartDateTime>
<udt:DateTimeString format="102">20250426</udt:DateTimeString>
</ram:StartDateTime>
<ram:EndDateTime>
<udt:DateTimeString format="102">20250525</udt:DateTimeString>
</ram:EndDateTime>
</ram:BillingSpecifiedPeriod>
<ram:SpecifiedTradePaymentTerms>
<ram:Description>Zahlbar sofort</ram:Description>
<ram:DueDateDateTime>
<udt:DateTimeString format="102">20250426</udt:DateTimeString>
</ram:DueDateDateTime>
</ram:SpecifiedTradePaymentTerms>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>119.00</ram:LineTotalAmount>
<ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount>
<ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount>
<ram:TaxBasisTotalAmount>119.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">22.61</ram:TaxTotalAmount>
<ram:GrandTotalAmount>141.61</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>141.61</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>

File diff suppressed because it is too large Load Diff

View File

@@ -1665,7 +1665,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Discount (net):</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Net list price:</div>
@@ -1977,7 +1977,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Discount (net):</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Net list price:</div>
@@ -2133,7 +2133,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Discount (net):</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Net list price:</div>
@@ -2629,4 +2629,4 @@ function downloadData (element_id) {
});
//
</script></html>
</script></html>

View File

@@ -1379,7 +1379,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Discount (net):</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Net list price:</div>
@@ -1535,7 +1535,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Discount (net):</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Net list price:</div>
@@ -1758,7 +1758,7 @@
</div>
<div class="boxzeile">
<div class="boxdaten legende">Process ID:</div>
<div id="BT-23" title="BT-23" class="boxdaten wert"></div>
<div id="BT-23" title="BT-23" class="boxdaten wert">urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende">Specification ID:</div>
@@ -1948,4 +1948,4 @@ function downloadData (element_id) {
});
//
</script></html>
</script></html>

View File

@@ -1395,7 +1395,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Remise nette:</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Prix catalogue (net):</div>
@@ -1551,7 +1551,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Remise nette:</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Prix catalogue (net):</div>
@@ -1707,7 +1707,7 @@
<div class="boxtabelle boxinhalt noPaddingTop borderSpacing">
<div class="boxzeile">
<div class="boxdaten legende ">Remise nette:</div>
<div id="BT-147" title="BT-147" class="boxdaten wert"></div>
<div id="BT-147" title="BT-147" class="boxdaten wert">0,00</div>
</div>
<div class="boxzeile">
<div class="boxdaten legende ">Prix catalogue (net):</div>

View File

@@ -1,174 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?><Invoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>RE-20201121/508</cbc:ID>
<cbc:IssueDate>2020-11-21</cbc:IssueDate>
<cbc:DueDate>2020-12-12</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>AB321</cbc:BuyerReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PostalAddress>
<cbc:StreetName>Ecke 12</cbc:StreetName>
<cbc:CityName>Stadthausen</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE136695976</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Bei Spiel GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>2</cbc:ID>
</cac:PartyIdentification>
<cac:PostalAddress>
<cbc:StreetName>Bahnstr. 42</cbc:StreetName>
<cbc:CityName>Spielkreis</cbc:CityName>
<cbc:PostalZone>88802</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Theodor Est</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2020-11-10</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Bank transfer">42</cbc:PaymentMeansCode>
<cbc:PaymentID>RE-20201121/508</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>DE88200800000970375700</cbc:ID>
<cbc:Name>Max Mustermann</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>COBADEFFXXX</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Zahlbar ohne Abzug bis 12.12.2020</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">75.04</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">160</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">11.2</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">336</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">63.84</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">496</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">496</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">571.04</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">571.04</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">160</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Of a sample invoice</cbc:Description>
<cbc:Name>Design (hours)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">160</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="HUR">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">160</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cec="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
<cbc:ID>RE-20201121/508</cbc:ID>
<cbc:IssueDate>2020-11-21</cbc:IssueDate>
<cbc:DueDate>2020-12-12</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cbc:BuyerReference>AB321</cbc:BuyerReference>
<cac:AccountingSupplierParty>
<cac:Party>
<cac:PostalAddress>
<cbc:StreetName>Ecke 12</cbc:StreetName>
<cbc:CityName>Stadthausen</cbc:CityName>
<cbc:PostalZone>12345</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE136695976</cbc:CompanyID>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Bei Spiel GmbH</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
<cac:Party>
<cac:PartyIdentification>
<cbc:ID>2</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">400</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">316</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>various colors, ~2000ml</cbc:Description>
<cbc:Name>Ballons</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.79</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="H87">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">0.79</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:InvoicedQuantity unitCode="LTR">800</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">20</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Hot air „heiße Luft“ (litres)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.025</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="LTR">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">0.025</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
</cac:PartyIdentification>
<cac:PostalAddress>
<cbc:StreetName>Bahnstr. 42</cbc:StreetName>
<cbc:CityName>Spielkreis</cbc:CityName>
<cbc:PostalZone>88802</cbc:PostalZone>
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode>
</cac:Country>
</cac:PostalAddress>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Theodor Est</cbc:RegistrationName>
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:Delivery>
<cbc:ActualDeliveryDate>2020-11-10</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:PaymentMeans>
<cbc:PaymentMeansCode name="Bank transfer">42</cbc:PaymentMeansCode>
<cbc:PaymentID>RE-20201121/508</cbc:PaymentID>
<cac:PayeeFinancialAccount>
<cbc:ID>DE88200800000970375700</cbc:ID>
<cbc:Name>Max Mustermann</cbc:Name>
<cac:FinancialInstitutionBranch>
<cbc:ID>COBADEFFXXX</cbc:ID>
</cac:FinancialInstitutionBranch>
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Zahlbar ohne Abzug bis 12.12.2020</cbc:Note>
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">75.04</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">160</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">11.2</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">336</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="EUR">63.84</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">496</cbc:LineExtensionAmount>
<cbc:TaxExclusiveAmount currencyID="EUR">496</cbc:TaxExclusiveAmount>
<cbc:TaxInclusiveAmount currencyID="EUR">571.04</cbc:TaxInclusiveAmount>
<cbc:AllowanceTotalAmount currencyID="EUR">0</cbc:AllowanceTotalAmount>
<cbc:ChargeTotalAmount currencyID="EUR">0</cbc:ChargeTotalAmount>
<cbc:PrepaidAmount currencyID="EUR">0</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">571.04</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="HUR">1</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">160</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>Of a sample invoice</cbc:Description>
<cbc:Name>Design (hours)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>7</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">160</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="HUR">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">160</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>2</cbc:ID>
<cbc:InvoicedQuantity unitCode="H87">400</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">316</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Description>various colors, ~2000ml</cbc:Description>
<cbc:Name>Ballons</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.79</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="H87">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">0.79</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
<cac:InvoiceLine>
<cbc:ID>3</cbc:ID>
<cbc:InvoicedQuantity unitCode="LTR">800</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="EUR">20</cbc:LineExtensionAmount>
<cac:Item>
<cbc:Name>Hot air „heiße Luft“ (litres)</cbc:Name>
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID>
<cbc:Percent>19</cbc:Percent>
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">0.025</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="LTR">1</cbc:BaseQuantity>
<cac:AllowanceCharge>
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
<cbc:Amount currencyID="EUR">0</cbc:Amount>
<cbc:BaseAmount currencyID="EUR">0.025</cbc:BaseAmount>
</cac:AllowanceCharge>
</cac:Price>
</cac:InvoiceLine>
</Invoice>

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.17.0-SNAPSHOT</version> <packaging>pom</packaging>
<version>2.17.1-SNAPSHOT</version> <packaging>pom</packaging>
<name>Mustang</name>
<modules>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>org.mustangproject</groupId>
<artifactId>core</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mustangproject</groupId>
@@ -11,7 +11,7 @@
<name>Library to validate e-invoices (ZUGFeRD, Factur-X and Xrechnung)</name>
<packaging>jar</packaging>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
<repositories>
<repository>
<!-- for jargs -->
@@ -38,7 +38,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>library</artifactId>
<version>2.17.0-SNAPSHOT</version>
<version>2.17.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>

View File

@@ -61,6 +61,9 @@ public abstract class Validator {
Source xmlData = new StreamSource(new ByteArrayInputStream(xmlRawData));
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
Schema schema = schemaFactory.newSchema(schemaFile);
javax.xml.validation.Validator validator = schema.newValidator();
validator.validate(xmlData);