Türchen 21: Kostenlose Produkte über Warenkorb-Preisregeln

21.12.2011   //   von Nikolai Krambrock   //   Adventskalender, Magento  //  29 Kommentare

Passend zum Thema Weihnachten versteckt sich hinter diesem Türchen die Möglichkeit Geschenke zu verteilen. Die vorgestellte Extension erweitert die Magento-Warenkorbpreisregeln (Salesrules) um kostenlose Produkte, die dem Warenkorb hinzugefügt werden können. In diesem Artikel möchte ich erklären, wie sich die Warenkorbpreisregeln auf diese Art in vier einfachen Schritten erweitern lassen.

Dieser Artikel liefert ein fertiges Modul, um Geschenke unter bestimmten Bedingungen dem Warenkorb hinzuzufügen. Das Modul kann unter http://www.krambrock.com/blog/magento/make-a-gift/ heruntergeladen und eingesetzt werden. Es ist eine Beta-Version – getestet, aber noch nicht live im Einsatz. Die Verwendung lässt sich einfach Anhand von zwei Screenshots erklären. Der erste Screenshot zeigt, wie eine neue Warenkorbpreisregel eingegeben wird:

Der zweite Screenshot zeigt die Auswirkungen im Warenkorb, wenn die Bedingungen der Regel zutreffen:

Neue Aktionen für Warenkorbpreisregeln selbst erstellen

Im Folgenden möchte ich an dem Modul erklären, wie sich die Aktionen der Warenkorbpreisregeln leicht selbst erweitern lassen. Das geschieht in vier Schritten:

  1. Im ersten wird der Warenkorbpreisregel-Editor um eine Aktion ‘Add a gift’ und ein Feld mit der SKU des Geschenks erweitert.
  2. Im zweiten Schritt werden die beiden Felder ausgewertet und es wird ein Geschenk hinzugefügt.
  3. Im dritten Schritt wird verhindert, dass sich die Geschenke mit jedem Klick auf den Warenkorb akkumulieren.
  4. Im letzten (optionalen) Schritt wird schließlich das Template angepasst.

Alle Quellcodebeispiele sind der Übersichtlichkeit und Verständlichkeit halber vereinfacht und gekürzt. Sie müssten auch so laufen – im Zweifel empfehle ich, dem getesteten Download zu vertrauen.

Schritt 1: Dem Warenkorbpreisregel-Fenster im Backend eine Aktion und ein Feld hinzufügen

Zunächst muss die Möglichkeit geschaffen werden, eine neue Aktion im Backend überhaupt einzutragen. Dies lässt sich elegant bewerkstelligen, indem über das Event ‘adminhtml_block_salesrule_actions_prepareform’ in den Aufbau des Warenkorbpreisregel-Formulars eingegriffen wird. Dazu muss in der app/code/community/C4B/Freeproduct/etc/config.xml ein Observer registriert werden:

<config>
  <!-- ... -->
  <adminhtml>
    <events>    
      <adminhtml_block_salesrule_actions_prepareform>
        <observers>
          <freeproduct>
            <type>singleton</type>
            <class>freeproduct/observer</class>
            <method>adminhtmlBlockSalesruleActionsPrepareform</method>
          </freeproduct>
        </observers>
      </adminhtml_block_salesrule_actions_prepareform>
    </events>
  </adminhtml>
  <!-- ... -->
</config>

Im entsprechenden Observer unter app/code/community/C4B/Freeproduct/Model/Observer.php geschieht Zweierlei: Zunächst werden die Aktionen um ‘Add a gift’ erweitert. Dazu werden im Quellcode unten die Aktionen (‘simple_action’) aus dem Formular geholt und um die Aktion ‘Add a gift’ erweitert. Schließlich werden die Formularfelder (‘action_fieldset’) aus dem Formular geholt und um das Feld ‘gift_sku’ erweitert. Damit das Ganze funktioniert muss die Datenbanktabelle Salesrule ein neues Feld ‘gift_sku’ haben. Dazu möchte ich auf den Download verweisen, um den Rahmen dieses Artikels nicht zu sprengen.

class C4B_Freeproduct_Model_Observer {
  public function adminhtmlBlockSalesruleActionsPrepareform($observer) {
    $field = $observer->getForm()->getElement('simple_action');
    $options = $field->getValues();
    $options[] = array('value' => 'add gift', 'label' => 'Add a Gift');
    $field->setValues($options);
        
    $fieldset = $observer->getForm()->getElement('action_fieldset');
    $fieldset->addField('gift_sku', 'text', array('name' => 'gift_sku', 'label' => 'Gift SKU'));
  }
}

Schritt 2: Dem Warenkorb Geschenke hinzufügen

Wurde die oben hinzugefügte Aktion verwendet und treffen die Bedingungen der Warenkorbpreisregel zu, dann muss auch ein Geschenk in den Warenkorb gelegt werden. Wieder macht Magento bereits den größten Teil. Wieder muss ein Observer in der app/code/community/C4B/Freeproduct/etc/config.xml registriert werden – diesmal für eine positiv ausgewertete Warenkorbpreisregel:

<config>
  <!-- ... -->
  <frontend>
    <events>    
      <salesrule_validator_process>
        <observers>
          <freeproduct>
            <type>singleton</type>
            <class>freeproduct/observer</class>
            <method>salesruleValidatorProcess</method>
          </freeproduct>
        </observers>
      </salesrule_validator_process>
    </events>	
  </frontend>
  <!-- ... -->
</config>

Die Auswertung im Observer app/code/community/C4B/Freeproduct/Model/Observer.php ist ein wenig aufwändiger und teilt sich in drei Blöcke im Quellcode. Im ersten Block wird geprüft, ob die Voraussetzungen für unser Geschenk erfüllt sind. Nur bei der Aktion ‘Add a gift’ kommt es zu einer Auswertung und das auch nur, wenn diese nicht bereits ausgewertet wurde. Im zweiten Block wird das Geschenk erstellt und mit einem Preis von 0 € versehen. Im dritten Block wird das Geschenk schließlich dem Warenkorb hinzugefügt. Auch dies funktioniert nur mit entsprechenden neuen Feldern in der Datenbank. Dies wird im Download gezeigt.

class C4B_Freeproduct_Model_Observer {
  public function salesruleValidatorProcess(Varien_Event_Observer $observer) {
    $quote = $observer-> getQuote();
    $item = $observer-> getItem();
    $rule = $observer-> getRule();
        
    if ($rule->getSimpleAction() != 'add gift' || $rule->getIsApplied()) {
        return;
    }

    $product = Mage::getModel('catalog/product')->loadByAttribute('sku', $rule->getGiftSku());
    Mage::getModel('cataloginventory/stock_item')->assignProduct($product);
    $freeItem = Mage::getModel('sales/quote_item')->setProduct($product);
    $freeItem ->setQuote($quote)
              ->setQty($rule->getDiscountAmount())
              ->setCustomPrice(0.0)
              ->setIsFreeProduct(true)
              ->setStoreId($item->getStoreId());    
    
    $quote->addItem($item);
    $rule->setIsApplied(true);    
  }
}

Schritt 3: Geschenke aus dem Warenkorb entfernen

Im jetzigen Stand werden zwar Geschenke in dem Warenkorb gelegt, allerdings nie entfernt. Damit führt jeder Klick in den Warenkorb zu mehr Geschenken – was leicht zu einem schlechten Geschäft für den betreffenden Online-Händler werden könnte. Vor dem Auswerten der Warenkorbpreisregel werden daher alle kostenlosen Produkte aus dem Warenkorb entfernt. Dazu wird – Überraschung – ein Observer in der app/code/community/C4B/Freeproduct/etc/config.xml registriert:

<config>
  <!-- ... -->
  <frontend>
    <events>    
      <sales_quote_collect_totals_before>
        <observers>
          <freeproduct>
            <type>singleton</type>
            <class>freeproduct/observer</class>
            <method>salesQuoteCollectTotalsBefore</method>
          </freeproduct>
        </observers>
      </sales_quote_collect_totals_before>
    </events>	
  </frontend>
  <!-- ... -->
