/**
  * Calculate rate based on default parameter
  *
  * @param int $productTaxClassID
  * @param int|null $customerId
  * @param string|null $storeId
  * @param bool $isDefault
  * @return float
  */
 protected function getRate($productTaxClassID, $customerId = null, $storeId = null, $isDefault = false)
 {
     if (is_null($storeId)) {
         $storeId = $this->storeManager->getStore()->getStoreId();
     }
     if (!$isDefault) {
         $addressRequestObject = $this->calculationTool->getRateRequest(null, null, null, $storeId, $customerId);
     } else {
         $addressRequestObject = $this->calculationTool->getDefaultRateRequest($storeId, $customerId);
     }
     $addressRequestObject->setProductClassId($productTaxClassID);
     return $this->calculationTool->getRate($addressRequestObject);
 }
 /**
  * Calculate item price and row total including/excluding tax based on total price rounding level
  *
  * @param QuoteDetailsItem $item
  * @param \Magento\Framework\Object $taxRateRequest
  * @param int $storeId
  * @return TaxDetailsItem
  */
 protected function totalBaseCalculation(QuoteDetailsItem $item, \Magento\Framework\Object $taxRateRequest, $storeId)
 {
     /** @var  \Magento\Tax\Service\V1\Data\TaxDetails\AppliedTax[] $appliedTaxes */
     $appliedTaxes = [];
     $appliedTaxBuilder = $this->taxDetailsItemBuilder->getAppliedTaxBuilder();
     $appliedTaxRateBuilder = $appliedTaxBuilder->getAppliedTaxRateBuilder();
     $taxRateRequest->setProductClassId($item->getTaxClassId());
     $appliedRates = $this->calculator->getAppliedRates($taxRateRequest);
     $rate = $this->calculator->getRate($taxRateRequest);
     $quantity = $this->getTotalQuantity($item);
     $price = $priceInclTax = $this->calculator->round($item->getUnitPrice());
     $rowTotal = $rowTotalInclTax = $taxableAmount = $this->calcRowTotal($item);
     $discountAmount = $item->getDiscountAmount();
     $applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($storeId);
     $discountTaxCompensationAmount = 0;
     $isTotalBasedCalculation = $this->config->getAlgorithm($storeId) == Calculation::CALC_TOTAL_BASE;
     if ($item->getTaxIncluded()) {
         if ($taxRateRequest->getSameRateAsStore()) {
             $rowTaxExact = $this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, false);
             if ($isTotalBasedCalculation) {
                 $rowTax = $this->deltaRound($rowTaxExact, $rate, true);
             } else {
                 $rowTax = $this->calculator->round($rowTaxExact);
             }
             $rowTotal = $rowTotalInclTax - $rowTax;
             $price = $this->calculator->round($rowTotal / $quantity);
         } else {
             $storeRate = $this->calculator->getStoreRate($taxRateRequest, $storeId);
             $priceInclTax = $this->calculatePriceInclTax($price, $storeRate, $rate);
             $rowTotalInclTax = $priceInclTax * $quantity;
             $taxableAmount = $rowTotalInclTax;
             if ($isTotalBasedCalculation) {
                 $rowTax = $this->deltaRound($this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, false), $rate, true);
             } else {
                 $rowTax = $this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, true);
             }
             $rowTotal = $rowTotalInclTax - $rowTax;
             $price = $this->calculator->round($rowTotal / $quantity);
         }
         //Handle discount
         if ($discountAmount && $applyTaxAfterDiscount) {
             //TODO: handle originalDiscountAmount
             $taxableAmount = max($taxableAmount - $discountAmount, 0);
             $rowTaxAfterDiscount = $this->calculator->calcTaxAmount($taxableAmount, $rate, true, false);
             if ($isTotalBasedCalculation) {
                 //Round the row tax using a different type so that we don't pollute the rounding deltas
                 $rowTaxAfterDiscount = $this->deltaRound($rowTaxAfterDiscount, $rate, true, self::KEY_TAX_AFTER_DISCOUNT_DELTA_ROUNDING);
             } else {
                 $rowTaxAfterDiscount = $this->calculator->round($rowTaxAfterDiscount);
             }
             // Set discount tax compensation
             $discountTaxCompensationAmount = $rowTax - $rowTaxAfterDiscount;
             $rowTax = $rowTaxAfterDiscount;
         }
         //save applied taxes
         $appliedTaxes = $this->getAppliedTaxes($appliedTaxBuilder, $rowTax, $rate, $appliedRates);
     } else {
         //catalog price does not include tax
         $appliedRates = $this->calculator->getAppliedRates($taxRateRequest);
         $rowTaxes = [];
         $rowTaxesBeforeDiscount = [];
         //Apply each tax rate separately
         foreach ($appliedRates as $appliedRate) {
             $taxId = $appliedRate['id'];
             $taxRate = $appliedRate['percent'];
             if ($isTotalBasedCalculation) {
                 $rowTaxPerRate = $this->deltaRound($this->calculator->calcTaxAmount($rowTotal, $taxRate, false, false), $taxId, false);
             } else {
                 $rowTaxPerRate = $this->calculator->calcTaxAmount($rowTotal, $taxRate, false, true);
             }
             $rowTaxAfterDiscount = $rowTaxPerRate;
             //Handle discount
             if ($discountAmount && $applyTaxAfterDiscount) {
                 //TODO: handle originalDiscountAmount
                 $rowTaxAfterDiscount = $this->calculator->calcTaxAmount(max($rowTotal - $discountAmount, 0), $taxRate, false, false);
                 if ($isTotalBasedCalculation) {
                     //Round the row tax using a different type so that we don't pollute the rounding deltas
                     $rowTaxAfterDiscount = $this->deltaRound($rowTaxAfterDiscount, $taxRate, false, self::KEY_TAX_AFTER_DISCOUNT_DELTA_ROUNDING);
                 } else {
                     $rowTaxAfterDiscount = $this->calculator->round($rowTaxAfterDiscount);
                 }
             }
             $appliedTaxes[$appliedRate['id']] = $this->getAppliedTax($appliedTaxBuilder, $rowTaxAfterDiscount, $appliedRate);
             $rowTaxes[] = $rowTaxAfterDiscount;
             $rowTaxesBeforeDiscount[] = $rowTaxPerRate;
         }
         $rowTax = array_sum($rowTaxes);
         $rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount);
         $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount;
         $priceInclTax = $this->calculator->round($rowTotalInclTax / $quantity);
     }
     $this->taxDetailsItemBuilder->setCode($item->getCode());
     $this->taxDetailsItemBuilder->setRowTax($rowTax);
     $this->taxDetailsItemBuilder->setPrice($price);
     $this->taxDetailsItemBuilder->setPriceInclTax($priceInclTax);
     $this->taxDetailsItemBuilder->setRowTotal($rowTotal);
     $this->taxDetailsItemBuilder->setRowTotalInclTax($rowTotalInclTax);
     $this->taxDetailsItemBuilder->setCode($item->getCode());
     $this->taxDetailsItemBuilder->setType($item->getType());
     $this->taxDetailsItemBuilder->setTaxPercent($rate);
     $this->taxDetailsItemBuilder->setDiscountTaxCompensationAmount($discountTaxCompensationAmount);
     $this->taxDetailsItemBuilder->setAppliedTaxes($appliedTaxes);
     return $this->taxDetailsItemBuilder->create();
 }
 /**
  * Apply Tax Rate
  *
  * @param int $classId
  * @param null $shippingAddress
  * @param null $billingAddress
  * @param null $customerTaxClass
  * @param int|null $customerId
  * @return float
  */
 protected function applyRate($classId, $shippingAddress = null, $billingAddress = null, $customerTaxClass = null, $customerId = null)
 {
     $rateRequest = $this->calculation->getRateRequest($shippingAddress, $billingAddress, $customerTaxClass, null, $customerId);
     $rateRequest->setProductClassId($classId);
     return $this->calculation->getRate($rateRequest);
 }
