Sales Rules in Magento 2 erweitern: Cart-Price-Regeln mit eigenen Conditions
AI generated
M2
di.xml
Magento 2 · Sales Rules · PHP 8.4 · Hyvä
Sales Rules in Magento 2 erweitern
eigene Bedingungen, Aktionen und Coupon-Logik für Cart-Price-Regeln

Cart-Price-Regeln sind eines der mächtigsten Werkzeuge in Magento 2, stoßen aber bei komplexer Rabattlogik schnell an ihre Grenzen. Wer eigene Conditions, individuelle Discount-Aktionen und programmatische Coupon-Generierung beherrscht, baut Sales Rules, die exakt zum Geschäftsmodell passen, statt Kompromisse im Standard-Regelwerk einzugehen. Dieser Beitrag zeigt an echtem Code, wie das Magento_SalesRule Modul über di.xml, Plugins und GraphQL sauber erweitert wird.

18 Min. Lesezeit AbstractCondition · RulesApplier · CouponGenerationSpec · GraphQL Magento 2.4.8-p4 · PHP 8.4

1. Sales Rules Grundlagen: die Cart-Price-Rules-Architektur im Magento_SalesRule Modul

Was im Magento-Backend als "Cart Price Rules" bezeichnet wird, heißt im Code schlicht Sales Rules und lebt vollständig im Modul Magento_SalesRule. Die Entity dahinter ist Magento\SalesRule\Model\Rule, gespeichert in der Tabelle salesrule mit Zuordnungstabellen für Website, Kundengruppe und Coupons. Anders als Catalog Price Rules, die als Cronjob den Katalogindex neu schreiben, werden Sales Rules live während der Totals-Berechnung im Warenkorb ausgewertet, jedes Mal wenn eine Quote neu kollektiert wird.

Die zentralen Bausteine des Moduls sind schnell benannt: RuleRepositoryInterface als Service Contract für CRUD-Operationen, Magento\SalesRule\Model\Validator als Einstiegspunkt für die Regelanwendung während collectTotals(), sowie RulesApplier für die eigentliche Rabattberechnung pro Artikel. Ergänzt wird das durch CouponGenerationSpecInterface für Massengenerierung von Gutscheincodes und ein Set von Condition- und Action-Klassen, die den Regelbaum im Admin-Formular abbilden.

Der eigentliche Mehrwert für Agenturen entsteht dort, wo die Standardkonfiguration nicht mehr ausreicht. Fast jedes Projekt erreicht irgendwann einen Punkt, an dem eine Kundenanforderung mit den mitgelieferten Bedingungen und Aktionen nicht mehr abbildbar ist. Genau dafür bietet Magento über di.xml, Plugins und Service Contracts saubere Erweiterungspunkte, ohne den Kern des SalesRule Moduls anfassen zu müssen, und genau diese Erweiterungspunkte sind Thema dieses Beitrags.

2. Rule Entity im Datenmodell: Conditions und Actions als serialisierte Objektbäume

Die Tabelle salesrule speichert die Bedingungslogik einer Regel nicht als normalisierte Zeilen, sondern in den Spalten conditions_serialized und actions_serialized als serialisierten Baum. Seit Magento 2.2 übernimmt Magento\Framework\Serialize\Serializer\Json diese Aufgabe, historisch war es PHP-serialize(). Beim Laden einer Regel baut Rule::getConditions() daraus eine Objekthierarchie aus Combine-Knoten (logische UND/ODER-Gruppen) und einzelnen Condition-Blättern auf, jeweils mit Attribut, Operator und Wert.

Diese Objektbäume werden nicht bei jedem Zugriff neu aufgebaut. Rule::getConditionsInstance() und getActionsInstance() cachen die instanziierte Struktur pro Rule-Objekt, damit die Validierung während der Totals-Kollektion nicht bei jedem Artikel im Warenkorb erneut deserialisiert. Wer eigene Conditions oder Actions einbaut, muss diesen Lebenszyklus verstehen: Ein Objekt, das im Admin-Formular korrekt gespeichert wird, aber beim Laden nicht denselben Klassennamen liefert wie beim Speichern, führt zu stillen Fehlfunktionen der Sales Rules statt zu einer Exception.

