Glad to see you again in our Magento blog!
Today I’ll continue Topic 8 “Checkout” with the article about shopping cart rules. Different from catalog cart rules, shopping cart rules define the promotion for customer only when customer checks out product. They can be specified by either coupon code or others. However, have you known how rules are applied in shopping cart? If not, my post will certainly helpful for you.
1. Discount total model
Magento uses a total model to calculate the discount by shopping cart rules and shows it in the cart total. This total is calculated after subtotal and shipping and before grand total.
<global> <sales> <quote> <totals> <discount> <class>salesrule/quote_discount</class> <after>subtotal,shipping</after> <before>grand_total</before> </discount> ... </totals> </quote> </sales> </global>
The total is collected:
public function collect(Mage_Sales_Model_Quote_Address $address) { parent::collect($address); $quote = $address->getQuote(); $store = Mage::app()->getStore($quote->getStoreId()); $this->_calculator->reset($address); $items = $this->_getAddressItems($address); if (!count($items)) { return $this; } $eventArgs = array( 'website_id' => $store->getWebsiteId(), 'customer_group_id' => $quote->getCustomerGroupId(), 'coupon_code' => $quote->getCouponCode(), ); $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode()); $this->_calculator->initTotals($items, $address); $address->setDiscountDescription(array()); foreach ($items as $item) { if ($item->getNoDiscount()) { $item->setDiscountAmount(0); $item->setBaseDiscountAmount(0); } else { /** * Child item discount we calculate for parent */ if ($item->getParentItemId()) { continue; } $eventArgs['item'] = $item; Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs); if ($item->getHasChildren() && $item->isChildrenCalculated()) { foreach ($item->getChildren() as $child) { $this->_calculator->process($child); $eventArgs['item'] = $child; Mage::dispatchEvent('sales_quote_address_discount_item', $eventArgs); $this->_aggregateItemDiscount($child); } } else { $this->_calculator->process($item); $this->_aggregateItemDiscount($item); } } } /** * Process shipping amount discount */ $address->setShippingDiscountAmount(0); $address->setBaseShippingDiscountAmount(0); if ($address->getShippingAmount()) { $this->_calculator->processShippingAmount($address); $this->_addAmount(-$address->getShippingDiscountAmount()); $this->_addBaseAmount(-$address->getBaseShippingDiscountAmount()); } $this->_calculator->prepareDescription($address); return $this; }
In this function, as you can see, the event that can work with discount item is sales_quote_address_discount_item.
2. Which rules are applied?
The code for Magento to select and apply rules and is located in the class Mage_SalesRule_Model_Validator. Each item is processed by the process method.
public function process(Mage_Sales_Model_Quote_Item_Abstract $item) { $item->setDiscountAmount(0); $item->setBaseDiscountAmount(0); $item->setDiscountPercent(0); $quote = $item->getQuote(); $address = $this->_getAddress($item); $itemPrice = $this->_getItemPrice($item); $baseItemPrice = $this->_getItemBasePrice($item); $itemOriginalPrice = $item->getOriginalPrice(); $baseItemOriginalPrice = $item->getBaseOriginalPrice(); if ($itemPrice <= 0) { return $this; } $appliedRuleIds = array(); foreach ($this->_getRules() as $rule) { /* @var $rule Mage_SalesRule_Model_Rule */ if (!$this->_canProcessRule($rule, $address)) { continue; } if (!$rule->getActions()->validate($item)) { continue; } ...//calculate and apply discount for item if ($rule->getStopRulesProcessing()) { break; } } $item->setAppliedRuleIds(join(',',$appliedRuleIds)); $address->setAppliedRuleIds($this->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds)); $quote->setAppliedRuleIds($this->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds)); return $this; }
Basically, rules are sorted by the priority and Magento applies rule by rule for each item. The rule needs to be validated for the current shopping cart and if that rule prevents further rule from processing, the loop will be broken.
You can go to file app\code\core\Mage\SalesRule\Model\Validator.php to view more details of the code.
Thanks for reading! If you want to get other tutorials on Magento developer exam, just visit our Magento Tutorial series.
3 Comments
Hi David! Thanks for the your wonderful articles! I just passed the magento certification exam this morning. Thanks alot! =D
Great! Nice to hear that. Congrats, Joby!
Thanks for this straightforward article! It was really helpful.