Zum Inhalt springen

This page is available in English. Switch to English

Arbeit, die jede Woche wiederkommt? Muss kein Mensch machen. Mehr zu KI und Automatisierung

Open Source

Die Bestellbestätigung von Magento 2, aus der Steinzeit geholt

Eine dünne, hell beleuchtete Metallplatte vor drei deutlich dickeren dunklen Platten, die dahinter zurücktreten.

magento2-module-sales-email ersetzt Bestellbestätigung und Versandbenachrichtigung von Magento 2 durch zwei Vorlagen, die für E-Mail-Clients gebaut sind. Die Frage des Kunden steht darin über dem Papierkram des Shops.

Angefangen hat es als E-Mail-Redesign für einen Shop, den ich betreue. Die Standardvorlagen waren mir schlicht zu langweilig, sie könnten eine Auffrischung vertragen. Die Arbeit ließ sich wiederverwenden, also habe ich ein Modul daraus gemacht und kein Theme-Override.

Was Magento ab Werk verschickt

Die Standard-Bestellbestätigung beginnt mit vier Kästen: Rechnungsadresse, Lieferadresse, Zahlungsart, Versandart. Darunter steht die vollständige Artikeltabelle. Jeder unveränderte Magento-Shop verschickt im Wesentlichen dieselbe E-Mail.

Lies sie als Kunde. Du hast gerade bezahlt und willst wissen, ob es geklappt hat und was kommt. Beide Antworten stehen unter einem Block, der nur für die Unterlagen des Shops da ist.

Wie du die Vorlage der Bestellbestätigung änderst

Magento legt die vier Vorlagen als Dateien in Magento_Sales ab: order_new.html und order_new_guest.html für die Bestätigung, shipment_new.html und shipment_new_guest.html für die Versandmeldung. Zu einer eigenen Vorlage führen zwei Wege, und sie unterscheiden sich darin, wo die Kopie liegt.

Im Backend gehst du auf Marketing > Communications > Email Templates > Add New Template, wählst unter Load template die Standardvorlage, bearbeitest die Kopie und speicherst sie. Danach wählst du sie unter Stores > Configuration > Sales > Sales Emails aus. Kunden und Gäste haben je eine Einstellung, wählst du die Kopie nur in einer davon, bleibt der andere Bestelltyp bei der Standardvorlage.

Im Theme legst du die Datei unter <theme>/Magento_Sales/email/order_new.html ab, die Gast- und Versandvorlagen daneben. Dort musst du nichts auswählen, und die Überschreibung liegt mit dem übrigen Theme in der Versionsverwaltung.

  • Die Einstellung gilt pro Store View. Ein Store View mit einer ausdrücklich gespeicherten Vorlagen-ID behält sie, egal worauf der Standard später zeigt, bis du die Einstellung auf „Use Default Value“ zurücksetzt.
  • Beide Wege frieren das Markup der Standardvorlage auf dem Stand ein, den du kopiert hast. Eine spätere Änderung im Magento-Core erreicht die Kopie nicht mehr.

Beide Wege erlauben es, die Adressen unter die Artikel zu schieben. Die Zeit geht in das Markup darunter: Tabellen, Styles, die einen Inliner überleben, und ein Test in den Clients, in denen deine Kunden wirklich lesen.

Was diese Vorlagen stattdessen verschicken

Bestellbestätigung, für Kunden und Gäste: Logo und Bestellnummer, eine Überschrift und ein kurzer Einleitungstext, ein Infostreifen mit Versandart und Bestelldatum, dann kompakt dargestellte Artikel mit Bild, Namen, Menge und Preis, die Summen, der Einladungsblock und erst danach Rechnungs- und Lieferadresse, Zahlungsart und die Kontaktangaben des Shops in der Fußzeile.

Versandbenachrichtigung, für Kunden und Gäste: ein Streifen mit Sendungsnummer, Zusteller und einem Button „Sendung verfolgen“, die Artikel in diesem Paket mit der versandten Menge, der Einladungsblock, die Lieferadresse.

