Rabattstapelung in Magento 2 verhindern — ein erweiterbarer Open-Source-Ansatz
Ein Produkt hat einen Sonderpreis: €100 auf €75, also 25% Rabatt. Ein Kunde löst einen 30%-Gutschein ein. Magento stapelt beides. Der Kunde zahlt €52,50, also 47,5% Gesamtrabatt. Das war nicht die Absicht.
Das ist Magento’s Standardverhalten. Warenkorb-Preisregeln werden auf Sonderpreise, Katalogpreisregeln und Staffelpreise obendrauf angewendet. Für die meisten Händler bedeutet das entweder ungeplanten Margenabbau, das manuelle Ausschließen jedes rabattierten Produkts aus jeder Gutscheinregel, oder die Hoffnung, dass Kunden es nicht bemerken. Keine dieser Optionen ist eine echte Lösung.
Was das Modul macht
PixelPerfect Discount Exclusion greift in Magento’s Rabattberechnung über ein Around-Plugin auf Validator::process() ein und wendet konfigurierbare Logik an, um ungewollte Stapelung zu verhindern.
Die wichtigste Designentscheidung: Es ist kein fest verdrahteter Blocker, sondern eine erweiterbare Architektur mit Guards und Strategies, die für eigene Geschäftsregeln angepasst werden kann.
Die Struktur sieht so aus:
Verarbeitung der Verkaufsregel
├── Guards: Soll überhaupt auf Ausschlüsse geprüft werden?
│ ├── CouponOnly — automatische Regeln überspringen, nur Gutschein-basiert prüfen
│ ├── Ampromo — Gratisartikel-Regeln nicht blockieren
│ └── ZeroPrice — Artikel mit Nullpreis überspringen
├── Strategies: Ist dieses Produkt bereits rabattiert?
│ ├── SpecialPriceStrategy — aktiver Sonderpreis erkannt
│ └── CatalogRuleStrategy — Katalogpreisregel aktiv
└── Entscheidung
├── Ausgeschlossen → Gutschein-Rabatt blockieren
└── Nicht ausgeschlossen → normal fortfahren
Der Bypass-Modus
Manchmal soll ein Gutschein auch bei bereits rabattierten Produkten funktionieren, aber nur wenn er einen besseren Preis ergibt als der vorhandene Rabatt. Jede Regel hat einen Schalter „Rabattausschluss umgehen", der die Max-Rabatt-Logik aktiviert.
Am Beispiel: Regulärpreis €100, Sonderpreis €75 (25%), Gutschein 30%.
Ohne Bypass: Gutschein wird blockiert, weil das Produkt bereits rabattiert ist.
Mit Bypass: max(25%, 30%) = 30%, Zielpreis also €70. Es wird ein zusätzlicher Rabatt von €5 angewendet. Der Kunde bekommt das bessere Angebot, aber die Rabatte stapeln sich nicht.
Meldungen für den Kunden
Das Modul blockiert Rabatte nicht still. Es erklärt dem Kunden, was passiert ist:
„Der Gutschein konnte auf [Produktname] nicht angewendet werden, weil dieses Produkt bereits einen Sonderpreis hat."
„[Produktname] hat bereits 25% Rabatt. Ihr 30%-Gutschein ist günstiger — wir haben die Differenz angewendet."
Meldungen erscheinen auf der Warenkorbseite und sind pro Store View konfigurierbar.
Eigene Erweiterungen
Wer einen benutzerdefinierten Guard braucht, zum Beispiel um die Ausschlusslogik für eine bestimmte Kundengruppe zu deaktivieren:
class VipCustomerGuard implements StrategyEligibilityGuardInterface
{
public function canProcess($product, $item, $rule): bool
{
$customerGroupId = $item->getQuote()->getCustomerGroupId();
return $customerGroupId !== self::VIP_GROUP_ID;
}
}
Oder eine benutzerdefinierte Strategy, um Hersteller-Aktionen aus einem Custom-Attribut zu prüfen:
class ManufacturerPromoStrategy implements DiscountExclusionStrategyInterface
{
public function shouldExcludeFromDiscount($product, $item): bool
{
return (bool) $product->getData('manufacturer_promo_active');
}
}
Beides wird über di.xml registriert, keine Core-Modifikationen notwendig.
Installation
composer require pixelperfectat/magento2-module-discount-exclusion
bin/magento module:enable PixelPerfect_DiscountExclusion
bin/magento setup:upgrade
Kompatibel mit PHP 8.3+ und Magento 2.4.x. Das Modul dient auch als Grundlage für das Checkout Discount Display-Modul, das die kundenseitige Darstellung desselben Problems übernimmt.
Wer schon einmal einen Anruf bekommen hat, weil ein Gutschein „zu viel Rabatt gegeben hat", kennt das Gespräch, aus dem dieses Modul entstanden ist. GitHub-Repository hier. Fragen oder individuelle Rabattlogik: Kontakt aufnehmen.