Beispiel #4
0
 /**
  * Calculate item fixed tax and prepare information for discount and regular taxation
  *
  * @param   \Magento\Sales\Model\Quote\Address $address
  * @param   \Magento\Sales\Model\Quote\Item\AbstractItem $item
  * @return  void|$this
  */
 protected function _process(\Magento\Sales\Model\Quote\Address $address, $item)
 {
     if (!$this->_weeeData->isEnabled($this->_store)) {
         return $this;
     }
     $attributes = $this->_weeeData->getProductWeeeAttributes($item->getProduct(), $address, $address->getQuote()->getBillingAddress(), $this->_store->getWebsiteId());
     $applied = array();
     $productTaxes = array();
     $defaultRateRequest = $this->_calculator->getRateOriginRequest($this->_store);
     $rateRequest = $this->_calculator->getRateRequest($address, $address->getQuote()->getBillingAddress(), $address->getQuote()->getCustomerTaxClassId(), $this->_store);
     $totalValueInclTax = 0;
     $baseTotalValueInclTax = 0;
     $totalRowValueInclTax = 0;
     $baseTotalRowValueInclTax = 0;
     $totalValueExclTax = 0;
     $baseTotalValueExclTax = 0;
     $totalRowValueExclTax = 0;
     $baseTotalRowValueExclTax = 0;
     $priceIncludesTax = $this->_taxData->priceIncludesTax($this->_store);
     $calculationAlgorithm = $this->_taxData->getCalculationAgorithm($this->_store);
     $defaultPercent = $currentPercent = 0;
     //when FPT is not taxable
     foreach ($attributes as $key => $attribute) {
         $title = $attribute->getName();
         $baseValue = $attribute->getAmount();
         $value = $this->_store->convertPrice($baseValue);
         $value = $this->_store->roundPrice($value);
         if ($this->_weeeData->isTaxable($this->_store)) {
             $defaultPercent = $this->_calculator->getRate($defaultRateRequest->setProductClassId($item->getProduct()->getTaxClassId()));
             $currentPercent = $this->_calculator->getRate($rateRequest->setProductClassId($item->getProduct()->getTaxClassId()));
         }
         if ($priceIncludesTax) {
             //Make sure that price including tax is rounded first
             $baseValueInclTax = $baseValue / (100 + $defaultPercent) * (100 + $currentPercent);
             $baseValueInclTax = $this->_store->roundPrice($baseValueInclTax);
             $valueInclTax = $value / (100 + $defaultPercent) * (100 + $currentPercent);
             $valueInclTax = $this->_store->roundPrice($valueInclTax);
             $baseValueExclTax = $baseValueInclTax / (100 + $currentPercent) * 100;
             $valueExclTax = $valueInclTax / (100 + $currentPercent) * 100;
             if ($calculationAlgorithm == Calculation::CALC_UNIT_BASE) {
                 $baseValueExclTax = $this->_store->roundPrice($baseValueExclTax);
                 $valueExclTax = $this->_store->roundPrice($valueExclTax);
             }
         } else {
             $valueExclTax = $value;
             $baseValueExclTax = $baseValue;
             $valueInclTax = $valueExclTax * (100 + $currentPercent) / 100;
             $baseValueInclTax = $baseValueExclTax * (100 + $currentPercent) / 100;
             if ($calculationAlgorithm == Calculation::CALC_UNIT_BASE) {
                 $baseValueInclTax = $this->_store->roundPrice($baseValueInclTax);
                 $valueInclTax = $this->_store->roundPrice($valueInclTax);
             }
         }
         $rowValueInclTax = $this->_store->roundPrice($valueInclTax * $item->getTotalQty());
         $baseRowValueInclTax = $this->_store->roundPrice($baseValueInclTax * $item->getTotalQty());
         $rowValueExclTax = $this->_store->roundPrice($valueExclTax * $item->getTotalQty());
         $baseRowValueExclTax = $this->_store->roundPrice($baseValueExclTax * $item->getTotalQty());
         //Now, round the unit price just in case
         $valueExclTax = $this->_store->roundPrice($valueExclTax);
         $baseValueExclTax = $this->_store->roundPrice($baseValueExclTax);
         $valueInclTax = $this->_store->roundPrice($valueInclTax);
         $baseValueInclTax = $this->_store->roundPrice($baseValueInclTax);
         $totalValueInclTax += $valueInclTax;
         $baseTotalValueInclTax += $baseValueInclTax;
         $totalRowValueInclTax += $rowValueInclTax;
         $baseTotalRowValueInclTax += $baseRowValueInclTax;
         $totalValueExclTax += $valueExclTax;
         $baseTotalValueExclTax += $baseValueExclTax;
         $totalRowValueExclTax += $rowValueExclTax;
         $baseTotalRowValueExclTax += $baseRowValueExclTax;
         $productTaxes[] = array('title' => $title, 'base_amount' => $baseValueExclTax, 'amount' => $valueExclTax, 'row_amount' => $rowValueExclTax, 'base_row_amount' => $baseRowValueExclTax, 'base_amount_incl_tax' => $baseValueInclTax, 'amount_incl_tax' => $valueInclTax, 'row_amount_incl_tax' => $rowValueInclTax, 'base_row_amount_incl_tax' => $baseRowValueInclTax);
         //This include FPT as applied tax, since tax on FPT is calculated separately, we use value excluding tax
         $applied[] = array('id' => $attribute->getCode(), 'percent' => null, 'hidden' => $this->_weeeData->includeInSubtotal($this->_store), 'rates' => array(array('base_real_amount' => $baseRowValueExclTax, 'base_amount' => $baseRowValueExclTax, 'amount' => $rowValueExclTax, 'code' => $attribute->getCode(), 'title' => $title, 'percent' => null, 'position' => 1, 'priority' => -1000 + $key)));
     }
     $item->setWeeeTaxAppliedAmount($totalValueExclTax)->setBaseWeeeTaxAppliedAmount($baseTotalValueExclTax)->setWeeeTaxAppliedRowAmount($totalRowValueExclTax)->setBaseWeeeTaxAppliedRowAmnt($baseTotalRowValueExclTax);
     $item->setWeeeTaxAppliedAmountInclTax($totalValueInclTax)->setBaseWeeeTaxAppliedAmountInclTax($baseTotalValueInclTax)->setWeeeTaxAppliedRowAmountInclTax($totalRowValueInclTax)->setBaseWeeeTaxAppliedRowAmntInclTax($baseTotalRowValueInclTax);
     if ($priceIncludesTax) {
         $this->_processTaxSettings($item, $totalValueInclTax, $baseTotalValueInclTax, $totalRowValueInclTax, $baseTotalRowValueInclTax);
     } else {
         $this->_processTaxSettings($item, $totalValueExclTax, $baseTotalValueExclTax, $totalRowValueExclTax, $baseTotalRowValueExclTax);
     }
     $this->_processTotalAmount($address, $totalRowValueExclTax, $baseTotalRowValueExclTax, $totalRowValueInclTax, $baseTotalRowValueInclTax);
     $this->_weeeData->setApplied($item, array_merge($this->_weeeData->getApplied($item), $productTaxes));
     //Update the applied taxes for the quote
     if ($applied) {
         $this->_saveAppliedTaxes($address, $applied, $item->getWeeeTaxAppliedAmount(), $item->getBaseWeeeTaxAppliedAmount(), null);
     }
 }