Wichtig für die Praxis: Das Admin-Formular für Sales Rules basiert weiterhin auf dem klassischen UI-Components-Stack mit Knockout.js, unabhängig davon, dass das Frontend mit Hyvä ohne jQuery und ohne Knockout auskommt. Wer eigene Conditions ergänzt, arbeitet also im Backend-Kontext mit den bestehenden Formularkomponenten und nicht mit Alpine.js, das ausschließlich für das Storefront relevant ist.

3. Eigene Bedingungen (Conditions) hinzufügen

Die mitgelieferten Bedingungen decken Attributvergleiche, Warenkorbsumme, Gewicht oder Kategorie-Zugehörigkeit ab, reichen aber nicht, sobald externe Geschäftslogik einfließen soll, etwa ein Loyalitäts-Tier aus einem ERP-System oder ein Abo-Flag aus einem Subscription-Modul. Für solche Fälle erstellt man eine eigene Condition-Klasse, die von Magento\Rule\Model\Condition\AbstractCondition erbt und die Methoden loadAttributeOptions(), getInputType(), getValueElementType() und validate() implementiert.

Die validate()-Methode erhält als Parameter das jeweilige Magento\Framework\Model\AbstractModel, im Kontext von Sales Rules entweder die Quote-Adresse oder das Quote-Item, je nachdem, an welcher Stelle im Combine-Baum die Bedingung eingehängt ist. Externe Abhängigkeiten wie ein Segment-Resolver werden per Constructor Property Promotion injiziert, exakt wie bei jeder anderen Service-Klasse auch. Damit bleibt die Condition testbar und frei von statischen Aufrufen.


<?php

declare(strict_types=1);

namespace Vendor\Module\Model\Rule\Condition;

use Magento\Framework\Model\AbstractModel;
use Magento\Rule\Model\Condition\AbstractCondition;
use Magento\Rule\Model\Condition\Context;
use Vendor\Module\Api\CustomerSegmentResolverInterface;

/**
 * Custom Sales Rule condition based on an externally resolved customer segment.
 */
class CustomerSegment extends AbstractCondition
{
    /**
     * @param Context $context Rule condition context required by the parent class.
     * @param CustomerSegmentResolverInterface $segmentResolver Resolves the segment for a customer id.
     * @param array $data Additional condition data.
     */
    public function __construct(
        Context $context,
        private readonly CustomerSegmentResolverInterface $segmentResolver,
        array $data = []
    ) {
        parent::__construct($context, $data);
        $this->setType(self::class);
    }

    /**
     * Registers the attribute option shown in the rule condition dropdown.
     *
     * @return $this
     */
    public function loadAttributeOptions(): self
    {
        $this->setAttributeOption(['segment' => __('Customer Segment (external)')]);
        return $this;
    }

    /**
     * @return string
     */
    public function getInputType(): string
    {
        return 'select';
    }

    /**
     * @return string
     */
    public function getValueElementType(): string
    {
        return 'select';
    }

    /**
     * @return array
     */
    public function getValueSelectOptions(): array
    {
        if (!$this->getData('value_select_options')) {
            $this->setData('value_select_options', [
                ['value' => 'vip', 'label' => __('VIP')],
                ['value' => 'wholesale', 'label' => __('Wholesale')],
                ['value' => 'standard', 'label' => __('Standard')],
            ]);
        }
        return $this->getData('value_select_options');
    }

    /**
     * Validates the condition against the current quote address.
     *
     * @param AbstractModel $model
     * @return bool
     */
    public function validate(AbstractModel $model): bool
    {
        $quote = $model->getQuote();
        $segment = $this->segmentResolver->resolveForCustomer((int) $quote->getCustomerId());
        return $this->validateAttribute($segment);
    }
}

