/**
  * {@inheritdoc}
  */
 public function calculateTax(QuoteDetails $quoteDetails, $storeId = null)
 {
     if (is_null($storeId)) {
         $storeId = $this->storeManager->getStore()->getStoreId();
     }
     // initial TaxDetails data
     $taxDetailsData = [TaxDetails::KEY_SUBTOTAL => 0.0, TaxDetails::KEY_TAX_AMOUNT => 0.0, TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, TaxDetails::KEY_APPLIED_TAXES => [], TaxDetails::KEY_ITEMS => []];
     $items = $quoteDetails->getItems();
     if (empty($items)) {
         return $this->taxDetailsBuilder->populateWithArray($taxDetailsData)->create();
     }
     $this->computeRelationships($items);
     $addressRequest = $this->getAddressTaxRequest($quoteDetails, $storeId, $quoteDetails->getCustomerId());
     if ($this->config->priceIncludesTax($storeId)) {
         $storeRequest = $this->getStoreTaxRequest($storeId);
         $classIds = [];
         foreach ($items as $item) {
             if ($item->getTaxClassId()) {
                 $classIds[] = $item->getTaxClassId();
             }
         }
         $classIds = array_unique($classIds);
         $addressRequest->setProductClassId($classIds);
         $storeRequest->setProductClassId($classIds);
         if ((bool) $this->config->crossBorderTradeEnabled($storeId)) {
             $addressRequest->setSameRateAsStore(true);
         } else {
             $addressRequest->setSameRateAsStore($this->calculator->compareRequests($storeRequest, $addressRequest));
         }
     }
     // init rounding deltas for this quote
     $this->roundingDeltas = [];
     // init discount tax compensations array
     $this->discountTaxCompensations = [];
     $processedItems = [];
     /** @var QuoteDetailsItem $item */
     foreach ($this->keyedItems as $item) {
         if (isset($this->parentToChildren[$item->getCode()])) {
             $processedChildren = [];
             foreach ($this->parentToChildren[$item->getCode()] as $child) {
                 $processedItem = $this->processItem($child, $addressRequest, $storeId);
                 $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem);
                 $processedItems[$processedItem->getCode()] = $processedItem;
                 $processedChildren[] = $processedItem;
             }
             $processedItemBuilder = $this->calculateParent($processedChildren, $item->getQuantity());
             $processedItemBuilder->setCode($item->getCode());
             $processedItemBuilder->setType($item->getType());
             $processedItem = $processedItemBuilder->create();
         } else {
             $processedItem = $this->processItem($item, $addressRequest, $storeId);
             $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem);
         }
         $processedItems[$processedItem->getCode()] = $processedItem;
     }
     return $this->taxDetailsBuilder->populateWithArray($taxDetailsData)->setItems($processedItems)->create();
 }
Example #2
0
 /**
  * Call tax calculation service to get tax details on the quote and items
  *
  * @param Address $address
  * @param bool $useBaseCurrency
  * @return \Magento\Tax\Api\Data\TaxDetailsInterface
  */
 protected function getQuoteTaxDetails($address, $useBaseCurrency)
 {
     //Setup taxable items
     $priceIncludesTax = $this->_config->priceIncludesTax($address->getQuote()->getStore());
     $itemDataObjects = $this->mapItems($address, $priceIncludesTax, $useBaseCurrency);
     //Add shipping
     $shippingDataObject = $this->getShippingDataObject($address, $useBaseCurrency);
     if ($shippingDataObject != null) {
         $itemDataObjects[] = $shippingDataObject;
     }
     //process extra taxable items associated only with quote
     $quoteExtraTaxables = $this->mapQuoteExtraTaxables($this->quoteDetailsItemDataObjectFactory, $address, $useBaseCurrency);
     if (!empty($quoteExtraTaxables)) {
         $itemDataObjects = array_merge($itemDataObjects, $quoteExtraTaxables);
     }
     //Preparation for calling taxCalculationService
     $quoteDetails = $this->prepareQuoteDetails($address, $itemDataObjects);
     $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails, $address->getQuote()->getStore()->getStoreId());
     return $taxDetails;
 }