</config>

Wer bis hierhin folgen konnte, den werden diese Zeilen in Quellcode in app/code/community/C4B/Freeproduct/Model/Observer.php nicht überraschen. Es werden alle Artikel aus dem Warenkorb entfernt, die Freeproducts sind:

class C4B_Freeproduct_Model_Observer {
  public function salesQuoteCollectTotalsBefore(Varien_Event_Observer $observer) {
    foreach ($observer->getEvent()->getQuote()->getAllItems() as $item) {
      if ($item->getIsFreeProduct()) {
        $quote->removeItem($item->getId());
      }
    }
  }
}

Schritt 4: Das Template anpassen (optional)

Bisher hat der Benutzer des Systems noch das trügerische Gefühl, er könnte die Zahl der Geschenke selbst anpassen oder durch einen Klick auf den Papierkorb darauf verzichten. Tatsächlich ist dem nicht so: Da bei jedem neuen Aufruf des Warenkorbs zunächst alle Freeproducts gelöscht werden, haben die Eingaben keine Auswirkungen. Schöner wäre es aber sicherlich, wenn der Benutzer die Menge nicht editieren könnte und der Papierkorb nicht angezeigt würde. Dazu muss das Template angepasst werden. Hierfür benötigen wir zunächst wieder einen Eintrag in der app/code/community/C4B/Freeproduct/etc/config.xml, in der eine neue Layout-Datei registriert wird:

<config>
  <!-- ... -->
  <frontend>
    <layout>
      <updates>
        <freeproduct>
          <file>freeproduct.xml</file>
        </freeproduct>
      </updates>
    </layout>	
  </frontend>
  <!-- ... -->
</config>

Die referenzierte Layout-Datei findet sich unter app/design/frontend/base/default/layout/freeproduct.xml. In ihr wird eine geänderte PHTML-Datei für einfache Produkte definiert, so dass wir die Darstellung der Geschenke ändern können:

<layout version="0.1.0">
  <checkout_cart_index>
    <reference name="checkout.cart">
      <action method="addItemRender">
        <type>simple</type>
        <block>checkout/cart_item_renderer</block>
        <template>freeproduct/checkout/cart/item/default.phtml</template>
      </action>
    </reference>
  </checkout_cart_index>
</layout>

In der recht unübersichtlichen Datei app/design/frontend/base/default/template/freeproduct/checkout/cart/item/default.phtml werden nun zwei kleine Veränderungen vorgenommen, die die Anzeige unseres Freeproduct korrigieren: Die Anzahl wird nicht als Form-Field angegeben, sondern einfach als Text und der Papierkorb samt Link wird entfernt. In diesem Quellcode-Schnipsel ist die If-Konstruktion und ‘echo $this->getQty()’ hinzugekommen, das Input-Feld bestand bereits vorher:

<?php if ($_item->getIsFreeProduct()) : ?>
  <?php echo $this->getQty() ?>
<?php else: ?>
  <input name="cart[<?php echo $_item->getId() ?>][qty]" 
         value="<?php echo $this->getQty() ?>" 
         size="4" 
         title="<?php echo $this->__('Qty') ?>" 
         class="input-text qty" maxlength="12" />
<?php endif; ?>

Bei dem zweiten Quellcode-Schnipsel ist lediglich die If-Konstruktion hinzugekommen:

<?php if (!$_item->getIsFreeProduct()) : ?>    
  <a href="<?php echo $this->getDeleteUrl()?>" 
     title="<?php echo $this->__('Remove item')?>" class="btn-remove btn-remove2">
    <?php echo $this->__('Remove item')?>
  </a>
<?php endif; ?>

Fazit