Registriert wird die neue Condition nicht über eine feste Konfigurationsliste, sondern über einen Plugin auf Magento\SalesRule\Model\Rule\Condition\Combine::getNewChildSelectOptions(), der die eigene Klasse samt Label in die Dropdown-Optionen des Regel-Editors einhängt. Das entspricht dem Projekt-Standard, Plugins statt Preferences zu verwenden, und vermeidet, dass ein Update des SalesRule Moduls die eigene Erweiterung überschreibt.


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\SalesRule\Model\Rule\Condition\Combine">
        <plugin name="vendor_module_custom_segment_condition"
                type="Vendor\Module\Plugin\Rule\Condition\CombinePlugin"
                sortOrder="10" />
    </type>
</config>

4. Eigene Aktionen (Discount-Aktionen) implementieren

Für die eigentliche Rabattberechnung ist Magento\SalesRule\Model\RulesApplier::applyRule() zuständig. Die Klasse iteriert über Warenkorbpositionen und berechnet, abhängig vom konfigurierten Action-Typ (by_percent, by_fixed, cart_fixed, buy_x_get_y), den Rabattbetrag pro Artikel. Für vollständig neue Rabattlogiken, etwa gestaffelte Rabatte nach externem Loyalitäts-Score oder abweichende Rundungsregeln je Währung, gibt es keinen nativen Erweiterungspunkt ohne Kernänderung. Der pragmatische und vom Projekt-Standard vorgegebene Weg ist ein Plugin auf RulesApplier.

Ein after-Plugin auf applyRule() erhält das bereits berechnete Discount-Ergebnis und kann es vor der Rückgabe anpassen. Externe Services wie ein Loyalitäts-Rechner werden wieder per Constructor Property Promotion injiziert. Wichtig ist, dass ein solcher Plugin ausschließlich greift, wenn die Sales Rule ein eigenes, zusätzliches Attribut trägt, etwa über eine Erweiterung von salesrule_extension per db_schema.xml, damit Standard-Regeln unangetastet bleiben.


<?php

declare(strict_types=1);

namespace Vendor\Module\Plugin\SalesRule;

use Magento\Quote\Model\Quote\Item\AbstractItem;
use Magento\SalesRule\Model\Rule;
use Magento\SalesRule\Model\Rule\Action\Discount\Data as DiscountData;
use Magento\SalesRule\Model\RulesApplier;
use Vendor\Module\Api\LoyaltyDiscountCalculatorInterface;

/**
 * Adds an additional loyalty-tier discount on top of the standard Sales Rule calculation.
 */
class LoyaltyDiscountPlugin
{
    /**
     * @param LoyaltyDiscountCalculatorInterface $loyaltyCalculator Calculates the extra discount amount.
     */
    public function __construct(
        private readonly LoyaltyDiscountCalculatorInterface $loyaltyCalculator
    ) {
    }

    /**
     * @param RulesApplier $subject
     * @param DiscountData $result
     * @param AbstractItem $item
     * @param Rule $rule
     * @param bool $qty
     * @param float $rulePercent
     * @return DiscountData
     */
    public function afterApplyRule(
        RulesApplier $subject,
        DiscountData $result,
        AbstractItem $item,
        Rule $rule,
        bool $qty,
        float $rulePercent
    ): DiscountData {
        if (!$rule->getData('loyalty_tier_discount')) {
            return $result;
        }

        $extraDiscount = $this->loyaltyCalculator->calculate($item, $rule);
        if ($extraDiscount > 0.0) {
            $result->setAmount($result->getAmount() + $extraDiscount);
            $result->setBaseAmount($result->getBaseAmount() + $extraDiscount);
        }

        return $result;
    }
}

