8

I've added an extra fee to my checkout and but the tax isn't calculated correctly.

The amount of tax is not added up correctly, if I do $this->_calculateTax($address); in the collect function it is added up to the total including tax but my amount of tax is still of then.

if is var_dump the set applied taxes after the line $address->setAppliedTaxes($previouslyAppliedTaxes); I do see the correct amount. It dumps this twice, the first time I see the correct amount of tax of just my extra fee, the second the correct amount of total tax. But in the frontend it shows the tax without the tax of my extra fee.

Any clue on what this could be?

    class Company_Customer_Model_Quote_Address_Total_PrintPrepCosts extends Mage_Sales_Model_Quote_Address_Total_Abstract
{
    public function __construct() {
        $this->setCode('printPrepCosts');
        $this->_store = Mage::app()->getStore();
        return $this;
    }

public function collect(Mage_Sales_Model_Quote_Address $address)
    {
        parent::collect($address);
        $address->setPrintPrepcosts(0);
        $address->setTaxAmount(0);
        $address->setBaseTaxAmount(0);

        if(count($address->getAllItems()) == 0)
        {
            return $this;
        }

        $pricePrint = $this->calcTotalPrintPrepCosts();

        $address->setPrintPrepcosts($pricePrint);
        $address->setBasePrintPrepcosts($pricePrint);

        $address->setBaseGrandTotal($address->getBaseGrandTotal() + $address->getPrintPrepcosts());
        $address->setGrandTotal($address->getGrandTotal() + $address->getPrintPrepcosts());

        $this->_calculateTax($address);
        return $this;
    }

    protected function _calculateTax(Mage_Sales_Model_Quote_Address $address)
    {
        $calculator     = Mage::getSingleton('tax/calculation');
        $inclTax        = Mage::getStoreConfig('tax/calculation/printing_prep_includes_tax', $this->_store);

        $taxRateRequest = $calculator->getRateRequest(
            $address,
            $address->getQuote()->getBillingAddress(),
            $address->getQuote()->getCustomerTaxClassId(),
            $this->_store
        );

        // TODO undef prop _store
        $taxRateRequest->setProductClassId(Mage::getStoreConfig('tax/classes/printing_prep_tax_class', $this->_store));

        $rate = $calculator->getRate($taxRateRequest);
        $baseTax = $tax = $calculator->calcTaxAmount($address->getPrepPrintcosts(), $rate, $inclTax, true);

        $address->addTotalAmount('tax', max(0, $tax));
        $address->addBaseTotalAmount('tax', max(0, $baseTax));

        $this->_saveAppliedTaxes($address, 
            $calculator->getAppliedRates($taxRateRequest), 
            $tax, 
            $baseTax, 
            $rate
        );

        // later on added - which fixes the total, lose tax amount still off
        $address->setTaxAmount($tax);
        $address->setBaseTaxAmount($baseTax);

        if($inclTax)
        {
            $address->setBaseGrandTotal($address->getBaseGrandTotal() - $baseTax);
            $address->setGrandTotal($address->getGrandTotal() - $tax);
        }
    }

    protected function _saveAppliedTaxes(Mage_Sales_Model_Quote_Address $address, $applied, $amount, $baseAmount, $rate)
    {
        $previouslyAppliedTaxes = $address->getAppliedTaxes();
        $process = count($previouslyAppliedTaxes);


        foreach ($applied as $row) {
            if (!isset($previouslyAppliedTaxes[$row['id']])) {
                $row['process'] = $process;
                $row['amount'] = 0;
                $row['base_amount'] = 0;
                $previouslyAppliedTaxes[$row['id']] = $row;
            }

            if (!is_null($row['percent'])) {
                $row['percent'] = $row['percent'] ? $row['percent'] : 1;
                $rate = $rate ? $rate : 1;

                $appliedAmount = $amount/$rate*$row['percent'];
                $baseAppliedAmount = $baseAmount/$rate*$row['percent'];
            } else {
                $appliedAmount = 0;
                $baseAppliedAmount = 0;
                foreach ($row['rates'] as $rate) {
                    $appliedAmount += $rate['amount'];
                    $baseAppliedAmount += $rate['base_amount'];
                }
            }


            if ($appliedAmount || $previouslyAppliedTaxes[$row['id']]['amount']) {
                $previouslyAppliedTaxes[$row['id']]['amount'] += $appliedAmount;
                $previouslyAppliedTaxes[$row['id']]['base_amount'] += $baseAppliedAmount;
            } else {
                unset($previouslyAppliedTaxes[$row['id']]);
            }
        }
        $address->setAppliedTaxes($previouslyAppliedTaxes);        
    }

    public function fetch(Mage_Sales_Model_Quote_Address $address)
    {
        $address->addTotal(array(
            'code'  => $this->getCode(),
            'title' => "Prep Print costs",
            'value' => $address->getPrintPrepcosts(),
        ));
        return $this;
    }

EDIT 1 XML contains this:

