Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Price.php
Go to the documentation of this file.
1 <?php
7 
11 use Magento\Catalog\Api\Data\ProductTierPriceExtensionFactory;
12 
18 class Price extends \Magento\Catalog\Model\Product\Type\Price
19 {
23  const PRICE_TYPE_FIXED = 1;
24 
28  const PRICE_TYPE_DYNAMIC = 0;
29 
36 
42  protected $_catalogData = null;
43 
49  private $serializer;
50 
68  public function __construct(
69  \Magento\CatalogRule\Model\ResourceModel\RuleFactory $ruleFactory,
71  \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
72  \Magento\Customer\Model\Session $customerSession,
73  \Magento\Framework\Event\ManagerInterface $eventManager,
75  GroupManagementInterface $groupManagement,
76  \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory,
77  \Magento\Framework\App\Config\ScopeConfigInterface $config,
78  \Magento\Catalog\Helper\Data $catalogData,
79  \Magento\Framework\Serialize\Serializer\Json $serializer = null,
80  ProductTierPriceExtensionFactory $tierPriceExtensionFactory = null
81  ) {
82  $this->_catalogData = $catalogData;
83  $this->serializer = $serializer ?: ObjectManager::getInstance()
84  ->get(\Magento\Framework\Serialize\Serializer\Json::class);
85  parent::__construct(
86  $ruleFactory,
88  $localeDate,
89  $customerSession,
90  $eventManager,
92  $groupManagement,
94  $config,
95  $tierPriceExtensionFactory
96  );
97  }
98 
106  {
108  }
109 
116  public function getPrice($product)
117  {
118  if ($product->getPriceType() == self::PRICE_TYPE_FIXED) {
119  return $product->getData('price');
120  } else {
121  return 0;
122  }
123  }
124 
132  public function getTotalBundleItemsPrice($product, $qty = null)
133  {
134  $price = 0.0;
135  if ($product->hasCustomOptions()) {
136  $selectionIds = $this->getBundleSelectionIds($product);
137  if ($selectionIds) {
138  $selections = $product->getTypeInstance()->getSelectionsByIds($selectionIds, $product);
139  $selections->addTierPriceData();
140  $this->_eventManager->dispatch(
141  'prepare_catalog_product_collection_prices',
142  ['collection' => $selections, 'store_id' => $product->getStoreId()]
143  );
144  foreach ($selections->getItems() as $selection) {
145  if ($selection->isSalable()) {
146  $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId());
147  if ($selectionQty) {
149  $product,
150  $selection,
151  $qty,
152  $selectionQty->getValue()
153  );
154  }
155  }
156  }
157  }
158  }
159  return $price;
160  }
161 
168  protected function getBundleSelectionIds(\Magento\Catalog\Model\Product $product)
169  {
170  $customOption = $product->getCustomOption('bundle_selection_ids');
171  if ($customOption) {
172  $selectionIds = $this->serializer->unserialize($customOption->getValue());
173  if (is_array($selectionIds) && !empty($selectionIds)) {
174  return $selectionIds;
175  }
176  }
177  return [];
178  }
179 
187  public function getFinalPrice($qty, $product)
188  {
189  if ($qty === null && $product->getCalculatedFinalPrice() !== null) {
190  return $product->getCalculatedFinalPrice();
191  }
192 
193  $finalPrice = $this->getBasePrice($product, $qty);
194  $product->setFinalPrice($finalPrice);
195  $this->_eventManager->dispatch('catalog_product_get_final_price', ['product' => $product, 'qty' => $qty]);
196  $finalPrice = $product->getData('final_price');
197 
198  $finalPrice = $this->_applyOptionsPrice($product, $qty, $finalPrice);
199  $finalPrice += $this->getTotalBundleItemsPrice($product, $qty);
200 
201  $finalPrice = max(0, $finalPrice);
202  $product->setFinalPrice($finalPrice);
203  return $finalPrice;
204  }
205 
215  public function getChildFinalPrice($product, $productQty, $childProduct, $childProductQty)
216  {
217  return $this->getSelectionFinalTotalPrice($product, $childProduct, $productQty, $childProductQty, false);
218  }
219 
232  public function getTotalPrices($product, $which = null, $includeTax = null, $takeTierPrice = true)
233  {
234  // check calculated price index
235  if ($product->getData('min_price') && $product->getData('max_price')) {
236  $minimalPrice = $this->_catalogData->getTaxPrice($product, $product->getData('min_price'), $includeTax);
237  $maximalPrice = $this->_catalogData->getTaxPrice($product, $product->getData('max_price'), $includeTax);
238  $this->_isPricesCalculatedByIndex = true;
239  } else {
243  $finalPrice = $product->getFinalPrice();
244  if ($product->getPriceType() == self::PRICE_TYPE_FIXED) {
245  $minimalPrice = $maximalPrice = $this->_catalogData->getTaxPrice($product, $finalPrice, $includeTax);
246  } else {
247  // PRICE_TYPE_DYNAMIC
249  }
250 
251  $options = $this->getOptions($product);
252  $minPriceFounded = false;
253 
254  if ($options) {
255  foreach ($options as $option) {
256  /* @var $option \Magento\Bundle\Model\Option */
257  $selections = $option->getSelections();
258  if ($selections) {
259  $selectionMinimalPrices = [];
260  $selectionMaximalPrices = [];
261 
262  foreach ($option->getSelections() as $selection) {
263  /* @var $selection \Magento\Bundle\Model\Selection */
264  if (!$selection->isSalable()) {
268  continue;
269  }
270 
271  $qty = $selection->getSelectionQty();
272 
273  $item = $product->getPriceType() == self::PRICE_TYPE_FIXED ? $product : $selection;
274 
275  $selectionMinimalPrices[] = $this->_catalogData->getTaxPrice(
276  $item,
278  $product,
279  $selection,
280  1,
281  $qty,
282  true,
283  $takeTierPrice
284  ),
285  $includeTax
286  );
287  $selectionMaximalPrices[] = $this->_catalogData->getTaxPrice(
288  $item,
290  $product,
291  $selection,
292  1,
293  null,
294  true,
295  $takeTierPrice
296  ),
297  $includeTax
298  );
299  }
300 
301  if (count($selectionMinimalPrices)) {
302  $selMinPrice = min($selectionMinimalPrices);
303  if ($option->getRequired()) {
304  $minimalPrice += $selMinPrice;
305  $minPriceFounded = true;
306  } elseif (true !== $minPriceFounded) {
307  $selMinPrice += $minimalPrice;
308  $minPriceFounded = false === $minPriceFounded ? $selMinPrice : min(
309  $minPriceFounded,
310  $selMinPrice
311  );
312  }
313 
314  if ($option->isMultiSelection()) {
315  $maximalPrice += array_sum($selectionMaximalPrices);
316  } else {
317  $maximalPrice += max($selectionMaximalPrices);
318  }
319  }
320  }
321  }
322  }
323  // condition is TRUE when all product options are NOT required
324  if (!is_bool($minPriceFounded)) {
325  $minimalPrice = $minPriceFounded;
326  }
327 
328  $customOptions = $product->getOptions();
329  if ($product->getPriceType() == self::PRICE_TYPE_FIXED && $customOptions) {
330  foreach ($customOptions as $customOption) {
331  /* @var $customOption \Magento\Catalog\Model\Product\Option */
332  $values = $customOption->getValues();
333  if ($values) {
334  $prices = [];
335  foreach ($values as $value) {
336  /* @var $value \Magento\Catalog\Model\Product\Option\Value */
337  $valuePrice = $value->getPrice(true);
338 
339  $prices[] = $valuePrice;
340  }
341  if (count($prices)) {
342  if ($customOption->getIsRequire()) {
343  $minimalPrice += $this->_catalogData->getTaxPrice($product, min($prices), $includeTax);
344  }
345 
346  $multiTypes = [
349  ];
350 
351  if (in_array($customOption->getType(), $multiTypes)) {
352  $maximalValue = array_sum($prices);
353  } else {
354  $maximalValue = max($prices);
355  }
356  $maximalPrice += $this->_catalogData->getTaxPrice($product, $maximalValue, $includeTax);
357  }
358  } else {
359  $valuePrice = $customOption->getPrice(true);
360 
361  if ($customOption->getIsRequire()) {
362  $minimalPrice += $this->_catalogData->getTaxPrice($product, $valuePrice, $includeTax);
363  }
364  $maximalPrice += $this->_catalogData->getTaxPrice($product, $valuePrice, $includeTax);
365  }
366  }
367  }
368  $this->_isPricesCalculatedByIndex = false;
369  }
370 
371  if ($which == 'max') {
372  return $maximalPrice;
373  } elseif ($which == 'min') {
374  return $minimalPrice;
375  }
376 
377  return [$minimalPrice, $maximalPrice];
378  }
379 
386  public function getOptions($product)
387  {
388  $product->getTypeInstance()->setStoreFilter($product->getStoreId(), $product);
389 
390  $optionCollection = $product->getTypeInstance()->getOptionsCollection($product);
391 
392  $selectionCollection = $product->getTypeInstance()->getSelectionsCollection(
393  $product->getTypeInstance()->getOptionsIds($product),
394  $product
395  );
396 
397  return $optionCollection->appendSelections($selectionCollection, false, false);
398  }
399 
411  public function getSelectionPrice($bundleProduct, $selectionProduct, $selectionQty = null, $multiplyQty = true)
412  {
413  return $this->getSelectionFinalTotalPrice($bundleProduct, $selectionProduct, 0, $selectionQty, $multiplyQty);
414  }
415 
424  public function getSelectionPreFinalPrice($bundleProduct, $selectionProduct, $qty = null)
425  {
426  return $this->getSelectionPrice($bundleProduct, $selectionProduct, $qty);
427  }
428 
443  $selectionProduct,
444  $bundleQty,
445  $selectionQty,
446  $multiplyQty = true,
447  $takeTierPrice = true
448  ) {
449  if (null === $bundleQty) {
450  $bundleQty = 1.;
451  }
452  if ($selectionQty === null) {
453  $selectionQty = $selectionProduct->getSelectionQty();
454  }
455 
456  if ($bundleProduct->getPriceType() == self::PRICE_TYPE_DYNAMIC) {
457  $price = $selectionProduct->getFinalPrice($takeTierPrice ? $selectionQty : 1);
458  } else {
459  if ($selectionProduct->getSelectionPriceType()) {
460  // percent
461  $product = clone $bundleProduct;
462  $product->setFinalPrice($this->getPrice($product));
463  $this->_eventManager->dispatch(
464  'catalog_product_get_final_price',
465  ['product' => $product, 'qty' => $bundleQty]
466  );
467  $price = $product->getData('final_price') * ($selectionProduct->getSelectionPriceValue() / 100);
468  } else {
469  // fixed
470  $price = $selectionProduct->getSelectionPriceValue();
471  }
472  }
473 
474  if ($multiplyQty) {
475  $price *= $selectionQty;
476  }
477 
478  return min(
479  $price,
480  $this->_applyTierPrice($bundleProduct, $bundleQty, $price),
482  );
483  }
484 
493  protected function _applyTierPrice($product, $qty, $finalPrice)
494  {
495  if ($qty === null) {
496  return $finalPrice;
497  }
498 
499  $tierPrice = $product->getTierPrice($qty);
500 
501  if (is_numeric($tierPrice)) {
502  $tierPrice = $finalPrice - $finalPrice * ($tierPrice / 100);
503  $finalPrice = min($finalPrice, $tierPrice);
504  }
505 
506  return $finalPrice;
507  }
508 
518  public function getTierPrice($qty, $product)
519  {
520  $allCustomersGroupId = $this->_groupManagement->getAllCustomersGroup()->getId();
521  $prices = $product->getData('tier_price');
522 
523  if ($prices === null) {
524  if ($attribute = $product->getResource()->getAttribute('tier_price')) {
525  $attribute->getBackend()->afterLoad($product);
526  $prices = $product->getData('tier_price');
527  }
528  }
529 
530  if ($prices === null || !is_array($prices)) {
531  if ($qty !== null) {
532  return $product->getPrice();
533  }
534  return [
535  [
536  'price' => $product->getPrice(),
537  'website_price' => $product->getPrice(),
538  'price_qty' => 1,
539  'cust_group' => $allCustomersGroupId,
540  ]
541  ];
542  }
543 
544  $custGroup = $this->_getCustomerGroupId($product);
545  if ($qty) {
546  $prevQty = 1;
547  $prevPrice = 0;
548  $prevGroup = $allCustomersGroupId;
549 
550  foreach ($prices as $price) {
551  if (empty($price['percentage_value'])) {
552  // can use only percentage tier price
553  continue;
554  }
555  if ($price['cust_group'] != $custGroup && $price['cust_group'] != $allCustomersGroupId) {
556  // tier not for current customer group nor is for all groups
557  continue;
558  }
559  if ($qty < $price['price_qty']) {
560  // tier is higher than product qty
561  continue;
562  }
563  if ($price['price_qty'] < $prevQty) {
564  // higher tier qty already found
565  continue;
566  }
567  if ($price['price_qty'] == $prevQty
568  && $prevGroup != $allCustomersGroupId
569  && $price['cust_group'] == $allCustomersGroupId
570  ) {
571  // found tier qty is same as current tier qty but current tier group is ALL_GROUPS
572  continue;
573  }
574 
575  if ($price['percentage_value'] > $prevPrice) {
576  $prevPrice = $price['percentage_value'];
577  $prevQty = $price['price_qty'];
578  $prevGroup = $price['cust_group'];
579  }
580  }
581 
582  return $prevPrice;
583  } else {
584  $qtyCache = [];
585  foreach ($prices as $i => $price) {
586  if ($price['cust_group'] != $custGroup && $price['cust_group'] != $allCustomersGroupId) {
587  unset($prices[$i]);
588  } elseif (isset($qtyCache[$price['price_qty']])) {
589  $j = $qtyCache[$price['price_qty']];
590  if ($prices[$j]['website_price'] < $price['website_price']) {
591  unset($prices[$j]);
592  $qtyCache[$price['price_qty']] = $i;
593  } else {
594  unset($prices[$i]);
595  }
596  } else {
597  $qtyCache[$price['price_qty']] = $i;
598  }
599  }
600  }
601 
602  return $prices ? $prices : [];
603  }
604 
615  public function calculateSpecialPrice(
616  $finalPrice,
617  $specialPrice,
618  $specialPriceFrom,
619  $specialPriceTo,
620  $store = null
621  ) {
622  if ($specialPrice !== null && $specialPrice != false) {
623  if ($this->_localeDate->isScopeDateInInterval($store, $specialPriceFrom, $specialPriceTo)) {
624  $specialPrice = $finalPrice * ($specialPrice / 100);
625  $finalPrice = min($finalPrice, $specialPrice);
626  }
627  }
628 
629  return $finalPrice;
630  }
631 
640  public function getLowestPrice($bundleProduct, $price, $bundleQty = 1)
641  {
642  $price = (float)$price;
643  return min(
644  $price,
645  $this->_applyTierPrice($bundleProduct, $bundleQty, $price),
647  );
648  }
649 }
_applyOptionsPrice($product, $qty, $finalPrice)
Definition: Price.php:548
$customOption
Definition: products.php:73
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
calculateSpecialPrice( $finalPrice, $specialPrice, $specialPriceFrom, $specialPriceTo, $store=null)
Definition: Price.php:615
getSelectionFinalTotalPrice( $bundleProduct, $selectionProduct, $bundleQty, $selectionQty, $multiplyQty=true, $takeTierPrice=true)
Definition: Price.php:441
getSelectionPrice($bundleProduct, $selectionProduct, $selectionQty=null, $multiplyQty=true)
Definition: Price.php:411
getBasePrice($product, $qty=null)
Definition: Price.php:151
getLowestPrice($bundleProduct, $price, $bundleQty=1)
Definition: Price.php:640
$maximalPrice
$storeManager
$values
Definition: options.phtml:88
$price
getBundleSelectionIds(\Magento\Catalog\Model\Product $product)
Definition: Price.php:168
getSelectionPreFinalPrice($bundleProduct, $selectionProduct, $qty=null)
Definition: Price.php:424
getTotalPrices($product, $which=null, $includeTax=null, $takeTierPrice=true)
Definition: Price.php:232
$value
Definition: gender.phtml:16
getFinalPrice($qty, $product)
Definition: Price.php:187
_applyTierPrice($product, $qty, $finalPrice)
Definition: Price.php:493
__construct(\Magento\CatalogRule\Model\ResourceModel\RuleFactory $ruleFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate, \Magento\Customer\Model\Session $customerSession, \Magento\Framework\Event\ManagerInterface $eventManager, PriceCurrencyInterface $priceCurrency, GroupManagementInterface $groupManagement, \Magento\Catalog\Api\Data\ProductTierPriceInterfaceFactory $tierPriceFactory, \Magento\Framework\App\Config\ScopeConfigInterface $config, \Magento\Catalog\Helper\Data $catalogData, \Magento\Framework\Serialize\Serializer\Json $serializer=null, ProductTierPriceExtensionFactory $tierPriceExtensionFactory=null)
Definition: Price.php:68
$selectionCollection
getTierPrice($qty, $product)
Definition: Price.php:518
getTotalBundleItemsPrice($product, $qty=null)
Definition: Price.php:132
$bundleProduct
$i
Definition: gallery.phtml:31
getChildFinalPrice($product, $productQty, $childProduct, $childProductQty)
Definition: Price.php:215
_applySpecialPrice($product, $finalPrice)
Definition: Price.php:449
$minimalPrice