Skip to content

Instantly share code, notes, and snippets.

@herveguetin
Created July 16, 2014 16:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save herveguetin/9546aedce530d1016fa5 to your computer and use it in GitHub Desktop.
Save herveguetin/9546aedce530d1016fa5 to your computer and use it in GitHub Desktop.
A powerful and easy freeshipping remainder block for Magento based on shopping cart promo rules
<?php
/*
Just create as many shopping cart price rules that are elligible for free shipping
and this block class will use the most appropriate one to define:
- minimum amount to get freeshipping
- remaining amount to purchase to get freeshipping.
If no rule is elligible, the block falls back to a CMS block.
Just call this block from layout and define a template from which you can call:
- $this->getMinimumPurchaseAmount()
- $this->getRemainingShippingAmount()
*/
/**
* Checkout_Freeshipping_Remainder Block
*/
class Namespace_Module_Block_Checkout_Freeshipping_Remainder extends Mage_Core_Block_Template
{
/**
* CMS block identifier for fallback
*/
const CMS_BLOCK_ID = 'my_cms_block_id';
/**
* Amount remaining to get freeshipping
*
* @var int
*/
protected $_remainingShippingAmount = 0;
/**
* Minimum purchase amount to get freeshipping
*
* @var int
*/
protected $_minimumPurchaseAmount = 0;
/**
* Retrieve minimum purchase amount to get freeshipping
*
* @return float
*/
public function getMinimumPurchaseAmount()
{
return $this->_minimumPurchaseAmount;
}
/**
* Retrieve remaining shipping amount
*
* @return float
*/
public function getRemainingShippingAmount()
{
return $this->_remainingShippingAmount;
}
/**
* Retrieve shipping cart promo rules that may be used
*
* @return array
*/
protected function _getApplicableRules()
{
$currentWebsiteId = Mage::app()->getStore()->getWebsiteId();
$currentCustomerGroup = (Mage::helper('customer')->isLoggedIn()) ? Mage::helper('customer')->getCustomer()->getGroupId() : Mage_Customer_Model_Group::NOT_LOGGED_IN_ID;
// Get sales rules that are active, with free shipping enabled and without any coupon
$availableRules = Mage::getModel('salesrule/rule')->getCollection()
->addWebsiteGroupDateFilter($currentWebsiteId, $currentCustomerGroup)
->addFieldToFilter('simple_free_shipping', Mage_SalesRule_Model_Rule::FREE_SHIPPING_ADDRESS)
->addFieldToFilter('coupon_type', Mage_SalesRule_Model_Rule::COUPON_TYPE_NO_COUPON)
;
$applicableRules = array();
foreach($availableRules as $rule) {
/* @var $rule Mage_SalesRule_Model_Rule */
/**
* Check if rule may be a candidate to be applicable.
* This is done by removing all conditions based on cart subtotal amount and testing rule against remaining conditions
*/
$originalConditions = $rule->getConditionsSerialized();
$conditions = unserialize($originalConditions);
// If there is no condition, rule is a candidate!
if(!isset($conditions['conditions'])) {
$applicableRules[] = $rule;
continue;
}
// If there is a condition based on cart subtotal, remove it...
foreach($conditions['conditions'] as $k => $condition) {
if(isset($condition['attribute']) && $condition['attribute'] == 'base_subtotal') {
unset($conditions['conditions'][$k]);
}
}
// ... and rebase conditions
$conditions['conditions'] = array_values($conditions['conditions']);
// If there is no condition anymore, remove its entry in its associated rule
if(count($conditions['conditions']) < 1) {
unset($conditions['conditions']);
}
// Update rule with reworked conditions
$rule->setConditionsSerialized(serialize($conditions));
// And check if updated rule is valid. If so, push it into $applicableRules array.
$address = Mage::helper('checkout/cart')->getQuote()->getShippingAddress();
if($rule->validate($address)) {
// Rollback rule to its original state for further processing
$rule->setConditionsSerialized($originalConditions);
$applicableRules[] = $rule;
}
}
return $applicableRules;
}
/**
* Calculate remaining amount to get free shipping
*
* @param array $rules
* @return Undiz_Local_Block_Checkout_Freeshipping_Remainder
*/
public function setRemainingShippingAmount($rules)
{
$lowestValue = null;
// Retrieve lowest minimum value to get free shipping
foreach($rules as $rule) {
$conditions = unserialize($rule->getConditionsSerialized());
// If there is no condition, value for free shipping is 0
if(!isset($conditions['conditions'])) {
$value = 0;
}
else {
foreach($conditions['conditions'] as $condition) {
if(isset($condition['attribute']) && $condition['attribute'] == 'base_subtotal') {
$value = $condition['value'];
if(is_null($lowestValue) || $value < $lowestValue) {
$lowestValue = $value;
}
}
}
}
if(is_null($lowestValue) || $value < $lowestValue) {
$lowestValue = $value;
}
}
if($lowestValue) {
$store = Mage::app()->getStore();
// Get cart subtotal excl. tax
$cartSubtotal = Mage::helper('checkout/cart')->getQuote()->getBaseSubtotal();
// Check if tax application for shipping is required
// If so, retrieve tax rate based on current store and using shipping tax class
$taxRate = 0;
if(Mage::getStoreConfig('tax/calculation/discount_tax') == 1) { // @see Mage_Tax_Model_System_Config_Source_PriceType
$request = Mage::getSingleton('tax/calculation')->getRateRequest(false, false, false, $store);
$taxRate = Mage::getSingleton('tax/calculation')
->getRate($request->setProductClassId(Mage::getStoreConfig('tax/classes/shipping_tax_class')));
}
// Get cart subtotal updated with tax rate
$cartSubtotal = $cartSubtotal * (1 + $taxRate/100);
// Calculate remaining shipping amount. If < 0, set it to 0
$remainingShippingAmount = ($lowestValue - $cartSubtotal < 0) ? 0 : $lowestValue - $cartSubtotal;
// Calculate remaining shipping amount for real
$this->_remainingShippingAmount = $store->roundPrice($remainingShippingAmount);
// Update minimum purchase amount to get free shiupping
$this->_minimumPurchaseAmount = $store->roundPrice($lowestValue);
}
return $this;
}
/**
* Switch html rendering:
* - if there is some available sales rule => calculate remaining shipping amount
* - else => render some CMS block
*
* @return string
*/
protected function _toHtml()
{
$applicableRules = $this->_getApplicableRules();
if(count($applicableRules) < 1) {
$cmsBlock = $this->getLayout()
->createBlock('cms/block')
->setBlockId(self::CMS_BLOCK_ID);
return $cmsBlock->toHtml();
}
$this->setRemainingShippingAmount($applicableRules);
return parent::_toHtml();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment