Logo
  • English icon
  • German icon
Artikel

E-Rechnung und PDF automatisch verarbeiten: exakt ausgelesen, geprüft vor dem Speichern

dotnete-invoicingxrechnungzugferdopenaipostgresql

Das Video ist auf Englisch.

Seit dem 1. Januar 2025 muss jedes Unternehmen in Deutschland E-Rechnungen von anderen Unternehmen empfangen können. Ab 2027 müssen größere Unternehmen sie auch verschicken, ab 2028 alle. Bis dahin kommen weiter PDF-Rechnungen, manche davon eingescannt. Der Rechnungseingang bleibt also über Jahre gemischt.

Dieser Artikel zeigt, wie eine Anwendung beides automatisch verarbeitet, ohne dass falsche Zahlen im System landen. Es ist mein eigenes Referenzprojekt, der Quellcode ist öffentlich. Stand Oktober 2026, keine Steuerberatung.

Das Wichtigste in Kürze

  • E-Rechnungen brauchen keine KI. XRechnung und ZUGFeRD enthalten alle Rechnungsdaten bereits strukturiert. Sie werden exakt ausgelesen, in Millisekunden.
  • KI nur für PDFs ohne E-Rechnungsdaten, auch für Scans. Das Modell liefert die Daten in einem festen JSON-Schema zurück, so wie sie gedruckt sind.
  • Jede Rechnung wird vor dem Speichern geprüft, egal woher sie kommt: Positionen, Summen, Umsatzsteuer je Steuersatz. Geht ein KI-Ergebnis nicht auf, liest die Anwendung das Dokument ein zweites Mal. Geht es dann immer noch nicht auf, wird die Rechnung nicht gespeichert, und der Grund steht direkt da.
  • Duplikate werden formatübergreifend erkannt: dieselbe Rechnung als XRechnung und als PDF per E-Mail.

Der Zeitplan in Deutschland

DatumWas sich ändert (Rechnungen zwischen Unternehmen im Inland)
1. Januar 2025Jedes Unternehmen muss E-Rechnungen empfangen können
1. Januar 2027Unternehmen mit mehr als 800.000 € Vorjahresumsatz müssen E-Rechnungen versenden
1. Januar 2028Alle Unternehmen müssen E-Rechnungen versenden

Bis zum Ende der Übergangsregeln bleiben Papier- und PDF-Rechnungen erlaubt. XRechnung (reines XML) und ZUGFeRD (ein PDF, in dem dieselben Daten als XML stecken) sind die gängigen deutschen Formate nach der europäischen Norm EN 16931.

Ein Weg für jede Datei

Jede Datei geht denselben Weg: lesen, prüfen, speichern.

  1. XRechnung (UBL oder CII) wird direkt aus dem XML gelesen.
  2. ZUGFeRD / Factur-X: Die Anwendung findet das XML in der PDF und liest es genauso aus. Keine KI, kein Raten.
  3. Jede andere PDF, auch ein Scan ohne Textebene, geht an die KI (OpenAI, PDF als Datei, Structured Outputs).
  4. Jedes Ergebnis durchläuft dieselbe Prüfung. Erst danach wird es in PostgreSQL gespeichert, mit dem Original im Archiv und einem Eintrag im Protokoll.

In einem Testlauf mit sieben erfundenen Rechnungen (zwei XRechnungen, eine ZUGFeRD-Rechnung, drei normale PDFs, ein Scan) waren die E-Rechnungen in Millisekunden fertig, jede PDF brauchte ein paar Sekunden. Ergebnis: fünf importiert, ein Duplikat, eine abgelehnt. Genau so soll es sein.

Ein Reader je Format

Jedes Format hat einen eigenen Reader hinter einer gemeinsamen Schnittstelle. Ein neues Format ist einfach ein weiterer Reader:

public interface IInvoiceReader
{
bool CanRead(InvoiceDocument document);
Task<ExtractionResult> ReadAsync(
InvoiceDocument document, CancellationToken ct);
}

Im Repository gibt es drei: XmlInvoiceReader (XRechnung UBL und CII), ZugferdPdfReader (ZUGFeRD und Factur-X) und OpenAiPdfReader (PDFs und Scans).

Ein festes Format für die KI

Das Modell muss genau diese Felder liefern, im Strict-Modus, damit die Antwort immer zum Schema passt. Ein Auszug:

