Skip to content

Tax calculation process does not follow "Apply Tax On" setting #14469

@vbuck

Description

@vbuck

Tax is not correctly calculated when "Apply Tax On" option is set to "Original price only." Tax is always based on item calculation price (custom price).

Preconditions

  • Magento Open Source 2.2.3
  • PHP 7.1.14
  • Apache 2.2.15
  • Magento sample data installed
  • Shipping methods Flat Rate, Table Rates, and UPS enabled
  • Payment methods Check/Money Order, Credit Card (Braintree) enabled
  • Setting "Base Currency" set to US Dollar
  • Setting "Default Display Currency" set to US Dollar
  • Setting "Tax Calculation Method Based On" set to Total
  • Setting "Tax Calculation Based On" set to Shipping Address
  • Setting "Shipping Tax Class" set to Taxable Goods
  • Setting "Apply Tax On" set to Original price only
  • New tax rate configured as: any zip code match (*), region of PA/US, rate of 6.0000
  • Existing tax rule "Rule1" expanded to accept new tax rate

Steps to reproduce

  1. From the admin panel, go to Sales > Orders
  2. Select Create New Order
  3. Choose existing customer Veronica Costello
  4. Click Add Products
  5. Select simple product Overnight Duffle ($45.00)
  6. Click Add Selected Product(s) to Order
  7. In the Shipping Address section, uncheck option Same As Billing Address
  8. Change the Street Address to 123 Main Street
  9. Change the City to Mechanicsburg
  10. Change the State to Pennsylvania
  11. Change the Zip Code to 17055
  12. Observe Tax line-item reads $2.70 (0.06 * 45 = 2.6999)
  13. For quote item Overnight Duffle, select option Custom Price
  14. Enter value 10.00
  15. Click Update Items and Quantities
  16. Observe Tax line-item reads $2.70 (0.06 * 45 = 2.6999)

Expected result

  • Final tax line-item reads $2.70

Actual result

  • Final tax line-item reads $0.60

Additional Information

Class Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector is the underlying processor for the total and subtotal tax collectors. This class declares method mapItem, and this method is responsible for converting quote items into a more specific instance of Magento\Tax\Api\Data\QuoteDetailsItemInterface. Lines 216-228 determine the unit price of the given item:

    if ($useBaseCurrency) {
        if (!$item->getBaseTaxCalculationPrice()) {
            $item->setBaseTaxCalculationPrice($item->getBaseCalculationPriceOriginal());
        }
        $itemDataObject->setUnitPrice($item->getBaseTaxCalculationPrice())
            ->setDiscountAmount($item->getBaseDiscountAmount());
    } else {
        if (!$item->getTaxCalculationPrice()) {
            $item->setTaxCalculationPrice($item->getCalculationPriceOriginal());
        }
        $itemDataObject->setUnitPrice($item->getTaxCalculationPrice())
            ->setDiscountAmount($item->getDiscountAmount());
    }

My understanding is as follows:

  • Look for presence of tax_calculation_price via magic getter (note: no implementation found)
  • If not set, use calculation_price via method AbstractItem::getCalculationPriceOriginal

Given the name "tax calculation price," it can be assumed that any modifications to the calculation price should be made here for the sake of tax-specific constraints. Yet, there are none made. Instead the original calculation price is used as-is.

This becomes problematic under the following conditions:

  • Method AbstractItem::setCustomPrice is used; and,
  • Tax configuration is set to "Apply Tax On" the original price

These conditions cause the mapItem method to incorrectly refer to a price that is different from the originating product's base price. That is, a custom price. Furthermore, the "Apply Tax On" setting itself appears to be unused in the core altogether. There are even helper methods in Magento\Tax\Helper\Data, applyTaxOnCustomPrice and applyTaxOnOriginalPrice, which appear to be useful to this mapping process.

I believe the solution is to conditionally fall back to AbstractItem::getCalculationPriceOriginal only when the setting "Apply Tax On" is set to "Custom price if available." Otherwise, if set to "Original price only," then the tax calculation price must be derived from AbstractItem::getBaseOriginalPrice.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Component: TaxIssue: Clear DescriptionGate 2 Passed. Manual verification of the issue description passedIssue: ConfirmedGate 3 Passed. Manual verification of the issue completed. Issue is confirmedIssue: Format is validGate 1 Passed. Automatic verification of issue format passedIssue: Ready for WorkGate 4. Acknowledged. Issue is added to backlog and ready for developmentReproduced on 2.2.xThe issue has been reproduced on latest 2.2 releaseReproduced on 2.3.xThe issue has been reproduced on latest 2.3 release

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions