/** * @param array $quoteDetailsData * @param array $taxDetailsData * @param string $calculateCallback Name of a function within this test class which will be executed to create * a tax details item. * @return void * @dataProvider calculateTaxProvider */ public function testCalculateTax($quoteDetailsData, $taxDetailsData, $calculateCallback = 'createTaxDetailsItem') { $storeMock = $this->getMockBuilder('Magento\\Store\\Model\\Store')->disableOriginalConstructor()->getMock(); $this->storeManager->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); $calculatorMock = $this->getMockBuilder('\\Magento\\Tax\\Model\\Calculation\\AbstractCalculator')->disableOriginalConstructor()->setMethods(['calculate'])->getMockForAbstractClass(); $this->calculatorFactory->expects($this->any())->method('create')->will($this->returnValue($calculatorMock)); $calculatorMock->expects($this->any())->method('calculate')->will($this->returnCallback([$this, $calculateCallback])); $quoteDetails = $this->quoteDetailsBuilder->populateWithArray($quoteDetailsData)->create(); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); $this->assertEquals($taxDetailsData, $taxDetails->__toArray()); }
/** * @magentoDbIsolation enabled * @dataProvider multiRulesUnitBasedDataProvider * @magentoConfigFixture default_store tax/calculation/algorithm UNIT_BASE_CALCULATION */ public function testMultiRulesUnitBased($quoteDetailsData, $expectedTaxDetails) { $quoteDetailsData = $this->performTaxClassSubstitution($quoteDetailsData); $quoteDetails = $this->quoteDetailsBuilder->populateWithArray($quoteDetailsData)->create(); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails); $this->assertEquals($expectedTaxDetails, $taxDetails->__toArray()); }
/** * Set current attribute to entry (for specified product) * * @param \Magento\Catalog\Model\Product $product * @param \Magento\Framework\Gdata\Gshopping\Entry $entry * @return \Magento\Framework\Gdata\Gshopping\Entry * @throws \Magento\Framework\Model\Exception */ public function convertAttribute($product, $entry) { $entry->cleanTaxes(); if ($this->_taxData->getConfig()->priceIncludesTax()) { return $entry; } $defaultCustomerTaxClassId = $this->_getDefaultCustomerTaxClassId($product->getStoreId()); $rates = $this->_taxRuleService->getRatesByCustomerAndProductTaxClassId($defaultCustomerTaxClassId, $product->getTaxClassId()); $targetCountry = $this->_config->getTargetCountry($product->getStoreId()); $ratesTotal = 0; foreach ($rates as $rate) { $countryId = $rate->getCountryId(); $postcode = $rate->getPostcode(); if ($targetCountry == $countryId) { $regions = $this->_getRegionsByRegionId($rate->getRegionId(), $postcode); $ratesTotal += count($regions); if ($ratesTotal > self::RATES_MAX) { throw new \Magento\Framework\Model\Exception(__("Google shopping only supports %1 tax rates per product", self::RATES_MAX)); } foreach ($regions as $region) { $adjustments = $product->getPriceInfo()->getAdjustments(); if (array_key_exists('tax', $adjustments)) { $taxIncluded = true; } else { $taxIncluded = false; } $quoteDetailsItemDataArray = ['code' => $product->getSku(), 'type' => 'product', 'tax_class_key' => [TaxClassKey::KEY_TYPE => TaxClassKey::TYPE_ID, TaxClassKey::KEY_VALUE => $product->getTaxClassId()], 'unit_price' => $product->getPrice(), 'quantity' => 1, 'tax_included' => $taxIncluded, 'short_description' => $product->getName()]; $billingAddressDataArray = ['country_id' => $countryId, 'region' => ['region_id' => $rate->getRegionId()], 'postcode' => $postcode]; $shippingAddressDataArray = ['country_id' => $countryId, 'region' => ['region_id' => $rate->getRegionId()], 'postcode' => $postcode]; $quoteDetailsDataArray = ['billing_address' => $billingAddressDataArray, 'shipping_address' => $shippingAddressDataArray, 'customer_tax_class_key' => [TaxClassKey::KEY_TYPE => TaxClassKey::TYPE_ID, TaxClassKey::KEY_VALUE => $defaultCustomerTaxClassId], 'items' => [$quoteDetailsItemDataArray]]; $quoteDetailsObject = $this->_quoteDetailsBuilder->populateWithArray($quoteDetailsDataArray)->create(); $taxDetails = $this->_taxCalculationService->calculateTax($quoteDetailsObject, $product->getStoreId()); $taxRate = $taxDetails->getTaxAmount() / $taxDetails->getSubtotal() * 100; $entry->addTax(['tax_rate' => $taxRate, 'tax_country' => $countryId, 'tax_region' => $region]); } } } return $entry; }
/** * Populate QuoteDetails object from Address object * * @param Address $address * @param ItemDataObject[] $itemDataObjects * @return \Magento\Tax\Service\V1\Data\QuoteDetails */ protected function prepareQuoteDetails(Address $address, $itemDataObjects) { $items = $this->_getAddressItems($address); if (!count($items)) { return $this->quoteDetailsBuilder->create(); } $this->populateAddressData($this->quoteDetailsBuilder, $address); //Set customer tax class $this->quoteDetailsBuilder->setCustomerTaxClassKey($this->quoteDetailsBuilder->getTaxClassKeyBuilder()->setType(TaxClassKey::TYPE_ID)->setValue($address->getQuote()->getCustomerTaxClassId())->create()); $this->quoteDetailsBuilder->setItems($itemDataObjects); $this->quoteDetailsBuilder->setCustomerId($address->getQuote()->getCustomerId()); $quoteDetails = $this->quoteDetailsBuilder->create(); return $quoteDetails; }
/** * 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; } }
/** * 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; }