/**
  * @param array $addressData
  * @param bool $useBaseCurrency
  * @param string $shippingTaxClass
  * @param bool $shippingPriceInclTax
  * @dataProvider getShippingDataObjectDataProvider
  */
 public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $shippingTaxClass, $shippingPriceInclTax)
 {
     $shippingAssignmentMock = $this->getMock('Magento\\Quote\\Api\\Data\\ShippingAssignmentInterface');
     $methods = ['getShippingDiscountAmount', 'getShippingTaxCalculationAmount', 'setShippingTaxCalculationAmount', 'getShippingAmount', 'setBaseShippingTaxCalculationAmount', 'getBaseShippingAmount', 'getBaseShippingDiscountAmount'];
     $totalsMock = $this->getMock('Magento\\Quote\\Model\\Quote\\Address\\Total', $methods, [], '', false);
     $shippingMock = $this->getMock('Magento\\Quote\\Api\\Data\\ShippingInterface');
     $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock);
     $shippingMock->expects($this->once())->method('getAddress')->willReturn($this->address);
     $baseShippingAmount = $addressData['base_shipping_amount'];
     $shippingAmount = $addressData['shipping_amount'];
     $totalsMock->expects($this->any())->method('getShippingTaxCalculationAmount')->willReturn($shippingAmount);
     $this->taxConfig->expects($this->any())->method('getShippingTaxClass')->with($this->store)->will($this->returnValue($shippingTaxClass));
     $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax));
     $totalsMock->expects($this->atLeastOnce())->method('getShippingDiscountAmount')->willReturn($shippingAmount);
     if ($shippingAmount) {
         if ($useBaseCurrency && $shippingAmount != 0) {
             $totalsMock->expects($this->once())->method('getBaseShippingDiscountAmount')->willReturn($baseShippingAmount);
             $expectedDiscountAmount = $baseShippingAmount;
         } else {
             $totalsMock->expects($this->never())->method('getBaseShippingDiscountAmount');
             $expectedDiscountAmount = $shippingAmount;
         }
     }
     foreach ($addressData as $key => $value) {
         $totalsMock->setData($key, $value);
     }
     $this->assertEquals($this->quoteDetailsItemDataObject, $this->commonTaxCollector->getShippingDataObject($shippingAssignmentMock, $totalsMock, $useBaseCurrency));
     if ($shippingAmount) {
         $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount());
     }
 }
 /**
  * {@inheritdoc}
  */
 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 $rowTotal
     $price = $this->calculationTool->round($item->getUnitPrice());
     $rowTotal = $price * $quantity;
     $rowTaxes = [];
     $rowTaxesBeforeDiscount = [];
     $appliedTaxes = [];
     //Apply each tax rate separately
     foreach ($appliedRates as $appliedRate) {
         $taxId = $appliedRate['id'];
         $taxRate = $appliedRate['percent'];
         $rowTaxPerRate = $this->calculationTool->calcTaxAmount($rowTotal, $taxRate, false, false);
         $deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
         if ($applyTaxAfterDiscount) {
             $deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
         }
         $rowTaxPerRate = $this->roundAmount($rowTaxPerRate, $taxId, false, $deltaRoundingType, $round);
         $rowTaxAfterDiscount = $rowTaxPerRate;
         //Handle discount
         if ($applyTaxAfterDiscount) {
             //TODO: handle originalDiscountAmount
             $taxableAmount = max($rowTotal - $discountAmount, 0);
             $rowTaxAfterDiscount = $this->calculationTool->calcTaxAmount($taxableAmount, $taxRate, false, false);
             $rowTaxAfterDiscount = $this->roundAmount($rowTaxAfterDiscount, $taxId, false, self::KEY_REGULAR_DELTA_ROUNDING, $round);
         }
         $appliedTaxes[$taxId] = $this->getAppliedTax($rowTaxAfterDiscount, $appliedRate);
         $rowTaxes[] = $rowTaxAfterDiscount;
         $rowTaxesBeforeDiscount[] = $rowTaxPerRate;
     }
     $rowTax = array_sum($rowTaxes);
     $rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount);
     $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount;
     $priceInclTax = $rowTotalInclTax / $quantity;
     if ($round) {
         $priceInclTax = $this->calculationTool->round($priceInclTax);
     }
     return $this->taxDetailsItemDataObjectFactory->create()->setCode($item->getCode())->setType($item->getType())->setRowTax($rowTax)->setPrice($price)->setPriceInclTax($priceInclTax)->setRowTotal($rowTotal)->setRowTotalInclTax($rowTotalInclTax)->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)->setAssociatedItemCode($item->getAssociatedItemCode())->setTaxPercent($rate)->setAppliedTaxes($appliedTaxes);
 }
 /**
  * @param array $addressData
  * @param bool $useBaseCurrency
  * @param string $shippingTaxClass
  * @param bool $shippingPriceInclTax
  * @dataProvider getShippingDataObjectDataProvider
  */
 public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $shippingTaxClass, $shippingPriceInclTax)
 {
     $baseShippingAmount = $addressData['base_shipping_amount'];
     $shippingAmount = $addressData['shipping_amount'];
     $this->taxConfig->expects($this->any())->method('getShippingTaxClass')->with($this->store)->will($this->returnValue($shippingTaxClass));
     $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax));
     $this->address->expects($this->atLeastOnce())->method('getShippingDiscountAmount')->willReturn($shippingAmount);
     if ($shippingAmount) {
         if ($useBaseCurrency && $shippingAmount != 0) {
             $this->address->expects($this->once())->method('getBaseShippingDiscountAmount')->willReturn($baseShippingAmount);
             $expectedDiscountAmount = $baseShippingAmount;
         } else {
             $this->address->expects($this->never())->method('getBaseShippingDiscountAmount');
             $expectedDiscountAmount = $shippingAmount;
         }
     }
     foreach ($addressData as $key => $value) {
         $this->address->setData($key, $value);
     }
     $this->assertEquals($this->quoteDetailsItemDataObject, $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency));
     if ($shippingAmount) {
         $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount());
     }
 }
 /**
  * {@inheritdoc}
  */
 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);
         $unitTaxAfterDiscount = $unitTaxPerRate;
         //Handle discount
         if ($discountAmount && $applyTaxAfterDiscount) {
             //TODO: handle originalDiscountAmount
             $unitDiscountAmount = $discountAmount / $quantity;
             $taxableAmount = max($price - $unitDiscountAmount, 0);
             $unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount($taxableAmount, $taxRate, false, true);
         }
         $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);
 }