Bei der Kombination mehrerer Aktionen gilt es, den Gesamtrabatt niemals den Artikelpreis übersteigen zu lassen. RulesApplier begrenzt das intern bereits, eigene Plugins müssen diese Begrenzung jedoch respektieren, wenn sie den Betrag nachträglich erhöhen. Ebenso wichtig: Der Plugin muss den Store-Scope prüfen, falls die Loyalitätslogik nur für bestimmte Websites gelten soll, sonst greift die zusätzliche Rabattierung ungewollt auf allen Shops im Setup.

5. Coupon-Generierung und -Validierung programmatisch steuern

Für die Massengenerierung von Gutscheincodes bietet Magento CouponGenerationSpecInterface zusammen mit Magento\SalesRule\Model\CouponGenerator. Im Admin-Grid steckt dahinter die Massaction "Generieren", die intern eine Spezifikation aus Präfix, Suffix, Länge, Zeichensatz und Anzahl aufbaut. Dieselbe Spezifikation lässt sich programmatisch erzeugen, etwa um für jeden Newsletter-Abonnenten oder jeden B2B-Account einen individuellen, eindeutigen Code zu erstellen, ohne den Umweg über das Backend zu nehmen.

Für zusätzliche Validierungsregeln, etwa eine Bindung eines Codes an ein bestimmtes Kundensegment oder eine maximale Einlösung pro Kalendermonat, bietet sich ein Plugin auf Magento\SalesRule\Model\Coupon::loadByCode() oder auf die Validierungslogik in Validator an. Solche Prüfungen greifen zusätzlich zu den Standardfeldern uses_per_customer und uses_per_coupon und werten häufig die Tabelle salesrule_coupon_usage aus, um bereits erfolgte Einlösungen pro Kunde nachzuvollziehen.

Agenturen, die regelmäßig große Mengen an Codes benötigen, bauen sich dafür meist einen eigenen Konsolenbefehl, der CouponGenerationSpecInterfaceFactory und CouponGenerator injiziert und über bin/magento aufrufbar ist. Das erspart den manuellen Weg über das Admin-Grid bei jeder Kampagne und lässt sich direkt in Deployment- oder Import-Pipelines einbinden, ohne dass ein Redakteur jeden Batch manuell auslösen muss.

6. Rule Collector und Performance bei komplexen Regelwerken

Magento\SalesRule\Model\Validator::process() ist der Einstiegspunkt, der bei jeder Totals-Kollektion einer Quote läuft. Intern lädt der Validator über die Rule-Collection alle Regeln, die für Website, Kundengruppe und gegebenenfalls Coupon-Code aktiv sind, und arbeitet sie in aufsteigender sort_order ab. Für jede Regel werden zunächst die Bedingungen gegen Adresse und Artikel geprüft, bevor RulesApplier die zugehörigen Aktionen anwendet.

Bei Shops mit vielen gleichzeitig aktiven Sales Rules multipliziert sich der Aufwand pro Artikel und Quote-Speicherung schnell. Jede zusätzliche Regel bedeutet zusätzliche Bedingungsprüfungen bei jedem Warenkorb-Update. Wer eigene Conditions schreibt, sollte deren validate()-Methode bewusst schlank halten und keine wiederholten Datenbankabfragen pro Artikel ausführen, sondern Ergebnisse innerhalb der Anfrage per injiziertem Cache-Array zwischenspeichern.

Ein zusätzlicher Performance-Hebel liegt darin, den Gültigkeitszeitraum (to_date) konsequent zu pflegen, statt abgelaufene Kampagnen nur zu deaktivieren. Inaktive, aber weiterhin in der Datenbank vorhandene Regeln werden zwar nicht angewendet, verlängern aber unnötig die Ladezeit der Rule-Collection, wenn sie nicht sauber gefiltert sind. Ein regelmäßiges Aufräumen alter Sales Rules zahlt sich bei wachsender Regelanzahl spürbar aus.

7. GraphQL und Rule-Anwendung im Checkout

