9

I have been working for 2 days for applying multiple coupon on cart I know there are modules which are available for that. But i don't want to use it. I want some custom code so that i can apply more than 1 coupon code in the single order.

Please help. i am very tired after working on the same stuff. enter image description here

5
  • 1
    Here you go stackoverflow.com/questions/15138237/… Commented May 18, 2015 at 16:24
  • Btw, your question is eerily similar to the one I just linked above, which is from 2013. Commented May 18, 2015 at 16:26
  • @Tim ~ I don't think that is the best method, since it involves adding columns directly to the sales tables bypassing Magento's conventional methods. I actually played around with this now, and with 2 rewrites and a few lines of code, this can easily be achieved. Also the answer in that link only allows for 2 codes to be added. Ill post an answer in a bit
    – Shaughn
    Commented May 21, 2015 at 8:41
  • @Shaughn pls post your code.
    – Zahirabbas
    Commented May 21, 2015 at 9:56
  • could give me an example zip, or be more specific directories please thank's
    – alexmalara
    Commented Sep 29, 2015 at 22:42

1 Answer 1

14

In your custom module, add the following to config.xml:

<models>
    <salesrule>
        <rewrite>
            <quote_discount>Namespace_Module_Rewrite_SalesRule_Model_Quote_Discount</quote_discount>
        </rewrite>
    </salesrule>
</models>
<frontend>
    <routers>
        <checkout>
            <args>
                <modules>
                    <Namespace_Module before="Mage_Checkout">Namespace_Module_Checkout</Namespace_Module>
                </modules>
            </args>
        </checkout>
    </routers>
</frontend>

The first is a rewrite of Mage_SalesRule_Model_Quote_Discount to Namespace_Module_Rewrite_SalesRule_Model_Quote_Discount

The second is the overloaded controller Mage_Checkout_CartController

Next add the following file app/code/community/Namespace/Module/controllers/Checkout/CartController.php and insert the following code:

<?php

require_once 'Mage/Checkout/controllers/CartController.php';

class Namespace_Module_Checkout_CartController extends Mage_Checkout_CartController
{
    /**
     * Initialize coupon
     */
    public function couponPostAction()
    {
        /**
         * No reason continue with empty shopping cart
         */
        if (!$this->_getCart()->getQuote()->getItemsCount()) {
            $this->_goBack();
            return;
        }

        $couponCode = (string) $this->getRequest()->getParam('coupon_code');
        if ($this->getRequest()->getParam('remove') == 1) {
            $couponCode = '';
        }
        $oldCouponCode = $this->_getQuote()->getCouponCode();

        if (!strlen($couponCode) && !strlen($oldCouponCode)) {
            $this->_goBack();
            return;
        }

        try {
            $codeLength = strlen($couponCode);
            $isCodeLengthValid = $codeLength && $codeLength <= Mage_Checkout_Helper_Cart::COUPON_CODE_MAX_LENGTH;

            // Combine multiple coupons
            $couponFlag = true;

            if ($isCodeLengthValid) {
                $del = ',';

                if ($oldCouponCode) {

                    if ($oldCouponCode == $couponCode) {
                        $couponCode = $oldCouponCode;
                    } else {
                        $couponCode = $oldCouponCode . $del . $couponCode;
                    }
                }
            } else {
                $couponCode = '';
            }

            $this->_getQuote()->getShippingAddress()->setCollectShippingRates(true);
            $this->_getQuote()->setCouponCode($couponCode)
                ->collectTotals()
                ->save();

            if ($codeLength) {
                if ($isCodeLengthValid && $couponFlag) {
                    $this->_getSession()->addSuccess(
                        $this->__('Coupon code "%s" was applied.', Mage::helper('core')->escapeHtml($couponCode))
                    );
                } else {
                    $this->_getSession()->addError(
                        $this->__('Coupon code "%s" is not valid.', Mage::helper('core')->escapeHtml($couponCode))
                    );
                }
            } else {
                $this->_getSession()->addSuccess($this->__('Coupon code was canceled.'));
            }

        } catch (Mage_Core_Exception $e) {
            $this->_getSession()->addError($e->getMessage());
        } catch (Exception $e) {
            $this->_getSession()->addError($this->__('Cannot apply the coupon code.'));
            Mage::logException($e);
        }

        $this->_goBack();
    }
}

