Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Calculator.php
Go to the documentation of this file.
1 <?php
8 
18 use Magento\Tax\Helper\Data as TaxHelper;
19 
25 {
29  protected $calculator;
30 
34  protected $amountFactory;
35 
39  protected $selectionFactory;
40 
46  protected $taxHelper;
47 
51  protected $priceCurrency;
52 
56  private $optionAmount = [];
57 
61  private $selectionPriceListProvider;
62 
71  public function __construct(
72  CalculatorBase $calculator,
74  BundleSelectionFactory $bundleSelectionFactory,
75  TaxHelper $taxHelper,
77  SelectionPriceListProviderInterface $selectionPriceListProvider = null
78  ) {
79  $this->calculator = $calculator;
80  $this->amountFactory = $amountFactory;
81  $this->selectionFactory = $bundleSelectionFactory;
82  $this->taxHelper = $taxHelper;
83  $this->priceCurrency = $priceCurrency;
84  $this->selectionPriceListProvider = $selectionPriceListProvider;
85  }
86 
97  public function getAmount($amount, SaleableInterface $saleableItem, $exclude = null, $context = [])
98  {
99  return $this->getOptionsAmount($saleableItem, $exclude, true, $amount);
100  }
101 
110  public function getMinRegularAmount($amount, Product $saleableItem, $exclude = null)
111  {
112  return $this->getOptionsAmount($saleableItem, $exclude, true, $amount, true);
113  }
114 
123  public function getMaxAmount($amount, Product $saleableItem, $exclude = null)
124  {
125  return $this->getOptionsAmount($saleableItem, $exclude, false, $amount);
126  }
127 
136  public function getMaxRegularAmount($amount, Product $saleableItem, $exclude = null)
137  {
138  return $this->getOptionsAmount($saleableItem, $exclude, false, $amount, true);
139  }
140 
151  public function getOptionsAmount(
152  Product $saleableItem,
153  $exclude = null,
154  $searchMin = true,
155  $baseAmount = 0.,
156  $useRegularPrice = false
157  ) {
158  $cacheKey = implode('-', [$saleableItem->getId(), $exclude, $searchMin, $baseAmount, $useRegularPrice]);
159  if (!isset($this->optionAmount[$cacheKey])) {
160  $this->optionAmount[$cacheKey] = $this->calculateBundleAmount(
161  $baseAmount,
162  $saleableItem,
163  $this->getSelectionAmounts($saleableItem, $searchMin, $useRegularPrice),
164  $exclude
165  );
166  }
167 
168  return $this->optionAmount[$cacheKey];
169  }
170 
178  public function getAmountWithoutOption($amount, Product $saleableItem)
179  {
180  return $this->calculateBundleAmount(
181  $amount,
182  $saleableItem,
183  []
184  );
185  }
186 
195  protected function getSelectionAmounts(Product $bundleProduct, $searchMin, $useRegularPrice = false)
196  {
197  return $this->getSelectionPriceListProvider()->getPriceList($bundleProduct, $searchMin, $useRegularPrice);
198  }
199 
204  private function getSelectionPriceListProvider()
205  {
206  if (null === $this->selectionPriceListProvider) {
207  $this->selectionPriceListProvider = \Magento\Framework\App\ObjectManager::getInstance()
208  ->get(SelectionPriceListProviderInterface::class);
209  }
210 
211  return $this->selectionPriceListProvider;
212  }
213 
222  protected function canSkipOption($option, $canSkipRequiredOption)
223  {
224  return !$option->getSelections() || ($canSkipRequiredOption && !$option->getRequired());
225  }
226 
234  protected function hasRequiredOption($bundleProduct)
235  {
236  $options = array_filter(
237  $this->getBundleOptions($bundleProduct),
238  function ($item) {
239  return $item->getRequired();
240  }
241  );
242  return !empty($options);
243  }
244 
252  protected function getBundleOptions(Product $saleableItem)
253  {
255  $bundlePrice = $saleableItem->getPriceInfo()->getPrice(
256  \Magento\Bundle\Pricing\Price\BundleOptionPrice::PRICE_CODE
257  );
258  return $bundlePrice->getOptions();
259  }
260 
270  public function calculateBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude = null)
271  {
272  if ($bundleProduct->getPriceType() == Price::PRICE_TYPE_FIXED) {
273  return $this->calculateFixedBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude);
274  }
275  return $this->calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude);
276  }
277 
287  protected function calculateFixedBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude)
288  {
289  $fullAmount = $basePriceValue;
291  foreach ($selectionPriceList as $selectionPrice) {
292  $fullAmount += ($selectionPrice->getValue() * $selectionPrice->getQuantity());
293  }
294  return $this->calculator->getAmount($fullAmount, $bundleProduct, $exclude);
295  }
296 
307  protected function calculateDynamicBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude)
308  {
309  $fullAmount = 0.;
310  $adjustments = [];
311  $i = 0;
312 
313  $amountList[$i]['amount'] = $this->calculator->getAmount($basePriceValue, $bundleProduct, $exclude);
314  $amountList[$i]['quantity'] = 1;
315 
316  foreach ($selectionPriceList as $selectionPrice) {
317  ++$i;
318  if ($selectionPrice) {
319  $amountList[$i]['amount'] = $selectionPrice->getAmount();
320  // always honor the quantity given
321  $amountList[$i]['quantity'] = $selectionPrice->getQuantity();
322  }
323  }
324 
326  $store = $bundleProduct->getStore();
327  $roundingMethod = $this->taxHelper->getCalculationAlgorithm($store);
328  foreach ($amountList as $amountInfo) {
330  $itemAmount = $amountInfo['amount'];
331  $qty = $amountInfo['quantity'];
332 
333  if ($roundingMethod != TaxCalculationInterface::CALC_TOTAL_BASE) {
334  //We need to round the individual selection first
335  $fullAmount += ($this->priceCurrency->round($itemAmount->getValue()) * $qty);
336  foreach ($itemAmount->getAdjustmentAmounts() as $code => $adjustment) {
337  $adjustment = $this->priceCurrency->round($adjustment) * $qty;
338  $adjustments[$code] = isset($adjustments[$code]) ? $adjustments[$code] + $adjustment : $adjustment;
339  }
340  } else {
341  $fullAmount += ($itemAmount->getValue() * $qty);
342  foreach ($itemAmount->getAdjustmentAmounts() as $code => $adjustment) {
343  $adjustment = $adjustment * $qty;
344  $adjustments[$code] = isset($adjustments[$code]) ? $adjustments[$code] + $adjustment : $adjustment;
345  }
346  }
347  }
348  if (is_array($exclude) == false) {
349  if ($exclude && isset($adjustments[$exclude])) {
350  $fullAmount -= $adjustments[$exclude];
351  unset($adjustments[$exclude]);
352  }
353  } else {
354  foreach ($exclude as $oneExclusion) {
355  if ($oneExclusion && isset($adjustments[$oneExclusion])) {
356  $fullAmount -= $adjustments[$oneExclusion];
357  unset($adjustments[$oneExclusion]);
358  }
359  }
360  }
361  return $this->amountFactory->create($fullAmount, $adjustments);
362  }
363 
372  public function createSelectionPriceList($option, $bundleProduct, $useRegularPrice = false)
373  {
374  $priceList = [];
375  $selections = $option->getSelections();
376  if ($selections === null) {
377  return $priceList;
378  }
379  /* @var $selection \Magento\Bundle\Model\Selection|\Magento\Catalog\Model\Product */
380  foreach ($selections as $selection) {
381  if (!$selection->isSalable()) {
382  // @todo CatalogInventory Show out of stock Products
383  continue;
384  }
385  $priceList[] = $this->selectionFactory->create(
387  $selection,
388  $selection->getSelectionQty(),
389  [
390  'useRegularPrice' => $useRegularPrice,
391  ]
392  );
393  }
394  return $priceList;
395  }
396 
406  public function processOptions($option, $selectionPriceList, $searchMin = true)
407  {
408  $result = [];
409  foreach ($selectionPriceList as $current) {
410  $qty = $current->getQuantity();
411  $currentValue = $current->getAmount()->getValue() * $qty;
412  if (empty($result)) {
413  $result = [$current];
414  } else {
415  $lastSelectionPrice = end($result);
416  $lastValue = $lastSelectionPrice->getAmount()->getValue() * $lastSelectionPrice->getQuantity();
417  if ($searchMin && $lastValue > $currentValue) {
418  $result = [$current];
419  } elseif (!$searchMin && $option->isMultiSelection()) {
420  $result[] = $current;
421  } elseif (!$searchMin
422  && !$option->isMultiSelection()
423  && $lastValue < $currentValue
424  ) {
425  $result = [$current];
426  }
427  }
428  }
429  return $result;
430  }
431 }
getMinRegularAmount($amount, Product $saleableItem, $exclude=null)
Definition: Calculator.php:110
__construct(CalculatorBase $calculator, AmountFactory $amountFactory, BundleSelectionFactory $bundleSelectionFactory, TaxHelper $taxHelper, PriceCurrencyInterface $priceCurrency, SelectionPriceListProviderInterface $selectionPriceListProvider=null)
Definition: Calculator.php:71
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
getSelectionAmounts(Product $bundleProduct, $searchMin, $useRegularPrice=false)
Definition: Calculator.php:195
createSelectionPriceList($option, $bundleProduct, $useRegularPrice=false)
Definition: Calculator.php:372
$baseAmount
Definition: tax.phtml:46
$amount
Definition: order.php:14
getMaxRegularAmount($amount, Product $saleableItem, $exclude=null)
Definition: Calculator.php:136
getMaxAmount($amount, Product $saleableItem, $exclude=null)
Definition: Calculator.php:123
getAmountWithoutOption($amount, Product $saleableItem)
Definition: Calculator.php:178
processOptions($option, $selectionPriceList, $searchMin=true)
Definition: Calculator.php:406
calculateBundleAmount($basePriceValue, $bundleProduct, $selectionPriceList, $exclude=null)
Definition: Calculator.php:270
$bundleProduct
getAmount($amount, SaleableInterface $saleableItem, $exclude=null, $context=[])
Definition: Calculator.php:97
$i
Definition: gallery.phtml:31
canSkipOption($option, $canSkipRequiredOption)
Definition: Calculator.php:222
$code
Definition: info.phtml:12
getOptionsAmount(Product $saleableItem, $exclude=null, $searchMin=true, $baseAmount=0., $useRegularPrice=false)
Definition: Calculator.php:151