Magento – Wrong tax calculation (hiddentax and rowtax) for b2b on Magento CE 1.9.1.0

b2bmagento-1.9tax

We have Magento CE 1.9.1.0 installed and we guess that we have a strange problem with the tax calculation. Here is our case study:

In our b2b-shop we want to display all prices without tax. Here are our tax setting in the backend.
Magento Backend - Taxconfiguration

The customer tax should applied after discount. The shipping prices are displayed exclusive tax. At the end we want to have a total excl. tax, then the final taxamount and the grandtotal at the end, like you see in the next image of our cart.
The shoppingcart

We have a taxrate of 19%.
Now the problem: When we calculate the grandtotal like this: 126,60 * 1,19 = 150,654 we see that our customer pays 1cent too much. Our dilemma: Statistically, every 10 order occur like this. Why?

What we have done before? The calculation in Magento at the beginning of the development was even worse. So we made a little adjustment in the tax.php at /app/code/local/Mage/Tax/Model/Sales/Total/Quote from line 548 in the method "_calcUnitTaxAmount" – near "case Mage_Tax_Model_Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:".

protected function _calcUnitTaxAmount(
    $item, $rate, &$taxGroups = null, $taxId = null, $recalculateRowTotalInclTax = false
)
{
    $qty = $item->getTotalQty();
    $inclTax = $item->getIsPriceInclTax();
    $price = $item->getTaxableAmount();
    $basePrice = $item->getBaseTaxableAmount();
    $rateKey = ($taxId == null) ? (string)$rate : $taxId;

    $isWeeeEnabled = $this->_weeeHelper->isEnabled();
    $isWeeeTaxable = $this->_weeeHelper->isTaxable();

    $hiddenTax = null;
    $baseHiddenTax = null;
    $weeeTax = null;
    $baseWeeeTax = null;
    $unitTaxBeforeDiscount = null;
    $weeeTaxBeforeDiscount = null;
    $baseUnitTaxBeforeDiscount = null;
    $baseWeeeTaxBeforeDiscount = null;

    switch ($this->_config->getCalculationSequence($this->_store)) {
        case Mage_Tax_Model_Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL:
        case Mage_Tax_Model_Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL:
            $unitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($price, $rate, $inclTax, false);
            $baseUnitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax, false);

            if ($isWeeeEnabled && $isWeeeTaxable) {
                $weeeTaxBeforeDiscount = $this->_calculateWeeeTax(0, $item, $rate, false);
                $unitTaxBeforeDiscount += $weeeTaxBeforeDiscount;
                $baseWeeeTaxBeforeDiscount = $this->_calculateWeeeTax(0, $item, $rate);
                $baseUnitTaxBeforeDiscount += $baseWeeeTaxBeforeDiscount;
            }
            $unitTaxBeforeDiscount = $unitTax = $this->_calculator->round($unitTaxBeforeDiscount);
            $baseUnitTaxBeforeDiscount = $baseUnitTax = $this->_calculator->round($baseUnitTaxBeforeDiscount);
            break;
        case Mage_Tax_Model_Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL:
        case Mage_Tax_Model_Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL:
            // our changes here - formerly: $discountAmount = $item->getDiscountAmount() / $qty;
            $discountAmount = $item->getDiscountAmount();
            $baseDiscountAmount = $item->getBaseDiscountAmount() / $qty;

            //We want to remove weee
            if ($isWeeeEnabled) {
                $discountAmount = $discountAmount - $item->getWeeeDiscount() / $qty;
                $baseDiscountAmount = $baseDiscountAmount - $item->getBaseWeeeDiscount() / $qty;
            }

            $unitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($price, $rate, $inclTax, false);
            $unitTaxDiscount = $this->_calculator->calcTaxAmount($discountAmount, $rate, $inclTax, false);
            $unitTax = $this->_calculator->round(max($unitTaxBeforeDiscount - $unitTaxDiscount, 0));

            $baseUnitTaxBeforeDiscount = $this->_calculator->calcTaxAmount($basePrice, $rate, $inclTax, false);
            $baseUnitTaxDiscount = $this->_calculator->calcTaxAmount($baseDiscountAmount, $rate, $inclTax, false);
            $baseUnitTax = $this->_calculator->round(max($baseUnitTaxBeforeDiscount - $baseUnitTaxDiscount, 0));

            if ($isWeeeEnabled && $this->_weeeHelper->isTaxable()) {
                $weeeTax = $this->_calculateRowWeeeTax($item->getWeeeDiscount(), $item, $rate, false);
                $weeeTax = $weeeTax / $qty;
                $unitTax += $weeeTax;
                $baseWeeeTax = $this->_calculateRowWeeeTax($item->getBaseWeeeDiscount(), $item, $rate);
                $baseWeeeTax = $baseWeeeTax / $qty;
                $baseUnitTax += $baseWeeeTax;
            }

            $unitTax = $this->_calculator->round($unitTax);
            $baseUnitTax = $this->_calculator->round($baseUnitTax);

            //Calculate the weee taxes before discount
            $weeeTaxBeforeDiscount = 0;
            $baseWeeeTaxBeforeDiscount = 0;

            if ($isWeeeTaxable) {
                $weeeTaxBeforeDiscount = $this->_calculateWeeeTax(0, $item, $rate, false);
                $unitTaxBeforeDiscount += $weeeTaxBeforeDiscount;
                $baseWeeeTaxBeforeDiscount = $this->_calculateWeeeTax(0, $item, $rate);
                $baseUnitTaxBeforeDiscount += $baseWeeeTaxBeforeDiscount;
            }

            $unitTaxBeforeDiscount = max(0, $this->_calculator->round($unitTaxBeforeDiscount));
            $baseUnitTaxBeforeDiscount = max(0, $this->_calculator->round($baseUnitTaxBeforeDiscount));


            if ($inclTax && $discountAmount > 0) {
                $hiddenTax = $unitTaxBeforeDiscount - $unitTax;
                $baseHiddenTax = $baseUnitTaxBeforeDiscount - $baseUnitTax;
                $this->_hiddenTaxes[] = array(
                    'rate_key' => $rateKey,
                    'qty' => $qty,
                    'item' => $item,
                    'value' => 0, // our changes here - formerly: $hiddenTax
                    'base_value' => 0, // our changes here - formerly: $baseHiddenTax
                    'incl_tax' => $inclTax,
                );
            } elseif ($discountAmount > $price) { // case with 100% discount on price incl. tax
                $hiddenTax = $discountAmount - $price;
                $baseHiddenTax = $baseDiscountAmount - $basePrice;
                $this->_hiddenTaxes[] = array(
                    'rate_key' => $rateKey,
                    'qty' => $qty,
                    'item' => $item,
                    'value' => $hiddenTax,
                    'base_value' => $baseHiddenTax,
                    'incl_tax' => $inclTax,
                );
            }
            // calculate discount compensation
            // We need the discount compensation when dont calculate the hidden taxes
            // (when product does not include taxes)
            if (!$item->getNoDiscount() && $item->getWeeeTaxApplied()) {
                $item->setDiscountTaxCompensation($item->getDiscountTaxCompensation() +
                $unitTaxBeforeDiscount * $qty - max(0, $unitTax) * $qty);
            }
            break;
    }

    /* begin of our addings */
    $totalWithoutTax = $item->getRowTotal();
    $totalReady = $totalWithoutTax - $discountAmount;
    $unitTax = $totalReady * ($rateKey / 100);
    $rowTax = $unitTax;
    /* end of our addings */

    // our changes here - formerly: $rowTax = $this->_store->roundPrice(max(0, $qty * $unitTax));
    $baseRowTax = $this->_store->roundPrice(max(0, $qty * $baseUnitTax));
    $item->setTaxAmount($item->getTaxAmount() + $rowTax);
    $item->setBaseTaxAmount($item->getBaseTaxAmount() + $baseRowTax);
    if (is_array($taxGroups)) {
        $taxGroups[$rateKey]['tax'] = max(0, $rowTax);
        $taxGroups[$rateKey]['base_tax'] = max(0, $baseRowTax);
    }

    $rowTotalInclTax = $item->getRowTotalInclTax();
    if (!isset($rowTotalInclTax) || $recalculateRowTotalInclTax) {
        if ($this->_config->priceIncludesTax($this->_store)) {
            $item->setRowTotalInclTax($price * $qty);
            $item->setBaseRowTotalInclTax($basePrice * $qty);
        } else {
            $item->setRowTotalInclTax(
                $item->getRowTotalInclTax() + ($unitTaxBeforeDiscount - $weeeTaxBeforeDiscount) * $qty);
            $item->setBaseRowTotalInclTax(
                $item->getBaseRowTotalInclTax() +
                ($baseUnitTaxBeforeDiscount - $baseWeeeTaxBeforeDiscount) * $qty);
        }
    }

    return $this;
}

The deleted the hiddentax amount and recalculated the rowtax. But this is not working for the 10th order. What we have done wrong? Have we basically did not understand Magento? We are struggeling with this since two weeks 🙁

Best Answer

There is a long history of rounding errors with Magento, see this question for where that was back in the 1.7 days:

What is the status of rounding issues in 1.7?

For a quick test to see if something can be done, try changing the rounding precision from 2 to 4 in app/code/core/Mage/Core/Model/Store.php:

public function roundPrice($price)
{
    return round($price, 4);
}

This will probably break the checkout for PayPal users, however, that problem can be solved. If the above forces the sums to add up correctly then you will know it is Magento and not your configuration that is at fault here.

Related Topic