Example #3
0
 /**
  * Check if product prices inputed include tax
  *
  * @param   null|int|string|Store $store
  * @return  bool
  */
 public function priceIncludesTax($store = null)
 {
     return $this->_config->priceIncludesTax($store) || $this->_config->getNeedUseShippingExcludeTax();
 }
Example #4
0
 /**
  * Get product price with all tax settings processing
  *
  * @param   \Magento\Catalog\Model\Product $product
  * @param   float $price inputted product price
  * @param   bool $includingTax return price include tax flag
  * @param   null|\Magento\Customer\Model\Address\AbstractAddress $shippingAddress
  * @param   null|\Magento\Customer\Model\Address\AbstractAddress $billingAddress
  * @param   null|int $ctc customer tax class
  * @param   null|string|bool|int|\Magento\Store\Model\Store $store
  * @param   bool $priceIncludesTax flag what price parameter contain tax
  * @param   bool $roundPrice
  * @return  float
  * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  * @SuppressWarnings(PHPMD.NPathComplexity)
  * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  */
 public function getTaxPrice($product, $price, $includingTax = null, $shippingAddress = null, $billingAddress = null, $ctc = null, $store = null, $priceIncludesTax = null, $roundPrice = true)
 {
     if (!$price) {
         return $price;
     }
     $store = $this->_storeManager->getStore($store);
     if ($this->_taxConfig->needPriceConversion($store)) {
         if ($priceIncludesTax === null) {
             $priceIncludesTax = $this->_taxConfig->priceIncludesTax($store);
         }
         $shippingAddressDataObject = null;
         if ($shippingAddress === null) {
             $shippingAddressDataObject = $this->convertDefaultTaxAddress($this->_customerSession->getDefaultTaxShippingAddress());
         } elseif ($shippingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
             $shippingAddressDataObject = $shippingAddress->getDataModel();
         }
         $billingAddressDataObject = null;
         if ($billingAddress === null) {
             $billingAddressDataObject = $this->convertDefaultTaxAddress($this->_customerSession->getDefaultTaxBillingAddress());
         } elseif ($billingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
             $billingAddressDataObject = $billingAddress->getDataModel();
         }
         $taxClassKey = $this->_taxClassKeyFactory->create();
         $taxClassKey->setType(TaxClassKeyInterface::TYPE_ID)->setValue($product->getTaxClassId());
         if ($ctc === null && $this->_customerSession->getCustomerGroupId() != null) {
             $ctc = $this->customerGroupRepository->getById($this->_customerSession->getCustomerGroupId())->getTaxClassId();
         }
         $customerTaxClassKey = $this->_taxClassKeyFactory->create();
         $customerTaxClassKey->setType(TaxClassKeyInterface::TYPE_ID)->setValue($ctc);
         $item = $this->_quoteDetailsItemFactory->create();
         $item->setQuantity(1)->setCode($product->getSku())->setShortDescription($product->getShortDescription())->setTaxClassKey($taxClassKey)->setIsTaxIncluded($priceIncludesTax)->setType('product')->setUnitPrice($price);
         $quoteDetails = $this->_quoteDetailsFactory->create();
         $quoteDetails->setShippingAddress($shippingAddressDataObject)->setBillingAddress($billingAddressDataObject)->setCustomerTaxClassKey($customerTaxClassKey)->setItems([$item])->setCustomerId($this->_customerSession->getCustomerId());
         $storeId = null;
         if ($store) {
             $storeId = $store->getId();
         }
         $taxDetails = $this->_taxCalculationService->calculateTax($quoteDetails, $storeId, $roundPrice);
         $items = $taxDetails->getItems();
         $taxDetailsItem = array_shift($items);
         if ($includingTax !== null) {
             if ($includingTax) {
                 $price = $taxDetailsItem->getPriceInclTax();
             } else {
                 $price = $taxDetailsItem->getPrice();
             }
         } else {
             switch ($this->_taxConfig->getPriceDisplayType($store)) {
                 case Config::DISPLAY_TYPE_EXCLUDING_TAX:
                 case Config::DISPLAY_TYPE_BOTH:
                     $price = $taxDetailsItem->getPrice();
                     break;
                 case Config::DISPLAY_TYPE_INCLUDING_TAX:
                     $price = $taxDetailsItem->getPriceInclTax();
                     break;
                 default:
                     break;
             }
         }
     }
     if ($roundPrice) {
         return $this->priceCurrency->round($price);
     } else {
         return $price;
     }
 }
 /**
  * @magentoConfigFixture current_store tax/calculation/price_includes_tax 1
  */
 public function testPriceIncludesTaxNonDefault()
 {
     $this->assertTrue($this->_model->priceIncludesTax());
 }
