Magento 2 : How To Apply Tax After Discount

Under System > Configuration > Tax > Calculation Settings there are options for the order tax rules are applied, including Apply Customer Tax which can be either Before Discount or After Discount.

Tax Calculation Based On:

You can select which Tax rules will be applicable for orders that customers make on your e-store: shipping address, billing address, or your store’s shipping origin.
Select whether Catalog Prices and Shipping Prices will include or exclude Tax.
Define whether you would like to Apply Customer Tax before or after Discount.
Define if you would like to Apply Discount on Prices including of excluding Tax.
Magento2 Tax module Class Magento\Tax\Model\Config and that method applyTaxAfterDiscount() is responsible to apply tax after discount.

Class:Magento\Tax\Model\Config

Method:applyTaxAfterDiscount()

public function applyTaxAfterDiscount($store = null)
{
return (bool)$this->_scopeConfig->getValue(
self::CONFIG_XML_PATH_APPLY_AFTER_DISCOUNT,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE,
$store
);
}

Summary of code flow (Stack)

Magento\Tax\Helper\Data::applyTaxAfterDiscount()
Magento\Tax\Model\Config::getCalculationSequence()
Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxInPrice()
Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxNotInPrice()
Magento\Tax\Model\Calculation\UnitBaseCalculator::calculateWithTaxInPrice()
Magento\Tax\Model\Calculation\UnitBaseCalculator::calculateWithTaxNotInPrice()
Magento\Tax\Model\System\Message\Notifications::checkDiscountSettings()
Magento\Tax\Model\System\Message\Notification\ApplyDiscountOnPrices::checkSettings()
Magento\Tax\Model\System\Message\Notification\DiscountErrors::checkSettings()

Detail Code Flow

Magento\Tax\Helper\Data::applyTaxAfterDiscount()

public function applyTaxAfterDiscount($store = null)
{
return $this->_config->applyTaxAfterDiscount($store);
}

Magento\Tax\Model\Config::getCalculationSequence()

public function getCalculationSequence($store = null)
{
if ($this->applyTaxAfterDiscount($store)) {
if ($this->discountTax($store)) {
$seq = \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_INCL;
} else {
$seq = \Magento\Tax\Model\Calculation::CALC_TAX_AFTER_DISCOUNT_ON_EXCL;
}
} else {
if ($this->discountTax($store)) {
$seq = \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_INCL;
} else {
$seq = \Magento\Tax\Model\Calculation::CALC_TAX_BEFORE_DISCOUNT_ON_EXCL;
}
}
return $seq;
}

Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxInPrice()

protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true)
{
$taxRateRequest = $this->getAddressRateRequest()->setProductClassId(
$this->taxClassManagement->getTaxClassId($item->getTaxClassKey())
);
$rate = $this->calculationTool->getRate($taxRateRequest);
$appliedRates = $this->calculationTool->getAppliedRates($taxRateRequest);
$applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($this->storeId);
$discountAmount = $item->getDiscountAmount();
$discountTaxCompensationAmount = 0;
// Calculate $price
$price = $this->calculationTool->round($item->getUnitPrice());
$unitTaxes = [];
$unitTaxesBeforeDiscount = [];
$appliedTaxes = [];
//Apply each tax rate separately
foreach ($appliedRates as $appliedRate) {
$taxId = $appliedRate[‘id’];
$taxRate = $appliedRate[‘percent’];
$unitTaxPerRate = $this->calculationTool->calcTaxAmount($price, $taxRate, false, false);
$deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
if ($applyTaxAfterDiscount) {
$deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
}
$unitTaxPerRate = $this->roundAmount($unitTaxPerRate, $taxId, false, $deltaRoundingType, $round, $item);
$unitTaxAfterDiscount = $unitTaxPerRate;
//Handle discount
if ($applyTaxAfterDiscount) {
//TODO: handle originalDiscountAmount
$unitDiscountAmount = $discountAmount / $quantity;
$taxableAmount = max($price – $unitDiscountAmount, 0);
$unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount(
$taxableAmount,
$taxRate,
false,
false
);
$unitTaxAfterDiscount = $this->roundAmount(
$unitTaxAfterDiscount,
$taxId,
false,
self::KEY_REGULAR_DELTA_ROUNDING,
$round,
$item
);
}
$appliedTaxes[$taxId] = $this->getAppliedTax(
$unitTaxAfterDiscount * $quantity,
$appliedRate
);
$unitTaxes[] = $unitTaxAfterDiscount;
$unitTaxesBeforeDiscount[] = $unitTaxPerRate;
}
$unitTax = array_sum($unitTaxes);
$unitTaxBeforeDiscount = array_sum($unitTaxesBeforeDiscount);
$rowTax = $unitTax * $quantity;
$priceInclTax = $price + $unitTaxBeforeDiscount;
return $this->taxDetailsItemDataObjectFactory->create()
->setCode($item->getCode())
->setType($item->getType())
->setRowTax($rowTax)
->setPrice($price)
->setPriceInclTax($priceInclTax)
->setRowTotal($price * $quantity)
->setRowTotalInclTax($priceInclTax * $quantity)
->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
->setAssociatedItemCode($item->getAssociatedItemCode())
->setTaxPercent($rate)
->setAppliedTaxes($appliedTaxes);
}

Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxNotInPrice()

protected function calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true)
{
$taxRateRequest = $this->getAddressRateRequest()->setProductClassId(
$this->taxClassManagement->getTaxClassId($item->getTaxClassKey())
);
$rate = $this->calculationTool->getRate($taxRateRequest);
$storeRate = $storeRate = $this->calculationTool->getStoreRate($taxRateRequest, $this->storeId);
// Calculate $priceInclTax
$applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($this->storeId);
$priceInclTax = $this->calculationTool->round($item->getUnitPrice());
if (!$this->isSameRateAsStore($rate, $storeRate)) {
$priceInclTax = $this->calculatePriceInclTax($priceInclTax, $storeRate, $rate, $round);
}
$uniTax = $this->calculationTool->calcTaxAmount($priceInclTax, $rate, true, false);
$deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
if ($applyTaxAfterDiscount) {
$deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
}
$uniTax = $this->roundAmount($uniTax, $rate, true, $deltaRoundingType, $round, $item);
$price = $priceInclTax – $uniTax;
//Handle discount
$discountTaxCompensationAmount = 0;
$discountAmount = $item->getDiscountAmount();
if ($applyTaxAfterDiscount) {
//TODO: handle originalDiscountAmount
$unitDiscountAmount = $discountAmount / $quantity;
$taxableAmount = max($priceInclTax – $unitDiscountAmount, 0);
$unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount(
$taxableAmount,
$rate,
true,
false
);
$unitTaxAfterDiscount = $this->roundAmount(
$unitTaxAfterDiscount,
$rate,
true,
self::KEY_REGULAR_DELTA_ROUNDING,
$round,
$item
);
// Set discount tax compensation
$unitDiscountTaxCompensationAmount = $uniTax – $unitTaxAfterDiscount;
$discountTaxCompensationAmount = $unitDiscountTaxCompensationAmount * $quantity;
$uniTax = $unitTaxAfterDiscount;
}
$rowTax = $uniTax * $quantity;
// Calculate applied taxes
/** @var \Magento\Tax\Api\Data\AppliedTaxInterface[] $appliedTaxes */
$appliedRates = $this->calculationTool->getAppliedRates($taxRateRequest);
$appliedTaxes = $this->getAppliedTaxes($rowTax, $rate, $appliedRates);
return $this->taxDetailsItemDataObjectFactory->create()
->setCode($item->getCode())
->setType($item->getType())
->setRowTax($rowTax)
->setPrice($price)
->setPriceInclTax($priceInclTax)
->setRowTotal($price * $quantity)
->setRowTotalInclTax($priceInclTax * $quantity)
->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
->setAssociatedItemCode($item->getAssociatedItemCode())
->setTaxPercent($rate)
->setAppliedTaxes($appliedTaxes);
}