Example #5
0
 /**
  * Convert \Magento\Tax\Model\Sales\Quote\ItemDetails to an array to be used for building an \AvaTax\Line object
  *
  * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterface $item
  * @return array
  */
 protected function convertTaxQuoteDetailsItemToData(\Magento\Tax\Api\Data\QuoteDetailsItemInterface $item)
 {
     $extensionAttributes = $item->getExtensionAttributes();
     if ($extensionAttributes) {
         $quantity = $extensionAttributes->getTotalQuantity() !== null ? $extensionAttributes->getTotalQuantity() : $item->getQuantity();
     } else {
         $quantity = $item->getQuantity();
     }
     $itemCode = $extensionAttributes ? $extensionAttributes->getAvataxItemCode() : '';
     $description = $extensionAttributes ? $extensionAttributes->getAvataxDescription() : '';
     $taxCode = $extensionAttributes ? $extensionAttributes->getAvataxTaxCode() : null;
     // The AvaTax 15 API doesn't support the concept of line-based discounts, so subtract discount amount
     // from taxable amount
     $amount = $item->getUnitPrice() * $quantity - $item->getDiscountAmount();
     $ref1 = $extensionAttributes ? $extensionAttributes->getAvataxRef1() : null;
     $ref2 = $extensionAttributes ? $extensionAttributes->getAvataxRef2() : null;
     return ['No' => $item->getCode(), 'ItemCode' => $itemCode, 'TaxCode' => $taxCode, 'Description' => $description, 'Qty' => $item->getQuantity(), 'Amount' => $amount, 'Discounted' => (bool) ($item->getDiscountAmount() > 0), 'TaxIncluded' => false, 'Ref1' => $ref1, 'Ref2' => $ref2];
 }
 /**
  * Calculates the total quantity for this item.
  *
  * What this really means is that if this is a child item, it return the parent quantity times
  * the child quantity and return that as the child's quantity.
  *
  * @param QuoteDetailsItemInterface $item
  * @return float
  */
 protected function getTotalQuantity(QuoteDetailsItemInterface $item)
 {
     if ($item->getParentCode()) {
         $parentQuantity = $this->keyedItems[$item->getParentCode()]->getQuantity();
         return $parentQuantity * $item->getQuantity();
     }
     return $item->getQuantity();
 }
 /**
  * Calculate tax details for quote item with given quantity
  *
  * @param QuoteDetailsItemInterface $item
  * @param int $quantity
  * @param bool $round
  * @return TaxDetailsItemInterface
  */
 public function calculate(QuoteDetailsItemInterface $item, $quantity, $round = true)
 {
     if ($item->getIsTaxIncluded()) {
         return $this->calculateWithTaxInPrice($item, $quantity, $round);
     } else {
         return $this->calculateWithTaxNotInPrice($item, $quantity, $round);
     }
 }
Example #8
0
 /**
  * Add AvaTax specific extension attribute fields to a \Magento\Tax\Model\Sales\Quote\ItemDetails object
  *
  * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterface $quoteDetailsItem
  * @param $avaTaxItemCode
  * @param $avaTaxTaxCode
  * @param $avaTaxDescription
  * @return $this
  */
 protected function addExtensionAttributesToTaxQuoteDetailsItem(\Magento\Tax\Api\Data\QuoteDetailsItemInterface $quoteDetailsItem, $avaTaxItemCode, $avaTaxTaxCode, $avaTaxDescription)
 {
     /** @var \Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterface $extensionAttribute */
     $extensionAttribute = $quoteDetailsItem->getExtensionAttributes() ? $quoteDetailsItem->getExtensionAttributes() : $this->extensionFactory->create();
     $extensionAttribute->setAvataxItemCode($avaTaxItemCode);
     $extensionAttribute->setAvataxTaxCode($avaTaxTaxCode);
     $extensionAttribute->setAvataxDescription($avaTaxDescription);
     $quoteDetailsItem->setExtensionAttributes($extensionAttribute);
     return $this;
 }
 /**
  * Calculates the total quantity for this item.
  *
  * What this really means is that if this is a child item, it return the parent quantity times
  * the child quantity and return that as the child's quantity. This code is a duplicate of the
  * @see \Magento\Tax\Model\TaxCalculation::getTotalQuantity()
  * method, but is refactored to accept the $keyedItems array.
  *
  * @param QuoteDetailsItemInterface $item
  * @param array $keyedItems
  * @return float
  */
 public function calculateTotalQuantity(QuoteDetailsItemInterface $item, array $keyedItems)
 {
     if ($item->getParentCode()) {
         $parentQuantity = $keyedItems[$item->getParentCode()]->getQuantity();
         return $parentQuantity * $item->getQuantity();
     }
     return $item->getQuantity();
 }