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

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:

Rule1

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

Cart1

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.



Ein Beitrag von Nikolai Krambrock
Nikolai's avatar

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.

Alle Beiträge von Nikolai

Kommentare
Jonathan am

Hi Nikolai, danke für das zur Verfügung stellen und die Erklärungen. Auch wenn es schon etwas her ist: Nachdem ich die Extension über Magento Connect erfolgreich installieren konnte, finde ich im Backup bei den Warenkrob-Preisregeln leider 0 Veränderung (Reindexierung, Cache-Leerung etc. gemacht). Unter dem Reiter Aktionen müsste doch dort die wie auch in deinem Screenshot gezeigten Veränderungen auftreten (Gift SKU).

Viele der Kommentare sprechen ja von Problemen im Warenkorb, bei mir ist es komischerweise schon im Backend nicht korrekt. Ich nutze eigene Templates, aber das dürfte doch im Backend keine Rolle spielen?! Gibt es Vorbedingungen, die ich nicht erfülle? Ich nutze CE 1.9.2.3.

Grüße & danke schonmal!

Timo am

Hallo, vielen dank für die Beschreibung. Habe das Toole mal in unserer Version 1.9. installiert, im Backend funktioniert auch soweit die Einstellung. Leider greift die Regel im Warenkorb nicht. Also ab einem Bestellwert von 50€ soll er Artikel X als Geschenk im Warenkorb hinzufügen. Leider nimmt er dies nicht. :-( Habt Ihr vielleicht eine Idee wieso nicht?

Gruß und Danke Timo

Prignesh am

Hello;

I have created rule Buy X Get Y free.

But i need same qty for like if customer BUY 2 X product then i need to allow 2 Y free product instead of one.

Can i know what i need to do?

Looking forward to hearing from you.

Thanks

Nikolai Krambrock am

@David: In der Datei

https://github.com/code4business/freeproduct/blob/master/app/code/community/C4B/Freeproduct/Model/Observer.php

gibt es diesen Quellcode:


$quoteItem->setQuote($quote)
        ->setQty($qty)
        ->setCustomPrice(0.0)
        ->setOriginalCustomPrice($product->getPrice())
        ->setIsFreeProduct(true)
        ->setWeeeTaxApplied('a:0:{}') // Set WeeTaxApplied Value by default so there are no "warnings" later on during invoice creation
        ->setStoreId($storeId);

Das würde ich versuchsweise das ->setCustomPrice(0.0) mal auskommentieren und schauen, was passiert. Wenn du dazu eine Lösung mit Konfiguration baust, freue ich mich auf einen Pull-Request unter https://github.com/code4business/freeproduct/.

Problem könnte sein, dass es nicht möglich ist, dass Geschenk wieder loszuwerden. Wenn man es rauswirft, ist es sofort wieder da. Das macht das Modul nämlich selbst auch bei jeder Änderung im Warenkorb.

David am

Ich habe festegestellt das es nicht möglich ist einen Artikel mit einem Preis größer 0 in den Warenkorb zu legen, -wie Fabiola. Da ich vorhabe bei einzelnen Artikeln eine Zusätzliche Gebühr zu erheben wäre es super wenn du mir da einen Tip geben kannst wie das "Geschenk" auch etwas kosten darf. Danke

Nikolai Krambrock am

@Adil: Same for you - standard ideas: Clean the cache, check the permissions via ssh (on a test system you can use 777), Logout and Login in backend, reindex. This are the most common problems.

If that does not work try to use an empty system and install it there. If it works there go through the differences between you system and an empty system.

Adil am

Hello I installed your extension just fine. But the "Add a gift" does not show up in the Apply dropdown menu. Also the gift SKUS does not save.

I am using 1.7.0.2

Please help.

Thanks, Adil

Nikolai Krambrock am

@Mary: Standard ideas: Clean the cache, check the permissions via ssh (on a test system you can use 777), Logout and Login in backend, reindex. This are the most common problems.

Mary am

Hi, I just installed your extension from github -the latest version I guess- on Magento 1.7.0.2 and it doesn't save the SKU in SKU gift. I've deleted the entry "freeproduct_setup" as you suggested to Sam but it doesn't work yet. Any ideas?

Thank you in advance, Mary

Warenkorb Preisregel um Geschenkoption erweitern | Mage::Formers am

[…] Verfügbar ist das Modul Freeproduct bei GitHub. Eine sehr gute Beschreibung zur technischen Funktionsweise gibt es bei den Kollegen von Webguys. […]

Nikolai Krambrock am

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.

Nikolai Krambrock am

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

Ana Paula am

Solution: I created a module to override reorderAction method.

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

Ana Paula am

Hi,

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

Manpreet Singh am

Hello,

I have managed to make it work.

Thanks

Manpreet Singh am

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

Nikolai Krambrock am

@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.

Fabiola am

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

Nikolai Krambrock am

@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.

Peter am

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?

Dein Kommentar