You will notice i added a section to combine coupon codes delimited by ",". This can obviously be more refined and you may want to add additional checking etc, but this code should work straight off the bat.

And finally we need to add the piece that does all the magic. Add the file app/code/community/Namespace/Module/Rewrite/SalesRule/Model/Quote/Discount.php

and add the content:

<?php

class Namespace_Module_Rewrite_SalesRule_Model_Quote_Discount extends Mage_SalesRule_Model_Quote_Discount
{
    /**
     * Collect address discount amount
     *
     * @param   Mage_Sales_Model_Quote_Address $address
     * @return  Mage_SalesRule_Model_Quote_Discount
     */
    public function collect(Mage_Sales_Model_Quote_Address $address)
    {
        Mage_Sales_Model_Quote_Address_Total_Abstract::collect($address);
        $quote = $address->getQuote();
        $store = Mage::app()->getStore($quote->getStoreId());
        $this->_calculator->reset($address);

        $items = $this->_getAddressItems($address);
        if (!count($items)) {
            return $this;
        }

        $couponCode = $quote->getCouponCode();
        $couponArray = explode(',',$couponCode);

        foreach ($couponArray as $couponCode) {
            $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $couponCode);
            $this->_calculator->initTotals($items, $address);

            $eventArgs = array(
                'website_id'        => $store->getWebsiteId(),
                'customer_group_id' => $quote->getCustomerGroupId(),
                'coupon_code'       => $couponCode,
            );

            $address->setDiscountDescription(array());
            $items = $this->_calculator->sortItemsByPriority($items);
            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 weee amount
             */
            if (Mage::helper('weee')->isEnabled() && Mage::helper('weee')->isDiscounted($store)) {
                $this->_calculator->processWeeeAmount($address, $items);
            }

            /**
             * 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;
    }
}

Basically, what this does is breaks the coupon sting up, loops through each coupon code, calculates and updates the quote totals.

To test, i have setup 2 shopping cart rules:

  • test 1 - 10 % product price discount - Stop Further Rules Processing: No
  • test 2 - 10 % product price discount - Stop Further Rules Processing: No

No coupon: no coupon

Added coupon test 1: added coupon test 1

Added coupon test 2 added coupon test 1

I have tested with fixed amount discount and this works as expected as well.

And like i said, you may need to add additional checking, possibly for duplicates, but this is where you would start. For the frontend, you could add some logic split the codes however you prefer or leave as is.

8
  • Also forgot to mention, you would obviously need to replace Namespace/Module with your actual module name, etc.
    – Shaughn
    Commented May 21, 2015 at 11:22
  • after edit this answer now it's working like above screenshot.now how can i cancel specific coupon after applying multiple coupon.
    – Zahirabbas
    Commented May 28, 2015 at 8:49
  • Thanks Shaughn for you answer, it worked for me on magento 1.9 but i couldn't get it to work on ver 1.8, it displays nothing in browser and throw memory size exhausted error in apache error.log (not magento error/system.log)
    – Haris
    Commented Jun 30, 2015 at 6:27
  • Hey Saddam, the memory issue is probably one of many issues, but what you could do is wrap the code in a try catch block and log any errors that crop up.Also check the max memory setting in php and make sure you have sufficient memory available. Just before the loop you could count the coupon codes and check how many there are as i suspect there are a few that is loaded into memory.
    – Shaughn
    Commented Jul 1, 2015 at 9:02
  • 1
    To easily prevent the multiple use of the same coupon codes you can just use array_unique $couponArray = array_unique(explode(',',$couponCode));
    – Julian
    Commented Jul 14, 2016 at 12:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.