Die Bestellbestätigung von oben nach unten. Die Adressen stehen weiterhin darin, unter den Artikeln und den Summen. Testdaten aus einer frischen Mage-OS-Installation.

Im Bild steckt zugleich eine der Grenzen: Die Texte des Moduls sind deutsch, der Einladungsblock ist englisch. Er ist ein CMS-Block, und CMS-Blöcke werden nicht automatisch übersetzt. Pflege ihn pro Store View.

Die Versandmail beantwortet eine einzige Frage, nämlich wo das Paket ist, und stellt den Button dafür ganz nach oben.

Bundle-Artikel, Download-Produkte und Geschenknachrichten werden dargestellt. Ein Produkt ohne Bild bekommt eine neutrale Platzhalterzelle statt eines kaputten Bildes.

Warum Tabellen

Beim Empfänger zeigt kein Browser die Mail an. Flexbox und Grid sind nicht verlässlich verfügbar. Manche Clients entfernen das style-Element, und der @media-Block für schmale Bildschirme steht genau dort, diese Clients bekommen also das Layout für breite Bildschirme. Webfonts fallen weg, SVG fällt weg, Bilder brauchen absolute URLs. Deshalb sind diese Vorlagen Tabellen, ihre Styles werden aus einem Stylesheet inline geschrieben, und einige Farben wiederholen sich als bgcolor-Attribut für Clients, die CSS ignorieren.

Fünf Farben stehen in PixelPerfect_SalesEmail::css/pp-sales-email.css: ink #2b2b2b, muted #6b6b6b, line #e3e3e3, tint #f6f6f4, accent #3a5a40. Lege dieselbe Datei in deinem Theme oder in einem kleinen Shop-Modul ab, um diese Farben zu ändern.

Die Ausnahme sind die bgcolor-Attribute auf der Buttonzelle, dem Sendungsstreifen, dem Bildplatzhalter und dem Seitenhintergrund. Sie stehen in den Vorlagen, wenn du sie änderst, musst du also auch die Vorlage kopieren.

Ein redaktioneller Block mit Filter davor

Shops wollen in einer Bestätigung etwas sagen, meist laden sie zum Newsletter ein. Dafür gibt es den Einladungsblock: ein normaler CMS-Block, redaktionell im Backend pflegbar, pro Store View und pro E-Mail wählbar. Er steht unter den Summen in der Bestellbestätigung und unter den Artikeln in der Versandmail.

Alles, was eine Redaktion in diesen Block einfügt, landet in einem E-Mail-Client, und deshalb ist dieser Block eine Sicherheitsfrage. Der Block wird vor dem Rendern bereinigt:

  • Erlaubte Tags: table tbody thead tr td th p a strong b em i br span div h1 h2 h3 ul ol li img hr. Das Tag selbst fällt weg, sein Inhalt bleibt erhalten.
  • Samt Inhalt entfernt: script style link svg form input button iframe object embed meta base textarea select noscript template math.
  • Erlaubte Attribute: href src alt width height style align valign border cellpadding cellspacing role title bgcolor. Alles andere fällt weg, einschließlich onclick, class und id.
  • URL-Schemata in href und src: http, https, mailto, tel und relative URLs. Eine javascript:- oder data:-URL fällt weg.
  • Style-Werte fallen weg, wenn sie Backslash-Escapes, Kommentare, expression, behavior, javascript, vbscript, -moz-binding, @import oder ein url() mit einem anderen Schema als http enthalten.

Das Backend warnt beim Speichern, wenn in einem Block Markup steht, das entfernt wird, und diese Warnung prüft nur den Rohinhalt. CMS-Direktiven wie {{widget}} und {{block}} werden erst beim Rendern aufgelöst, deren Ausgabe wird dort bereinigt, ohne Hinweis.

Ein Mutationstest hat vor dem Release eine Lücke in diesem Filter gefunden: Ein HTML-kodierter java&Tab;script:-Link kam durch die Schema-Prüfung. Der Wert wird jetzt vor der Prüfung dekodiert.

