/** * Quote item free shipping ability check * This process not affect information about applied rules, coupon code etc. * This information will be added during discount amounts processing * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return \Magento\OfflineShipping\Model\SalesRule\Calculator */ public function processFreeShipping(\Magento\Quote\Model\Quote\Item\AbstractItem $item) { $address = $item->getAddress(); $item->setFreeShipping(false); foreach ($this->_getRules($address) as $rule) { /* @var $rule \Magento\SalesRule\Model\Rule */ if (!$this->validatorUtility->canProcessRule($rule, $address)) { continue; } if (!$rule->getActions()->validate($item)) { continue; } switch ($rule->getSimpleFreeShipping()) { case Rule::FREE_SHIPPING_ITEM: $item->setFreeShipping($rule->getDiscountQty() ? $rule->getDiscountQty() : true); break; case Rule::FREE_SHIPPING_ADDRESS: $address->setFreeShipping(true); break; } if ($rule->getStopRulesProcessing()) { break; } } return $this; }
/** * @param \Magento\SalesRule\Model\Rule $rule * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param float $qty * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data */ public function calculate($rule, $item, $qty) { /** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */ $discountData = $this->discountFactory->create(); $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $item->getQuote()->getStore()); $discountData->setAmount($qty * $quoteAmount); $discountData->setBaseAmount($qty * $rule->getDiscountAmount()); return $discountData; }
/** * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param bool $isFreeShipping * @return void */ protected function applyToChildren(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $isFreeShipping) { if ($item->getHasChildren() && $item->isChildrenCalculated()) { foreach ($item->getChildren() as $child) { $this->calculator->processFreeShipping($child); if ($isFreeShipping) { $child->setFreeShipping($isFreeShipping); } } } }
/** * Adds item ID to giftOptionsCartItem configuration and name * * @param array $jsLayout * @param AbstractItem $item * @return array */ public function process($jsLayout, AbstractItem $item) { if (isset($jsLayout['components']['giftOptionsCartItem'])) { if (!isset($jsLayout['components']['giftOptionsCartItem']['config'])) { $jsLayout['components']['giftOptionsCartItem']['config'] = []; } $jsLayout['components']['giftOptionsCartItem']['config']['itemId'] = $item->getId(); $jsLayout['components']['giftOptionsCartItem-' . $item->getId()] = $jsLayout['components']['giftOptionsCartItem']; unset($jsLayout['components']['giftOptionsCartItem']); } return $jsLayout; }
protected function setUp() { $this->rule = $this->getMock('Magento\\Framework\\DataObject', null, [], 'Rule', true); $this->item = $this->getMock('Magento\\Quote\\Model\\Quote\\Item\\AbstractItem', [], [], '', false); $this->data = $this->getMock('Magento\\SalesRule\\Model\\Rule\\Action\\Discount\\Data', null); $this->quote = $this->getMock('Magento\\Quote\\Model\\Quote', [], [], '', false); $this->address = $this->getMock('Magento\\Quote\\Model\\Quote\\Address', ['getCartFixedRules', 'setCartFixedRules', '__wakeup'], [], '', false); $this->item->expects($this->any())->method('getQuote')->will($this->returnValue($this->quote)); $this->item->expects($this->any())->method('getAddress')->will($this->returnValue($this->address)); $this->validator = $this->getMock('Magento\\SalesRule\\Model\\Validator', [], [], '', false); $dataFactory = $this->getMock('Magento\\SalesRule\\Model\\Rule\\Action\\Discount\\DataFactory', ['create'], [], '', false); $dataFactory->expects($this->any())->method('create')->will($this->returnValue($this->data)); $this->priceCurrency = $this->getMockBuilder('Magento\\Framework\\Pricing\\PriceCurrencyInterface')->getMock(); $this->model = new \Magento\SalesRule\Model\Rule\Action\Discount\CartFixed($this->validator, $dataFactory, $this->priceCurrency); }
/** * @param \Magento\SalesRule\Model\Rule $rule * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param float $qty * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data */ public function calculate($rule, $item, $qty) { /** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */ $discountData = $this->discountFactory->create(); $ruleTotals = $this->validator->getRuleItemTotalsInfo($rule->getId()); $quote = $item->getQuote(); $address = $item->getAddress(); $itemPrice = $this->validator->getItemPrice($item); $baseItemPrice = $this->validator->getItemBasePrice($item); $itemOriginalPrice = $this->validator->getItemOriginalPrice($item); $baseItemOriginalPrice = $this->validator->getItemBaseOriginalPrice($item); /** * prevent applying whole cart discount for every shipping order, but only for first order */ if ($quote->getIsMultiShipping()) { $usedForAddressId = $this->getCartFixedRuleUsedForAddress($rule->getId()); if ($usedForAddressId && $usedForAddressId != $address->getId()) { return $discountData; } else { $this->setCartFixedRuleUsedForAddress($rule->getId(), $address->getId()); } } $cartRules = $address->getCartFixedRules(); if (!isset($cartRules[$rule->getId()])) { $cartRules[$rule->getId()] = $rule->getDiscountAmount(); } if ($cartRules[$rule->getId()] > 0) { $store = $quote->getStore(); if ($ruleTotals['items_count'] <= 1) { $quoteAmount = $this->priceCurrency->convert($cartRules[$rule->getId()], $store); $baseDiscountAmount = min($baseItemPrice * $qty, $cartRules[$rule->getId()]); } else { $discountRate = $baseItemPrice * $qty / $ruleTotals['base_items_price']; $maximumItemDiscount = $rule->getDiscountAmount() * $discountRate; $quoteAmount = $this->priceCurrency->convert($maximumItemDiscount, $store); $baseDiscountAmount = min($baseItemPrice * $qty, $maximumItemDiscount); $this->validator->decrementRuleItemTotalsCount($rule->getId()); } $baseDiscountAmount = $this->priceCurrency->round($baseDiscountAmount); $cartRules[$rule->getId()] -= $baseDiscountAmount; $discountData->setAmount($this->priceCurrency->round(min($itemPrice * $qty, $quoteAmount))); $discountData->setBaseAmount($baseDiscountAmount); $discountData->setOriginalAmount(min($itemOriginalPrice * $qty, $quoteAmount)); $discountData->setBaseOriginalAmount($this->priceCurrency->round($baseItemOriginalPrice)); } $address->setCartFixedRules($cartRules); return $discountData; }
/** * @param \Magento\SalesRule\Model\Rule $rule * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param float $qty * @return \Magento\SalesRule\Model\Rule\Action\Discount\Data */ public function calculate($rule, $item, $qty) { /** @var \Magento\SalesRule\Model\Rule\Action\Discount\Data $discountData */ $discountData = $this->discountFactory->create(); $store = $item->getQuote()->getStore(); $itemPrice = $this->validator->getItemPrice($item); $baseItemPrice = $this->validator->getItemBasePrice($item); $itemOriginalPrice = $this->validator->getItemOriginalPrice($item); $baseItemOriginalPrice = $this->validator->getItemBaseOriginalPrice($item); $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $store); $discountData->setAmount($qty * ($itemPrice - $quoteAmount)); $discountData->setBaseAmount($qty * ($baseItemPrice - $rule->getDiscountAmount())); $discountData->setOriginalAmount($qty * ($itemOriginalPrice - $quoteAmount)); $discountData->setBaseOriginalAmount($qty * ($baseItemOriginalPrice - $rule->getDiscountAmount())); return $discountData; }
/** * Return item base price * * @param AbstractItem $item * @return float */ public function getItemBasePrice($item) { $price = $item->getDiscountCalculationPrice(); return $price !== null ? $item->getBaseDiscountCalculationPrice() : $item->getBaseCalculationPrice(); }
/** * Get post parameters for delete from cart * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return string */ public function getDeletePostJson($item) { $url = $this->_getUrl(self::DELETE_URL); $data = ['id' => $item->getId()]; if (!$this->_request->isAjax()) { $data[\Magento\Framework\App\Action\Action::PARAM_NAME_URL_ENCODED] = $this->getCurrentBase64Url(); } return json_encode(['action' => $url, 'data' => $data]); }
/** * Sets applied weee taxes * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param array $value * @return $this */ public function setApplied($item, $value) { $item->setWeeeTaxApplied(serialize($value)); return $this; }
/** * @return $this|\Magento\Quote\Model\Quote\Item\AbstractItem */ public function beforeSave() { parent::beforeSave(); if ($this->getAddress()) { $this->setQuoteAddressId($this->getAddress()->getId()); } return $this; }
/** * Whether the item row total may be compounded with others * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return bool * @SuppressWarnings(PHPMD.BooleanGetMethodName) */ public function getIsItemRowTotalCompoundable(\Magento\Quote\Model\Quote\Item\AbstractItem $item) { if ($item->getData("skip_compound_{$this->_itemRowTotalKey}")) { return false; } return true; }
/** * Recalculate parent item amounts based on children results * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return void * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function recalculateParent(\Magento\Quote\Model\Quote\Item\AbstractItem $item) { $associatedTaxables = []; foreach ($item->getChildren() as $child) { $associatedTaxables = array_merge($associatedTaxables, $child->getAssociatedTaxables()); } $item->setAssociatedTaxables($associatedTaxables); }
/** * Compare quote items and ensure fields match * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $nativeItem * @param \Magento\Quote\Model\Quote\Item\AbstractItem $avaTaxItem * @return $this */ protected function compareItems(\Magento\Quote\Model\Quote\Item\AbstractItem $nativeItem, \Magento\Quote\Model\Quote\Item\AbstractItem $avaTaxItem) { foreach ($this->quoteItemFieldsEnsureMatch as $value) { $this->assertEquals($nativeItem->getData($value), $avaTaxItem->getData($value), 'native/AvaTax calculation does not match for quote item field: ' . $value); } return $this; }
/** * Sets applied weee taxes * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param array $value * @return $this */ public function setApplied($item, $value) { $item->setWeeeTaxApplied(\Zend_Json::encode($value)); return $this; }
/** * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param int[] $appliedRuleIds * @return $this */ public function setAppliedRuleIds(\Magento\Quote\Model\Quote\Item\AbstractItem $item, array $appliedRuleIds) { $address = $item->getAddress(); $quote = $item->getQuote(); $item->setAppliedRuleIds(join(',', $appliedRuleIds)); $address->setAppliedRuleIds($this->validatorUtility->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds)); $quote->setAppliedRuleIds($this->validatorUtility->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds)); return $this; }
/** * Set item for render * * @param QuoteItem|OrderItem|InvoiceItem|CreditMemoItem $item * @return $this */ public function setItem($item) { $this->item = $item; $this->storeId = $item->getStoreId(); return $this; }
/** * Update tax related fields for quote item * * @param AbstractItem $quoteItem * @param TaxDetailsItemInterface $itemTaxDetails * @param TaxDetailsItemInterface $baseItemTaxDetails * @param Store $store * @return $this */ public function updateItemTaxInfo($quoteItem, $itemTaxDetails, $baseItemTaxDetails, $store) { //The price should be base price $quoteItem->setPrice($baseItemTaxDetails->getPrice()); $quoteItem->setConvertedPrice($itemTaxDetails->getPrice()); $quoteItem->setPriceInclTax($itemTaxDetails->getPriceInclTax()); $quoteItem->setRowTotal($itemTaxDetails->getRowTotal()); $quoteItem->setRowTotalInclTax($itemTaxDetails->getRowTotalInclTax()); $quoteItem->setTaxAmount($itemTaxDetails->getRowTax()); $quoteItem->setTaxPercent($itemTaxDetails->getTaxPercent()); $quoteItem->setDiscountTaxCompensationAmount($itemTaxDetails->getDiscountTaxCompensationAmount()); $quoteItem->setBasePrice($baseItemTaxDetails->getPrice()); $quoteItem->setBasePriceInclTax($baseItemTaxDetails->getPriceInclTax()); $quoteItem->setBaseRowTotal($baseItemTaxDetails->getRowTotal()); $quoteItem->setBaseRowTotalInclTax($baseItemTaxDetails->getRowTotalInclTax()); $quoteItem->setBaseTaxAmount($baseItemTaxDetails->getRowTax()); $quoteItem->setTaxPercent($baseItemTaxDetails->getTaxPercent()); $quoteItem->setBaseDiscountTaxCompensationAmount($baseItemTaxDetails->getDiscountTaxCompensationAmount()); //Set discount calculation price, this may be needed by discount collector if ($this->_config->discountTax($store)) { $quoteItem->setDiscountCalculationPrice($itemTaxDetails->getPriceInclTax()); $quoteItem->setBaseDiscountCalculationPrice($baseItemTaxDetails->getPriceInclTax()); } else { $quoteItem->setDiscountCalculationPrice($itemTaxDetails->getPrice()); $quoteItem->setBaseDiscountCalculationPrice($baseItemTaxDetails->getPrice()); } return $this; }
/** * @param AbstractItem $item * @return float */ public function getBaseSubtotalInclTax($item) { $tax = $item->getBaseTaxAmount() + $item->getBaseDiscountTaxCompensation(); return $item->getBaseRowTotal() + $tax; }
/** * Distribute discount at parent item to children items * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return $this */ protected function distributeDiscount(\Magento\Quote\Model\Quote\Item\AbstractItem $item) { $parentBaseRowTotal = $item->getBaseRowTotal(); $keys = ['discount_amount', 'base_discount_amount', 'original_discount_amount', 'base_original_discount_amount']; $roundingDelta = []; foreach ($keys as $key) { //Initialize the rounding delta to a tiny number to avoid floating point precision problem $roundingDelta[$key] = 1.0E-7; } foreach ($item->getChildren() as $child) { $ratio = $child->getBaseRowTotal() / $parentBaseRowTotal; foreach ($keys as $key) { if (!$item->hasData($key)) { continue; } $value = $item->getData($key) * $ratio; $roundedValue = $this->priceCurrency->round($value + $roundingDelta[$key]); $roundingDelta[$key] += $value - $roundedValue; $child->setData($key, $roundedValue); } } foreach ($keys as $key) { $item->setData($key, 0); } return $this; }
/** * Return discount item qty * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @param \Magento\SalesRule\Model\Rule $rule * @return int */ public function getItemQty($item, $rule) { $qty = $item->getTotalQty(); $discountQty = $rule->getDiscountQty(); return $discountQty ? min($qty, $discountQty) : $qty; }
/** * Request shipping rates for entire address or specified address item * Returns true if current selected shipping method code corresponds to one of the found rates * * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractItem $item = null) { /** @var $request \Magento\Quote\Model\Quote\Address\RateRequest */ $request = $this->_rateRequestFactory->create(); $request->setAllItems($item ? [$item] : $this->getAllItems()); $request->setDestCountryId($this->getCountryId()); $request->setDestRegionId($this->getRegionId()); $request->setDestRegionCode($this->getRegionCode()); $request->setDestStreet($this->getStreetFull()); $request->setDestCity($this->getCity()); $request->setDestPostcode($this->getPostcode()); $request->setPackageValue($item ? $item->getBaseRowTotal() : $this->getBaseSubtotal()); $packageWithDiscount = $item ? $item->getBaseRowTotal() - $item->getBaseDiscountAmount() : $this->getBaseSubtotalWithDiscount(); $request->setPackageValueWithDiscount($packageWithDiscount); $request->setPackageWeight($item ? $item->getRowWeight() : $this->getWeight()); $request->setPackageQty($item ? $item->getQty() : $this->getItemQty()); /** * Need for shipping methods that use insurance based on price of physical products */ $packagePhysicalValue = $item ? $item->getBaseRowTotal() : $this->getBaseSubtotal() - $this->getBaseVirtualAmount(); $request->setPackagePhysicalValue($packagePhysicalValue); $request->setFreeMethodWeight($item ? 0 : $this->getFreeMethodWeight()); /** * Store and website identifiers need specify from quote */ /*$request->setStoreId($this->_storeManager->getStore()->getId()); $request->setWebsiteId($this->_storeManager->getStore()->getWebsiteId());*/ $request->setStoreId($this->getQuote()->getStore()->getId()); $request->setWebsiteId($this->getQuote()->getStore()->getWebsiteId()); $request->setFreeShipping($this->getFreeShipping()); /** * Currencies need to convert in free shipping */ $request->setBaseCurrency($this->getQuote()->getStore()->getBaseCurrency()); $request->setPackageCurrency($this->getQuote()->getStore()->getCurrentCurrency()); $request->setLimitCarrier($this->getLimitCarrier()); $request->setBaseSubtotalInclTax($this->getBaseSubtotalInclTax()); $result = $this->_rateCollector->create()->collectRates($request)->getResult(); $found = false; if ($result) { $shippingRates = $result->getAllRates(); foreach ($shippingRates as $shippingRate) { $rate = $this->_addressRateFactory->create()->importShippingRate($shippingRate); if (!$item) { $this->addShippingRate($rate); } if ($this->getShippingMethod() == $rate->getCode()) { if ($item) { $item->setBaseShippingAmount($rate->getPrice()); } else { /** * possible bug: this should be setBaseShippingAmount(), * see \Magento\Quote\Model\Quote\Address\Total\Shipping::collect() * where this value is set again from the current specified rate price * (looks like a workaround for this bug) */ $this->setShippingAmount($rate->getPrice()); } $found = true; } } } return $found; }
/** * Calculate base total amount for the item * * @param QuoteItem|Item|InvoiceItem|CreditmemoItem $item * @return mixed */ public function getBaseTotalAmount($item) { $baseTotalAmount = $item->getBaseRowTotal() - $item->getBaseDiscountAmount(); return $baseTotalAmount; }
/** * Clone quote item * * @return $this */ public function __clone() { parent::__clone(); $options = $this->getOptions(); $this->_quote = null; $this->_options = []; $this->_optionsByCode = []; foreach ($options as $option) { $this->addOption(clone $option); } return $this; }
/** * Calculate item fixed tax and prepare information for discount and regular taxation * * @param \Magento\Quote\Model\Quote\Address $address * @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, $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->getQty(), 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($address, $totalRowValueExclTax, $baseTotalRowValueExclTax, $totalRowValueInclTax, $baseTotalRowValueInclTax); $this->weeeData->setApplied($item, array_merge($this->weeeData->getApplied($item), $productTaxes)); }