Im Headless- und Hyvä-Checkout-Kontext läuft die Coupon-Anwendung über die GraphQL-Mutation applyCouponToCart. Der zugehörige Resolver aus Magento\QuoteGraphQl delegiert an Magento\Quote\Model\Cart\CouponManagement, welches wiederum collectTotals() auf der Quote auslöst und damit denselben Validator::process()-Pfad durchläuft wie bei einem klassischen Storefront-Request. Für Hyvä-Checkouts ist das der zentrale Einstiegspunkt, den eigene Frontend-Komponenten aufrufen.


mutation ApplyCouponToCart($cartId: String!, $couponCode: String!) {
  applyCouponToCart(
    input: { cart_id: $cartId, coupon_code: $couponCode }
  ) {
    cart {
      applied_coupons {
        code
      }
      prices {
        discounts {
          amount {
            value
          }
          label
        }
        grand_total {
          value
          currency
        }
      }
    }
  }
}

Beim finalen placeOrder werden Sales Rules ein weiteres Mal ausgewertet, weil die Quote unmittelbar vor der Umwandlung in eine Order erneut collectTotals() durchläuft. Das verhindert, dass ein Warenkorb, der lange offen im Browser lag, mit einem inzwischen abgelaufenen oder deaktivierten Rabatt bestellt wird. Eigene Conditions und Plugins müssen also idempotent sein, weil sie potenziell mehrfach pro Bestellvorgang ausgeführt werden.

Für Fehlerfälle gilt: Custom-Validierungen in eigenen Plugins sollten Magento\Framework\Exception\LocalizedException werfen und keine generische Exception. Nur so formatiert GraphQL die Fehlermeldung korrekt im errors-Array der Response, statt einen internen Serverfehler ohne verständlichen Text auszugeben, den ein Frontend-Entwickler dann kaum sinnvoll anzeigen kann.

8. Testing von Cart Price Rules

Für Integration Tests von Sales Rules bietet sich Magento\TestFramework\Helper\Bootstrap::getObjectManager() an, kombiniert mit Fixtures, die eine Regel per RuleRepositoryInterface::save() anlegen und einer Quote-Fixture zuordnen. Der Test prüft anschließend, ob collectTotals() den erwarteten Rabattbetrag auf die Quote schreibt, und deckt damit die vollständige Kette von Condition-Validierung bis Discount-Berechnung ab.


<?php

declare(strict_types=1);

namespace Vendor\Module\Test\Integration\SalesRule;

use Magento\Quote\Api\CartRepositoryInterface;
use Magento\SalesRule\Api\RuleRepositoryInterface;
use Magento\SalesRule\Api\Data\RuleInterfaceFactory;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;

/**
 * Integration test verifying that a custom loyalty discount rule
 * reduces the quote subtotal by the expected amount.
 */
class LoyaltyDiscountRuleTest extends TestCase
{
    /**
     * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
     * @magentoDbIsolation enabled
     */
    public function testLoyaltyDiscountReducesSubtotal(): void
    {
        $objectManager = Bootstrap::getObjectManager();

        $ruleFactory = $objectManager->get(RuleInterfaceFactory::class);
        $ruleRepository = $objectManager->get(RuleRepositoryInterface::class);

        $rule = $ruleFactory->create();
        $rule->setName('Loyalty Tier Test Rule');
        $rule->setCustomerGroupIds([0, 1]);
        $rule->setWebsiteIds([1]);
        $rule->setSimpleAction('by_percent');
        $rule->setDiscountAmount(10);
        $rule->setData('loyalty_tier_discount', 1);
        $ruleRepository->save($rule);

        $quoteRepository = $objectManager->get(CartRepositoryInterface::class);
        $quote = $quoteRepository->get(1);
        $quote->collectTotals();
        $quoteRepository->save($quote);

        $this->assertGreaterThan(0, (float) $quote->getSubtotal() - (float) $quote->getSubtotalWithDiscount());
    }
}