Damit sind alle Änderungen beschrieben, um neue Aktionen zu Warenkorb-Preisregeln hinzufügen zu können. Dies lässt sich einigermaßen einfach und ohne gefährliche Rewrites durchführen. Da ich bis jetzt keine andere kostenfreie Extension für diese Aufgabe finden konnte, möchte ich meine Extension zur Verfügung stellen. Ich freue mich auf Feedback. Dem relativ übersichtlichen Aufwand steht aus meiner Sicht ein ordentlicher potentieller Nutzen gegenüber: Statt Preisnachlässen können nun Produkte verschenkt werden, was geringere Kosten verursacht und eine höhere Identifikation stiftet.

Der Autor

Nikolai Krambrock ist Diplom-Informatiker und kommt aus der "klassischen" Softwareentwicklung mit Java und C#. Seit 2009 arbeitet er mit seinem Unternehmen code4business überwiegend in der Magento-Modulentwicklung (Frontend, Backend und Schnittstellen). Daneben betreut er auch einige größere Magento-Kunden. Sie erreichen ihn unter www.krambrock.com, seine Firma unter www.code4business.de.

29 Kommentare

  • Hallo,

    habe zurzeit ein ähnliches Problem.
    Gibt es auch eine Möglichkeit einen Preis für das Produkt zu erstellen?

  • Danke für die Extension, leider funktioniert sie bei mir nicht:

    Fatal error: Call to undefined method Mage_Catalog_Helper_Data::canApplyMsrp() in /xxx/app/design/frontend/base/default/template/freeproduct/checkout/cart/item/default.phtml on line 30

    Verwende Magento 1.5.1

  • Funktioniert Super! Vielen Dank!

  • Hallo,

    ich verwende Magento 1.5.0.1 und bekomme die selbe Fehlermeldung wie Martin:
    Fatal error: Call to undefined method Mage_Catalog_Helper_Data::canApplyMsrp() in /e4ychdsm/e4you_testsystem/releases/20111129175725/app/design/frontend/em0001/default/template/freeproduct/checkout/cart/item/default.phtml on line 30

  • Nachtrag ich habe gemerkt dass die GiftSKU bei mir nicht gespeichert wird. Daher kommt dann wahrscheinlich auch die Fehlermeldung. Was könnte ich hier machen?

  • Nachtrag ich habe die Datei default\template\freeproduct\checkout\cart\item\default.phtml mit meiner originalen default.phtml ausgetauscht und die zwei Passagen die die qty und den remove button deaktivieren manuell hinzugefügt. Jetzt ist die Fehlermeldung zwar weg jedoch wird auch kein Geschenk hinzugefügt. Einen Logeintrag oder eine Fehlermeldung bekomme ich auch nicht…wo könnte ich hier ansetzen um den Fehler zu finden?

  • @easyMaxi: Ein Preis für das Produkt ist nicht vorgesehen.

  • @Martin und Stefan: Wir haben das Modul unter nur ab Magento 1.6 getestet. Die Layout-Updates sind optional. Die kann man also einfach aus dem Modul entfernen. Wenn die Gift-SKU unter Magento 1.5.0.1 allerdings nicht gespeichert wird, müsste man den Schritt 1 anpassen.

  • Das Modul funktioniert bei mir ohne Fehlermeldungen.

    Jedoch ist es so das mit jedem Klick auf den Warenkorb mein kostenloses Produkt immer wieder eingefügt wird! 1x wird es nicht berechnet … jedes weitere kostet dann.

    Wie kann man das ausschalten?
    Wäre für Hilfe sehr dankbar, da dass Modul ansich sonst super praktisch ist.

  • @Matt: Klingt nach einem super Geschäftsmodell ;-) Im Ernst: Das tut das Modul zumindest nicht in jeder Konstellation. Poste oder schick mir mal folgendes: Magento-Version (das Modul läuft nicht unter Magento 1.6), weitere installierte Module und Screenshots von den Einstellungen in der Warenkorbpreisregel. Kontaktformular/E-Mail-Adresse gibt es auf meiner Webseite.

  • Tested it for 1.7 version. It is not saving the SKU value for “Gift SKU” field. Any ideas? Thank you.

  • @Sam: I just set up a new Magento 1.7.0.2 CE with my extension and it works fine. I am not even overriding anything in the backend. Maybe the sql-file did not run through. Try it on a new installation.

    If it does work in a clean system, delete the entry “freeproduct_setup” from the table “core_resource” in the system where it did not work and try it again.

    If it does not work in a clean system, you have properbly some settings problem. I cannot help you with it.

    Hope that helps you.

  • Thanks a lot for the response. It is working now. Thanks for the great plugin.

  • Anyone have this working with a onepage checkout too?

    I am on enterprise version with a onepage checkout and i’m wondering where the disconnect is.

    Meghan

  • @Meghan: I never tried it with enterprise as it does only use observers (and layout updates). I would remove the layout-updates and I guess that it should work then. If you want the layout-updates (not qty changeable, not delete-icon) you have to change the enterprise layout. Please post your experience here if you decide to change the module. It would also be great if you could send me your results.

  • So I have it installed and there are no errors, but when you apply a correct rule it sends me to an all white screen??

    Any idea what the cause of that is?

    All other shopping cart rules appear to be funcitoning as normal.

  • here is the error we are getting:

    this is the error I’m tracking down Call to a member function getId() on a non-object in /www/sites/dev.johnsoncreeksmokejuice.com/files/html/app/code/core/Mage/CatalogInventory/Model/Stock/Item.php on line 266, referer: http://dev.johnsoncreeksmokejuice.com.vhost.zerolag.com/checkout/cart/

    any ideas?

  • @Meghan: I am guessing: Do you have a stock problem with the gift you want to put into your cart? Make sure that it is on stock. If this does not help: Install an empty system with just two products and try it again.

  • […] werden können, erweitert. Die Funktion und der Aufbau der Extension wird ausführlich unter http://www.webguys.de/magento/turchen-21-kostenlose-produkte-uber-warenkorb-preisregeln/ […]

  • Hi Krambrock,
    When the Gift Product stock go to 0,the cart will be can not checkout.
    Will you tell me how to set the Gift Product remove out of cart when it go out stock?

  • @Peter: Easy to change in C4B_Freeproduct_Model_Observer->salesruleValidatorProcess. You have the $product already and just need to get the stock status. First hit in google: http://snipplr.com/view/63055/. That should be working.

  • Hi dear,

    I’m using your Magento module for free gift.

    I’ts very good, works very well and help me to increase my sales.

    But now I have a problem, I use a PagSeguro as payment gateway (like PayPal), but it dont accept products with zero price.

    I looked the Observer.php and changed the “->setCustomPrice(0.0)” to “ ->setCustomPrice(0.01)”, but no success, row_total continues 0,0. I have not found where else I could change.

    I needed to pause my promotional sale, and I really need to resolve this question to continue this one.

    Could you help me to set a free product price as 0,01?

    Thank you for your attention.
    Fabiola Videira

  • @Fabiola: I do not have an idea straight away. Did you try with higher prices or commenting the setCustomPrice? What happens then? The product should maybe also have an original price higher than 0.0.

  • Hello,

    Thank you for the wonderful extension.

    I have been able to install it successfully & set a rule, but the free product is not getting added in the cart.

    I am using Easy Checkout 2.0 Extension. Can you please assist me in getting it worked?

    Thanks

  • Hello,

    I have managed to make it work.

    Thanks

  • Hi,

    In button reorder is adding 2 items in cart. How to fix?

  • Solution: I created a module to override reorderAction method.

    if (!$item->getIsFreeProduct())
    {
    $cart->addOrderItem($item);
    }

  • @Manpreet Singh @Ana Paula: Thanks for using the module. It seems there is nothing to do for me …

  • The module is available on github now: https://github.com/code4business/freeproduct. If you think there is a programming problem, please file an issue. I am also very happy about contributions.

Kommentar schreiben

eMail-Benachrichtigung bei weiteren Kommentaren.
Auch möglich: Abo ohne Kommentar.