"lines": [{
"quantity": { "type": "number" },
"free_quantity": { "type": "number" },
"unit_price": { "type": "number" },
"discount_percent": { "type": ["number", "null"] },
"net_amount": { "type": "number" },
"vat_rate": { "type": ["number", "null"] }
}]

Die wichtigste Zeile der Anweisung an das Modell:

Return exactly the values printed on the document. Never calculate, correct or guess values. If printed totals do not add up, still return them as printed.

Das Modell darf nichts ausrechnen und nichts korrigieren. Nur so kann die Prüfung danach Fehler finden. Im Testlauf hat die KI Positionen, Gratismengen, Rabatte und zwei Steuersätze erkannt (19 % für Getränke, 7 % für Kaffee), auf dem Scan auch eine Bonusmenge als eigene Position.

Die Prüfung

Die KI liest gut, aber nicht fehlerfrei. Und auch gedruckte Rechnungen können falsch sein. Deshalb rechnet die Anwendung jede Rechnung nach:

  • je Position: Menge × Preis − Rabatt = Positionsbetrag
  • Summe der Positionen (minus Abschläge, plus Zuschläge) = Nettobetrag
  • Netto + Umsatzsteuer = Brutto
  • Umsatzsteuer je Steuersatz, auch bei mehreren Sätzen auf einer Rechnung
public static decimal ExpectedLineAmount(InvoiceLine line)
{
var gross = line.Quantity * line.UnitPrice;
var discount = line.DiscountAmount
?? (line.DiscountPercent is { } percent ? Round(gross * percent / 100m) : 0m);
return Round(gross - discount);
}

Fällt ein KI-Ergebnis durch die Prüfung, wird das Dokument ein zweites Mal gelesen; eine falsch gelesene Ziffer wiederholt sich meist nicht. Deterministische Reader (XML) lesen nicht erneut. Geht die Rechnung dann immer noch nicht auf, wird sie nicht gespeichert, und der Grund steht direkt da, zum Beispiel: Summe der Positionen 456,70 ≠ Nettobetrag 465,70. Im Testlauf war das eine Testrechnung mit absichtlich falsch gedrucktem Nettobetrag, abgelehnt auch beim zweiten Lesen.

Duplikate

  • Dieselbe Datei noch einmal wird am SHA-256-Fingerabdruck erkannt, bevor überhaupt die KI gefragt wird.
  • Dieselbe Rechnung in einem anderen Format, zum Beispiel als XRechnung und noch einmal als PDF per E-Mail, wird an der Umsatzsteuer-ID des Lieferanten und an der Rechnungsnummer erkannt.

Grenzen und Datenschutz

  • Sehr kleine Schrift auf einem Scan liest die KI nicht immer richtig. Die Beträge sichert die Prüfung ab, aber eine ungewöhnliche Anschrift sollte ein Mensch ansehen.
  • An die KI gehen nur PDFs ohne E-Rechnungsdaten. XRechnung und ZUGFeRD bleiben im Haus. Anfragen gehen mit store = false raus. Ob Rechnungsdaten an einen externen Dienst dürfen, entscheiden Sie.

Unter der Haube

C# und .NET 10 mit WPF-Oberfläche, PostgreSQL mit Npgsql und Dapper, ZUGFeRD-csharp für XRechnung und ZUGFeRD, PdfPig für eingebettetes XML, das OpenAI .NET SDK. Alle Bibliotheken sind Open Source; Unit- und Integrationstests laufen bei jeder Änderung in GitHub Actions. Die Architekturentscheidungen stehen im Repository (DECISIONS.md).

Zum Projekt

Das ist mein eigenes Referenzprojekt mit erfundenen Testdaten, kein Kundenprojekt. Ich entwickle seit über acht Jahren mit .NET, die letzten Jahre Backends und Integrationen in der Energiewirtschaft. Wenn Sie Ihre Rechnungsverarbeitung automatisieren oder an Ihr System anbinden wollen, schreiben Sie mir.

Quellen

Fragen zu diesem Thema?

Wenn Sie das Thema für Ihre eigene Umgebung besprechen möchten oder Unterstützung mit .NET, Azure oder Dynamics 365 suchen, freue ich mich auf den Austausch.

Kontakt aufnehmen