        <sales>
        <quote>
            <totals>
                <printPrepCosts>
                    <class>Company_Customer_Model_Quote_Address_Total_PrintPrepCosts</class>
                    <after>subtotal</after>
                    <before>tax</before>
                </printPrepCosts>
            </totals>
        </quote>
    </sales>

EDIT 2 I have added the following lines to my calcTax function, this does fix the grand total but the amount of TAX is still off.

  $address->setTaxAmount($tax);
  $address->setBaseTaxAmount($baseTax);

OUTPUT - example

Subtotal    € 67,50
printPrepCosts  € 40,00
Shipping    € 50,00
TAX     € 22,33
Total   € 187,43

EDIT 3 My bad, only the GoMage onepage checkout gives the correct grand total, the cart doesn't. The regular Magento onepage checkout gives the wrong grandtotal as well.

Cœur
  • 37,241
  • 25
  • 195
  • 267
PvdL
  • 1,578
  • 1
  • 20
  • 33
  • What are the contents of `calcTotalPrepPrintCosts()` ? – B00MER Aug 06 '12 at 20:56
  • nothing more then a loop through the checkout cart and creating a preparation price. I have checked and this does return an int or float. Even when I remove the function and replace it with hard coded values, the problem still occurs. – PvdL Aug 07 '12 at 07:25
  • where functions are defined in magento "getInvoiceTaxAmount", "getBaseInvoiceFeeExcludedVat" i couldn't find these function in magento can anybody explain what they are and how they are use – Vince Roy Aug 27 '13 at 07:47

3 Answers3

5

I found a workaround, it isn't the best solution but it does work :)

In my address I will set the tax to an extra var called tax and use that in the observer to manipulate the tax.

class Company_Client_Model_Observer
{   
    public function setCorrectTax ($observer)
    {
        $quote = $observer->getQuote();
        foreach ($quote->getAllAddresses() as $address) {
            $printPrepCosts = $address->getPrintPrepcosts();
            if(!empty($printPrepCosts)) {
                $address->setTaxAmount($address->tax);
            }
        }
    }
}

And the XML

    <events>
    <sales_quote_collect_totals_after>
        <observers>
            <client>
                <type>singleton</type>
                <class>client/observer</class>
                <method>setCorrectTax</method>
            </client>
        </observers>
    </sales_quote_collect_totals_after>
    </events>
PvdL
  • 1,578
  • 1
  • 20
  • 33
2

Are you trying to modify the existing tax line item or add a new one? Can you show your config.xml entry for the total model?

If you're adding a new tax line item, this should be sufficient, just tested this against CE 1.7 vanilla install:

class KJ_Mymodule_Model_Sales_Quote_Address_Total_Kjtest extends Mage_Sales_Model_Quote_Address_Total_Abstract
{
    public function __construct()
    {
        $this->setCode('kjtest');
    }

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

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

        return $this;
    }

    public function processConfigArray($config, $store) {
        return $config;
    }

    public function fetch(Mage_Sales_Model_Quote_Address $address)
    {
        $address->addTotal(array(
            'code'  => $this->getCode(),
            'title' => "Prep Print costs",
            'value' => 1.01,
        ));
        return $this;
    }
}

By sufficient, I mean that this should be the bare minimum you need to get that tax line item to show up.

With this in your config.xml:

<sales>
    <quote>
        <totals>
            <kjtest>
                <class>mymodule/sales_quote_address_total_kjtest</class>
                <after>tax</after>
            </kjtest>
        </totals>
    </quote>     
</sales>
kalenjordan
  • 2,446
  • 1
  • 24
  • 38
  • I've edited the class names to make them all more alike. My XML looks like: ` Company_Client_Model_Quote_Address_Total_PrintPrepCosts subtotal tax ` which gives Subtotaal € 65,20 printPrepCosts € 40,00 Shipping € 50,00 Tax € 21,89 – PvdL Aug 08 '12 at 08:13
  • Thanks. Are you trying to modify the Tax line? It sounds like the printPrepCosts line is rendering just fine, right? You may need to change it to tax. – kalenjordan Aug 08 '12 at 15:01
  • It should just get the tax from my printPrepCosts and add it up with the rest. The after tax isn't working either. – PvdL Aug 09 '12 at 07:45
  • So you're wanting the "Tax" line to be modified, correct? And the "prepPrintCosts" line is already fine? – kalenjordan Aug 09 '12 at 15:12
  • where functions are defined in magento "getInvoiceTaxAmount", "getBaseInvoiceFeeExcludedVat" i couldn't find these function in magento can anybody explain what they are and how they are use – Vince Roy Aug 27 '13 at 07:47
2

Read below I think this very help full to you:-

See url Below

http://www.excellencemagentoblog.com/magento-add-fee-discount-order-total

Magento Add fee or discount order total

