/** * Convert and format price value for specified store * * @param float $value * @param int|\Magento\Store\Model\Store $store * @param bool $format * @param bool $includeContainer * @return float|string */ public function currencyByStore($value, $store = null, $format = true, $includeContainer = true) { if ($format) { $value = $this->priceCurrency->convertAndFormat($value, $includeContainer, PriceCurrencyInterface::DEFAULT_PRECISION, $store); } else { $value = $this->priceCurrency->convert($value, $store); } return $value; }
/** * Collect recurring item parameters and copy to the address items * * @param \Magento\Sales\Model\Quote\Address $address * @return $this */ public function collect(\Magento\Sales\Model\Quote\Address $address) { parent::collect($address); $items = $this->_getAddressItems($address); foreach ($items as $item) { if ($item->getProduct()->getIsRecurring()) { $paymentData = $item->getProduct()->getRecurringPayment(); if (!empty($paymentData[$this->_paymentDataKey])) { $item->setData($this->_itemRowTotalKey, $this->priceCurrency->convert($paymentData[$this->_paymentDataKey], $address->getQuote()->getStore())); $this->_afterCollectSuccess($address, $item); } } } return $this; }
/** * Get item price converted to quote currency * @return float */ public function getConvertedPrice() { $price = $this->_getData('converted_price'); if ($price === null) { $price = $this->priceCurrency->convert($this->getPrice(), $this->getStore()); $this->setData('converted_price', $price); } return $price; }
/** * Convert price from default currency to current currency * * @param float $price * @param bool $round * @return float */ protected function _convertPrice($price, $round = false) { if (empty($price)) { return 0; } $price = $this->priceCurrency->convert($price); if ($round) { $price = $this->priceCurrency->round($price); } return $price; }
/** * Obtain amount * * @param SaleableInterface $saleableItem * @return float */ protected function getAmount(SaleableInterface $saleableItem) { $weeeTaxAmount = 0; $attributes = $this->weeeHelper->getProductWeeeAttributes($saleableItem, null, null, null, true, false); if ($attributes != null) { foreach ($attributes as $attribute) { $weeeTaxAmount += $attribute->getData('tax_amount'); } } $weeeTaxAmount = $this->priceCurrency->convert($weeeTaxAmount); return $weeeTaxAmount; }
/** * Calculate item fixed tax and prepare information for discount and regular taxation * * @param \Magento\Quote\Model\Quote\Address $address * @param \Magento\Quote\Model\Quote\Address\Total $total * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return void|$this * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function process(\Magento\Quote\Model\Quote\Address $address, \Magento\Quote\Model\Quote\Address\Total $total, $item) { $attributes = $this->weeeData->getProductWeeeAttributes($item->getProduct(), $address, $address->getQuote()->getBillingAddress(), $this->_store->getWebsiteId()); $productTaxes = []; $totalValueInclTax = 0; $baseTotalValueInclTax = 0; $totalRowValueInclTax = 0; $baseTotalRowValueInclTax = 0; $totalValueExclTax = 0; $baseTotalValueExclTax = 0; $totalRowValueExclTax = 0; $baseTotalRowValueExclTax = 0; $associatedTaxables = $item->getAssociatedTaxables(); if (!$associatedTaxables) { $associatedTaxables = []; } else { // remove existing weee associated taxables foreach ($associatedTaxables as $iTaxable => $taxable) { if ($taxable[CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_TYPE] == self::ITEM_TYPE) { unset($associatedTaxables[$iTaxable]); } } } foreach ($attributes as $key => $attribute) { $title = $attribute->getName(); $baseValueExclTax = $baseValueInclTax = $attribute->getAmount(); $valueExclTax = $valueInclTax = $this->priceCurrency->round($this->priceCurrency->convert($baseValueExclTax, $this->_store)); $rowValueInclTax = $rowValueExclTax = $this->priceCurrency->round($valueInclTax * $item->getTotalQty()); $baseRowValueInclTax = $this->priceCurrency->round($baseValueInclTax * $item->getTotalQty()); $baseRowValueExclTax = $baseRowValueInclTax; $totalValueInclTax += $valueInclTax; $baseTotalValueInclTax += $baseValueInclTax; $totalRowValueInclTax += $rowValueInclTax; $baseTotalRowValueInclTax += $baseRowValueInclTax; $totalValueExclTax += $valueExclTax; $baseTotalValueExclTax += $baseValueExclTax; $totalRowValueExclTax += $rowValueExclTax; $baseTotalRowValueExclTax += $baseRowValueExclTax; $productTaxes[] = ['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]; if ($this->weeeData->isTaxable($this->_store)) { $weeeItemCode = self::ITEM_CODE_WEEE_PREFIX . $this->getNextIncrement(); $weeeItemCode .= '-' . $title; $associatedTaxables[] = [CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_TYPE => self::ITEM_TYPE, CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_CODE => $weeeItemCode, CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_UNIT_PRICE => $valueExclTax, CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_BASE_UNIT_PRICE => $baseValueExclTax, CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_QUANTITY => $item->getTotalQty(), CommonTaxCollector::KEY_ASSOCIATED_TAXABLE_TAX_CLASS_ID => $item->getProduct()->getTaxClassId()]; $this->weeeCodeToItemMap[$weeeItemCode] = $item; } } $item->setAssociatedTaxables($associatedTaxables); $item->setWeeeTaxAppliedAmount($totalValueExclTax)->setBaseWeeeTaxAppliedAmount($baseTotalValueExclTax)->setWeeeTaxAppliedRowAmount($totalRowValueExclTax)->setBaseWeeeTaxAppliedRowAmnt($baseTotalRowValueExclTax); $item->setWeeeTaxAppliedAmountInclTax($totalValueInclTax)->setBaseWeeeTaxAppliedAmountInclTax($baseTotalValueInclTax)->setWeeeTaxAppliedRowAmountInclTax($totalRowValueInclTax)->setBaseWeeeTaxAppliedRowAmntInclTax($baseTotalRowValueInclTax); $this->processTotalAmount($total, $totalRowValueExclTax, $baseTotalRowValueExclTax, $totalRowValueInclTax, $baseTotalRowValueInclTax); $this->weeeData->setApplied($item, array_merge($this->weeeData->getApplied($item), $productTaxes)); }
/** * Get JSON encoded configuration array which can be used for JS dynamic * price calculation depending on product options * * @return string */ public function getJsonConfig() { /* @var $product \Magento\Catalog\Model\Product */ $product = $this->getProduct(); if (!$this->hasOptions()) { $config = ['productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat()]; return $this->_jsonEncoder->encode($config); } $tierPrices = []; $tierPricesList = $product->getPriceInfo()->getPrice('tier_price')->getTierPriceList(); foreach ($tierPricesList as $tierPrice) { $tierPrices[] = $this->priceCurrency->convert($tierPrice['price']->getValue()); } $config = ['productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), 'prices' => ['oldPrice' => ['amount' => $this->priceCurrency->convert($product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue()), 'adjustments' => []], 'basePrice' => ['amount' => $this->priceCurrency->convert($product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount()), 'adjustments' => []], 'finalPrice' => ['amount' => $this->priceCurrency->convert($product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue()), 'adjustments' => []]], 'idSuffix' => '_clone', 'tierPrices' => $tierPrices]; $responseObject = new \Magento\Framework\DataObject(); $this->_eventManager->dispatch('catalog_product_view_config', ['response_object' => $responseObject]); if (is_array($responseObject->getAdditionalOptions())) { foreach ($responseObject->getAdditionalOptions() as $option => $value) { $config[$option] = $value; } } return $this->_jsonEncoder->encode($config); }
/** * Apply discounts to shipping amount * * @param Address $address * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function processShippingAmount(Address $address) { $shippingAmount = $address->getShippingAmountForDiscount(); if ($shippingAmount !== null) { $baseShippingAmount = $address->getBaseShippingAmountForDiscount(); } else { $shippingAmount = $address->getShippingAmount(); $baseShippingAmount = $address->getBaseShippingAmount(); } $quote = $address->getQuote(); $appliedRuleIds = []; foreach ($this->_getRules() as $rule) { /* @var \Magento\SalesRule\Model\Rule $rule */ if (!$rule->getApplyToShipping() || !$this->validatorUtility->canProcessRule($rule, $address)) { continue; } $discountAmount = 0; $baseDiscountAmount = 0; $rulePercent = min(100, $rule->getDiscountAmount()); switch ($rule->getSimpleAction()) { case \Magento\SalesRule\Model\Rule::TO_PERCENT_ACTION: $rulePercent = max(0, 100 - $rule->getDiscountAmount()); // break is intentionally omitted // break is intentionally omitted case \Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION: $discountAmount = ($shippingAmount - $address->getShippingDiscountAmount()) * $rulePercent / 100; $baseDiscountAmount = ($baseShippingAmount - $address->getBaseShippingDiscountAmount()) * $rulePercent / 100; $discountPercent = min(100, $address->getShippingDiscountPercent() + $rulePercent); $address->setShippingDiscountPercent($discountPercent); break; case \Magento\SalesRule\Model\Rule::TO_FIXED_ACTION: $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore()); $discountAmount = $shippingAmount - $quoteAmount; $baseDiscountAmount = $baseShippingAmount - $rule->getDiscountAmount(); break; case \Magento\SalesRule\Model\Rule::BY_FIXED_ACTION: $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore()); $discountAmount = $quoteAmount; $baseDiscountAmount = $rule->getDiscountAmount(); break; case \Magento\SalesRule\Model\Rule::CART_FIXED_ACTION: $cartRules = $address->getCartFixedRules(); if (!isset($cartRules[$rule->getId()])) { $cartRules[$rule->getId()] = $rule->getDiscountAmount(); } if ($cartRules[$rule->getId()] > 0) { $quoteAmount = $this->priceCurrency->convert($cartRules[$rule->getId()], $quote->getStore()); $discountAmount = min($shippingAmount - $address->getShippingDiscountAmount(), $quoteAmount); $baseDiscountAmount = min($baseShippingAmount - $address->getBaseShippingDiscountAmount(), $cartRules[$rule->getId()]); $cartRules[$rule->getId()] -= $baseDiscountAmount; } $address->setCartFixedRules($cartRules); break; } $discountAmount = min($address->getShippingDiscountAmount() + $discountAmount, $shippingAmount); $baseDiscountAmount = min($address->getBaseShippingDiscountAmount() + $baseDiscountAmount, $baseShippingAmount); $address->setShippingDiscountAmount($discountAmount); $address->setBaseShippingDiscountAmount($baseDiscountAmount); $appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId(); $this->rulesApplier->maintainAddressCouponCode($address, $rule, $this->getCouponCode()); $this->rulesApplier->addDiscountDescription($address, $rule); if ($rule->getStopRulesProcessing()) { break; } } $address->setAppliedRuleIds($this->validatorUtility->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds)); $quote->setAppliedRuleIds($this->validatorUtility->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds)); return $this; }
/** * Obtain amount * * @param SaleableInterface $saleableItem * @return float */ protected function getAmount(SaleableInterface $saleableItem) { $weeeAmount = $this->weeeHelper->getAmountExclTax($saleableItem); $weeeAmount = $this->priceCurrency->convert($weeeAmount); return $weeeAmount; }
/** * @param float $price * @param bool $format * @return float */ public function convertPrice($price, $format = true) { return $format ? $this->priceCurrency->convertAndFormat($price) : $this->priceCurrency->convert($price); }
/** * Collect totals information about shipping * * @param \Magento\Quote\Model\Quote\Address $address * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function collect(\Magento\Quote\Model\Quote\Address $address) { parent::collect($address); $address->setWeight(0); $address->setFreeMethodWeight(0); $this->_setAmount(0)->_setBaseAmount(0); $items = $this->_getAddressItems($address); if (!count($items)) { return $this; } $method = $address->getShippingMethod(); $freeAddress = $address->getFreeShipping(); $addressWeight = $address->getWeight(); $freeMethodWeight = $address->getFreeMethodWeight(); $addressQty = 0; foreach ($items as $item) { /** * Skip if this item is virtual */ if ($item->getProduct()->isVirtual()) { continue; } /** * Children weight we calculate for parent */ if ($item->getParentItem()) { continue; } if ($item->getHasChildren() && $item->isShipSeparately()) { foreach ($item->getChildren() as $child) { if ($child->getProduct()->isVirtual()) { continue; } $addressQty += $child->getTotalQty(); if (!$item->getProduct()->getWeightType()) { $itemWeight = $child->getWeight(); $itemQty = $child->getTotalQty(); $rowWeight = $itemWeight * $itemQty; $addressWeight += $rowWeight; if ($freeAddress || $child->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($child->getFreeShipping())) { $freeQty = $child->getFreeShipping(); if ($itemQty > $freeQty) { $rowWeight = $itemWeight * ($itemQty - $freeQty); } else { $rowWeight = 0; } } $freeMethodWeight += $rowWeight; $item->setRowWeight($rowWeight); } } if ($item->getProduct()->getWeightType()) { $itemWeight = $item->getWeight(); $rowWeight = $itemWeight * $item->getQty(); $addressWeight += $rowWeight; if ($freeAddress || $item->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($item->getFreeShipping())) { $freeQty = $item->getFreeShipping(); if ($item->getQty() > $freeQty) { $rowWeight = $itemWeight * ($item->getQty() - $freeQty); } else { $rowWeight = 0; } } $freeMethodWeight += $rowWeight; $item->setRowWeight($rowWeight); } } else { if (!$item->getProduct()->isVirtual()) { $addressQty += $item->getQty(); } $itemWeight = $item->getWeight(); $rowWeight = $itemWeight * $item->getQty(); $addressWeight += $rowWeight; if ($freeAddress || $item->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($item->getFreeShipping())) { $freeQty = $item->getFreeShipping(); if ($item->getQty() > $freeQty) { $rowWeight = $itemWeight * ($item->getQty() - $freeQty); } else { $rowWeight = 0; } } $freeMethodWeight += $rowWeight; $item->setRowWeight($rowWeight); } } if (isset($addressQty)) { $address->setItemQty($addressQty); } $address->setWeight($addressWeight); $address->setFreeMethodWeight($freeMethodWeight); $address->collectShippingRates(); $this->_setAmount(0)->_setBaseAmount(0); if ($method) { foreach ($address->getAllShippingRates() as $rate) { if ($rate->getCode() == $method) { $amountPrice = $this->priceCurrency->convert($rate->getPrice(), $address->getQuote()->getStore()); $this->_setAmount($amountPrice); $this->_setBaseAmount($rate->getPrice()); $shippingDescription = $rate->getCarrierTitle() . ' - ' . $rate->getMethodTitle(); $address->setShippingDescription(trim($shippingDescription, ' -')); break; } } } return $this; }
/** * Custom setter for 'price' attribute * * @param Entry $entry * @param Product $product * @param mixed $value Fload price value * @param string $name Google Content attribute name * @return Entry */ protected function _setAttributePrice($entry, $product, $value, $name = 'price') { $store = $this->_storeManager->getStore($product->getStoreId()); $price = $this->priceCurrency->convert($value, $store); return $this->_setAttribute($entry, $name, self::ATTRIBUTE_TYPE_FLOAT, sprintf('%.2f', $this->priceCurrency->round($price)), $store->getDefaultCurrencyCode()); }
/** * Convert price * * @param float $value * @param bool $format * @return float */ public function convertPrice($value, $format = true) { return $format ? $this->priceCurrency->convertAndFormat($value, true, PriceCurrencyInterface::DEFAULT_PRECISION, $this->getStore()) : $this->priceCurrency->convert($value, $this->getStore()); }
/** * Get product price including store convertion rate * * @param \Magento\Catalog\Model\Product $product * @param null|string $format * @return float|string * @deprecated */ public function getProductPrice($product, $format = null) { try { $value = $product->getPrice(); $value = $format ? $this->priceCurrency->convertAndFormat($value) : $this->priceCurrency->convert($value); } catch (\Exception $e) { $value = $e->getMessage(); } return $value; }
/** * Returns JSON encoded config to be used in JS scripts * * @return string * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function getJsonConfig() { /** @var \Magento\Bundle\Model\Option[] $optionsArray */ $optionsArray = $this->getOptions(); $options = array(); $selected = array(); $currentProduct = $this->getProduct(); if ($preConfiguredFlag = $currentProduct->hasPreconfiguredValues()) { $preConfiguredValues = $currentProduct->getPreconfiguredValues(); $defaultValues = array(); } $position = 0; foreach ($optionsArray as $optionItem) { /* @var $optionItem \Magento\Bundle\Model\Option */ if (!$optionItem->getSelections()) { continue; } $optionId = $optionItem->getId(); $option = array('selections' => array(), 'title' => $optionItem->getTitle(), 'isMulti' => in_array($optionItem->getType(), array('multi', 'checkbox')), 'position' => $position++); $selectionCount = count($optionItem->getSelections()); foreach ($optionItem->getSelections() as $selectionItem) { /* @var $selectionItem \Magento\Catalog\Model\Product */ $selectionId = $selectionItem->getSelectionId(); $qty = !($selectionItem->getSelectionQty() * 1) ? '1' : $selectionItem->getSelectionQty() * 1; // recalculate currency $tierPrices = $selectionItem->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\TierPrice::PRICE_CODE)->getTierPriceList(); foreach ($tierPrices as &$tierPriceInfo) { /** @var \Magento\Framework\Pricing\Amount\Base $price */ $price = $tierPriceInfo['price']; $priceBaseAmount = $price->getBaseAmount(); $priceValue = $price->getValue(); $bundleProductPrice = $this->_productPrice->create(); $priceBaseAmount = $bundleProductPrice->getLowestPrice($currentProduct, $priceBaseAmount); $priceValue = $bundleProductPrice->getLowestPrice($currentProduct, $priceValue); $tierPriceInfo['price'] = $this->priceCurrency->convert($this->_taxData->displayPriceIncludingTax() ? $priceValue : $priceBaseAmount); $tierPriceInfo['exclTaxPrice'] = $this->priceCurrency->convert($priceBaseAmount); $tierPriceInfo['inclTaxPrice'] = $this->priceCurrency->convert($priceValue); } // break the reference with the last element $canApplyMAP = false; $bundleOptionPriceAmount = $currentProduct->getPriceInfo()->getPrice('bundle_option')->getOptionSelectionAmount($selectionItem); $priceInclTax = $bundleOptionPriceAmount->getValue(); $priceExclTax = $bundleOptionPriceAmount->getBaseAmount(); $selection = array('qty' => $qty, 'customQty' => $selectionItem->getSelectionCanChangeQty(), 'inclTaxPrice' => $this->priceCurrency->convert($priceInclTax), 'exclTaxPrice' => $this->priceCurrency->convert($priceExclTax), 'priceType' => $selectionItem->getSelectionPriceType(), 'tierPrice' => $tierPrices, 'name' => $selectionItem->getName(), 'plusDisposition' => 0, 'minusDisposition' => 0, 'canApplyMAP' => $canApplyMAP); $selection['price'] = $this->_taxData->displayPriceIncludingTax() ? $selection['inclTaxPrice'] : $selection['exclTaxPrice']; $responseObject = new \Magento\Framework\Object(); $args = array('response_object' => $responseObject, 'selection' => $selectionItem); $this->_eventManager->dispatch('bundle_product_view_config', $args); if (is_array($responseObject->getAdditionalOptions())) { foreach ($responseObject->getAdditionalOptions() as $index => $value) { $selection[$index] = $value; } } $option['selections'][$selectionId] = $selection; if (($selectionItem->getIsDefault() || $selectionCount == 1 && $optionItem->getRequired()) && $selectionItem->isSalable()) { $selected[$optionId][] = $selectionId; } } $options[$optionId] = $option; // Add attribute default value (if set) if ($preConfiguredFlag) { $configValue = $preConfiguredValues->getData('bundle_option/' . $optionId); if ($configValue) { $defaultValues[$optionId] = $configValue; } } } $isFixedPrice = $this->getProduct()->getPriceType() == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED; $productAmount = $currentProduct->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount(); $baseProductAmount = $currentProduct->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\RegularPrice::PRICE_CODE)->getAmount(); $config = array('options' => $options, 'selected' => $selected, 'bundleId' => $currentProduct->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), 'basePrice' => $this->priceCurrency->convert($baseProductAmount->getValue()), 'finalBasePriceInclTax' => $isFixedPrice ? $this->priceCurrency->convert($productAmount->getValue()) : 0, 'finalBasePriceExclTax' => $isFixedPrice ? $this->priceCurrency->convert($productAmount->getBaseAmount()) : 0, 'priceType' => $currentProduct->getPriceType(), 'specialPrice' => $currentProduct->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\SpecialPrice::PRICE_CODE)->getValue(), 'includeTax' => $this->_taxData->priceIncludesTax() ? 'true' : 'false', 'isFixedPrice' => $isFixedPrice); $config['finalPrice'] = $this->_taxData->displayPriceIncludingTax() ? $config['finalBasePriceInclTax'] : $config['finalBasePriceExclTax']; if ($preConfiguredFlag && !empty($defaultValues)) { $config['defaultValues'] = $defaultValues; } return $this->jsonEncoder->encode($config); }
/** * Prepare additional options/information for order item which will be * created from this product * * @param \Magento\Catalog\Model\Product $product * @return array */ public function getOrderOptions($product) { $optionArr = parent::getOrderOptions($product); $bundleOptions = []; if ($product->hasCustomOptions()) { $customOption = $product->getCustomOption('bundle_option_ids'); $optionIds = unserialize($customOption->getValue()); $options = $this->getOptionsByIds($optionIds, $product); $customOption = $product->getCustomOption('bundle_selection_ids'); $selectionIds = unserialize($customOption->getValue()); $selections = $this->getSelectionsByIds($selectionIds, $product); foreach ($selections->getItems() as $selection) { if ($selection->isSalable()) { $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId()); if ($selectionQty) { $price = $product->getPriceModel()->getSelectionFinalTotalPrice($product, $selection, 0, $selectionQty->getValue()); $option = $options->getItemById($selection->getOptionId()); if (!isset($bundleOptions[$option->getId()])) { $bundleOptions[$option->getId()] = ['option_id' => $option->getId(), 'label' => $option->getTitle(), 'value' => []]; } $bundleOptions[$option->getId()]['value'][] = ['title' => $selection->getName(), 'qty' => $selectionQty->getValue(), 'price' => $this->priceCurrency->convert($price)]; } } } } $optionArr['bundle_options'] = $bundleOptions; /** * Product Prices calculations save */ if ($product->getPriceType()) { $optionArr['product_calculations'] = self::CALCULATE_PARENT; } else { $optionArr['product_calculations'] = self::CALCULATE_CHILD; } $optionArr['shipment_type'] = $product->getShipmentType(); return $optionArr; }
/** * Convert prices for template * * @param float $amount * @param bool $format * @return float */ public function convertPrice($amount, $format = false) { return $format ? $this->priceCurrency->convertAndFormat($amount) : $this->priceCurrency->convert($amount); }
/** * Convert price from default currency to current currency * * @param float $price * @param boolean $format Format price to currency format * @param boolean $includeContainer Enclose into <span class="price"><span> * @return float */ public function formatPrice($price, $format = true, $includeContainer = true) { return $format ? $this->priceCurrency->convertAndFormat($price, $includeContainer) : $this->priceCurrency->convert($price); }
/** * Convert a quote/order/invoice/credit memo item to a tax details item objects * * This includes tax for the item as well as any additional line item tax information like Gift Wrapping * * @param QuoteDetailsItemInterface $item * @param GetTaxResult $getTaxResult * @param bool $useBaseCurrency * @param \Magento\Framework\App\ScopeInterface $scope * @return \Magento\Tax\Api\Data\TaxDetailsItemInterface */ protected function getTaxDetailsItem(QuoteDetailsItemInterface $item, GetTaxResult $getTaxResult, $useBaseCurrency, $scope) { $price = $item->getUnitPrice(); /* @var $taxLine \AvaTax\TaxLine */ $taxLine = $getTaxResult->getTaxLine($item->getCode()); // Items that are children of other items won't have lines in the response if (!$taxLine instanceof \AvaTax\TaxLine) { return false; } $rate = (double) ($taxLine->getRate() * Tax::RATE_MULTIPLIER); $tax = (double) $taxLine->getTax(); /** * Magento uses base rates for determining what to charge a customer, not the currency rate (i.e., the non-base * rate). Because of this, the base amounts are what is being sent to AvaTax for rate calculation. When we get * the base tax amounts back from AvaTax, we have to convert those to the current store's currency using the * \Magento\Framework\Pricing\PriceCurrencyInterface::convert() method. However if we simply convert the AvaTax * base tax amount * currency multiplier, we may run into issues due to rounding. * * For example, a $9.90 USD base price * a 6% tax rate equals a tax amount of $0.59 (.594 rounded). Assume the * current currency has a conversion rate of 2x. The price will display to the user as $19.80. There are two * ways we can calculate the tax amount: * 1. Multiply the tax amount received back from AvaTax, which would be $1.18 ($0.59 * 2). * 2. Multiply using this formula (base price * currency rate) * tax rate) ((9.99 * 2) * .06) * which would be $1.19 (1.188 rounded) * * The second approach is more accurate and is what we are doing here. */ if (!$useBaseCurrency) { /** * We could recalculate the amount using the same logic found in this class: * @see \ClassyLlama\AvaTax\Framework\Interaction\Line::convertTaxQuoteDetailsItemToData, * but using the taxable amount returned back from AvaTax is the only way to get an accurate amount as * some items sent to AvaTax may be tax exempt */ $taxableAmount = (double) $taxLine->getTaxable(); $amount = $this->priceCurrency->convert($taxableAmount, $scope); $tax = $amount * $taxLine->getRate(); $tax = $this->calculationTool->round($tax); } $rowTax = $tax; /** * In native Magento, the "row_total_incl_tax" and "base_row_total_incl_tax" fields contain the tax before * discount. The AvaTax 15 API doesn't have the concept of before/after discount tax, so in order to determine * the "before discount tax amount", we need to multiply the discount by the rate returned by AvaTax. * @see \Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxNotInPrice * * If the rate is 0, then this product doesn't have taxes applied and tax on discount shouldn't be calculated. * If tax is 0, then item was tax-exempt for some reason and tax on discount shouldn't be calculated */ if ($taxLine->getRate() > 0 && $tax > 0) { /** * Accurately calculating what AvaTax would have charged before discount requires checking to see if any * of the tax amount is tax exempt. If so, we need to find out what percentage of the total amount AvaTax * deemed as taxable and then use that percentage when calculating the discount amount. This partially * taxable scenario can arise in a situation like this: * @see https://help.avalara.com/kb/001/Why_is_freight_taxed_partially_on_my_sale * * To test this functionality, you can create a "Base Override" Tax Rule in the AvaTax admin to mark certain * jurisdictions as partially taxable. */ $taxableAmountPercentage = 1; if ($taxLine->getExemption() > 0) { // This value is the total amount sent to AvaTax for tax calculation, before AvaTax determined what // portion of the amount is taxable $totalAmount = $taxLine->getTaxable() + $taxLine->getExemption(); // Avoid division by 0 if ($totalAmount != 0) { $taxableAmountPercentage = $taxLine->getTaxable() / $totalAmount; } } $effectiveDiscountAmount = $taxableAmountPercentage * $item->getDiscountAmount(); $taxOnDiscountAmount = $effectiveDiscountAmount * $taxLine->getRate(); $taxOnDiscountAmount = $this->calculationTool->round($taxOnDiscountAmount); $rowTaxBeforeDiscount = $rowTax + $taxOnDiscountAmount; } else { $rowTaxBeforeDiscount = 0; } $extensionAttributes = $item->getExtensionAttributes(); if ($extensionAttributes) { $quantity = $extensionAttributes->getTotalQuantity() !== null ? $extensionAttributes->getTotalQuantity() : $item->getQuantity(); } else { $quantity = $item->getQuantity(); } $rowTotal = $price * $quantity; $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $rowTotalInclTax / $quantity; /** * Since the AvaTax extension does not support merchants adding products with tax already factored into the * price, we don't need to do any calculations for this number. The only time this value would be something * other than 0 is when this method runs: * @see \Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxInPrice */ $discountTaxCompensationAmount = 0; /** * The \Magento\Tax\Model\Calculation\AbstractAggregateCalculator::calculateWithTaxNotInPrice method that this * method is patterned off of has $round as a variable, but any time that method is used in the context of a * collect totals on a quote, rounding is always used. */ $round = true; if ($round) { $priceInclTax = $this->calculationTool->round($priceInclTax); } $appliedTax = $this->getAppliedTax($getTaxResult, $rowTax); $appliedTaxes = [$appliedTax->getTaxRateKey() => $appliedTax]; 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); }