Markus Michalski FAQ

PDF-Rechnungen: Technische Details

Technische Details des PDF-Rechnungen Moduls für OXID eShop 7.x: Ablauf der Rechnungserstellung, Komponenten, Nummernvergabe, Ablage, Datenbanktabellen und Snapshot-Aufbau.

English Version

Zurück zur Übersicht

PDF-Rechnungen: Technische Details

Live Demo: Teste dieses und weitere Plugins live — ohne Installation, ohne Risiko: demo.markus-michalski.net

Diese Dokumentation bezieht sich immer auf die aktuellste veröffentlichte Version.

Ablauf der Rechnungserstellung

Die Rechnung wird nicht von einem Bestell-Event ausgelöst. Sie entsteht entweder beim Versand der Bestellmail oder manuell im Admin. Beide Wege rufen denselben Service auf (InvoiceService.createForOrder).

graph TD
    A["Mail-Weg: Bestellmail, fehlende Rechnung wird automatisch erstellt"] --> E["InvoiceService.createForOrder"]
    M["Admin-Weg: Rechnung erstellen (InvoiceController.createInvoice)"] --> E
    E --> N["Rechnungsnummer vergeben"]
    N --> S["Snapshot erzeugen (ExtendedOrder.createInvoiceSnapshot)"]
    S --> P["PDF rendern (Twig-Template und Dompdf)"]
    P --> F["PDF ablegen und SHA-256 berechnen"]
    F --> R["Datensatz in mmd_pdfinvoice speichern"]

Die Reihenfolge in createForOrder ist: vorhandene Rechnung prüfen, Nummer vergeben, Snapshot erzeugen, PDF erzeugen, Datei ablegen, Hashwert berechnen, Datensatz speichern. Die Nummer steht also vor dem PDF fest.

Der Admin-Weg endet mit dem gespeicherten PDF. Er hängt nichts an eine Bestellmail an und setzt keinen Versandstatus. Das Anhängen und das Setzen des Versandstatus gehören nur zum Mail-Weg in ExtendedEmail.send:

graph TD
    A["Bestellmail wird versendet (ExtendedEmail.send)"] --> B{"Anhang-Modus passt zum Empfänger?"}
    B -->|"Nein"| X["Mail ohne Rechnung"]
    B -->|"Ja"| C{"Rechnung zur Bestellung vorhanden?"}
    C -->|"Ja"| G["PDF aus der Ablage lesen und Hash prüfen"]
    C -->|"Nein"| D{"Automatisch erstellen aktiv?"}
    D -->|"Nein"| X
    D -->|"Ja"| E["InvoiceService.createForOrder (siehe oben)"]
    E --> G
    G --> H["PDF an die Mail anhängen und Versandstatus setzen"]

Komponenten

Klasse Aufgabe
ExtendedEmail Erweitert Email, merkt sich Bestellmails und hängt in send() das PDF an
ExtendedOrder Erweitert Order, liefert Rechnung und erzeugt den Snapshot
InvoiceService Orchestriert Erstellung, Laden, Hash-Prüfung und Neugenerierung
InvoiceNumberGenerator Vergibt Rechnungsnummern über die Tabelle mmd_pdfinvoice_seq
PdfGenerator Bereitet die Template-Variablen vor, rendert das Twig-Template und erzeugt das PDF mit Dompdf
InvoiceStorage Schreibt, liest und löscht die PDF-Dateien
InvoiceRepository Liest und schreibt die Tabelle mmd_pdfinvoice
InvoiceConfiguration Liest die Moduleinstellungen
DebugLogger Schreibt Debug-Meldungen nur bei aktivem Debug-Logging
InvoiceController Admin-Controller mmd_pdfinvoice_admin mit den Aktionen createInvoice, downloadPdf, sendInvoiceEmail, regeneratePdf
ModuleEvents Legt bei der Aktivierung Tabellen und Verzeichnis an

Moduldaten

Eigenschaft Wert
Modul-ID mmd_pdfinvoice
Composer-Paket mmd/oxid7-pdf-invoice
Installationsverzeichnis source/modules/mmd/pdf-invoice
Admin-Controller mmd_pdfinvoice_admin
Admin-Tab tbclorder_pdfinvoice (Bestellungen > Bestellung)
Templates @mmd_pdfinvoice/pdf/invoice, @mmd_pdfinvoice/admin/invoice_main
Events onActivate (Tabellen und Verzeichnis anlegen), onDeactivate (löscht nichts)