Example #6
0
 /**
  * Get product price with all tax settings processing
  *
  * @param   \Magento\Catalog\Model\Product $product
  * @param   float $price inputted product price
  * @param   bool $includingTax return price include tax flag
  * @param   null|Address $shippingAddress
  * @param   null|Address $billingAddress
  * @param   null|int $ctc customer tax class
  * @param   null|string|bool|int|Store $store
  * @param   bool $priceIncludesTax flag what price parameter contain tax
  * @param   bool $roundPrice
  * @return  float
  */
 public function getTaxPrice($product, $price, $includingTax = null, $shippingAddress = null, $billingAddress = null, $ctc = null, $store = null, $priceIncludesTax = null, $roundPrice = true)
 {
     if (!$price) {
         return $price;
     }
     $store = $this->_storeManager->getStore($store);
     if ($this->_taxConfig->needPriceConversion($store)) {
         if (is_null($priceIncludesTax)) {
             $priceIncludesTax = $this->_taxConfig->priceIncludesTax($store);
         }
         $shippingAddressDataObject = null;
         if ($shippingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
             $shippingAddressDataObject = $this->_addressConverter->createAddressFromModel($shippingAddress, null, null);
         }
         $billingAddressDataObject = null;
         if ($billingAddress instanceof \Magento\Customer\Model\Address\AbstractAddress) {
             $billingAddressDataObject = $this->_addressConverter->createAddressFromModel($billingAddress, null, null);
         }
         $item = $this->_quoteDetailsItemBuilder->setQuantity(1)->setCode($product->getSku())->setShortDescription($product->getShortDescription())->setTaxClassKey($this->_taxClassKeyBuilder->setType(TaxClassKey::TYPE_ID)->setValue($product->getTaxClassId())->create())->setTaxIncluded($priceIncludesTax)->setType('product')->setUnitPrice($price)->create();
         $quoteDetails = $this->_quoteDetailsBuilder->setShippingAddress($shippingAddressDataObject)->setBillingAddress($billingAddressDataObject)->setCustomerTaxClassKey($this->_taxClassKeyBuilder->setType(TaxClassKey::TYPE_ID)->setValue($ctc)->create())->setItems([$item])->setCustomerId($this->_customerSession->getCustomerId())->create();
         $storeId = null;
         if ($store) {
             $storeId = $store->getId();
         }
         $taxDetails = $this->_taxCalculationService->calculateTax($quoteDetails, $storeId);
         $items = $taxDetails->getItems();
         $taxDetailsItem = array_shift($items);
         if (!is_null($includingTax)) {
             if ($includingTax) {
                 $price = $taxDetailsItem->getPriceInclTax();
             } else {
                 $price = $taxDetailsItem->getPrice();
             }
         } else {
             switch ($this->_taxConfig->getPriceDisplayType($store)) {
                 case Config::DISPLAY_TYPE_EXCLUDING_TAX:
                 case Config::DISPLAY_TYPE_BOTH:
                     $price = $taxDetailsItem->getPrice();
                     break;
                 case Config::DISPLAY_TYPE_INCLUDING_TAX:
                     $price = $taxDetailsItem->getPriceInclTax();
                     break;
                 default:
                     break;
             }
         }
     }
     if ($roundPrice) {
         return $store->roundPrice($price);
     } else {
         return $price;
     }
 }
