Erste Versuche mit mustangproject scheitern

Hein_nieH

Bekanntes Mitglied
Hallo,

ich mache erste Versuche mit dem mustangproject.
Mein Ziel ist es eine, pdf-Rechnung mit PDFBox zu bauen (Das funktioniert auch)
Im Anschluss soll die erzeugte Rechnung in eine Zugferd-Rechnung mit pdf/a-3 Format überführt werden.
Im Mustangproject sollen u.a. auch die zugehörige xml-Daten generiert werden.

Das im Netz gefundenen Testbeispiel bekomme ich leider nicht zum Laufen :-(
Es klemmt an nachstehenden zwei Stellen:

Item item = new Item()
.setProduct(new org.mustangproject.Product("Beratungsleistung", "Stunden", BigDecimal.valueOf(100.00)))
.setQuantity(BigDecimal.valueOf(5));
The constructor Product(String, String, BigDecimal) is undefined


ZUGFeRDExporterFromPDFA exporter = new ZUGFeRDExporterFromPDFA()
.setZUGFeRDVersion(2) // z.B. Version 2.x
.setProfile("comfort"); // Profil (z.B. comfort / Factur-X)
(hier bei setProfile)
The method setProfile(Profile) in the type IZUGFeRDExporter is not applicable for the arguments (String)

Ich nutze Java 11.
Vom Mustangprojekt habe ich die Version 2.25.0 eingebunden.

XML:
        <dependency>
            <groupId>org.mustangproject</groupId>
            <artifactId>library</artifactId>
            <version>2.26.0</version>
        </dependency>

Hier der Experimentiercode:

Java:
import java.io.IOException;
import org.mustangproject.ZUGFeRD.*;
import org.mustangproject.Invoice;
import org.mustangproject.Item;
import org.mustangproject.TradeParty;

import java.math.BigDecimal;
import java.util.Date;

public class ZugferdErsteller {
    public static void main(String[] args) {
        try {
            // Rechnungsdaten initialisieren
            Invoice invoice = new Invoice()
                    .setNumber("RE-2026-0001")
                    .setIssueDate(new Date())
                    .setDeliveryDate(new Date())
                    .setSender(new TradeParty("Lieferanten GmbH", "Musterstraße 1", "12345", "Musterstadt", "DE"))
                    .setRecipient(new TradeParty("Kunden AG", "Beispielweg 5", "54321", "Beispieldorf", "DE"));

            // Position hinzufügen
            Item item = new Item()
                    .setProduct(new org.mustangproject.Product("Beratungsleistung", "Stunden", BigDecimal.valueOf(100.00)))
                    .setQuantity(BigDecimal.valueOf(5));
            invoice.addItem(item);

            // ZUGFeRD-Transaktor /Exporter einrichten
            ZUGFeRDExporterFromPDFA exporter = new ZUGFeRDExporterFromPDFA()
                    .setZUGFeRDVersion(2) // z.B. Version 2.x
                    .setProfile("comfort"); // Profil (z.B. comfort / Factur-X)

            // XML in ein bestehendes PDF einbetten (oder ein neues generieren)
            // Hier als Beispiel-Methodenaufruf:
            // exporter.pdfAsynchronToPDF("input.pdf", "output_zugferd.pdf", invoice);
           
            System.out.println("ZUGFeRD-Rechnung erfolgreich vorbereitet!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Hat jemand eine Idee was hier nicht richtig läuft.
Ich wäre froh, wenn ich eine einfache Routine zum Laufen bekommen würde, um damit weiter zu experimentieren.

Viele Gruesse
Hein_nieH
 
Die Übersetzungsprobleme deuten darauf hin, dass die API nicht richtig genutzt wird. Evtl. hat sich die API verändert von der Version der Beispiele hin zu der genutzten Version. Also einmal prüfen, welche Version die Beispiele nutzten (was nicht die 2.26.0 sein dürfte - evtl. die 2.25.0, die Du im Text erwähnst, falls das nicht einfach nur ein Tippfehler war).

Die Meldungen besagen halt, dass der Konstruktor nicht existiert. Also die API Beschreibung ansehen, was die Klasse Product für Konstruktoren bereitstellt.

Ebenso setProfile: Da wird kein String erwartet sondern ein Profile. Also musst Du vermutlich erst das Profile mit dem gewünschten Namen heraussuchen.

Ansonsten wäre mein Tipp: Hast Du mal eine KI genutzt für diese Fragestellung? Ich habe es einfach einmal an ChatGPT gegeben und da kam dann noch einiges mehr an Hinweisen zur Nutzung, aber da ich mich damit nicht auskenne konnte ich es nicht bewerten. Daher packe ich es einfach mal als Spoiler mit rein, falls Du einmal einen Blick drauf werfen möchtest. (Aber ich selbst bin halt ständig mit KI unterwegs - gerade das Thema Analysen und so ist einfach unschlagbar ... gerade mit einer Lösung wie Cursor, die das ganze Projekt auch indiziert hat und so mit wenig Token auf relevante Dinge zugreifen kann.)

Ja. Das Problem ist ziemlich klar: Das Beispiel, das Hein_nieH gefunden hat, passt nicht zur aktuellen Mustangproject-API. Seine beiden Compilerfehler sind keine Java-11-Probleme, sondern schlicht falsche bzw. veraltete API-Aufrufe. Im Thread steht zwar einmal 2.25.0, im Maven-Snippet aber bereits library:2.26.0; entscheidend ist: Die verwendeten Aufrufe stimmen mit der aktuellen API nicht überein. Java - Hilfe | Java-Forum.org

1.​

Er hat:

new Product(
"Beratungsleistung",
"Stunden",
BigDecimal.valueOf(100.00)
)
Der aktuelle Konstruktor wird in Mustang selbst beispielsweise so verwendet:

new Product(
"Testprodukt",
"",
"C62",
new BigDecimal("19")
)
Die Parameter sind dabei sinngemäß:

Name
Beschreibung
Mengeneinheit (UN/ECE Recommendation 20 Code)
Umsatzsteuersatz
Der Preis gehört nicht in Product. Er gehört zum Item.

Für 5 Stunden Beratungsleistung à 100 € mit 19 % USt. wäre das also beispielsweise:

Product product = new Product(
"Beratungsleistung",
"Stunden",
"HUR",
new BigDecimal("19")
);

Item item = new Item(
product,
new BigDecimal("100.00"),
new BigDecimal("5")
);
HUR ist der Unit-Code für Stunde. Die Mustang-eigenen Tests zeigen genau dieses Schema new Item(new Product(...), price, quantity). GitHub

Das im Forum gefundene Beispiel hat offenbar die Semantik durcheinandergebracht: Die 100.00 wurden dort als dritter Product-Parameter interpretiert, tatsächlich braucht Mustang an dieser Stelle erst den Unit-Code und dann den Steuersatz.

2.​

Der Compiler sagt völlig korrekt:

The method setProfile(Profile) ... is not applicable
for the arguments (String)
Also:

.setProfile("comfort")
ersetzen durch beispielsweise:

.setProfile(Profiles.getByName("EN16931"))
bzw. je nach Ziel:

.setProfile(Profiles.getByName("XRechnung"))
Die aktuellen Mustang-Beispiele verwenden genau dieses Verfahren. Auch die Release Notes dokumentieren Profiles.getByName(...). GitHub

Interessant ist allerdings: Im aktuellen Mustang-Repository finden sich in Tests teilweise auch noch String-Aufrufe wie .setProfile("extended"). Das dürfte erklären, warum man im Internet widersprüchliche Beispiele findet; je nach konkretem Typ bzw. API-Kette landet man aber beim IZUGFeRDExporter, dessen setProfile einen Profile erwartet. Für neuen Code würde ich daher konsequent die typsichere Profile-Variante verwenden. GitHub

3.​

Das ist ein weiterer Punkt des Beispiels. COMFORT stammt aus der ZUGFeRD-1-Profilwelt. Mustang unterscheidet bei ZUGFeRD 2 unter anderem:

MINIMUM
BASICWL
BASIC
CIUS
EN16931
XRECHNUNG
EXTENDED
Die Mustang-CLI selbst bildet bei ZUGFeRD 2 beispielsweise E auf EN16931 und X auf XRECHNUNG ab. COMFORT wird dagegen bei ZUGFeRD 1 verwendet. GitHub

Wenn das Ziel eine normale aktuelle ZUGFeRD-Rechnung ist, würde ich daher eher

.setProfile(Profiles.getByName("EN16931"))
nehmen.

4. Der größere Fehler: So funktioniert der Export noch nicht​

Selbst wenn man die beiden Compilerfehler korrigiert, macht der gezeigte Code noch keine ZUGFeRD-Rechnung.

Der richtige Ablauf ist ungefähr:

Invoice
│
├── Sender/Empfänger
├── Positionen
└── Zahlungs-/Steuerinformationen
│
▼
ZUGFeRDExporterFromPDFA
│
├── load("rechnung.pdf")
├── setProfile(...)
├── setTransaction(invoice)
│
▼
export("rechnung-zugferd.pdf")
Genau dieses Prinzip verwendet Mustang selbst. Die CLI erzeugt einen ZUGFeRDExporterFromPDFA, lädt das vorhandene PDF mit load(...), setzt Version und Profil und exportiert anschließend. GitHub

Ein minimales Grundgerüst wäre daher eher:

Invoice invoice = new Invoice()
.setNumber("RE-2026-0001")
.setIssueDate(new Date())
.setDeliveryDate(new Date())
.setSender(
new TradeParty(
"Lieferanten GmbH",
"Musterstraße 1",
"12345",
"Musterstadt",
"DE"
)
)
.setRecipient(
new TradeParty(
"Kunden AG",
"Beispielweg 5",
"54321",
"Beispieldorf",
"DE"
)
);

Product product = new Product(
"Beratungsleistung",
"Stunden",
"HUR",
new BigDecimal("19")
);

Item item = new Item(
product,
new BigDecimal("100.00"),
new BigDecimal("5")
);

invoice.addItem(item);

try (ZUGFeRDExporterFromPDFA exporter =
new ZUGFeRDExporterFromPDFA()) {

exporter
.load("rechnung.pdf")
.setZUGFeRDVersion(2)
.setProfile(Profiles.getByName("EN16931"))
.setTransaction(invoice)
.export("rechnung-zugferd.pdf");
}
Das entspricht wesentlich eher der API, die Mustang selbst verwendet. GitHub

Allerdings fehlen für eine wirklich valide EN16931/ZUGFeRD-Rechnung noch einige Rechnungsdaten. Ich würde diesen Code deshalb nicht einfach als „fertiges Beispiel“ ins Forum stellen, sondern noch vervollständigen und anschließend mit Mustang bzw. einem unabhängigen Validator prüfen.

5. Ein Punkt, der mich an dem Forumspost stutzig macht​

Hein_nieH schreibt:

„Vom Mustangprojekt habe ich die Version 2.25.0 eingebunden.“
Direkt darunter steht aber:

<version>2.26.0</version>
``` :chatgpt-content-reference{index="7"}


Das sollte er zuerst eindeutig klären. Die aktuelle Mustang-Historie zeigt zudem, dass sich die Bibliothek weiterentwickelt und APIs/Validierungsverhalten verändert wurden; 2.21.0 stammt beispielsweise vom Dezember 2025 und spätere Releases enthalten zahlreiche Änderungen. :chatgpt-content-reference{index="8"}

Unterm Strich: Sein Java-Setup ist sehr wahrscheinlich in Ordnung. Das „Testbeispiel aus dem Netz“ ist das Problem. Es verwendet mindestens einen falschen Product-Konstruktor, behandelt den Preis fälschlich als Produkteigenschaft und verwendet eine nicht passende Profil-API bzw. mit comfort sogar ein Profil aus der ZUGFeRD-1-Welt.

Wenn Du möchtest, kann ich als Nächstes die aktuelle Mustangproject-2.26.0-API direkt aus dem GitHub-Repository durchgehen und daraus ein wirklich vollständiges, kompilierbares Java-11-Beispiel bauen: vorhandenes PDF → EN16931 XML erzeugen → XML als ZUGFeRD in PDF/A-3 einbetten → fertige PDF validieren. Das wäre für eine Antwort im Forum wahrscheinlich am hilfreichsten.

Edit: Evtl. bezüglich KI noch den dezenten Hinweis: Gerade mit veränderten APIs haben KIs oft eher Probleme, da diese ja mit irgendwelchen Daten trainiert wurden, d.h. sie kennen alle möglichen Beispiele von allen Versionen der APIs. Das führt zu Problemen und da hilft es, den KIs klares Wissen zu der verwendeten API zu geben. Das gibt es oft bereits schon fertig aber lässt sich ansonsten auch von der KI selbst generieren - was aber etwas mehr Aufwand ist aber bei Open Source auch oft nur eine Hand voll Anfragen.
 
Hallo KonradN,

vielen Dank für die Antwort.
Leider habe ich für ChatGPT noch kein professionelles Abonement.

Ich habe für den Beispielcode die Version 2.26.0 eingebunden. (2.25.0 war ein Schreibfehler in der eile heute morgen).
Nach anderen einfachen Codebeispielen zur Erstellung von Zugferd-2 Rechnungen habe ich im Netz auch gesucht.
Leider habe ich noch nichts passendes (auf meinem Rechner Lauffähiges) gefunden.
Es bleibt mir wohl nur übrig, die Dokumentationen der Libarys durchzuforsten, um dann eine lauffähige Experimentierversion zu basteln.

Gruss Hein_nieH


​

 
Leider habe ich für ChatGPT noch kein professionelles Abonement.
Hol dir doch ein Copilot-Abo?

Hier ist eine Korrektur...

Java:
import java.math.BigDecimal;
import java.util.Date;
import org.mustangproject.Invoice;
import org.mustangproject.Item;
import org.mustangproject.Product;
import org.mustangproject.TradeParty;
import org.mustangproject.ZUGFeRD.Profiles;
import org.mustangproject.ZUGFeRD.ZUGFeRDExporterFromPDFA;

public class ZugferdErsteller {
  public static void main(String[] args) {
    try {
      // Rechnungsdaten initialisieren
      Invoice invoice =
          new Invoice()
              .setNumber("RE-2026-0001")
              .setIssueDate(new Date())
              .setDeliveryDate(new Date())
              .setSender(
                  new TradeParty(
                      "Lieferanten GmbH", "Musterstraße 1", "12345", "Musterstadt", "DE"))
              .setRecipient(
                  new TradeParty("Kunden AG", "Beispielweg 5", "54321", "Beispieldorf", "DE"));

      // Position hinzufügen
      Item item =
          new Item()
              .setProduct(
                  new Product(
                      "Beratungsleistung", "Beratungsleistung", "HUR", BigDecimal.valueOf(19)))
              .setPrice(BigDecimal.valueOf(100))
              .setQuantity(BigDecimal.valueOf(5));
      invoice.addItem(item);

      // ZUGFeRD-Daten in ein vorhandenes PDF/A einbetten.
      try (ZUGFeRDExporterFromPDFA exporter = new ZUGFeRDExporterFromPDFA()) {
        exporter.load("input.pdf");
        exporter.setZUGFeRDVersion(2);
        exporter.setProfile(Profiles.getByName("EN16931", 2));
        exporter.setTransaction(invoice);
        exporter.export("output_zugferd.pdf");
      }

      System.out.println("ZUGFeRD-Rechnung erfolgreich vorbereitet!");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Die Compile-Fehler in ZugferdErsteller.java sind behoben:
•
Korrekte Product-Konstruktorparameter inklusive Mehrwertsteuersatz verwendet.
•
Einzelpreis über Item.setPrice(...) gesetzt.
•
Ungültige Verkettung des Exporters entfernt.
•
Nicht vorhandenen Aufruf pdfAsynchronToPDF(...) durch die gültige API ersetzt: load(...), setTransaction(...), export(...).
•
Gültiges ZUGFeRD-2-Profil EN16931 verwendet.
•
Imports bereinigt.
Für die Ausführung muss weiterhin eine gültige PDF/A-Datei unter input.pdf vorhanden sein.
 
Hallo,

ich habe gestern Abend die Hinweise von ChatGPT umgesetzt.
Danach lief alles prima 🙂
Bei der Korrektur bin ich dann zu fast identischen Codezeilen gekommen wie der User Kaltblueter
(Trotzdem Danke für die Bemühung).

Kurze Frage zu den Profilen
Profiles.getByName("EN16931", 2)

Wie kann ich mir die vorhandenen Profile per Schleife auflisten lassen.
Welche Bedeutung hat hier der Parameter 2?

Nun kann ich endlich am Wochenende mit dem Codegerüst etwas experimentieren.

Noch eine Frage zu PDFBox.
Gibt es eine Möglichkeit, dass der im PDF-Dokument erzeugte Inhalt nicht kopierbar ist?
Ich habe da noch wenig Erfahrung.

Gruss Hein_nieH
 
Also Profiles sollte ein getList() haben, daher sollte etwas funktionieren wie

Java:
import org.mustangproject.ZUGFeRD.Profiles;

public class Main {
    public static void main(String[] args) {
        Profiles.getList().forEach(profile ->
            System.out.println(profile.getName())
        );
    }
}

Ansonsten ist der zweite Parameter bei getByName die Version von ZUGFeRD. Mustang unterstützt mehrere Versionen, daher muss man das mit angeben.

Bezüglich KI ist mein Tipp immer noch cursor. Ich habe einiges ausprobiert und das ist aktuell von der Leistung und den Kosten die beste Lösung. Maximal das Problem: Die 20€ / Monat hatten bei mir teilweise Meldungen gebracht, dass es ausgelastet ist und ich es später probieren solle. Daher habe ich die 60€/Monat Subscription damit meine Prioritäten höher sind. Und ich arbeite mit deren höchsten Modell in High Fast Einstellung und trotz täglicher Nutzung reichen da die einbezogenen Token. (Bei Github Copilot und auch ChatGPT brenne ich durch die Token extrem schnell...)
 
Hallo KonradN

danke für die Rückmeldung.
Ich war etwas irritiert mit dem zweiten Parameter, da der Exporter einen eigenen Setter
setZUGFeRDVersion(2) hatte.
setProfile hat dann wohl mehrere Varianten (einmal nur das Profil und einmal Profil und Zugferdversion).

Gruss Hein_nieH
 

Neue Themen


Zurück
Oben