Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Validator.php
Go to the documentation of this file.
1 <?php
8 
11 
26 {
32  protected $_rules;
33 
40  protected $_isFirstTimeResetRun = true;
41 
47  protected $_rulesItemTotals = [];
48 
54  protected $_skipActionsValidation = false;
55 
61  protected $_catalogData = null;
62 
67 
71  protected $validatorUtility;
72 
76  protected $rulesApplier;
77 
81  protected $priceCurrency;
82 
86  protected $validators;
87 
91  protected $messageManager;
92 
98  protected $counter = 0;
99 
115  public function __construct(
116  \Magento\Framework\Model\Context $context,
117  \Magento\Framework\Registry $registry,
118  \Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory $collectionFactory,
119  \Magento\Catalog\Helper\Data $catalogData,
120  \Magento\SalesRule\Model\Utility $utility,
121  \Magento\SalesRule\Model\RulesApplier $rulesApplier,
122  \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
123  \Magento\SalesRule\Model\Validator\Pool $validators,
124  \Magento\Framework\Message\ManagerInterface $messageManager,
125  \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
126  \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
127  array $data = []
128  ) {
129  $this->_collectionFactory = $collectionFactory;
130  $this->_catalogData = $catalogData;
131  $this->validatorUtility = $utility;
132  $this->rulesApplier = $rulesApplier;
133  $this->priceCurrency = $priceCurrency;
134  $this->validators = $validators;
135  $this->messageManager = $messageManager;
136  parent::__construct($context, $registry, $resource, $resourceCollection, $data);
137  }
138 
149  public function init($websiteId, $customerGroupId, $couponCode)
150  {
151  $this->setWebsiteId($websiteId)->setCustomerGroupId($customerGroupId)->setCouponCode($couponCode);
152 
153  return $this;
154  }
155 
162  protected function _getRules(Address $address = null)
163  {
164  $addressId = $this->getAddressId($address);
165  $key = $this->getWebsiteId() . '_'
166  . $this->getCustomerGroupId() . '_'
167  . $this->getCouponCode() . '_'
168  . $addressId;
169  if (!isset($this->_rules[$key])) {
170  $this->_rules[$key] = $this->_collectionFactory->create()
171  ->setValidationFilter(
172  $this->getWebsiteId(),
173  $this->getCustomerGroupId(),
174  $this->getCouponCode(),
175  null,
176  $address
177  )
178  ->addFieldToFilter('is_active', 1)
179  ->load();
180  }
181  return $this->_rules[$key];
182  }
183 
188  protected function getAddressId(Address $address)
189  {
190  if ($address == null) {
191  return '';
192  }
193  if (!$address->hasData('address_sales_rule_id')) {
194  if ($address->hasData('address_id')) {
195  $address->setData('address_sales_rule_id', $address->getData('address_id'));
196  } else {
197  $type = $address->getAddressType();
198  $tempId = $type . $this->counter++;
199  $address->setData('address_sales_rule_id', $tempId);
200  }
201  }
202  return $address->getData('address_sales_rule_id');
203  }
204 
211  public function setSkipActionsValidation($flag)
212  {
213  $this->_skipActionsValidation = $flag;
214  return $this;
215  }
216 
224  {
225  $address = $item->getAddress();
226  foreach ($this->_getRules($address) as $rule) {
227  if (!$this->validatorUtility->canProcessRule($rule, $address) || !$rule->getActions()->validate($item)) {
228  return false;
229  }
230  }
231 
232  return true;
233  }
234 
241  public function reset(Address $address)
242  {
243  $this->validatorUtility->resetRoundingDeltas();
244  if ($this->_isFirstTimeResetRun) {
245  $address->setAppliedRuleIds('');
246  $address->getQuote()->setAppliedRuleIds('');
247  $this->_isFirstTimeResetRun = false;
248  }
249 
250  return $this;
251  }
252 
259  public function process(AbstractItem $item)
260  {
261  $item->setDiscountAmount(0);
262  $item->setBaseDiscountAmount(0);
263  $item->setDiscountPercent(0);
264 
265  $itemPrice = $this->getItemPrice($item);
266  if ($itemPrice < 0) {
267  return $this;
268  }
269 
270  $appliedRuleIds = $this->rulesApplier->applyRules(
271  $item,
272  $this->_getRules($item->getAddress()),
273  $this->_skipActionsValidation,
274  $this->getCouponCode()
275  );
276  $this->rulesApplier->setAppliedRuleIds($item, $appliedRuleIds);
277 
278  return $this;
279  }
280 
289  {
290  $shippingAmount = $address->getShippingAmountForDiscount();
291  if ($shippingAmount !== null) {
292  $baseShippingAmount = $address->getBaseShippingAmountForDiscount();
293  } else {
294  $shippingAmount = $address->getShippingAmount();
295  $baseShippingAmount = $address->getBaseShippingAmount();
296  }
297  $quote = $address->getQuote();
298  $appliedRuleIds = [];
299  foreach ($this->_getRules($address) as $rule) {
300  /* @var \Magento\SalesRule\Model\Rule $rule */
301  if (!$rule->getApplyToShipping() || !$this->validatorUtility->canProcessRule($rule, $address)) {
302  continue;
303  }
304 
305  $discountAmount = 0;
306  $baseDiscountAmount = 0;
307  $rulePercent = min(100, $rule->getDiscountAmount());
308  switch ($rule->getSimpleAction()) {
309  case \Magento\SalesRule\Model\Rule::TO_PERCENT_ACTION:
310  $rulePercent = max(0, 100 - $rule->getDiscountAmount());
311  // break is intentionally omitted
312  case \Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION:
313  $discountAmount = ($shippingAmount - $address->getShippingDiscountAmount()) * $rulePercent / 100;
314  $baseDiscountAmount = ($baseShippingAmount -
315  $address->getBaseShippingDiscountAmount()) * $rulePercent / 100;
316  $discountPercent = min(100, $address->getShippingDiscountPercent() + $rulePercent);
317  $address->setShippingDiscountPercent($discountPercent);
318  break;
319  case \Magento\SalesRule\Model\Rule::TO_FIXED_ACTION:
320  $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore());
321  $discountAmount = $shippingAmount - $quoteAmount;
322  $baseDiscountAmount = $baseShippingAmount - $rule->getDiscountAmount();
323  break;
324  case \Magento\SalesRule\Model\Rule::BY_FIXED_ACTION:
325  $quoteAmount = $this->priceCurrency->convert($rule->getDiscountAmount(), $quote->getStore());
326  $discountAmount = $quoteAmount;
327  $baseDiscountAmount = $rule->getDiscountAmount();
328  break;
329  case \Magento\SalesRule\Model\Rule::CART_FIXED_ACTION:
330  $cartRules = $address->getCartFixedRules();
331  if (!isset($cartRules[$rule->getId()])) {
332  $cartRules[$rule->getId()] = $rule->getDiscountAmount();
333  }
334  if ($cartRules[$rule->getId()] > 0) {
335  $quoteAmount = $this->priceCurrency->convert($cartRules[$rule->getId()], $quote->getStore());
336  $discountAmount = min($shippingAmount - $address->getShippingDiscountAmount(), $quoteAmount);
337  $baseDiscountAmount = min(
338  $baseShippingAmount - $address->getBaseShippingDiscountAmount(),
339  $cartRules[$rule->getId()]
340  );
341  $cartRules[$rule->getId()] -= $baseDiscountAmount;
342  }
343 
344  $address->setCartFixedRules($cartRules);
345  break;
346  }
347 
348  $discountAmount = min($address->getShippingDiscountAmount() + $discountAmount, $shippingAmount);
349  $baseDiscountAmount = min(
350  $address->getBaseShippingDiscountAmount() + $baseDiscountAmount,
351  $baseShippingAmount
352  );
353  $address->setShippingDiscountAmount($discountAmount);
354  $address->setBaseShippingDiscountAmount($baseDiscountAmount);
355  $appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId();
356 
357  $this->rulesApplier->maintainAddressCouponCode($address, $rule, $this->getCouponCode());
358  $this->rulesApplier->addDiscountDescription($address, $rule);
359  if ($rule->getStopRulesProcessing()) {
360  break;
361  }
362  }
363 
364  $address->setAppliedRuleIds($this->validatorUtility->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds));
365  $quote->setAppliedRuleIds($this->validatorUtility->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds));
366 
367  return $this;
368  }
369 
377  public function initTotals($items, Address $address)
378  {
379  $address->setCartFixedRules([]);
380 
381  if (!$items) {
382  return $this;
383  }
384 
386  foreach ($this->_getRules($address) as $rule) {
387  if (\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION == $rule->getSimpleAction()
388  && $this->validatorUtility->canProcessRule($rule, $address)
389  ) {
390  $ruleTotalItemsPrice = 0;
391  $ruleTotalBaseItemsPrice = 0;
392  $validItemsCount = 0;
393 
394  foreach ($items as $item) {
395  //Skipping child items to avoid double calculations
396  if ($item->getParentItemId()) {
397  continue;
398  }
399  if (!$rule->getActions()->validate($item)) {
400  continue;
401  }
402  if (!$this->canApplyDiscount($item)) {
403  continue;
404  }
405  $qty = $this->validatorUtility->getItemQty($item, $rule);
406  $ruleTotalItemsPrice += $this->getItemPrice($item) * $qty;
407  $ruleTotalBaseItemsPrice += $this->getItemBasePrice($item) * $qty;
408  $validItemsCount++;
409  }
410 
411  $this->_rulesItemTotals[$rule->getId()] = [
412  'items_price' => $ruleTotalItemsPrice,
413  'base_items_price' => $ruleTotalBaseItemsPrice,
414  'items_count' => $validItemsCount,
415  ];
416  }
417  }
418 
419  return $this;
420  }
421 
428  public function getItemPrice($item)
429  {
430  $price = $item->getDiscountCalculationPrice();
431  $calcPrice = $item->getCalculationPrice();
432  return $price === null ? $calcPrice : $price;
433  }
434 
441  public function getItemOriginalPrice($item)
442  {
443  return $this->_catalogData->getTaxPrice($item, $item->getOriginalPrice(), true);
444  }
445 
452  public function getItemBasePrice($item)
453  {
454  $price = $item->getDiscountCalculationPrice();
455  return $price !== null ? $item->getBaseDiscountCalculationPrice() : $item->getBaseCalculationPrice();
456  }
457 
465  {
466  return $this->_catalogData->getTaxPrice($item, $item->getBaseOriginalPrice(), true);
467  }
468 
476  public function prepareDescription($address, $separator = ', ')
477  {
478  $descriptionArray = $address->getDiscountDescriptionArray();
479  if (!$descriptionArray && $address->getQuote()->getItemVirtualQty() > 0) {
480  $descriptionArray = $address->getQuote()->getBillingAddress()->getDiscountDescriptionArray();
481  }
482 
483  $description = $descriptionArray && is_array(
484  $descriptionArray
485  ) ? implode(
486  $separator,
487  array_unique($descriptionArray)
488  ) : '';
489 
490  $address->setDiscountDescription($description);
491  return $this;
492  }
493 
501  public function sortItemsByPriority($items, Address $address = null)
502  {
503  $itemsSorted = [];
505  foreach ($this->_getRules($address) as $rule) {
506  foreach ($items as $itemKey => $itemValue) {
507  if ($rule->getActions()->validate($itemValue)) {
508  unset($items[$itemKey]);
509  $itemsSorted[] = $itemValue;
510  }
511  }
512  }
513 
514  if (!empty($itemsSorted)) {
515  $items = array_merge($itemsSorted, $items);
516  }
517 
518  return $items;
519  }
520 
526  public function getRuleItemTotalsInfo($key)
527  {
528  if (empty($this->_rulesItemTotals[$key])) {
529  throw new \Magento\Framework\Exception\LocalizedException(__('Item totals are not set for the rule.'));
530  }
531 
532  return $this->_rulesItemTotals[$key];
533  }
534 
539  public function decrementRuleItemTotalsCount($key)
540  {
541  $this->_rulesItemTotals[$key]['items_count']--;
542 
543  return $this;
544  }
545 
552  public function canApplyDiscount(AbstractItem $item)
553  {
554  $result = true;
556  foreach ($this->validators->getValidators('discount') as $validator) {
557  $result = $validator->isValid($item);
558  if (!$result) {
559  break;
560  }
561  }
562  return $result;
563  }
564 }
getAddressId(Address $address)
Definition: Validator.php:188
$quote
__()
Definition: __.php:13
$resource
Definition: bulk.php:12
$price
canApplyRules(AbstractItem $item)
Definition: Validator.php:223
$address
Definition: customer.php:38
$type
Definition: item.phtml:13
prepareDescription($address, $separator=', ')
Definition: Validator.php:476
processShippingAmount(Address $address)
Definition: Validator.php:288
init($websiteId, $customerGroupId, $couponCode)
Definition: Validator.php:149
_getRules(Address $address=null)
Definition: Validator.php:162
__construct(\Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, \Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory $collectionFactory, \Magento\Catalog\Helper\Data $catalogData, \Magento\SalesRule\Model\Utility $utility, \Magento\SalesRule\Model\RulesApplier $rulesApplier, \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency, \Magento\SalesRule\Model\Validator\Pool $validators, \Magento\Framework\Message\ManagerInterface $messageManager, \Magento\Framework\Model\ResourceModel\AbstractResource $resource=null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection=null, array $data=[])
Definition: Validator.php:115
process(AbstractItem $item)
Definition: Validator.php:259
$items