Example #7
0
 /**
  * Populate QuoteDetails object from Address object
  *
  * @param Address $address
  * @param bool $useBaseCurrency
  * @return \Magento\Tax\Service\V1\Data\QuoteDetails
  */
 protected function prepareQuoteDetails(Address $address, $useBaseCurrency)
 {
     $items = $this->_getAddressItems($address);
     if (!count($items)) {
         return $this->quoteDetailsBuilder->create();
     }
     $addressBuilder = $this->quoteDetailsBuilder->getAddressBuilder();
     //Set billing address
     $this->quoteDetailsBuilder->setBillingAddress($this->mapAddress($addressBuilder, $address->getQuote()->getBillingAddress()));
     //Set shipping address
     $this->quoteDetailsBuilder->setShippingAddress($this->mapAddress($addressBuilder, $address));
     //Set customer tax class
     $this->quoteDetailsBuilder->setCustomerTaxClassId($address->getQuote()->getCustomerTaxClassId());
     //Populate with items
     $priceIncludesTax = $this->_config->priceIncludesTax($this->_store);
     $itemBuilder = $this->quoteDetailsBuilder->getItemBuilder();
     $itemDataObjects = [];
     foreach ($items as $item) {
         if ($item->getParentItem()) {
             continue;
         }
         if ($item->getHasChildren() && $item->isChildrenCalculated()) {
             $parentItemDataObject = $this->mapItem($itemBuilder, $item, $priceIncludesTax, $useBaseCurrency);
             $itemDataObjects[] = $parentItemDataObject;
             foreach ($item->getChildren() as $child) {
                 $childItemDataObject = $this->mapItem($itemBuilder, $child, $priceIncludesTax, $useBaseCurrency, $parentItemDataObject->getCode());
                 $itemDataObjects[] = $childItemDataObject;
             }
         } else {
             $itemDataObject = $this->mapItem($itemBuilder, $item, $priceIncludesTax, $useBaseCurrency);
             $itemDataObjects[] = $itemDataObject;
         }
     }
     if ($this->includeShipping()) {
         //Add shipping as an item
         if (!$address->getShippingTaxCalculationAmount() || $address->getShippingTaxCalculationAmount() <= 0) {
             //Save the original shipping amount because shipping amount will be overridden
             //with shipping amount excluding tax
             $address->setShippingTaxCalculationAmount($address->getShippingAmount());
             $address->setBaseShippingTaxCalculationAmount($address->getBaseShippingAmount());
         }
         if ($address->getShippingTaxCalculationAmount() > 0) {
             $itemBuilder->setType(self::SHIPPING_ITEM_TYPE);
             $itemBuilder->setCode(self::SHIPPING_ITEM_CODE);
             $itemBuilder->setQuantity(1);
             if ($useBaseCurrency) {
                 $itemBuilder->setUnitPrice($address->getBaseShippingTaxCalculationAmount());
             } else {
                 $itemBuilder->setUnitPrice($address->getShippingTaxCalculationAmount());
             }
             if ($address->getShippingDiscountAmount()) {
                 if ($useBaseCurrency) {
                     $itemBuilder->setDiscountAmount($address->getBaseShippingDiscountAmount());
                 } else {
                     $itemBuilder->setDiscountAmount($address->getShippingDiscountAmount());
                 }
             }
             $itemBuilder->setTaxClassId($this->_config->getShippingTaxClass($this->_store));
             $itemBuilder->setTaxIncluded($this->_config->shippingPriceIncludesTax($this->_store));
             $itemDataObjects[] = $itemBuilder->create();
         }
     }
     $this->quoteDetailsBuilder->setItems($itemDataObjects);
     $quoteDetails = $this->quoteDetailsBuilder->create();
     return $quoteDetails;
 }