Rechnungsnummerierung

Format: {Präfix}{Jahr}-{Nummer} mit fünfstelliger Nummer und führenden Nullen, zum Beispiel RE-2026-00001.

Im Admin erscheint die Einstellung "Nummerierung jährlich zurücksetzen" (mmdPdfInvoiceResetYearly). Sie hat im Code keine Wirkung. Die Nummerierung läuft immer je Shop und Jahr, unabhängig davon, ob die Option an- oder ausgeschaltet ist.

Ablage der PDF-Dateien

Pfad: {Speicherpfad}/{Shop-ID}/{Jahr}/{Monat}/{Rechnungsnummer}.pdf, relativ zum Shop-Verzeichnis. Aus der Rechnungsnummer werden alle Zeichen außer Buchstaben, Ziffern, - und _ durch _ ersetzt. Jahr und Monat stammen vom Rechnungsdatum.

Beim Lesen vergleicht das Modul den SHA-256-Hashwert der Datei mit dem in PDF_HASH gespeicherten Wert. Weicht er ab, wird die Datei nicht ausgeliefert, bis das PDF neu generiert wurde.

Dompdf legt seinen Schrift-Cache in source/tmp/dompdf ab. Das PDF wird im Format A4 hochkant mit der Standardschrift DejaVu Sans erzeugt.

Datenbank

mmd_pdfinvoice

Spalte Beschreibung
OXID Primärschlüssel
OXORDERID Bestell-ID
OXSHOPID Shop-ID
INVOICE_NUMBER Formatierte Rechnungsnummer
INVOICE_DATE Rechnungsdatum
PDF_PATH Relativer Pfad der PDF-Datei
PDF_HASH SHA-256-Hashwert der PDF-Datei
SENT_TO_CUSTOMER Kennzeichen: an Kunden gesendet
SENT_AT_CUSTOMER Zeitpunkt des Versands an den Kunden
SENT_TO_OWNER Kennzeichen: an Shopbetreiber gesendet
SENT_AT_OWNER Zeitpunkt des Versands an den Shopbetreiber
SNAPSHOT_DATA JSON-Snapshot der Bestelldaten
CREATED_AT Zeitpunkt der Erstellung
OXTIMESTAMP Zeitstempel der letzten Änderung (automatisch)

Eindeutige Schlüssel: OXORDERID + OXSHOPID (eine Rechnung je Bestellung und Shop) und INVOICE_NUMBER + OXSHOPID (eine Rechnungsnummer je Shop).

mmd_pdfinvoice_seq

Spalte Beschreibung
OXSHOPID Shop-ID (Teil des Primärschlüssels)
YEAR Jahr (Teil des Primärschlüssels)
CURRENT_NUMBER Zuletzt vergebene Nummer
PREFIX Präfix zum Zeitpunkt der Anlage der Zeile

Die Spalte PREFIX wird beim Anlegen der Zeile befüllt. Für die Nummernbildung wird immer die aktuelle Einstellung verwendet.

Daten-Snapshot

Beim Erstellen der Rechnung speichert das Modul den Snapshot in SNAPSHOT_DATA. Er wird aus der Bestellung erzeugt und enthält:

Schlüssel Inhalt
version Format-Version des Snapshots, aktuell der Wert 1.0
created_at Zeitpunkt der Erstellung
order Bestellnummer, Bestelldatum, Versanddatum, Trackingcode, Währung, Kurs, Bestellnotiz
billing Rechnungsadresse inklusive Telefon, Fax, E-Mail und USt-IdNr.
delivery Lieferadresse, wenn in der Bestellung ein Liefernachname hinterlegt ist, sonst null (kein Vergleich mit der Rechnungsadresse)
items Positionen mit Artikelnummer, Titel, Menge, Preisen, MwSt. und Gewicht
totals Netto-, Brutto- und Gesamtsumme, Versand-, Zahlungs-, Verpackungs- und Geschenkkartenkosten, Rabatt, MwSt.-Aufschlüsselung
payment Zahlungsart-ID, Bezeichnung und bei Lastschrift die Bankdaten

Nicht im Snapshot enthalten sind die Shop-Stammdaten und das Logo. Beim Neugenerieren eines PDFs werden sie aktuell gelesen.

Verhalten bei Fehlern