Fixtures für Sales Rules sollten mindestens Kundengruppe, Website-Zuordnung und Gültigkeitszeitraum abdecken, weil genau diese Felder in der Praxis am häufigsten vergessen werden und Tests scheinbar grundlos fehlschlagen. Ergänzend lohnen sich isolierte Unit Tests für die eigene Condition-Klasse, bei denen $model und die Quote-Objekte gemockt werden. Das prüft die reine Bedingungslogik in Millisekunden, ohne den kompletten Objektmanager-Bootstrap eines Integration Tests zu benötigen.

9. Häufige Fallstricke bei Rule-Kombinationen und Priorität

Sobald mehrere Sales Rules gleichzeitig aktiv sind, entscheidet die Reihenfolge über das Ergebnis. Das Feld sort_order bestimmt, in welcher Reihenfolge Regeln geprüft werden, stop_rules_processing beendet die Auswertung weiterer, niedriger priorisierter Regeln, sobald eine Regel greift, und discard_subsequent_rules markiert einzelne Positionen als "bereits rabattiert", damit nachfolgende Aktionen sie überspringen. Ein klassischer Produktionsbug entsteht, wenn zwei Regeln denselben sort_order-Wert tragen: Die tatsächliche Reihenfolge hängt dann von der rule_id ab und verhält sich auf Staging und Live unterschiedlich, sobald Regeln in anderer Reihenfolge angelegt wurden.

Option Wirkungsbereich Effekt Typischer Einsatz
sort_order Regel-Ebene Bestimmt die Prüfreihenfolge, niedrigere Zahl zuerst Exklusive Regel vor generischen Promotions ausführen
stop_rules_processing Regel-Ebene Stoppt Auswertung weiterer, niedriger priorisierter Regeln Verhindert Stapeln mit generischen Rabatten
discard_subsequent_rules Artikel-Ebene Markiert Positionen als bereits rabattiert Verhindert Doppelrabatt bei Gratisartikel-Regeln
from_date / to_date Regel-Ebene Begrenzt die Gültigkeit zeitlich Zeitlich befristete Kampagnen ohne manuelles Deaktivieren
coupon_type Regel-Ebene Steuert, ob ein Code erforderlich ist Öffentliche Aktion vs. exklusive Partner-Codes

Weitere typische Fallstricke: Eine Freeversand-Regel interagiert mit einer prozentualen Rabattregel oft anders, als der Regel-Editor auf den ersten Blick suggeriert, weil Versandkosten separat berechnet werden. Coupon-Codes werden intern case-insensitiv abgeglichen, aber im Admin-Grid case-sensitiv angezeigt, was bei Import-Skripten zu Duplikaten führen kann. Und wer ein eigenes Attribut an einer Regel per Plugin auswertet, muss daran denken, dass getConditionsInstance() und getActionsInstance() gecacht werden, ein reines Datenbank-Update am Rule-Datensatz also erst nach erneutem Laden der Regel wirkt.

10. Zusammenfassung

Sales Rules sind in Magento 2 keine starre Konfigurationsoberfläche, sondern ein Modul mit klaren, sauberen Erweiterungspunkten. Eigene Conditions ergänzen den Bedingungsbaum über AbstractCondition und einen Plugin auf Combine, ohne den Kern zu verändern. Eigene Discount-Logik entsteht über Plugins auf RulesApplier, konsequent mit Constructor Property Promotion und Service Contracts umgesetzt. Coupon-Generierung und -Validierung lassen sich vollständig programmatisch steuern, inklusive Massengenerierung über eigene CLI-Befehle.

Wer Cart-Price-Regeln erweitert, muss zusätzlich die Performance im Blick behalten: Jede zusätzliche Regel und jede zusätzliche Condition kostet Rechenzeit bei jeder Totals-Kollektion. Priorität und Reihenfolge über sort_order, stop_rules_processing und discard_subsequent_rules entscheiden über korrektes Zusammenspiel mehrerer Regeln, und genau hier entstehen die häufigsten Produktionsfehler. Integration Tests mit dem Magento Test Framework sichern ab, dass eigene Sales Rules auch nach künftigen Änderungen zuverlässig funktionieren.

