5

Ok, I created custom Total class for adding the special discount, and everything seems to work fine except, for some reason I can't find, my total is calculated twice! That results in double amount of discount, and incorrect grand total. Now, this happens on cart page and on checkout pages...BUT...when I complete the order the total is fine, calculated only once, and grand total is fine.

That is strange, it's like collect method is called twice for cart pages, but only once when finishing the order, but I can' track down where all this happens, and why.

To skip the junk code, I'll paste only important

     <sales>
        <quote>
            <totals>
                <mydiscount>
                    <class>ucon_mydiscount/total_mydiscount</class>
                    <before>subtotal</before>
                </mydiscount>
            </totals>
        </quote>
    </sales>

and the collector's methods

    public function collect(Mage_Sales_Model_Quote_Address $address)
{
    parent::collect($address);

    $quote = $address->getQuote();
    $quoteId = $quote->getEntityId();

    $items = $quote->getAllItems();
    if (!count($items)) {
        return $this;
    }       


    $discount = 0;
    $productId = 2556;  

    foreach($items as $item)
    {       
        if($item->getProduct()->getId() == $productId)
        {
            $qty = $item->getQty();
            $totalPrice = round(($item->getRowTotal()+$item->getTaxAmount()),2);

            //discount 10%              
            $discount = round($totalPrice * 0.1,2);     

            $discount = 0 - $discount;
        }
    }

    if($discount == 0)
        return $this;

    $this->_setAmount($discount);
    $this->_setBaseAmount($discount);


    return $this;
}

and fetcher

    public function fetch(Mage_Sales_Model_Quote_Address $address)
{
    $amount = $address->getMydiscountAmount();
    if ($amount != 0) {
        $title = Mage::helper('ucon_mydiscount')->__('My discount');
        $address->addTotal(array(
            'code' => $this->getCode(),
            'title' => $title,
            'value' => $amount
        ));
    }
    return $this;
}

edit: One more thing I find very strange - I'm doing the setValue in my collect method, not the addValue, so even if the method is called twice, it shouldn't be double value, it should simply set it twice to the correct value.

Toto
  • 89,455
  • 62
  • 89
  • 125
Relja
  • 678
  • 5
  • 13
  • 25
  • I used Andrey's comment as a starting point, from [here](http://stackoverflow.com/questions/4363876/how-to-set-custom-grand-total-before-checkout-process-in-magento) – Relja Nov 24 '11 at 18:12
  • I experienced this once but cannot now find what I did to fix it. I believe I copied one of the Mage classes which explicitly checked if the total had already been used somehow. Try adding breakpoints or a `Mage::log(__METHOD__)` in each of the collect and fetch functions to see what is being called twice - that is how I debugged it last time. – clockworkgeek Dec 04 '11 at 01:00

3 Answers3

16

Could the problem be that a total object belongs to an address object and Magento orders typically have TWO addresses - one for shipping and one for billing?

Your total will therefore be called to run twice - once with the billing address and once with the shipping address and the amount is totalled per-address. You can try checking which address you've been handed and only applying a value to one of them like this;

public function collect(Mage_Sales_Model_Quote_Address $address) {

  $this->_setAddress($address);
  $this->_setAmount(0);
  $this->_setBaseAmount(0);

  if ($address->getAddressType() == 'shipping') { 
    //only apply an actual value to the shipping address

    //... Do your calculation here as above ...

  } 

  return $this;
}

You will also have to do something similar in the fetch method as well...

public function fetch(Mage_Sales_Model_Quote_Address $address) {

  $amount = $address->getMydiscountAmount();

  if ($amount != 0 && $address->getAddressType() == 'shipping') {

    $title = Mage::helper('ucon_mydiscount')->__('My discount');

    $address->addTotal(array(
        'code' => $this->getCode(),
        'title' => $title,
        'value' => $amount
    ));

  }

  return $this;
}

I'll admit that collect function could be prettier, but hopefully you get the idea anyway.

Try that and see if your totals add up correctly on the frontend and the admin area.

0x00h
  • 760
  • 5
  • 12
  • can't believe what a **** thanks, wouldn't find it in a million years :) – Relja Dec 13 '11 at 16:52
  • Hi, I added `if ($address->getAddressType() == 'shipping') { `, grand total is correct in the checkout page. But wrong after complete the order, It seems my custom total not count. Anyone know what is wrong? – Wakanina Oct 06 '14 at 11:49
  • @Wakanina I've never made a frontend total, like this one, into a back end total myself; I needed the total to be converted into additional products in the back end, which I did with an observer on the `checkout_type_onepage_save_order` event that collected the total, as this does, and adds products of that value to the order. This blog looks like it's on the right track: http://www.classyllama.com/blog/unravelling-magentos-collecttotals-orders-and-caveats – 0x00h Oct 06 '14 at 13:55
  • Ya, I followed that tutorial too. I added my own answer... thanks :) – Wakanina Oct 06 '14 at 14:20
2

After searching around, here is another solution

public function collect(Mage_Sales_Model_Quote_Address $address) {
    parent::collect($address);

    //Pay attention to this code
    $items = $this->_getAddressItems($address);
    if (!count($items)) {
        return $this; //this makes only address type shipping to come through
    }

    //Do whatever you want here to add discount or fee...

    return $this;
}

By doing this, the discount or fee will only added to the shipping address, and it will count once. So we even don't need to add the if ($address->getAddressType() == 'shipping') { in the fetch function.

Wakanina
  • 592
  • 3
  • 6
  • 20
0

Is it possible that you are adding in your own layout xml code for a shopping cart block? If you are, there is a solid chance that block is being called twice (one from the base code, and again for your code -- even if you are just extending it), thus duplicating the price total. If that is the case, you will need to remove (destructively with the <remove> tag) the base layout xml for that block, and things should then fall into place and work.

Mark Shust at M.academy
  • 6,300
  • 4
  • 32
  • 50
  • Unfortunately, that's not it. I have two cart blocks call in my layouts - one for regular cart page and one for cart/sidebar. But I tried disabling all of my own layouts, and using just base layouts, and the issue is still there. Is there perhaps a way to see from which methods/blocks my collect method is being called? Thanks for the answer. – Relja Nov 25 '11 at 10:45