Zusammen mit Discount Lines

Keines der beiden Module braucht das andere.

Ist pixelperfectat/magento2-module-discount-lines installiert, zeigen die Summen eine Zeile pro Rabattregel, mit dem Gutscheincode in einem Monospace-Chip und „Geschenk“ oder „Gratis“ in der Betragsspalte für Geschenk- und Gratisversandzeilen ohne Wert. Die Einstellung pp_discount_lines/documents/email_icon steuert die kleinen Symbole neben diesen Zeilen, standardmäßig sind sie aus.

Beide Module installiert: Die Summen zeigen eine Zeile pro Regel, und der Gutscheincode behält seine eigene Schrift, damit erkennbar bleibt, was der Kunde eingegeben hat.

Ohne das Modul erscheint die normale Rabattzeile von Magento, und dort hängt Magento die Beschreibung der Warenkorbregel an die Beschriftung an. Genau so landet eine interne Notiz beim Kunden. Der Schwesterbeitrag beschreibt, wie eine Zeile pro Warenkorbregel entsteht.

Bitte mach es kaputt

Das Modul ist neu, und ein Test im Browser sagt wenig darüber aus. Getestet ist es nur in einer Testumgebung. Deine Kunden lesen ihre Mails in Outlook, in einem iOS-Client im Dunkelmodus, in einem Webmailer, der das style-Element entfernt.

Installiere es in einer Testumgebung, schick dir eine Bestellbestätigung und öffne sie dort, wo deine Kunden lesen. Füge schwer darstellbare Inhalte in den Einladungsblock ein. Wenn ein Layout zusammenfällt oder der Filter etwas entfernt, das bleiben sollte, ist das ein Issue auf GitHub wert. Screenshots von Darstellungsfehlern im E-Mail-Client helfen mir am meisten.

Installation

composer require pixelperfectat/magento2-module-sales-email
bin/magento setup:upgrade
bin/magento cache:flush

Das Setup legt den Standard-Einladungsblock pp_sales_email_invitation an und lässt einen bestehenden Block mit dieser Kennung unberührt. Danach öffnest du Stores > Configuration > Sales > Modern Sales Emails, wählst unter Invitation Block den CMS-Block und legst fest, in welchen E-Mails er erscheint. Der Standardblock ist installiert, aber nicht ausgewählt, es erscheint also keine Einladung, bis du eine wählst.

Ein Schalter pro Store View: pp_sales_email/general/enabled. Steht er für einen Store View auf 0, verschickt dieser wieder die Standardvorlagen von Magento, ohne andere Store Views zu beeinflussen.

Ein Store View, in dem die Standardvorlage ausdrücklich gespeichert ist, behält sie, bis du die Einstellung auf „Use Default Value“ zurücksetzt.

Grenzen

  • In Version 1.0 nur Bestellbestätigung und Versandbenachrichtigung. Mails für Rechnung und Gutschrift sind geplant.
  • Die Darstellung für schmale Bildschirme hängt davon ab, dass der Client das <style>-Element stehen lässt. Ein Client, der es entfernt, zeigt das Desktop-Layout.
  • Sendungslinks führen auf das Popup von Magento, nicht auf die Seite des Zustellers.
  • Echte Produktbilder sind nicht getestet, nur Platzhalterzellen. Die Bild-URLs werden über die Store-Emulation des Bestell-Store-Views aufgelöst.
  • Der Einladungstext wird nicht automatisch je Sprache übersetzt, solange der CMS-Block nicht pro Store View zugewiesen ist.
  • Ein Block, der per Import oder direkt über das Resource Model gespeichert wird, umgeht die Warnung im Backend. Sein Inhalt wird beim Rendern trotzdem bereinigt.

Voraussetzungen und Qualität