Sales Rules in Magento 2 erweitern, das Wichtigste auf einen Blick

Eigene Conditions

AbstractCondition erweitern, Registrierung per Plugin auf Combine::getNewChildSelectOptions(), statt fester Konfigurationslisten.

Eigene Discount-Logik

Plugin auf RulesApplier::applyRule() für individuelle Rabattberechnung, sauber testbar und update-sicher gegenüber dem Core.

Coupons programmatisch

CouponGenerationSpecInterface für Massengenerierung, eigene Validierung per Plugin auf Coupon::loadByCode().

Priorität und Performance

sort_order, stop_rules_processing und discard_subsequent_rules konsequent pflegen, Conditions schlank halten.

11. FAQ: Sales Rules in Magento 2 erweitern

1Was ist der Unterschied zu Catalog Price Rules?
Sales Rules laufen live im Warenkorb und unterstützen Coupon-Codes. Catalog Price Rules laufen als Cronjob im Katalogindex. Dieser Beitrag behandelt nur die Erweiterung von Sales Rules.
2Wovon erbt eine eigene Condition-Klasse?
Von Magento\Rule\Model\Condition\AbstractCondition, mit den zu überschreibenden Methoden loadAttributeOptions(), getInputType(), getValueElementType() und validate().
3Wie registriere ich eine eigene Condition?
Per Plugin auf Combine::getNewChildSelectOptions(), der die eigene Klasse in die Dropdown-Liste des Regel-Editors einhängt.
4Wie implementiere ich eigene Rabattlogik?
Per Plugin auf RulesApplier::applyRule(), das den berechneten Discount vor der Rückgabe anpasst, etwa für gestaffelte Loyalitätsrabatte.
5Wie generiere ich Coupon-Codes programmatisch?
Über CouponGenerationSpecInterface und CouponGenerator, ideal in einem eigenen bin/magento Konsolenbefehl für Massengenerierung ohne Admin-Grid.
6Was bedeutet stop_rules_processing?
Beendet die Auswertung weiterer, niedriger priorisierter Regeln, sobald diese Regel zutrifft. Verhindert ungewollte Kombination mit generischen Promotions.
7Unterschied zu discard_subsequent_rules?
stop_rules_processing wirkt auf Regelebene, discard_subsequent_rules nur auf Artikelebene und markiert einzelne Positionen als bereits rabattiert.
8Wie hängt applyCouponToCart mit Rules zusammen?
Die Mutation delegiert an CouponManagement, das collectTotals() auslöst und damit denselben Validator-Pfad durchläuft wie ein klassischer Request.
9Wie teste ich eigene Sales Rules?
Mit Integration Tests über Bootstrap::getObjectManager() und Fixtures, ergänzt durch isolierte Unit Tests für die reine Condition-Logik.
10Warum wirkt eine Regeländerung verzögert?
getConditionsInstance() und getActionsInstance() cachen die Objektstruktur pro Rule-Objekt. Ein Update wirkt erst nach erneutem Laden der Regel.

Mironsoft

Magento 2 Entwicklung, Sales Rules und Checkout-Logik

Sales Rules, die euer Geschäftsmodell wirklich abbilden?

Wir erweitern eure Cart-Price-Regeln um eigene Conditions, individuelle Discount-Aktionen und programmatische Coupon-Generierung, sauber über di.xml und Plugins statt über riskante Core-Änderungen.

Custom Conditions

Eigene Bedingungen für externe Segmente, Loyalitätsstufen und individuelle Kundenlogik

RulesApplier-Plugins

Individuelle Rabattlogik ohne Core-Änderung, sauber testbar und update-sicher

GraphQL Checkout

Coupon-Anwendung und Rule-Validierung für Hyvä- und Headless-Checkouts