Rechnungen als reine PDF-Dateien sind für automatisierte Finanzprozesse ein großes Problem. Zwar können Menschen das Dokument mühelos lesen und ausdrucken, doch Buchhaltungssysteme können daraus keine strukturierten Daten extrahieren. Das führt zu manueller Dateneingabe, Erfassungsfehlern und verlorener Zeit. Seit Juni 2025 gilt für Unternehmen, die mit öffentlichen Auftraggebern arbeiten oder B2B-Rechnungen versenden, die Verpflichtung, strukturierte E-Rechnungsformate zu unterstützen. ZUGFeRD und Factur-X sind die führenden hybriden Formate in Deutschland und Europa, die dieser Anforderung genügen.
Mit Python können Sie ZUGFeRD-Rechnungen automatisiert erstellen – das heißt, Sie erzeugen ein PDF, das Menschen lesen können, mit eingebetteten strukturierten XML-Daten, die Computer direkt verarbeiten. Dieser Guide zeigt Ihnen, wie Sie mit Python und Open-Source-Bibliotheken vollständig konforme ZUGFeRD-Rechnungen generieren, validieren und in Ihre bestehenden Rechnungsprozesse integrieren – mit klarem Fokus auf den Umsetzungsaufwand, die wirtschaftliche Rentabilität und die praktische Anwendung im realen Geschäftsbetrieb.
ZUGFeRD (Zentraler User Guide des Forums elektronische Rechnung Deutschland) ist ein hybrides Rechnungsformat, das PDF und strukturierte XML-Daten kombiniert. Das PDF macht die Rechnung für Menschen lesbar und druckbar. Die eingebettete XML enthält alle Rechnungsdaten in standardisierter, maschinenlesbarer Form. Factur-X ist die europäische Entsprechung desselben Standards.
Python eignet sich hervorragend für diese Aufgabe, weil die Sprache Datenverarbeitung, XML-Erzeugung und PDF-Generierung elegant verbinden kann. Mit Open-Source-Bibliotheken wie lxml für XML-Handling und WeasyPrint für PDF-Rendering schaffen Sie eine kostengünstige, wartbare Lösung, die Sie vollständig kontrollieren. Das ist besonders wertvoll, wenn Sie automatisierte Rechnungserstellung in Ihre bestehende Backend-Infrastruktur, Webanwendung oder Ihr ERP-System integrieren möchten.
Allerdings wird eine kritische Frage oft übersehen: Ist eine Custom-Python-Lösung wirtschaftlich sinnvoll, oder sollten Sie ein Standard-Tool oder ein ERP-Add-on nutzen? Diese Entscheidung hängt stark von Ihrem Rechnungsvolumen, Ihrer technischen Kapazität und Ihren spezifischen Anforderungen ab. Eine Python-Lösung ist sinnvoll, wenn Sie hohe Automatisierungsanforderungen haben, bestehende Python-Infrastruktur nutzen oder wenn Standard-Lösungen Ihre Anforderungen nicht erfüllen. Für kleinere Mengen oder weniger komplexe Szenarien können spezialisierte E-Rechnungssoftware oder ERP-Erweiterungen schneller amortisieren.
Unternehmen profitieren bei korrekter Umsetzung sofort: Die Automatisierung reduziert Fehler, beschleunigt die Rechnungsverarbeitung und ermöglicht einen direkten Datenfluss in Buchhaltung und Archivierungssysteme. Gleichzeitig sichern Sie langfristig Compliance mit europäischen E-Rechnungsanforderungen, die seit Juni 2025 verbindlich sind.
Bevor Sie beginnen, sollten Sie folgende Voraussetzungen erfüllen:
Installieren Sie folgende Python-Bibliotheken:
ZUGFeRD basiert auf EN 16931, der europäischen Norm für strukturierte E-Rechnungen. Diese Norm definiert exakt, welche Felder Pflicht sind, welche optional und in welchem Format sie vorliegen müssen. Eine ZUGFeRD-Rechnung besteht aus drei Komponenten:
Die folgende Tabelle zeigt die wichtigsten Pflichtfelder einer ZUGFeRD-Rechnung nach EN 16931 und ihre genaue Bedeutung:
| Feldname | Format & Anforderung | Beispiel |
|---|---|---|
| Rechnungsnummer | Eindeutig, max. 20 Zeichen, keine Sonderzeichen außer Bindestrich/Unterstrich | RE-2026-001234 |
| Rechnungsdatum | ISO-8601-Format: YYYY-MM-DD, unveränderbar nach Erstellung | 2026-01-15 |
| Rechnungssteller (Verkäufer) | Name, vollständige Adresse, gültige Steuer-ID oder UID-Nummer | Musterfirma GmbH, DE123456789 |
| Rechnungsempfänger (Käufer) | Name, vollständige Adresse, Steuer-ID falls B2B, sonst Privatadresse | Kundenfirma AG, AT987654321 |
| Rechnungspositionen | Für jede Position: Beschreibung, Menge, Einheit, Preis pro Einheit, Steuersatz | Beratung: 8 h à 150,00 EUR, 19 % MwSt. |
| Summen & Steuern | Nettobetrag, Steuerbetrag (pro Satz), Gesamtbetrag – exakt auf Cent gerundet | Netto: 1.200,00 EUR, MwSt.: 228,00 EUR, Brutto: 1.428,00 EUR |
| Zahlungsbedingungen | Zahlungsfrist (Tage), IBAN/BIC oder Kontoangaben, optional Skonto | Zahlbar in 30 Tagen nach Rechnungsdatum, IBAN: DE89... |
| Währung | Dreistelliger ISO-Code, gilt für alle Beträge in der Rechnung | EUR |
Jedes dieser Felder hat exakte Anforderungen an Format und Validität. Eine falsche oder fehlende Information führt zu Validierungsfehlern und wird von Empfängersystemen abgelehnt. Deshalb ist es essenziell, die Daten vor der Rechnungserstellung gründlich zu prüfen und eine strikte Qualitätskontroll-Governance zu etablieren.
Der erste praktische Schritt ist, ein Python-Datenmodell zu definieren, das die Rechnungsdaten sauber abbildet und validiert. Verwenden Sie dataclasses oder validataclass, um Fehler früh abzufangen, bevor die XML-Erzeugung beginnt.
from dataclasses import dataclass
from datetime import date
from decimal import Decimal
import re
@dataclass
class Address:
name: str
street: str
postal_code: str
city: str
country_code: str # ISO 3166-1 alpha-2, z.B. 'DE'
def __post_init__(self):
if len(self.country_code) != 2:
raise ValueError(f"Country code must be 2 characters")
if not re.match(r'^[A-Z0-9 ,.-]+$', self.name):
raise ValueError("Name contains invalid characters")
@dataclass
class InvoiceItem:
description: str
quantity: Decimal
unit_price: Decimal
tax_rate: Decimal # z.B. Decimal('19.0') für 19 %
def __post_init__(self):
if self.quantity <= 0:
raise ValueError("Quantity must be positive")
if self.unit_price < 0:
raise ValueError("Price must be non-negative")
if self.tax_rate < 0 or self.tax_rate > 100:
raise ValueError("Tax rate must be between 0 and 100")
@dataclass
class Invoice:
invoice_number: str
invoice_date: date
seller: Address
buyer: Address
items: list[InvoiceItem]
currency: str = "EUR"
due_date: date = None
def __post_init__(self):
if not re.match(r'^[A-Z0-9\-_]{1,20}$', self.invoice_number):
raise ValueError("Invoice number invalid")
if len(self.items) == 0:
raise ValueError("At least one item required")
if self.due_date and self.due_date < self.invoice_date:
raise ValueError("Due date cannot be before invoice date")
Mit diesem Datenmodell prüfen Sie vor der XML-Erzeugung, dass alle Felder vorhanden und im korrekten Format sind. Das verhindert Fehler später im Prozess und macht Ihre Rechnungserstellung vertrauenswürdig.
Die Erzeugung der strukturierten XML-Rechnung ist das Herzstück der Automatisierung. Verwenden Sie lxml mit ElementBuilder, um die XML sauber zu konstruieren – nicht durch String-Verkettung, die fehleranfällig ist. Achten Sie dabei besonders auf die korrekten Namespaces; falsche oder fehlende Namespace-Deklarationen führen zu Validierungsfehlern.
from lxml import etree
from lxml.builder import ElementMaker
from decimal import Decimal
namespaces = {
'rsm': 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100',
'ram': 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100',
'udt': 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'
}
em_rsm = ElementMaker(namespace=namespaces['rsm'], nsmap=namespaces)
em_ram = ElementMaker(namespace=namespaces['ram'], nsmap=namespaces)
em_udt = ElementMaker(namespace=namespaces['udt'])
# Beispiel: Rechnungssteller-Adresse
def build_address_element(address, party_type='SellerTradeParty'):
return em_ram.PostalTradeAddress(
em_udt.CountryID(address.country_code),
em_ram.LineOne(address.street),
em_ram.CityName(address.city),
em_ram.PostcodeCode(address.postal_code)
)
# Hauptdokument zusammensetzen
def build_invoice_xml(invoice: Invoice):
# Dokument-Header mit korrekter Guideline-ID
context = em_rsm.ExchangedDocumentContext(
em_ram.GuidelineSpecifiedDocumentContextParameter(
em_udt.ID('urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:extended')
)
)
# Dokumentmetadaten
document = em_rsm.ExchangedDocument(
em_udt.ID(invoice.invoice_number),
em_udt.IssueDateTime(
em_udt.DateTimeString(
invoice.invoice_date.isoformat(),
format='102' # YYYYMMDD
)
)
)
# Weitere Elemente folgen ...
invoice_xml = em_rsm.CrossIndustryInvoice(
context,
document
# Weitere Elemente werden hier eingefügt
)
return invoice_xml
# XML zu Bytes konvertieren
invoice_xml = build_invoice_xml(invoice_data)
xml_bytes = etree.to_string(
invoice_xml,
xml_declaration=True,
encoding='utf-8',
pretty_print=True
)
Dieser Ansatz garantiert, dass die Namespaces korrekt gesetzt werden und die XML-Struktur valide ist. String-Verkettung würde zu unauffälligen Fehlern führen, die erst bei der Validierung auffallen würden.
Parallel zur XML erzeugen Sie das menschlich lesbare PDF. WeasyPrint rendert HTML und CSS in ein PDF-Dokument. Allerdings ist die PDF/A-3-Erzeugung in WeasyPrint noch relativ begrenzt – die Library erstellt ein PDF, das grundsätzlich PDF/A-3-konform strukturiert ist, aber für eine vollständige Zertifizierung benötigen Sie zusätzliche Nachbearbeitung mit pikepdf oder ähnlichen Tools.
from weasyprint import HTML, CSS, FontConfiguration import io # HTML-Template (z.B. mit Jinja2 gerendert) pdf_html = f"""Rechnung {invoice_data.invoice_number}
Rechnungssteller: {invoice_data.seller.name}
Rechnungsempfänger: {invoice_data.buyer.name}
| Beschreibung | Menge | Preis pro Einheit | Gesamtbetrag |
|---|
Gesamtbetrag: XXX EUR
Die WeasyPrint-Bibliothek erstellt ein PDF, das Sie dann mit zusätzlichen PDF/A-3-Metadaten und der XML-Datei korrekt zusammenfügen.
Jetzt verbinden Sie beide Teile: Sie betten die XML-Datei in das PDF ein und setzen die erforderlichen RDF-Metadaten für die PDF/A-3-Konformität. Dies ist keine triviale Aufgabe – PDF/A-3 hat genaue Anforderungen an Metadaten-Struktur und Anhang-Definition.
import pikepdf
from pikepdf import Name, Dictionary, Stream, Array
from hashlib import md5
from datetime import datetime
import io
# XML-Daten als Bytes
xml_data = etree.to_string(
invoice_xml,
xml_declaration=True,
encoding='utf-8',
pretty_print=True
)
# PDF mit pikepdf öffnen
pdf_stream = io.BytesIO(pdf_bytes)
pdf = pikepdf.open(pdf_stream)
# XML als EmbeddedFile hinzufügen
xml_stream = Stream(pdf, xml_data)
xml_ref = pdf.add_object(Dictionary(
Type=Name('/EmbeddedFile'),
Subtype=Name('/application#2Fxml'),
Length=len(xml_data),
Filter=Name('/FlateDecode')
))
# FileSpec für den Anhang
filespec = Dictionary(
Type=Name('/Filespec'),
F='factur-x.xml',
UF='factur-x.xml',
EF=Dictionary(F=xml_ref),
Desc='Factur-X Invoice'
)
# Zu PDF hinzufügen
if not hasattr(pdf.root, 'Names'):
pdf.root.Names = Dictionary()
if not hasattr(pdf.root.Names, 'EmbeddedFiles'):
pdf.root.Names.EmbeddedFiles = Array()
pdf.root.Names.EmbeddedFiles.append(filespec)
# PDF/A-3-Metadaten setzen
pdf.metadata['/Producer'] = 'Python ZUGFeRD Generator v1.0'
pdf.metadata['/CreationDate'] = f'D:{datetime.now().strftime("%Y%m%d%H%M%S")}'
pdf.metadata['/ModDate'] = f'D:{datetime.now().strftime("%Y%m%d%H%M%S")}'
# PDF/A-3-Standardkennung
pdf_identifier = md5(
f"{invoice_data.invoice_number}{invoice_data.invoice_date}".encode()
).hexdigest()
# PDF speichern mit PDF/A-3-Validierung
output = io.BytesIO()
pdf.save(output, min_version='1.7')
pdf_final = output.getvalue()
with open(f'rechnung_{invoice_data.invoice_number}.pdf', 'wb') as f:
f.write(pdf_final)
Das Ergebnis ist eine Rechnung im PDF/A-3-Format mit eingebetteter XML. Allerdings ist wichtig zu verstehen: Auch mit Nachbearbeitung durch pikepdf garantiert eine Python-Lösung nicht zu 100 %, dass die PDF/A-3-Konformität bestanden wird – das erfordert eine externe Validierung durch spezialisierte Tools wie Mustang für ZUGFeRD oder VeraPDF.
Bevor Sie die Rechnung versenden oder archivieren, validieren Sie sie gründlich. Das verhindert, dass fehlerhafte Rechnungen in den Prozess gelangen. Ohne Validierung ist Ihre gesamte Automatisierung nutzlos. Die Validierung ist ein nicht verhandelbares Qualitätssicherungs-Gate und muss für JEDE Rechnung durchlaufen werden.
Validierung auf drei Ebenen:
1. XML-Validierung gegen XSD-Schema:
from lxml import etree
from pathlib import Path
# XSD-Schema laden (aus dem ZUGFeRD-Download-Paket)
schema_file = Path('factur_x_schema/Factur-X_1.07.2_EN16931.xsd')
if not schema_file.exists():
raise FileNotFoundError(f"Schema nicht gefunden: {schema_file}")
schema = etree.XMLSchema(file=str(schema_file))
# XML gegen Schema validieren
is_valid = schema.validate(invoice_xml)
if not is_valid:
print("FEHLER: XML erfüllt EN 16931 nicht:")
for error in schema.error_log:
print(f" Zeile {error.line}: {error.message}")
raise ValueError("XML-Validierung fehlgeschlagen")
else:
print("✓ XML ist EN 16931-konform")
2. PDF-Validierung mit externem Tool (empfohlen):
import subprocess
import sys
# Mustang-CLI aufrufen (Java erforderlich, nicht ideal für Produktion)
# Bessere Alternative: Verfahren über externe Validierungs-API
try:
result = subprocess.run(
['java', '-jar', 'mustang-cli.jar', pdf_file_path],
capture_output=True,
text=True,
timeout=30
)
if 'isCompliant=true' in result.stdout:
print("✓ PDF ist PDF/A-3-konform")
return True
else:
print("✗ PDF erfüllt PDF/A-3 nicht")
print(result.stdout)
return False
except FileNotFoundError:
print("WARNUNG: Mustang nicht installiert, externe PDF-Validierung übersprungen")
return None
3. Datenplausibilität prüfen:
def validate_invoice_data(invoice: Invoice) -> bool:
"""Prüfe geschäftslogische Konsistenz"""
errors = []
# Summen prüfen
net_sum = sum(
item.quantity * item.unit_price
for item in invoice.items
)
# Steuern prüfen: Pro Steuersatz müssen Summen stimmen
tax_groups = {}
for item in invoice.items:
key = item.tax_rate
if key not in tax_groups:
tax_groups[key] = Decimal(0)
tax_groups[key] += item.quantity * item.unit_price
# Gesamtsteuern berechnen
total_tax = sum(
amount * (tax_rate / 100)
for tax_rate, amount in tax_groups.items()
)
# Plausibilitätsprüfungen
if len(invoice.invoice_number) == 0:
errors.append("Rechnungsnummer fehlt")
if len(invoice.items) == 0:
errors.append("Keine Rechnungspositionen")
if invoice.seller == invoice.buyer:
errors.append("Rechnungssteller und -empfänger identisch")
if errors:
for error in errors:
print(f"✗ {error}")
return False
print(f"✓ Datenplausibilität OK (Netto: {net_sum}, Steuern: {total_tax})")
return True
Die Validierung prüft auf drei Ebenen, ob die Rechnung korrekt ist. Nur fehlerfreie Rechnungen dürfen versandt werden. Für zusätzliche Prüfungen kann auch ein E-Rechnung-Validator sinnvoll sein.
In der Praxis integrieren Sie die ZUGFeRD-Erzeugung in einen automatisierten Workflow. Das könnte so aussehen:
import logging
from datetime import datetime
logger = logging.getLogger('invoice_generator')
def generate_invoice_workflow(invoice_data: dict, output_dir: str = 'invoices'):
"""Vollständiger Workflow zur Rechnungserstellung mit Fehlerbehandlung"""
try:
# 1. Daten validieren
invoice = Invoice(**invoice_data)
logger.info(f"Rechnung {invoice.invoice_number} wird erstellt...")
# 2. Geschäftslogik prüfen
if not validate_invoice_data(invoice):
raise ValueError("Geschäftsdaten ungültig")
# 3. XML erzeugen
invoice_xml = build_invoice_xml(invoice)
# 4. XML gegen EN 16931 validieren
schema = etree.XMLSchema(file='factur_x_schema.xsd')
if not schema.validate(invoice_xml):
for error in schema.error_log:
logger.error(f"XML-Fehler: {error.message}")
raise ValueError("XML erfüllt EN 16931 nicht")
# 5. PDF erzeugen
pdf_html = render_invoice_template(invoice)
pdf_document = HTML(string=pdf_html).render()
pdf_bytes = pdf_document.write_pdf(optimize_size=('fonts', 'images'))
# 6. XML einbetten und PDF/A-3 finalisieren
pdf_final = embed_xml_in_pdf(pdf_bytes, invoice_xml)
# 7. Datei speichern
filepath = f"{output_dir}/rechnung_{invoice.invoice_number}.pdf"
with open(filepath, 'wb') as f:
f.write(pdf_final)
# 8. Ergebnis validieren
logger.info(f"Rechnungsdatei erstellt: {filepath}")
# 9. Logging und Archivierung
log_invoice_generated(
invoice_number=invoice.invoice_number,
filepath=filepath,
timestamp=datetime.now()
)
return filepath
except Exception as e:
logger.error(f"Fehler bei der Rechnungserstellung: {str(e)}")
# Fehlerbehandlung: Benachrichtigung, Rollback etc.
raise
# Beispielaufrufe
pending_orders = get_pending_orders_from_database()
for order in pending_orders:
try:
invoice_data = prepare_invoice_from_order(order)
pdf_path = generate_invoice_workflow(invoice_data)
send_invoice_by_email(
pdf_path,
order.customer_email,
subject=f"Rechnung {invoice_data['invoice_number']}"
)
mark_order_invoiced(order.id)
except Exception as e:
logger.error(f"Fehler für Bestellung {order.id}: {e}")
create_alert_for_manual_review(order.id, str(e))
Diesen Workflow können Sie in Ihre ERP-, Web- oder Backend-Anwendung einbinden. Die Automatisierung erspart manuelles Erstellen und reduziert Fehler erheblich – vorausgesetzt, Sie haben fehlertolerante Fehlerbehandlung, Logging und ein klares Governance-Modell für Rechnungskorrekturen implementiert. In größeren Umgebungen ist das häufig Teil eines durchgängigen Purchase-to-Pay-Prozesses.
Bei der Umsetzung treten regelmäßig Probleme auf. Hier sind die häufigsten und wie Sie sie beheben:
Hier ist eine praktische Checkliste für die Qualitätssicherung vor dem Produktiveinsatz:
| Prüfpunkt | Status | Kritikalität |
|---|---|---|
| Alle Rechnungsdaten (Beträge, Daten, Nummern) auf Format und Vollständigkeit prüfen | ☐ | KRITISCH |
| XML gegen das offizielle EN-16931-XSD-Schema validieren | ☐ | KRITISCH |
| PDF mit Mustang oder einem Verifikationstool auf PDF/A-3 prüfen | ☐ | KRITISCH |
| Testrechnung mit Testkunden tatsächlich verarbeiten lassen (technischer Testlauf) | ☐ | KRITISCH |
| Sicherstellen, dass die Datei korrekt versandt wird (E-Mail-Encoding, MIME-Type prüfen) | ☐ | HOCH |
| Empfängersystem kann die Rechnung auslesen und verarbeitet die XML-Daten korrekt | ☐ | HOCH |
| Archivierungssystem kann die Datei langfristig speichern und später wieder laden | ☐ | HOCH |
| Logging und Fehlerbehandlung sind implementiert und getestet | ☐ | HOCH |
| Backup-Strategie für generierte Rechnungen existiert | ☐ | HOCH |
| Datenschutz und Zugriffsschutz sind konfiguriert (Verschlüsselung, Berechtigungen) | ☐ | HOCH |
| Nummernkreis-Verwaltung ist eindeutig und lückenlos | ☐ | KRITISCH |
| Governance für Rechnungskorrekturen und Stornierungen definiert | ☐ | HOCH |
| Performance und Skalierbarkeit unter Last getestet (100+ Rechnungen pro Minute?) | ☐ | MITTEL |
| Notfall-Recovery-Verfahren dokumentiert (Was tun, wenn das System ausfällt?) | ☐ | HOCH |
Einer der größten Fehler ist technische Euphorie ohne wirtschaftliche Bewertung. Bevor Sie sich auf eine Python-Eigenentwicklung einlassen, beantworten Sie diese Fragen ehrlich:
Die ehrliche Antwort: Für die meisten Unternehmen ist eine spezialisierte Rechnungssoftware oder ein ERP-Add-on wirtschaftlicher. Eine Python-Lösung macht Sinn, wenn Sie sehr hohe Volumina haben, spezialisierte Anforderungen oder wenn Sie bereits ein Python-basiertes Backend haben und die Integration enger erfolgen soll. Wenn Sie bei Strategie, Auswahl oder Umsetzung Unterstützung benötigen, kann eine E-Rechnung-Beratung helfen. Aber: Die Gesamtbetriebskosten (Total Cost of Ownership) einer Eigenentwicklung sind fast immer höher als auf den ersten Blick angenommen.
Die Erstellung von ZUGFeRD-Rechnungen mit Python folgt einem klaren Ablauf:
Mit diesem Ansatz schaffen Sie eine zuverlässige, konforme und skalierbare Rechnungserzeugung – aber nur, wenn Sie die technische UND wirtschaftliche Seite sorgfältig planen.
Seit Juni 2025 gilt die Verpflichtung für Rechnungen an öffentliche Auftraggeber in Deutschland: Diese müssen im XRechnung-Format oder in kompatiblen Formaten wie ZUGFeRD übermittelt werden. Für E-Rechnung im B2B-Bereich zwischen privaten Unternehmen ist es nicht zwingend, aber zunehmend erwartet und wirtschaftlich sinnvoll. Die technische Vorbereitung ist definitiv zu empfehlen, um zukunftssicher zu sein.
Ja, aber nur, wenn Sie nicht an öffentliche Auftraggeber Rechnungen stellen. ZUGFeRD ist eine hybride Form – das PDF bleibt lesbar und druckbar. Sie verlieren also keine Rückwärtskompatibilität, gewinnen aber strukturierte Daten obendrauf.
lxml und WeasyPrint funktionieren mit Python 3.8+. Verwenden Sie möglichst eine aktuelle Version (3.11 oder neuer), um von Sicherheitsupdates zu profitieren und eine bessere Performance zu erreichen.
Das hängt stark von Ihrer Situation ab. Wenn Sie technische Ressourcen haben und hohe Automatisierungsanforderungen, ist eine Python-Lösung wartbar und flexibel. Wenn Sie keine Entwicklung im Haus haben oder das Volumen niedrig ist, sind kommerzielle Lösungen (spezialisierte Faktura-Systeme, ERP-Add-ons, LibreOffice-Erweiterungen) oft wirtschaftlicher und risikoärmer.
ZUGFeRD selbst beinhaltet keine digitale Signatur – das ist ein Design-Schwachpunkt: Theoretisch könnte jemand die XML im PDF nachträglich ändern, ohne dass es bemerkt wird. Abhilfe: Signieren Sie das PDF selbst digital oder versenden Sie es über sichere Kanäle. Für solche Prozesse spielt auch Informationssicherheit eine wichtige Rolle. Die Rechnungssicherheit ist in der deutschen E-Rechnungspraxis noch nicht vollständig gelöst. Das ist ein Governance- und Prozessthema, nicht nur technisch.
Ja, aber die XML-Struktur unterscheidet sich je nach Profil. Ein einfacheres Profil benötigt weniger Felder, ein erweitertes mehr. Ihre Python-Lösung muss über Parameter steuern können, welches Profil erzeugt wird. Für den Anfang (EN 16931-konform) reicht ein Basis- oder Kern-Profil aus.
Fehlerhafte Rechnungen können von Empfängersystemen nicht automatisch verarbeitet werden. Sie landen dann manuell beim Sachbearbeiter – das soll gerade vermieden werden. Daher: Immer lokal und vollständig validieren, bevor Sie die Rechnung versenden. Nur fehlerfreie Rechnungen versenden. Implementieren Sie Fehlerbehandlung und Alerts, damit Fehler sofort bemerkt werden.
In Deutschland beträgt die Aufbewahrungsfrist für Geschäftsunterlagen (inkl. Rechnungen) gemäß GoBD zehn Jahre. PDF/A-3 ist für diese Dauer geeignet, da das Format archivierbar ist. Sie sollten nicht nur die Dateien speichern, sondern auch Metadaten (Erstellungsdatum, Versender etc.) dokumentieren.