In this tutorial, we will see how to add new line item to magento order totals. What this means is that, how to add an additional Fee or Discount, or any kind of charge to order total of the magento checkout process. In a typical order, the order totals usually comprises of Sub Total, Shipping Cost, Taxes, Discount, based on these values the total order grand total is calculated. Now if we want to add an additional Credit Card Fee or Convince Free or Affiliate Discount or any other order total which will affect the order grand total we need to create a magento module. This extra fee which we are adding to the total would reflect in the

  • Checkout Page Order Total
  • Cart Page Order Total
  • My Account Order View Page
  • Order EMails
  • Admin Order View/Email/PDF
  • Admin Invoice View/Email/PDF
  • Admin Credit Memo View/Email/PDF

as you can see based on the above list this module is not be simple. In this tutorial, i am attaching the source of such a very basic module which you can use a your starting point to add an extra change. I would also explain the basics of how to implement this. In this tutorial i will add a new order total called ‘Fee’ with a fixed cost of 10$.

Or try it:-

Checkout Page Total Order Total Basics

We will see how to add the totals only to the checkout page. All the totals line items that show up the checkout page come from files located at folder Mage\Sales\Model\Quote\Address\Total. In magento before order is placed all order data is stored in a quote object and after order is placed it gets transferred to the order object. The quote totals follow the collector pattern and we can add collector as many collector classes. To add collector to the quote object in our config.xml we add the lines

<global>
        <sales>
            <quote>
                <totals>
                    <fee>
                        <class>fee/sales_quote_address_total_fee</class>
                    </fee>
                </totals>
            </quote>
       </sales>
</global>

This means whenever the totals are calculated for a quote, it will also call this class. All collectors are called from the collectTotals() function in the Quote Model. In our collector class we put in the code

<?php
   class Excellence_Fee_Model_Sales_Quote_Address_Total_Fee extends Mage_Sales_Model_Quote_Address_Total_Abstract{
    protected $_code = 'fee';

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

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

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


        $quote = $address->getQuote();

        if(Excellence_Fee_Model_Fee::canApply($address)){ //your business logic
            $exist_amount = $quote->getFeeAmount();
            $fee = Excellence_Fee_Model_Fee::getFee();
            $balance = $fee - $exist_amount;
            $address->setFeeAmount($balance);
            $address->setBaseFeeAmount($balance);

            $quote->setFeeAmount($balance);

            $address->setGrandTotal($address->getGrandTotal() + $address->getFeeAmount());
            $address->setBaseGrandTotal($address->getBaseGrandTotal() + $address->getBaseFeeAmount());
        }
    }

    public function fetch(Mage_Sales_Model_Quote_Address $address)
    {
        $amt = $address->getFeeAmount();
        $address->addTotal(array(
                'code'=>$this->getCode(),
                'title'=>Mage::helper('fee')->__('Fee'),
                'value'=> $amt
        ));
        return $this;
    }
}

The two main functions here are collect() and fetch(). In collect function you add whatever amount you want to the order totals, and fetch() is used for display purposes. If this is done properly, you should see your order total line in the checkout and cart page. Here we are using two fields fee_amount and base_fee_amount, which contain our fee amount. We will have to see save these two fields to database, so in our module installer file we add this code

ALTER TABLE  `".$this->getTable('sales/quote_address')."` ADD  `fee_amount` DECIMAL( 10, 2 ) NOT NULL;
ALTER TABLE  `".$this->getTable('sales/quote_address')."` ADD  `base_fee_amount` DECIMAL( 10, 2 ) NOT NULL;

Order Page

Till now, all code written has been done only for the quote object. But after order is placed, we need to transfer all information to the order object. As you would have seen above we are using two fields fee_amount and base_fee_amount, we now need to store these two fields in the order table as well. To do all the above we need to do two things. First in the config.xml file add this code inside the global tab,

<fieldsets>
        <sales_convert_quote_address>
            <fee_amount><to_order>*</to_order></fee_amount>
            <base_fee_amount><to_order>*</to_order></base_fee_amount>
        </sales_convert_quote_address>
    </fieldsets>

and in our module install file

ALTER TABLE  `".$this->getTable('sales/order')."` ADD  `fee_amount` DECIMAL( 10, 2 ) NOT NULL;
ALTER TABLE  `".$this->getTable('sales/order')."` ADD  `base_fee_amount` DECIMAL( 10, 2 ) NOT NULL;

After doing this, these two fields should get saved to the order table from the quote table.

This is only basics of the adding a line item to order total. Rest there is lot of code written inside the attached module, please go through it in detail to understand more.

Abid Hussain
  • 7,724
  • 3
  • 35
  • 53
  • 2
    I have seen this page and followed it, without any luck – PvdL Aug 08 '12 at 08:18
  • Where do we have to put the file where the class `Excellence_Fee_Model_Sales_Quote_Address_Total_Fee` is defined, and how should the file be named? – Black Jan 15 '19 at 13:29