Magento – Create a custom shopping cart price rule

Read this post in English

Share on:

Acesta nu este un tutorial despre cum se seteaza o regula de tip Shopping Cart Price Rule in Magento, dar despre cum se implementeaza una noua.

Un tip nou de regula in Magento presupune doua lucuri:
– modificarea admin-ului pt a adauga noua regula folosind un observer pentru adminhtml_block_salesrule_actions_prepareform,
– un mod de aplicare pentru noua regula folosind un observer pentru salesrule_validator_process.

Sa luam un exemplu. Sa zicem ca exista o regula de tip Shopping Cart Price Rule care ofera discount diferit in functie de numarul de produse din cos. Se va calcula o valoare de incrementare pentru fiecare pas ($step). Primul produs nu va primi nici un discount, al doilea produs va primi un discount de $step, al doilea produs un discount de 2*$step, pana se ajunge la valoarea maxima de discount. Urmatoarele produse vor avea discount maxim. Ex:

Discount Amount = 50
Discount Qty = 5
Step = Discount Amount / Discount Qty = 10

Discountul rezultat:
– 0% prod 1
– 10% prod 2

– 50% prod 6
– 50% prod 7

Primul pas este activarea modulului cu fisierul: app/etc/modules/CP_ProductNrDiscount.xml

1<?xml version="1.0" encoding="UTF-8"?>
2<config>
3    <modules>
4        <CP_ProductNrDiscount>
5            <active>true</active>
6            <codePool>local</codePool>
7        </CP_ProductNrDiscount>
8    </modules>
9</config>

Primul observer, adminhtml_block_salesrule_actions_prepareform, trebuie sa fie in config in zona “adminhtml”, pentru ca se va aplica admin-ul. Acest observer are acces la formularul din admin, permitand modificarea acestuia.

Al doilea observer, salesrule_validator_process, poate sa fie in zona de “frontend” sau “global” din config. Daca este in “frontend” se va aplica doar in partea de frontend, daca este in global acesta se va aplica si in backend daca este nevoie. In general global este necesar atunci cand se fac operatiuni pe cart din backend.

 1<?xml version="1.0" encoding="UTF-8"?>
 2<config>
 3    <modules>
 4        <CP_ProductNrDiscount>
 5            <version>0.0.1</version>
 6        </CP_ProductNrDiscount>
 7    </modules>
 8    <global>
 9        <models>
10            <productnrdiscount>
11                <class>CP_ProductNrDiscount_Model</class>
12            </productnrdiscount>
13        </models>
14        <events>
15            <salesrule_validator_process>
16                <observers>
17                    <productnrdiscount>
18                        <type>model</type>
19                        <class>productnrdiscount/observer</class>
20                        <method>salesruleValidatorProcess</method>
21                    </productnrdiscount>
22                </observers>
23            </salesrule_validator_process>
24        </events>
25    </global>
26    <adminhtml>
27        <events>
28            <adminhtml_block_salesrule_actions_prepareform>
29            <observers>
30                <productnrdiscount>
31                    <type>model</type>
32                    <class>productnrdiscount/observer</class>
33                <method>adminhtmlBlockSalesruleActionsPrepareform</method>
34                </productnrdiscount>
35            </observers>
36            </adminhtml_block_salesrule_actions_prepareform>
37        </events>
38    </adminhtml>
39</config>

Al doilea observer trebuie sa fie atasat la “frontend” sau “global”, daca trebuie sa ruleze doar in frontend sau si in backend.

Asa cum se poate vedea mai sus, trebuie facut un model Observer care sa aiba cele doua metode care modifica admin-ul si aplica discountul.

 1<?php
 2/**
 3 * Number of product discount module
 4 *
 5 * @author Claudiu Persoiu https://blog.claudiupersoiu.ro
 6 */
 7class CP_ProductNrDiscount_Model_Observer {
 8
 9    // Noul tip de regula
10    const PRODUCT_NR_DISCOUNT = 'product_nr_discount';
11
12    /**
13     * Adaugare nou tip de regula in meniul de administrare
14     *
15     * @param Varien_Event_Observer $observer
16     */
17    public function adminhtmlBlockSalesruleActionsPrepareform
18              (Varien_Event_Observer $observer) {
19        // Extragem campul din formular
20    	$field = $observer->getForm()->getElement('simple_action');
21        // Extragem valorile campului
22        $options = $field->getValues();
23        // Adaugam noua valoare
24        $options[] = array(
25            'value' => self::PRODUCT_NR_DISCOUNT,
26            'label' => 'Product Number Discount'
27        );
28        // Setare camp
29        $field->setValues($options);
30    }
31
32    /**
33     * Aplicare discount
34     * Discountul se va aplica la minim 2 produse progresiv cate un "step" pentru
35     * fiecare produs, unde un "step" este discountul maxim / numarul de produse
36     * pe care se aplica.
37     *
38     * @param Varien_Event_Observer $observer
39     */
40    public function salesruleValidatorProcess(Varien_Event_Observer $observer) {
41
42        // $item typeof Mage_Sales_Model_Quote_Item
43        $item = $observer->getEvent()->getItem();
44        // $rule typeof Mage_SalesRule_Model_Rule
45        $rule = $observer->getEvent()->getRule();
46
47        // Numarul de produse de acest fel
48        $qty = $item->getQty();
49
50        // Trebuie verificat ce tip de regula este, pentru a izola tipul
51        // nostu de regula
52        if($rule->getSimpleAction() == self::PRODUCT_NR_DISCOUNT && $qty > 1) {
53
54            // Detalii regula
55            $discountAmount = $rule->getDiscountAmount();
56            $discountQty = $rule->getDiscountQty();
57
58            // Step de discount
59            $step = $discountAmount/$discountQty;
60
61            // Calcul discount
62            $discount = 0;
63            for($i = 1; $i < $qty; $i++) {
64			    $itemDiscount = $i * $step;
65                // Daca discountul este mai mare decat discountul maxim
66                // atunci se foloseste discount maxim
67                if($itemDiscount > $discountAmount) {
68                    $itemDiscount = $discountAmount;
69                }
70
71                $discount += $itemDiscount;
72            }
73
74            // Discountul propriuzis
75            $totalDiscountAmount = ($item->getPrice() * $discount)/100;
76
77            // Discount in procente pentru fiecare quote item
78            $item->setDiscountPercent($discount / $qty);
79
80            // Setare discount efectiv, practic aceasta este valoarea de discount
81            $result = $observer->getResult();
82            $result->setDiscountAmount($totalDiscountAmount);
83            $result->setBaseDiscountAmount($totalDiscountAmount);
84
85        }
86    }
87
88}

Acest observer se va aplica la fiecare request cand exista module in cart pentru care se aplica regula. Daca discountul trebuie sa se aplice doar pentru anumite produse acestea se pot filtra folosind sesiunea de “Conditions” a regulii definite, asa cum este normal.