CustomOptionPrice.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Catalog\Pricing\Price;
  7. use Magento\Catalog\Model\Product\Option\Value;
  8. use Magento\Catalog\Pricing\Price;
  9. use Magento\Framework\Pricing\Price\AbstractPrice;
  10. use Magento\Framework\Pricing\SaleableInterface;
  11. use Magento\Framework\Pricing\Adjustment\CalculatorInterface;
  12. use Magento\Framework\Pricing\Amount\AmountInterface;
  13. /**
  14. * Class OptionPrice
  15. *
  16. */
  17. class CustomOptionPrice extends AbstractPrice implements CustomOptionPriceInterface
  18. {
  19. /**
  20. * Price model code
  21. */
  22. const PRICE_CODE = 'custom_option_price';
  23. /**
  24. * @var array
  25. */
  26. protected $priceOptions;
  27. /**
  28. * Code of parent adjustment to be skipped from calculation
  29. *
  30. * @var string
  31. */
  32. protected $excludeAdjustment = null;
  33. /**
  34. * @var CustomOptionPriceCalculator
  35. */
  36. private $customOptionPriceCalculator;
  37. /**
  38. * @param SaleableInterface $saleableItem
  39. * @param float $quantity
  40. * @param CalculatorInterface $calculator
  41. * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
  42. * @param array|null $excludeAdjustment
  43. * @param CustomOptionPriceCalculator|null $customOptionPriceCalculator
  44. */
  45. public function __construct(
  46. SaleableInterface $saleableItem,
  47. $quantity,
  48. CalculatorInterface $calculator,
  49. \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
  50. $excludeAdjustment = null,
  51. CustomOptionPriceCalculator $customOptionPriceCalculator = null
  52. ) {
  53. parent::__construct($saleableItem, $quantity, $calculator, $priceCurrency);
  54. $this->excludeAdjustment = $excludeAdjustment;
  55. $this->customOptionPriceCalculator = $customOptionPriceCalculator
  56. ?? \Magento\Framework\App\ObjectManager::getInstance()->get(CustomOptionPriceCalculator::class);
  57. }
  58. /**
  59. * Get minimal and maximal option values.
  60. *
  61. * @param string $priceCode
  62. * @return array
  63. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  64. * @SuppressWarnings(PHPMD.NPathComplexity)
  65. */
  66. public function getValue($priceCode = \Magento\Catalog\Pricing\Price\BasePrice::PRICE_CODE)
  67. {
  68. $optionValues = [];
  69. $options = $this->product->getOptions();
  70. if ($options) {
  71. /** @var $optionItem \Magento\Catalog\Model\Product\Option */
  72. foreach ($options as $optionItem) {
  73. $min = null;
  74. if (!$optionItem->getIsRequire()) {
  75. $min = 0.;
  76. }
  77. $max = 0.;
  78. if ($optionItem->getValues() === null && $optionItem->getPrice() !== null) {
  79. $price = $optionItem->getPrice($optionItem->getPriceType() == Value::TYPE_PERCENT);
  80. if ($min === null) {
  81. $min = $price;
  82. } elseif ($price < $min) {
  83. $min = $price;
  84. }
  85. if ($price > $max) {
  86. $max = $price;
  87. }
  88. } else {
  89. /** @var $optionValue \Magento\Catalog\Model\Product\Option\Value */
  90. foreach ($optionItem->getValues() as $optionValue) {
  91. $price =
  92. $this->customOptionPriceCalculator->getOptionPriceByPriceCode($optionValue, $priceCode);
  93. if ($min === null) {
  94. $min = $price;
  95. } elseif ($price < $min) {
  96. $min = $price;
  97. }
  98. $type = $optionItem->getType();
  99. if ($type == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX ||
  100. $type == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE
  101. ) {
  102. $max += $price;
  103. } elseif ($price > $max) {
  104. $max = $price;
  105. }
  106. }
  107. }
  108. $optionValues[] = [
  109. 'option_id' => $optionItem->getId(),
  110. 'type' => $optionItem->getType(),
  111. 'min' => ($min === null) ? 0. : $min,
  112. 'max' => $max,
  113. ];
  114. }
  115. }
  116. return $optionValues;
  117. }
  118. /**
  119. * @param float $amount
  120. * @param null|bool|string|array $exclude
  121. * @param null|array $context
  122. * @return AmountInterface|bool|float
  123. */
  124. public function getCustomAmount($amount = null, $exclude = null, $context = [])
  125. {
  126. if (null !== $amount) {
  127. $amount = $this->priceCurrency->convertAndRound($amount);
  128. } else {
  129. $amount = $this->getValue();
  130. }
  131. $exclude = $this->excludeAdjustment;
  132. return $this->calculator->getAmount($amount, $this->getProduct(), $exclude, $context);
  133. }
  134. /**
  135. * Return the minimal or maximal price for custom options.
  136. *
  137. * @param bool $getMin
  138. * @param string $priceCode
  139. * @return float
  140. */
  141. public function getCustomOptionRange($getMin, $priceCode = \Magento\Catalog\Pricing\Price\BasePrice::PRICE_CODE)
  142. {
  143. $optionValue = 0.;
  144. $options = $this->getValue($priceCode);
  145. foreach ($options as $option) {
  146. if ($getMin) {
  147. $optionValue += $option['min'];
  148. } else {
  149. $optionValue += $option['max'];
  150. }
  151. }
  152. return $this->priceCurrency->convertAndRound($optionValue);
  153. }
  154. /**
  155. * Return price for select custom options
  156. *
  157. * @return float
  158. */
  159. public function getSelectedOptions()
  160. {
  161. if (null !== $this->value) {
  162. return $this->value;
  163. }
  164. $this->value = false;
  165. $optionIds = $this->product->getCustomOption('option_ids');
  166. if (!$optionIds) {
  167. return $this->value;
  168. }
  169. $this->value = 0.;
  170. if ($optionIds) {
  171. $values = explode(',', $optionIds->getValue());
  172. $values = array_filter($values);
  173. if (!empty($values)) {
  174. $this->value = $this->processOptions($values);
  175. }
  176. }
  177. return $this->value;
  178. }
  179. /**
  180. * Process Product Options
  181. *
  182. * @param array $values
  183. * @return float
  184. */
  185. protected function processOptions(array $values)
  186. {
  187. $value = 0.;
  188. foreach ($values as $optionId) {
  189. $option = $this->product->getOptionById($optionId);
  190. if (!$option) {
  191. continue;
  192. }
  193. $confItemOption = $this->product->getCustomOption('option_' . $option->getId());
  194. $group = $option->groupFactory($option->getType())
  195. ->setOption($option)
  196. ->setConfigurationItemOption($confItemOption);
  197. $value += $group->getOptionPrice($confItemOption->getValue(), $this->value);
  198. }
  199. return $value;
  200. }
  201. /**
  202. * Get Product Options
  203. *
  204. * @return array
  205. */
  206. public function getOptions()
  207. {
  208. if (null !== $this->priceOptions) {
  209. return $this->priceOptions;
  210. }
  211. $this->priceOptions = [];
  212. $options = $this->product->getOptions();
  213. if ($options) {
  214. /** @var $optionItem \Magento\Catalog\Model\Product\Option */
  215. foreach ($options as $optionItem) {
  216. /** @var $optionValue \Magento\Catalog\Model\Product\Option\Value */
  217. foreach ($optionItem->getValues() as $optionValue) {
  218. $price = $optionValue->getPrice($optionValue->getPriceType() == Value::TYPE_PERCENT);
  219. $this->priceOptions[$optionValue->getId()][$price] = [
  220. 'base_amount' => $price,
  221. 'adjustment' => $this->getCustomAmount($price)->getValue(),
  222. ];
  223. }
  224. }
  225. }
  226. return $this->priceOptions;
  227. }
  228. }