PHP 8.3 mit ext-intl, Magento 2.4 oder Mage-OS. Version 1.0.0 erschien am

  1. November 2026, MIT-lizenziert, auf GitHub und Packagist. Ich habe das Modul in einer frischen Mage-OS-3.1.0-Installation auf PHP 8.5 gebaut und getestet, ausschließlich mit Demodaten, jeder Screenshot im Repository zeigt also Platzhalterdaten.

In der CI läuft composer check: composer validate, PHP-Lint, PHP_CodeSniffer, PHPStan, PHPUnit und Mutationstests mit Infection. Der Mutationsscore ist 100 / 100 (MSI / covered-code MSI), der Lauf scheitert unter 85 / 90. Der Score stammt aus dem CI-Lauf vom 1. November 2026, die Schwelle steht in der Konfiguration.

Das Modul liefert de_DE und en_US nur für seine eigenen Texte, das Deutsche in der Sie-Form. Zwischensumme, Gesamtsumme, „Versand & Bearbeitung“ und der Betreff der Bestellmail kommen aus dem Sprachpaket des Shops, das Modul pflegt davon also keine zweite Kopie.

Fragen

Wie ändere ich die Vorlage der Bestellbestätigung in Magento 2?

Kopiere die Standardvorlage und wähle deine Kopie aus. Im Backend: Gehe auf Marketing > Communications > Email Templates > Add New Template, wähle unter Load template die Standardvorlage, bearbeite sie, speichere sie und wähle sie unter Stores > Configuration > Sales > Sales Emails aus, wo Kunden und Gäste je eine Einstellung haben. Im Theme: Lege die Datei unter `<theme>/Magento_Sales/email/order_new.html` ab. Beide Wege frieren das Markup der Standardvorlage auf dem Stand ein, den du kopiert hast, und die Einstellung gilt pro Store View, ein Store View mit ausdrücklich gespeicherter Vorlagen-ID behält sie also, bis du die Einstellung auf „Use Default Value“ zurücksetzt. Mit `magento2-module-sales-email` sind dessen Vorlagen bereits der Standard für `sales_email/order/template`, `sales_email/order/guest_template` und die beiden entsprechenden Einstellungen für Versandbenachrichtigungen, und sie werden auf denselben zwei Wegen unter `PixelPerfect_SalesEmail` überschrieben.

Das Modul ist installiert, mein Shop schickt aber weiter die alte Mail. Warum?

Ein Store View, in dem die Standardvorlage ausdrücklich gespeichert ist, in dem also im Dropdown die Standardvorlage statt „(Default)“ steht, behält sie. Das Modul überschreibt keine ausdrückliche Auswahl. Setze die Einstellung auf „Use Default Value“ zurück, oder wähle die neue Vorlage im Dropdown.

Warum sind Transaktionsmails aus Tabellen gebaut?

Weil beim Empfänger kein Browser die Mail anzeigt. Gängige E-Mail-Clients unterstützen Flexbox und Grid nicht verlässlich, manche entfernen das `style`-Element, andere ignorieren Teile des CSS. Diese Vorlagen sind deshalb Tabellen, laden keine Webfonts, verwenden kein SVG und wiederholen einige Farben als `bgcolor`-Attribut für Clients, die das Stylesheet ignorieren.

Ist ein CMS-Block in einer E-Mail sicher?

Nur mit einem Filter davor. Der Einladungsblock ist ein normaler CMS-Block, damit die Redaktion ihn ändern kann, und er wird vor dem Rendern bereinigt: `script`, `style`, `iframe`, `form`, `svg` und andere werden samt Inhalt entfernt, `onclick`, `class` und `id` fallen weg, und in `href` und `src` überleben nur URLs mit `http`, `https`, `mailto`, `tel` und relative URLs. Die Warnung beim Speichern prüft nur den Rohinhalt, die Ausgabe von CMS-Direktiven wird stattdessen beim Rendern bereinigt.

Wo steht dein Magento-Projekt gerade?

Schildere mir kurz, woran du arbeitest, wo du stehst und was bereits versucht wurde. Ich sage dir ehrlich, ob ich der richtige Entwickler dafür bin.