Magento\Tax\Model\Calculation\UnitBaseCalculator::calculateWithTaxNotInPrice()

protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true)
{
$taxRateRequest = $this->getAddressRateRequest()->setProductClassId(
$this->taxClassManagement->getTaxClassId($item->getTaxClassKey())
);
$rate = $this->calculationTool->getRate($taxRateRequest);
$appliedRates = $this->calculationTool->getAppliedRates($taxRateRequest);
$applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($this->storeId);
$discountAmount = $item->getDiscountAmount();
$discountTaxCompensationAmount = 0;
// Calculate $price
$price = $this->calculationTool->round($item->getUnitPrice());
$unitTaxes = [];
$unitTaxesBeforeDiscount = [];
$appliedTaxes = [];
//Apply each tax rate separately
foreach ($appliedRates as $appliedRate) {
$taxId = $appliedRate[‘id’];
$taxRate = $appliedRate[‘percent’];
$unitTaxPerRate = $this->calculationTool->calcTaxAmount($price, $taxRate, false, false);
$deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
if ($applyTaxAfterDiscount) {
$deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
}
$unitTaxPerRate = $this->roundAmount($unitTaxPerRate, $taxId, false, $deltaRoundingType, $round, $item);
$unitTaxAfterDiscount = $unitTaxPerRate;
//Handle discount
if ($applyTaxAfterDiscount) {
//TODO: handle originalDiscountAmount
$unitDiscountAmount = $discountAmount / $quantity;
$taxableAmount = max($price – $unitDiscountAmount, 0);
$unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount(
$taxableAmount,
$taxRate,
false,
false
);
$unitTaxAfterDiscount = $this->roundAmount(
$unitTaxAfterDiscount,
$taxId,
false,
self::KEY_REGULAR_DELTA_ROUNDING,
$round,
$item
);
}
$appliedTaxes[$taxId] = $this->getAppliedTax(
$unitTaxAfterDiscount * $quantity,
$appliedRate
);
$unitTaxes[] = $unitTaxAfterDiscount;
$unitTaxesBeforeDiscount[] = $unitTaxPerRate;
}
$unitTax = array_sum($unitTaxes);
$unitTaxBeforeDiscount = array_sum($unitTaxesBeforeDiscount);
$rowTax = $unitTax * $quantity;
$priceInclTax = $price + $unitTaxBeforeDiscount;
return $this->taxDetailsItemDataObjectFactory->create()
->setCode($item->getCode())
->setType($item->getType())
->setRowTax($rowTax)
->setPrice($price)
->setPriceInclTax($priceInclTax)
->setRowTotal($price * $quantity)
->setRowTotalInclTax($priceInclTax * $quantity)
->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
->setAssociatedItemCode($item->getAssociatedItemCode())
->setTaxPercent($rate)
->setAppliedTaxes($appliedTaxes);
}

Magento\Tax\Model\System\Message\Notifications::checkDiscountSettings()

public function checkDiscountSettings($store = null)
{
return $this->taxConfig->applyTaxAfterDiscount($store);
}

Magento\Tax\Model\System\Message\Notifications::checkDiscountSettings()

private function checkSettings($store = null)
{
return $this->taxConfig->priceIncludesTax($store)
|| !$this->taxConfig->applyTaxAfterDiscount($store)
|| !$this->taxConfig->discountTax($store);
}

Magento\Tax\Model\System\Message\Notification\DiscountErrors::checkSettings()

private function checkSettings($store = null)
{
return $this->taxConfig->applyTaxAfterDiscount($store);
}

For Further Details or Additional Information

OR

If you want to know more about our Services

Jul 23rd, 2019|