I have an E-commerce website where i want the users to pay extra some money if they choose "Cash on delivery" as payment option. Can you please help me achieve this? Thanks in advance
Step 1: Create a file app/etc/modules/Stack_ExtraFee.xml and add the following content:
<?xml version="1.0"?>
<config>
<modules>
<Stack_ExtraFee>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_SalesRule/>
</depends>
</Stack_ExtraFee>
</modules>
</config>
Step 2: Create a file config.xml under app/code/local/Stack/ExtraFee/etc and add the following content:
<?xml version="1.0"?>
<config>
<modules>
<Stack_ExtraFee>
<version>1.0.6</version>
</Stack_ExtraFee>
</modules>
<global>
<models>
<stack_extrafee>
<class>Stack_ExtraFee_Model</class>
<resourceModel>stack_extrafee_resource</resourceModel>
</stack_extrafee>
<stack_extrafee_resource>
<class>Stack_ExtraFee_Model_Resource</class>
</stack_extrafee_resource>
<salesrule>
<rewrite>
<validator>Stack_ExtraFee_Model_SalesRule_Discount_Validator</validator>
</rewrite>
</salesrule>
</models>
<blocks>
<stack_extrafee>
<class>Stack_ExtraFee_Block</class>
</stack_extrafee>
</blocks>
<helpers>
<stack_extrafee>
<class>Stack_ExtraFee_Helper</class>
</stack_extrafee>
</helpers>
<sales>
<quote>
<totals>
<extra_fee_rule>
<class>stack_extrafee/quote_address_total_fee_rule</class>
<after>discount</after>
<before>grand_total</before>
</extra_fee_rule>
</totals>
</quote>
<order_invoice>
<totals>
<extra_fee_rule>
<class>stack_extrafee/sales_order_invoice_total_fee_rule</class>
<after>discount</after>
</extra_fee_rule>
</totals>
</order_invoice>
</sales>
<pdf>
<totals>
<extra_fee_rule>
<title>Extra Rule Fee</title>
<source_field>extra_fee_rule_amount</source_field>
<title_source_field>extra_fee_rule_description</title_source_field>
<font_size>7</font_size>
<display_zero>0</display_zero>
<sort_order>300</sort_order>
</extra_fee_rule>
</totals>
</pdf>
<fieldsets>
<sales_convert_quote_address>
<base_extra_fee_rule_amount>
<to_order>*</to_order>
</base_extra_fee_rule_amount>
<extra_fee_rule_amount>
<to_order>*</to_order>
</extra_fee_rule_amount>
<extra_fee_rule_description>
<to_order>*</to_order>
</extra_fee_rule_description>
</sales_convert_quote_address>
<sales_convert_order>
<extra_fee_rule_description>
<to_invoice>*</to_invoice>
</extra_fee_rule_description>
</sales_convert_order>
</fieldsets>
<resources>
<stack_extrafee_setup>
<setup>
<module>Stack_ExtraFee</module>
<class>Mage_Sales_Model_Resource_Setup</class>
</setup>
</stack_extrafee_setup>
</resources>
<events>
<salesrule_rule_save_before>
<observers>
<stack_extrafee>
<class>stack_extrafee/observer</class>
<method>beforeSaveSalesRuleModel</method>
</stack_extrafee>
</observers>
</salesrule_rule_save_before>
<adminhtml_block_salesrule_actions_prepareform>
<observers>
<stack_extrafee_prepare_form>
<class>stack_extrafee/observer</class>
<method>prepareFormSalesRuleEdit</method>
</stack_extrafee_prepare_form>
</observers>
</adminhtml_block_salesrule_actions_prepareform>
<paypal_prepare_line_items>
<observers>
<stack_extrafee_paypal_prepare>
<class>stack_extrafee/observer</class>
<method>paypalPrepareLineItems</method>
</stack_extrafee_paypal_prepare>
</observers>
</paypal_prepare_line_items>
</events>
</global>
<frontend>
<layout>
<updates>
<stack_extrafee>
<file>stack_extrafee.xml</file>
</stack_extrafee>
</updates>
</layout>
</frontend>
<adminhtml>
<layout>
<updates>
<stack_extrafee>
<file>stack_extrafee.xml</file>
</stack_extrafee>
</updates>
</layout>
</adminhtml>
<default>
<extra_fee_settings>
<extra_fee_rule>
<active>1</active>
<label>Extra Fee</label>
</extra_fee_rule>
</extra_fee_settings>
</default>
</config>
Step 3: Add the file app/code/local/Stack/ExtraFee/etc/adminhtml.xml for defining ACL:
<?xml version="1.0"?>
<config>
<acl>
<resources>
<admin>
<children>
<system>
<children>
<config>
<children>
<extra_fee_settings translate="title">
<title>Extra Fee Settings</title>
<sort_order>55</sort_order>
</extra_fee_settings>
</children>
</config>
</children>
</system>
</children>
</admin>
</resources>
</acl>
</config>
Step 4: Add module's system configuration in app/code/local/Stack/ExtraFee/etc/system.xml:
<?xml version="1.0"?>
<config>
<sections>
<extra_fee_settings translate="label" module="sales">
<label>Extra Fee Settings</label>
<tab>sales</tab>
<frontend_type>text</frontend_type>
<sort_order>304</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<groups>
<extra_fee_rule translate="label">
<label>Shopping Cart Rule Extra Fee</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<fields>
<active translate="label">
<label>Enabled</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>5</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</active>
<label translate="label">
<label>Label</label>
<frontend_type>text</frontend_type>
<sort_order>10</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</label>
</fields>
</extra_fee_rule>
</groups>
</extra_fee_settings>
</sections>
</config>
Step 5: Add helper class in app/code/local/Voronoy/Stack/Helper/Data.php
<?php
class Stack_ExtraFee_Helper_Data extends Mage_Core_Helper_Abstract
{
const XML_PATH_EXTRA_FEE_RULE_ACTIVE = 'extra_fee_settings/extra_fee_rule/active';
const XML_PATH_EXTRA_FEE_RULE_LABEL = 'extra_fee_settings/extra_fee_rule/label';
public function isRuleExtraFeeEnabled()
{
$result = (bool) Mage::getStoreConfig(self::XML_PATH_EXTRA_FEE_RULE_ACTIVE);
return $result;
}
public function getExtraFeeRuleLabel()
{
return (string) Mage::getStoreConfig(self::XML_PATH_EXTRA_FEE_RULE_LABEL);
}
}
Step 6: Add observer class app/code/local/Stack/ExtraFee/Model/Observer.php
<?php
class Stack_ExtraFee_Model_Observer
{
public function beforeSaveSalesRuleModel($observer)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return $this;
}
if (Mage::app()->getRequest()->isPost()) {
$postData = Mage::app()->getRequest()->getPost();
if (isset($postData['extra_fee_amount'])) {
$salesRuleModel = $observer->getEvent()->getDataObject();
$salesRuleModel->setExtraFeeAmount($postData['extra_fee_amount']);
}
}
}
public function prepareFormSalesRuleEdit($observer)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return $this;
}
$model = Mage::registry('current_promo_quote_rule');
if (!$model) {
return $this;
}
$form = $observer->getEvent()->getForm();
$fieldset = $form->getElement('action_fieldset');
$fieldset->addField('extra_fee_amount', 'text', array(
'name' => 'extra_fee_amount',
'class' => 'validate-not-negative-number',
'label' => Mage::helper('salesrule')->__('Extra Fee Amount'),
), 'discount_amount');
$model->setExtraFeeAmount($model->getExtraFeeAmount()*1);
Mage::app()->getLayout()->getBlock('promo_quote_edit_tab_actions')
->setChild('form_after', Mage::app()->getLayout()->createBlock('adminhtml/widget_form_element_dependence')
->addFieldMap('rule_extra_fee_amount', 'extra_fee_amount')
->addFieldMap('rule_simple_action', 'simple_action')
->addFieldDependence('extra_fee_amount', 'simple_action', array(
Mage_SalesRule_Model_Rule::BY_PERCENT_ACTION,
Mage_SalesRule_Model_Rule::BY_FIXED_ACTION,
Mage_SalesRule_Model_Rule::CART_FIXED_ACTION))
);
}
public function paypalPrepareLineItems($observer)
{
/* @var $cart Mage_Paypal_Model_Cart */
$cart = $observer->getEvent()->getPaypalCart();
$address = $cart->getSalesEntity()->getIsVirtual() ?
$cart->getSalesEntity()->getBillingAddress() : $cart->getSalesEntity()->getShippingAddress();
$feeAmount = $address->getExtraFeeRuleAmount();
$cart->updateTotal(Mage_Paypal_Model_Cart::TOTAL_TAX, $feeAmount);
}
}
Step 7: app/code/local/Stack/ExtraFee/Model/SalesRule/Validator.php
<?php
class Stack_ExtraFee_Model_SalesRule_Validator extends Mage_SalesRule_Model_Validator
{
public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
{
$item->setExtraFeeRuleAmount(0);
$item->setBaseExtraFeeRuleAmount(0);
$item->setExtraFeeRulePercent(0);
$quote = $item->getQuote();
$address = $this->_getAddress($item);
$itemPrice = $this->_getItemPrice($item);
$baseItemPrice = $this->_getItemBasePrice($item);
if ($itemPrice < 0) {
return $this;
}
$appliedRuleIds = array();
$this->_stopFurtherRules = false;
foreach ($this->_getRules() as $rule) {
if (!$this->_isRuleApplicableForItem($rule, $item)) {
continue;
}
$qty = $this->_getItemQty($item, $rule);
$extraFeeAmount = 0;
$baseExtraFeeAmount = 0;
switch ($rule->getSimpleAction()) {
case Mage_SalesRule_Model_Rule::BY_PERCENT_ACTION:
$extraFeePercent = min(100, $rule->getExtraFeeAmount());
$step = $rule->getDiscountStep();
if ($step) {
$qty = floor($qty/$step)*$step;
}
$_rulePct = $extraFeePercent/100;
$extraFeeAmount = ($qty * $itemPrice - $item->getExtraFeeRuleAmount()) * $_rulePct;
$baseExtraFeeAmount = ($qty * $baseItemPrice - $item->getBaseExtraFeeRuleAmount()) * $_rulePct;
if (!$rule->getDiscountQty() || $rule->getDiscountQty()>$qty) {
$extraFeePercent = min(100, $item->getExtraFeeRulePercent()+$extraFeePercent);
$item->setExtraFeeRulePercent($extraFeePercent);
}
break;
case Mage_SalesRule_Model_Rule::BY_FIXED_ACTION:
$step = $rule->getDiscountStep();
if ($step) {
$qty = floor($qty/$step)*$step;
}
$quoteAmount = $quote->getStore()->convertPrice($rule->getExtraFeeAmount());
$extraFeeAmount = $qty * $quoteAmount;
$baseExtraFeeAmount = $qty * $rule->getExtraFeeAmount();
break;
case Mage_SalesRule_Model_Rule::CART_FIXED_ACTION:
if (empty($this->_rulesItemTotals[$rule->getId()])) {
Mage::throwException(Mage::helper('salesrule')->__('Item totals are not set for rule.'));
}
if ($quote->getIsMultiShipping()) {
$usedForAddressId = $this->getCartFixedRuleUsedForAddress($rule->getId());
if ($usedForAddressId && $usedForAddressId != $address->getId()) {
break;
} else {
$this->setCartFixedRuleUsedForAddress($rule->getId(), $address->getId());
}
}
$cartRules = $address->getCartFixedRules();
if (!isset($cartRules[$rule->getId()])) {
$cartRules[$rule->getId()] = $rule->getExtraFeeAmount();
}
if ($cartRules[$rule->getId()] > 0) {
if ($this->_rulesItemTotals[$rule->getId()]['items_count'] <= 1) {
$quoteAmount = $quote->getStore()->convertPrice($cartRules[$rule->getId()]);
$baseExtraFeeAmount= $cartRules[$rule->getId()];
} else {
$discountRate = $baseItemPrice * $qty /
$this->_rulesItemTotals[$rule->getId()]['base_items_price'];
$maximumItemDiscount = $rule->getExtraFeeAmount() * $discountRate;
$quoteAmount = $quote->getStore()->convertPrice($maximumItemDiscount);
$baseExtraFeeAmount = $maximumItemDiscount;
$this->_rulesItemTotals[$rule->getId()]['items_count']--;
}
$extraFeeAmount = $quoteAmount;
$extraFeeAmount = $quote->getStore()->roundPrice($extraFeeAmount);
$baseExtraFeeAmount = $quote->getStore()->roundPrice($baseExtraFeeAmount);
$cartRules[$rule->getId()] -= $baseExtraFeeAmount;
}
$address->setCartFixedRules($cartRules);
break;
}
$percentKey = $item->getExtraFeeRulePercent();
if ($percentKey) {
$delta = isset($this->_roundingDeltas[$percentKey]) ? $this->_roundingDeltas[$percentKey] : 0;
$baseDelta = isset($this->_baseRoundingDeltas[$percentKey])
? $this->_baseRoundingDeltas[$percentKey]
: 0;
$extraFeeAmount += $delta;
$baseExtraFeeAmount += $baseDelta;
$this->_roundingDeltas[$percentKey] = $extraFeeAmount -
$quote->getStore()->roundPrice($extraFeeAmount);
$this->_baseRoundingDeltas[$percentKey] = $baseExtraFeeAmount -
$quote->getStore()->roundPrice($baseExtraFeeAmount);
$extraFeeAmount = $quote->getStore()->roundPrice($extraFeeAmount);
$baseExtraFeeAmount = $quote->getStore()->roundPrice($baseExtraFeeAmount);
} else {
$extraFeeAmount = $quote->getStore()->roundPrice($extraFeeAmount);
$baseExtraFeeAmount = $quote->getStore()->roundPrice($baseExtraFeeAmount);
}
$itemExtraFeeRuleAmount = $item->getExtraFeeRuleAmount();
$itemBaseExtraFeeRuleAmount = $item->getBaseExtraFeeRuleAmount();
$extraFeeAmount = $itemExtraFeeRuleAmount + $extraFeeAmount;
$baseExtraFeeAmount = $itemBaseExtraFeeRuleAmount + $baseExtraFeeAmount;
$item->setExtraFeeRuleAmount($extraFeeAmount);
$item->setBaseExtraFeeRuleAmount($baseExtraFeeAmount);
$appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId();
$this->_maintainAddressCouponCode($address, $rule);
$this->_addDiscountDescription($address, $rule);
if ($rule->getStopRulesProcessing()) {
$this->_stopFurtherRules = true;
break;
}
}
$item->setAppliedRuleIds(join(',',$appliedRuleIds));
$address->setAppliedRuleIds($this->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds));
$quote->setAppliedRuleIds($this->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds));
return $this;
}
protected function _addDiscountDescription($address, $rule)
{
$description = $address->getExtraFeeRuleDescriptionArray();
$ruleLabel = $rule->getStoreLabel($address->getQuote()->getStore());
$label = '';
if ($ruleLabel) {
$label = $ruleLabel;
} else if (strlen($address->getCouponCode())) {
$label = $address->getCouponCode();
}
if (strlen($label)) {
$description[$rule->getId()] = $label;
}
$address->setExtraFeeRuleDescriptionArray($description);
return $this;
}
public function prepareDescription($address, $separator = ', ')
{
$descriptionArray = $address->getExtraFeeRuleDescriptionArray();
if (!$descriptionArray && $address->getQuote()->getItemVirtualQty() > 0) {
$descriptionArray = $address->getQuote()->getBillingAddress()->getExtraFeeRuleDescriptionArray();
}
$description = $descriptionArray && is_array($descriptionArray)
? implode($separator, array_unique($descriptionArray))
: '';
$address->setExtraFeeRuleDescription($description);
return $this;
}
protected function _isRuleApplicableForItem($rule, $item)
{
$address = $this->_getAddress($item);
if (!$this->_canProcessRule($rule, $address)) {
return false;
}
if (!$rule->getActions()->validate($item)) {
return false;
}
return true;
}
protected function _canProcessRule($rule, $address)
{
if ($rule->getExtraFeeAmount() == 0) {
return false;
}
return parent::_canProcessRule($rule, $address);
}
}
Step 8: app/code/local/Stack/ExtraFee/Model/SalesRule/Discount/Validator.php
<?php
class Stack_ExtraFee_Model_SalesRule_Discount_Validator extends Mage_SalesRule_Model_Validator
{
protected function _canProcessRule($rule, $address)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return parent::_canProcessRule($rule, $address);
}
if ($rule->getDiscountAmount() == 0) {
return false;
}
return parent::_canProcessRule($rule, $address);
}
}
Step 9: app/code/local/Stack/ExtraFee/Model/Sales/Order/Invoice/Total/Fee/Rule.php
<?php
class Stack_ExtraFee_Model_Sales_Order_Invoice_Total_Fee_Rule extends Mage_Sales_Model_Order_Invoice_Total_Abstract
{
public function collect(Mage_Sales_Model_Order_Invoice $invoice)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return $this;
}
$invoice->setExtraFeeRuleAmount(0);
$invoice->setBaseExtraFeeRuleAmount(0);
if ($this->_isAmountInvoiced($invoice)) {
return $this;
}
$extraFeeRuleAmount = $invoice->getOrder()->getExtraFeeRuleAmount();
$baseExtraFeeRuleAmount = $invoice->getOrder()->getBaseExtraFeeRuleAmount();
if ($extraFeeRuleAmount) {
$invoice->setExtraFeeRuleAmount($extraFeeRuleAmount);
$invoice->setBaseExtraFeeRuleAmount($baseExtraFeeRuleAmount);
$invoice->setGrandTotal($invoice->getGrandTotal() + $extraFeeRuleAmount);
$invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() + $baseExtraFeeRuleAmount);
}
return $this;
}
protected function _isAmountInvoiced($invoice)
{
foreach ($invoice->getOrder()->getInvoiceCollection() as $previusInvoice) {
if ($previusInvoice->getExtraFeeRule() && !$previusInvoice->isCanceled()) {
return true;
}
}
return false;
}
}
Step 10: app/code/local/Stack/ExtraFee/Model/Quote/Address/Total/Fee/Rule.php
<?php
class Stack_ExtraFee_Model_Quote_Address_Total_Fee_Rule extends Mage_Sales_Model_Quote_Address_Total_Abstract
{
protected $_calculator;
public function __construct()
{
$this->_calculator = Mage::getSingleton('stack_extrafee/salesRule_validator');
}
public function collect(Mage_Sales_Model_Quote_Address $address)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return $this;
}
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;
}
$this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode());
$this->_calculator->initTotals($items, $address);
$items = $this->_calculator->sortItemsByPriority($items);
foreach ($items as $item) {
if ($item->getParentItemId()) {
continue;
}
if ($item->getHasChildren() && $item->isChildrenCalculated()) {
foreach ($item->getChildren() as $child) {
$this->_calculator->process($child);
$this->_addAmount($child->getExtraFeeRuleAmount());
$this->_addBaseAmount($child->getBaseExtraFeeRuleAmount());
}
} else {
$this->_calculator->process($item);
$this->_addAmount($item->getExtraFeeRuleAmount());
$this->_addBaseAmount($item->getBaseExtraFeeRuleAmount());
}
}
$this->_calculator->prepareDescription($address);
}
public function fetch(Mage_Sales_Model_Quote_Address $address)
{
if (!Mage::helper('stack_extrafee')->isRuleExtraFeeEnabled()) {
return $this;
}
$amount = $address->getExtraFeeRuleAmount();
if ($address->getExtraFeeRuleDescription()) {
$discountLabel = Mage::helper('stack_extrafee')->__('%s (%s)',
Mage::helper('stack_extrafee')->getExtraFeeRuleLabel(), $address->getExtraFeeRuleDescription());
} else {
$discountLabel = Mage::helper('stack_extrafee')->getExtraFeeRuleLabel();
}
if ($amount > 0) {
$address->addTotal(array(
'code' => $this->getCode(),
'title' => $discountLabel,
'value' => $amount
));
}
return $this;
}
}
Step 11: app/code/local/Stack/ExtraFee/Block/Sales/Order/Totals/Rule.php:
<?php
class Stack_ExtraFee_Block_Sales_Order_Totals_Rule extends Mage_Core_Block_Abstract
{
public function getSource()
{
return $this->getParentBlock()->getSource();
}
public function initTotals()
{
if ((float) $this->getSource()->getExtraFeeRuleAmount() <= 0) {
return $this;
}
if ($this->getSource()->getExtraFeeRuleDescription()) {
$discountLabel = $this->__('%s (%s)', Mage::helper('stack_extrafee')->getExtraFeeRuleLabel(),
$this->getSource()->getExtraFeeRuleDescription());
} else {
$discountLabel = Mage::helper('stack_extrafee')->getExtraFeeRuleLabel();
}
$total = new Varien_Object(array(
'code' => 'extra_fee_rule',
'field' => 'extra_fee_rule_amount',
'value' => $this->getSource()->getExtraFeeRuleAmount(),
'label' => $discountLabel
));
$this->getParentBlock()->addTotalBefore($total, 'grand_total');
return $this;
}
}
-
-
Hi Mohit Kumar Arora, Thank you so much for this. Is it a kind of extension? I think i have installed this in my magento admin panel. But in the promotions menu when i try to add the Extra charges the field is not getting saved. – Praveen Sep 5 '18 at 7:51
-
Ok @Praveen, I will check it in my localhost when I reach home and will share the complete module with you. – Mohit Kumar Arora Sep 5 '18 at 12:52
-
@MohitKumarArora I am using github.com/yvoronoy/magento-extension-extra-fee Everything working fine, I need to add small feature, how to set extra fees on order grant total permanently, if i go to check order in back-end the extra fees not available i.stack.imgur.com/OPfap.png – zus Sep 3 at 11:48
You can use this free module
I'm using it for both cash on delivery and card swipe on delivery.
I have implemented this on my website and it works perfect.