Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
discountbase.php
1<?php
8namespace Bitrix\Sale;
9
10use Bitrix\Main,
16
17Loc::loadMessages(__FILE__);
18
19abstract class DiscountBase
20{
21 const EVENT_EXTEND_ORDER_DATA = 'onExtendOrderData';
22
23 const USE_MODE_FULL = 0x00001;
24 const USE_MODE_APPLY = 0x0002;
25 const USE_MODE_MIXED = 0x0004;
26 const USE_MODE_COUPONS = 0x0008;
27
28 const EXECUTE_FIELD_PREFIX = 'EXECUTE_';
29
30 const ERROR_ID = 'BX_SALE_DISCOUNT';
31
32 const APPLY_MODE_ADD = 0x0001;
33 const APPLY_MODE_DISABLE = 0x0002;
34 const APPLY_MODE_LAST = 0x0004;
36 const APPLY_MODE_FULL_LAST = 0x0010;
37
40 const ROUND_MODE_FINAL_PRICE = 0x0004;
41
42 const ENTITY_BASKET_ITEM = 'BASKET_ITEM';
43 const ENTITY_DELIVERY = 'DELIVERY';
44 const ENTITY_ORDER = 'ORDER';
45
46 /* Instances */
48 private static $instances = array();
49 /* Instances end */
50
51 /* System variables */
53 protected $isClone = false;
55 protected $orderRefresh = false;
57 protected $newOrder = null;
59 protected $useMode = null;
61 protected $context;
63 protected $order = null;
64 /* System variables end */
65
66 /* Calculate variables */
68 protected $executeModuleFilter = array('all', 'sale', 'catalog');
70 protected $loadedModules = array();
71 /* Calculate variables end */
72
73 /* Sale discount hit cache */
75 protected $discountIds = null;
77 protected $saleDiscountCache = array();
79 protected $saleDiscountCacheKey = '';
80 /* Sale discount hit cache end */
81
82 /* Sale objects */
84 protected $basket = null;
85
86 /* Calculate data */
88 protected $orderData = null;
89
90 /* Calculate options */
92 protected $valid = true;
94 protected $saleOptions = array();
95
96 /* Product discounts for basket items */
98 protected $basketDiscountList = array();
99 /* Various basket items data */
101 protected $basketItemsData = array();
102
103 /* Order discounts and coupons, converted to unified format */
105 protected $discountsCache = array();
107 protected $couponsCache = array();
108
109 /* Calculation results and applyed flags */
111 protected $discountResult = array();
115 protected $applyResult = array();
116
117 /* Contains additional data used to calculate discounts for an existing order */
118 protected $discountStoredActionData = array();
119
121 protected $entityList = array();
123 protected $entityResultCache = array();
125 protected $currentStep = array();
126
128 protected $forwardBasketTable = array();
130 protected $reverseBasketTable = array();
131
132 /* Round mode and data */
134 protected $roundApplyConfig = array();
135
141 protected $fullDiscountList = array();
142
143 protected function __construct()
144 {
145
146 }
147
148 public function __destruct()
149 {
150
151 }
152
153 /* system methods */
154
163 public function createClone(\SplObjectStorage $cloneEntity)
164 {
165 if ($this->isClone() && $cloneEntity->contains($this))
166 return $cloneEntity[$this];
167
168 $discountClone = clone $this;
169 $discountClone->isClone = true;
170
171 if (!$cloneEntity->contains($this))
172 $cloneEntity[$this] = $discountClone;
173
174 if ($this->isOrderExists())
175 {
176 if ($cloneEntity->contains($this->order))
177 $discountClone->order = $cloneEntity[$this->order];
178 }
179 elseif ($this->isBasketExist())
180 {
181 if ($cloneEntity->contains($this->basket))
182 $discountClone->basket = $cloneEntity[$this->basket];
183 }
184
185 return $discountClone;
186 }
187
193 public function isClone()
194 {
195 return $this->isClone;
196 }
197
204 public function setOrderRefresh($state)
205 {
206 if ($state !== true && $state !== false)
207 return;
208 $this->orderRefresh = $state;
209 }
210
216 public function isOrderRefresh()
217 {
218 return $this->orderRefresh;
219 }
220
226 public function isOrderNew()
227 {
228 return $this->newOrder;
229 }
230
236 protected function setNewOrder()
237 {
238 if ($this->newOrder !== null)
239 return;
240 $this->newOrder = true;
241 if ($this->isOrderExists())
242 $this->newOrder = ((int)$this->getOrder()->getId() <= 0);
243 }
244
250 protected function isLoaded()
251 {
252 return !empty($this->orderData);
253 }
254
255 /* system methods end */
256
263 public static function buildFromOrder(OrderBase $order)
264 {
265 $instanceIndex = static::getInstanceIndexByOrder($order);
266 if (!static::instanceExists($instanceIndex))
267 {
269 $discount = static::getInstance($instanceIndex);
270 $discount->order = $order;
271 $discount->context = new Context\User($order->getUserId());
272 $discount->initInstanceData();
273 unset($discount);
274 }
275 return static::getInstance($instanceIndex);
276 }
277
289 {
290 if ($basket->getOrder())
291 {
293 'Could not build discounts from basket which has the order. You have to use buildFromOrder.'
294 );
295 }
296
297 if ($basket->count() == 0)
298 return null;
299
300 $instanceIndex = static::getInstanceIndexByBasket($basket, $context);
301 $discount = static::getInstance($instanceIndex);
302 $discount->basket = $basket;
303 $discount->context = $context;
304 $discount->initInstanceData();
305 unset($discount);
306
307 return static::getInstance($instanceIndex);
308 }
309
316 public static function setOrder(BasketBase $basket)
317 {
318 $order = $basket->getOrder();
319 if (!($order instanceof OrderBase))
320 {
322 }
323 $instanceIndex = static::getInstanceIndexByBasket($basket);
324 if (!static::instanceExists($instanceIndex))
325 return static::buildFromOrder($order);
326
327 $newInstanceIndex = static::getInstanceIndexByOrder($order);
328 if (!static::instanceExists($newInstanceIndex))
329 {
331 $discount = static::getInstance($instanceIndex);
332 $discount->basket = null;
333 $discount->order = $order;
334 $discount->context = new Context\User($order->getUserId());
335 $discount->initInstanceFromOrder();
336 unset($discount);
337 static::migrateInstance($instanceIndex, $newInstanceIndex);
338 }
339 else
340 {
341 static::removeInstance($instanceIndex);
342 }
343 return static::getInstance($newInstanceIndex);
344 }
345
346 /* calculate methods */
347
354 public function setUseMode($useMode)
355 {
356 $useMode = (int)$useMode;
357 if ($useMode <= 0)
358 return;
359 $this->useMode = $useMode;
360 }
361
367 public function getUseMode()
368 {
369 return $this->useMode;
370 }
371
379 public function setExecuteModuleFilter(array $moduleList)
380 {
381 $this->executeModuleFilter = $moduleList;
382 }
383
390 public static function getApplyModeList($extendedMode = false)
391 {
392 $extendedMode = ($extendedMode === true);
393 if ($extendedMode)
394 {
395 return array(
396 self::APPLY_MODE_ADD => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_ADD_EXT'),
397 self::APPLY_MODE_LAST => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_LAST_EXT'),
398 self::APPLY_MODE_DISABLE => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_DISABLE_EXT'),
399 self::APPLY_MODE_FULL_LAST => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_FULL_LAST'),
400 self::APPLY_MODE_FULL_DISABLE => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_FULL_DISABLE')
401 );
402 }
403 return array(
404 self::APPLY_MODE_ADD,
405 self::APPLY_MODE_LAST,
406 self::APPLY_MODE_DISABLE,
407 self::APPLY_MODE_FULL_LAST,
408 self::APPLY_MODE_FULL_DISABLE
409 );
410 }
411
418 public static function getApplyMode()
419 {
420 $applyMode = self::APPLY_MODE_ADD;
421 if ((string)Main\Config\Option::get('sale', 'use_sale_discount_only') != 'Y')
422 {
423 $applyMode = (int)Main\Config\Option::get('sale', 'discount_apply_mode');
424 if (!in_array($applyMode, self::getApplyModeList(false)))
425 $applyMode = self::APPLY_MODE_ADD;
426 }
427 return $applyMode;
428 }
429
435 public function calculate()
436 {
438 $result = new Result;
439 $process = true;
440
441 if ($this->stopCalculate())
442 return $result;
443
444 $this->discountsCache = array();
445 $this->couponsCache = array();
446
447 if (Compatible\DiscountCompatibility::isUsed())
448 return $result;
449
450 $this->initUseMode();
451
453 $couponClassName = $this->getDiscountCouponClassName();
454 if ($this->isOrderExists() && !$this->isOrderNew())
455 {
456 if ($this->isOrderRefresh())
457 {
458 $this->setApplyResult(array());
459 $couponClassName::useSavedCouponsForApply(true);
460 }
461 }
462
463 $this->orderData = null;
464 $orderResult = $this->loadOrderData();
465 if (!$orderResult->isSuccess())
466 {
467 $process = false;
468 $result->addErrors($orderResult->getErrors());
469 }
470 unset($orderResult);
471
472 if (!$this->isValidState())
473 return $result;
474
475 if ($process)
476 {
477 $couponClassName::setUseOnlySaleDiscounts($this->useOnlySaleDiscounts());
478 unset($couponClassName);
479
480 $this->resetOrderState();
481 switch ($this->getUseMode())
482 {
484 $calculateResult = $this->calculateApply();
485 break;
487 $calculateResult = $this->calculateMixed();
488 break;
490 $calculateResult = $this->calculateFull();
491 break;
492 default:
493 $calculateResult = new Result;
494 $calculateResult->addError(new Main\Entity\EntityError(
495 Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_USE_MODE'),
496 self::ERROR_ID
497 ));
498 break;
499 }
500 if (!$calculateResult->isSuccess())
501 $result->addErrors($calculateResult->getErrors());
502 else
503 $result->setData($this->fillDiscountResult());
504 unset($calculateResult);
505 }
506
507 return $result;
508 }
509
510 /* calculate methods end */
511
512 /* apply result methods */
513
521 {
522 if (is_array($applyResult))
523 $this->applyResult = $applyResult;
524
525 if (!empty($this->applyResult['DISCOUNT_LIST']))
526 {
527 if (!empty($this->applyResult['BASKET']) && is_array($this->applyResult['BASKET']))
528 {
529 foreach ($this->applyResult['BASKET'] as $discountList)
530 {
531 if (empty($discountList) || !is_array($discountList))
532 continue;
533 foreach ($discountList as $orderDiscountId => $apply)
534 {
535 if ($apply == 'Y')
536 $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] = 'Y';
537 }
538 unset($apply, $orderDiscountId);
539 }
540 unset($discountList);
541 }
542 }
543 }
544
551 public function getApplyResult($extMode = false)
552 {
553 $extMode = ($extMode === true);
554
555 if (!$this->isOrderNew() && !$this->isLoaded())
556 {
557 $this->initUseMode();
558 $this->loadOrderData();
559 }
560
561 $this->getApplyDiscounts();
562 $this->getApplyPrices();
563 if ($extMode)
564 $this->remakingDiscountResult();
565
566 $result = $this->discountResult;
567 $result['FULL_DISCOUNT_LIST'] = $this->fullDiscountList;
568
569 if ($extMode)
570 unset($result['APPLY_BLOCKS']);
571
572 return $result;
573 }
574
575 /* apply result methods finish */
576
584 public function verify()
585 {
586 $result = new Result();
587
588 $useMode = $this->getUseMode();
589 if ($useMode == self::USE_MODE_APPLY || $useMode == self::USE_MODE_MIXED)
590 {
591 if (!$this->isValidState())
592 return $result;
593 }
594
595 if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]))
596 return $result;
597
599 $couponClassName = $this->getDiscountCouponClassName();
600
601 $checkCoupons = $couponClassName::verifyApplied();
602 if (!$checkCoupons->isSuccess())
603 {
604 $result->addError(new Main\Error(
605 Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_COUPONS_USED')
606 ));
607 $errors = $checkCoupons->getErrors();
608 $row = reset($errors);
609 foreach ($row->getCustomData() as $coupon => $description)
610 {
611 $result->addError(new Main\Error(
612 $coupon.' : '.$description
613 ));
614 }
615 unset($coupon, $description, $row, $errors);
616 }
617 unset($checkCoupons, $couponClassName);
618
619 return $result;
620 }
621
627 public function save()
628 {
629 $process = true;
630 $result = new Result;
631 if (!$this->isOrderExists() || !$this->isBasketNotEmpty())
632 return $result;
633 $orderId = (int)$this->getOrder()->getId();
634
635 if ($this->getUseMode() === null)
636 return $result;
637
638 if ($process)
639 {
640 switch ($this->getUseMode())
641 {
643 $saveResult = $this->saveFull();
644 break;
646 $saveResult = $this->saveApply();
647 break;
649 $saveResult = $this->saveMixed();
650 break;
651 default:
652 $saveResult = new Result;
653 $saveResult->addError(new Main\Entity\EntityError(
654 Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_USE_MODE'),
655 self::ERROR_ID
656 ));
657 }
658 if (!$saveResult->isSuccess())
659 {
660 $result->addErrors($saveResult->getErrors());
661 }
662 else
663 {
664 if ($orderId > 0)
665 {
666 $registry = Registry::getInstance(static::getRegistryType());
667
669 $orderHistory = $registry->getOrderHistoryClassName();
670 $orderHistory::addLog(
671 'DISCOUNT',
672 $orderId,
673 'DISCOUNT_SAVED',
674 null,
675 null,
676 array(),
677 $orderHistory::SALE_ORDER_HISTORY_LOG_LEVEL_1
678 );
679 }
680
681 }
682 unset($saveResult);
683 }
684
685 if ($orderId > 0)
686 {
687 $registry = Registry::getInstance(static::getRegistryType());
688
690 $orderHistory = $registry->getOrderHistoryClassName();
691 $orderHistory::collectEntityFields('DISCOUNT', $orderId);
692 }
693
694 return $result;
695 }
696
697 public function isValidState()
698 {
699 return $this->valid === true;
700 }
701
702 protected function setValidState($value)
703 {
704 if (!is_bool($value))
705 return;
706 $this->valid = $value;
707 }
708
714 protected function initInstanceData()
715 {
716 $this->orderData = null;
717 $this->isClone = false;
718 $this->entityResultCache = array();
719 $this->setNewOrder();
721
722 $orderDiscountConfig = array(
723 'SITE_ID' => $this->getSiteId(),
724 'CURRENCY' => $this->getCurrency()
725 );
727 $storageClassName = $this->getOrderDiscountClassName();
728 $storageClassName::setManagerConfig($orderDiscountConfig);
729 unset($storageClassName, $orderDiscountConfig);
730 }
731
737 protected function initInstanceFromOrder()
738 {
739 $this->setNewOrder();
740 }
741
747 protected function stopCalculate()
748 {
749 if (!$this->isBasketNotEmpty())
750 return true;
751 if ($this->isOrderExists() && $this->getOrder()->isExternal())
752 return true;
753 return false;
754 }
755
761 protected function useOnlySaleDiscounts()
762 {
763 if (!$this->isOrderExists() || $this->isOrderNew() || $this->isOrderRefresh())
764 return (string)Main\Config\Option::get('sale', 'use_sale_discount_only') == 'Y';
765 else
766 return (isset($this->saleOptions['SALE_DISCOUNT_ONLY']) && $this->saleOptions['SALE_DISCOUNT_ONLY'] == 'Y');
767 }
768
774 protected function getBasket()
775 {
776 if ($this->isOrderExists())
777 return $this->getOrder()->getBasket();
778 else
779 return $this->basket;
780 }
781
787 protected function isBasketExist()
788 {
789 if ($this->isOrderExists())
790 return ($this->getOrder()->getBasket() instanceof BasketBase);
791 else
792 return ($this->basket instanceof BasketBase);
793 }
794
800 protected function isBasketNotEmpty()
801 {
802 if ($this->isOrderExists())
803 {
804 $basket = $this->getOrder()->getBasket();
805 $result = ($basket instanceof Basket && $basket->count() > 0);
806 unset($basket);
807 }
808 else
809 {
810 $result = ($this->basket instanceof Basket && $this->basket->count() > 0);
811 }
812 return $result;
813 }
814
820 protected function initUseMode()
821 {
822 $this->setUseMode(self::USE_MODE_FULL);
823 if ($this->isOrderExists() && !$this->isOrderNew())
824 {
825 if ($this->isOrderRefresh())
826 $this->setUseMode(self::USE_MODE_FULL);
827 elseif ($this->isOrderChanged())
828 $this->setUseMode(self::USE_MODE_MIXED);
829 elseif ($this->getOrder()->getCalculateType() == $this->getOrder()::SALE_ORDER_CALC_TYPE_REFRESH)
830 $this->setUseMode(self::USE_MODE_FULL);
831 else
832 $this->setUseMode(self::USE_MODE_APPLY);
833 }
834 }
835
841 protected function loadOrderData()
842 {
843 $result = new Result;
844 $orderId = 0;
845 if ($this->isOrderExists())
846 $orderId = $this->getOrder()->getId();
847
848 if (!$this->isLoaded())
849 $this->fillEmptyOrderData();
850
851 $basketResult = $this->loadBasket();
852 if (!$basketResult->isSuccess())
853 {
854 $result->addErrors($basketResult->getErrors());
855 return $result;
856 }
857 unset($basketResult);
858
859 if ($this->isOrderExists() && $orderId > 0)
860 {
861 $basketResult = $this->getBasketTables();
862 if (!$basketResult->isSuccess())
863 {
864 $result->addErrors($basketResult->getErrors());
865 return $result;
866 }
867 unset($basketResult);
868 }
869
870 $this->loadOrderConfig();
871
872 $discountResult = $this->loadOrderDiscounts();
873 if (!$discountResult->isSuccess())
874 $result->addErrors($discountResult->getErrors());
875 unset($discountResult);
876
877 $dataResult = $this->loadBasketStoredData();
878 if (!$dataResult->isSuccess())
879 $result->addErrors($dataResult->getErrors());
880 unset($dataResult);
881
882 return $result;
883 }
884
890 protected function fillEmptyOrderData()
891 {
893 $basket = $this->getBasket();
894 $siteId = $this->getSiteId();
895 $this->orderData = [
896 'ID' => 0,
897 'USER_ID' => $this->context->getUserId(),
898 'USER_GROUPS' => $this->context->getUserGroups(),
899 'SITE_ID' => $siteId,
900 'LID' => $siteId, // compatibility only
901 'ORDER_PRICE' => $basket->getBasePrice(),
902 'ORDER_WEIGHT' => $basket->getWeight(),
903 'CURRENCY' => $this->getCurrency(),
904 'PERSON_TYPE_ID' => 0,
905 'RECURRING_ID' => null,
906 'BASKET_ITEMS' => [],
907 'ORDER_PROP' => []
908 ];
909
910 if ($this->isOrderExists())
911 {
912 $order = $this->getOrder();
913
914 $this->orderData['ID'] = $order->getId();
915 $this->orderData['USER_ID'] = $order->getUserId();
916 $this->orderData['ORDER_PRICE'] = $order->getBasePrice();
917 $this->orderData['PERSON_TYPE_ID'] = $order->getPersonTypeId();
918 $this->orderData['RECURRING_ID'] = $order->getField('RECURRING_ID');
919
921 $propertyCollection = $order->getPropertyCollection();
923 foreach ($propertyCollection as $orderProperty)
924 $this->orderData['ORDER_PROP'][$orderProperty->getPropertyId()] = $orderProperty->getValue();
925 unset($orderProperty);
926 foreach ($this->getOrderPropertyCodes() as $propertyCode => $attribute)
927 {
928 $this->orderData[$propertyCode] = '';
929 $orderProperty = $propertyCollection->getAttribute($attribute);
930 if ($orderProperty instanceof PropertyValue)
931 $this->orderData[$propertyCode] = $orderProperty->getValue();
932 unset($orderProperty);
933 }
934 unset($propertyCode, $attribute);
935 unset($propertyCollection);
936
937 unset($order);
938 }
939 unset($siteId);
940 unset($basket);
941 }
942
949 protected function loadBasket()
950 {
951 $result = new Result;
952
953 if (!$this->isBasketExist())
954 throw new Main\ObjectNotFoundException('Entity "Basket" not found');
955 elseif (!$this->isBasketNotEmpty())
956 return $result;
957
959 $basket = $this->getBasket();
961 foreach ($basket as $basketItem)
962 {
963 if (!$basketItem->canBuy())
964 continue;
965 $this->orderData['BASKET_ITEMS'][$basketItem->getBasketCode()] = $this->getBasketItemFields($basketItem);
966 }
967 unset($basketItem, $basket);
968
969 return $result;
970 }
971
978 protected function getBasketItemFields(BasketItemBase $basketItem)
979 {
980 $item = $basketItem->getFieldValues();
981 $item['BASE_PRICE'] = $basketItem->getField('BASE_PRICE');
982 unset($item['DATE_INSERT']);
983 unset($item['DATE_UPDATE']);
984 unset($item['DATE_REFRESH']);
985 $item['PROPERTIES'] = $basketItem->getPropertyCollection()->getPropertyValues();
986 if (!isset($item['DISCOUNT_PRICE']))
987 $item['DISCOUNT_PRICE'] = 0;
988 if ($item['BASE_PRICE'] === null)
989 $item['BASE_PRICE'] = $item['PRICE'] + $item['DISCOUNT_PRICE'];
990 $item['ACTION_APPLIED'] = 'N';
991 return $item;
992 }
993
999 protected function loadOrderConfig()
1000 {
1001 $this->setValidState(true);
1002 $this->loadDefaultOrderConfig();
1003
1004 if (!$this->isOrderExists()
1005 || $this->isOrderNew()
1006 || $this->getUseMode() == self::USE_MODE_FULL
1007 )
1008 return;
1009
1011 $storageClassName = $this->getOrderDiscountClassName();
1012 $entityData = $storageClassName::loadOrderStoredDataFromDb(
1013 $this->getOrder()->getId(),
1014 $storageClassName::STORAGE_TYPE_ORDER_CONFIG
1015 );
1016 if (!$this->validateLoadedOrderConfig($entityData))
1017 {
1018 $this->setValidState(false);
1019 return;
1020 }
1021 $this->applyLoadedOrderConfig($entityData);
1022 if (isset($entityData['OLD_ORDER']))
1023 $this->setValidState(false);
1024 unset($entityData);
1025
1026 $this->loadRoundConfig();
1027 }
1028
1036 protected function getModuleSettings()
1037 {
1038 return array(
1039 'USE_BASE_PRICE' => Main\Config\Option::get('sale', 'get_discount_percent_from_base_price'),
1040 'SALE_DISCOUNT_ONLY' => Main\Config\Option::get('sale', 'use_sale_discount_only'),
1041 'APPLY_MODE' => Main\Config\Option::get('sale', 'discount_apply_mode')
1042 );
1043 }
1044
1050 protected function loadDefaultOrderConfig()
1051 {
1052 $this->saleOptions = $this->getModuleSettings();
1053 }
1054
1061 protected function validateLoadedOrderConfig($config)
1062 {
1063 if (empty($config) || !is_array($config))
1064 return false;
1065 if (empty($config['OPTIONS']) || !is_array($config['OPTIONS']))
1066 return false;
1067 return true;
1068 }
1069
1076 protected function applyLoadedOrderConfig(array $data)
1077 {
1078 if (!empty($data['OPTIONS']) && is_array($data['OPTIONS']))
1079 {
1080 foreach (array_keys($this->saleOptions) as $key)
1081 {
1082 if (isset($data['OPTIONS'][$key]))
1083 $this->saleOptions[$key] = $data['OPTIONS'][$key];
1084 }
1085 unset($key);
1086 }
1087 }
1088
1094 protected function loadOrderDiscounts()
1095 {
1096 $result = new Result;
1097 $this->discountsCache = array();
1098 $this->couponsCache = array();
1099
1100 if (!$this->isOrderExists())
1101 return $result;
1102
1103 $order = $this->getOrder();
1104 if ($this->isOrderNew() || $this->getUseMode() == self::USE_MODE_FULL)
1105 return $result;
1106
1108 $couponClassName = $this->getDiscountCouponClassName();
1109
1111 $storageClassName = $this->getOrderDiscountClassName();
1112 $applyResult = $storageClassName::loadResultFromDb(
1113 $order->getId(),
1114 $this->reverseBasketTable,
1115 $this->orderData['BASKET_ITEMS']
1116 );
1117
1118 if (!$applyResult->isSuccess())
1119 $result->addErrors($applyResult->getErrors());
1120
1121 $applyResultData = $applyResult->getData();
1122
1123 if (!empty($applyResultData['DISCOUNT_LIST']))
1124 {
1125 foreach ($applyResultData['DISCOUNT_LIST'] as $orderDiscountId => $discountData)
1126 {
1127 $discountData['ACTIONS_DESCR_DATA'] = false;
1128 if (!empty($discountData['ACTIONS_DESCR']) && is_array($discountData['ACTIONS_DESCR']))
1129 {
1130 $discountData['ACTIONS_DESCR_DATA'] = $discountData['ACTIONS_DESCR'];
1131 $discountData['ACTIONS_DESCR'] = $this->formatDescription($discountData['ACTIONS_DESCR']);
1132 }
1133 else
1134 {
1135 $discountData['ACTIONS_DESCR'] = false;
1136 }
1137 if (empty($discountData['ACTIONS_DESCR']))
1138 {
1139 $discountData['ACTIONS_DESCR'] = false;
1140 $discountData['ACTIONS_DESCR_DATA'] = false;
1141 }
1142 $this->discountsCache[$orderDiscountId] = $discountData;
1143 }
1144 unset($orderDiscountId, $discountData);
1145 }
1146 if (!empty($applyResultData['COUPON_LIST']))
1147 $this->couponsCache = $applyResultData['COUPON_LIST'];
1148
1149 $this->discountResultCounter = 0;
1150 $this->discountResult['APPLY_BLOCKS'] = $applyResultData['APPLY_BLOCKS'];
1151 if (!empty($this->discountResult['APPLY_BLOCKS']))
1152 {
1153 foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock)
1154 {
1155 if (!empty($applyBlock['BASKET']))
1156 {
1157 foreach ($applyBlock['BASKET'] as $discountList)
1158 {
1159 foreach ($discountList as $discount)
1160 {
1161 if ($discount['COUPON_ID'] == '')
1162 continue;
1163 $couponClassName::setApplyByProduct($discount, array($discount['COUPON_ID']));
1164 }
1165 }
1166 unset($discountList);
1167 }
1168
1169 if (!empty($applyBlock['ORDER']))
1170 {
1171 foreach ($applyBlock['ORDER'] as $discount)
1172 {
1173 if ($discount['COUPON_ID'] != '')
1174 $couponClassName::setApply($discount['COUPON_ID'], $discount['RESULT']);
1175 }
1176 unset($discount);
1177 }
1178
1179 $this->discountResultCounter = $counter + 1;
1180 }
1181 unset($counter, $applyBlock);
1182 }
1183
1184 if (!empty($applyResultData['STORED_ACTION_DATA']) && is_array($applyResultData['STORED_ACTION_DATA']))
1185 $this->discountStoredActionData = $applyResultData['STORED_ACTION_DATA'];
1186
1187 unset($applyResultData, $applyResult);
1188
1189 return $result;
1190 }
1191
1197 protected function loadBasketStoredData()
1198 {
1199 $result = new Result;
1200
1201 $this->basketItemsData = [];
1202
1203 if (!$this->isOrderExists())
1204 return $result;
1205
1206 $order = $this->getOrder();
1207 if ($this->isOrderNew() || $this->getUseMode() == self::USE_MODE_FULL)
1208 return $result;
1209
1211 $storageClassName = $this->getOrderDiscountClassName();
1212 $basketData = $storageClassName::loadStoredDataFromDb(
1213 $order->getId(),
1214 $storageClassName::STORAGE_TYPE_BASKET_ITEM
1215 );
1216
1217 if (empty($basketData))
1218 return $result;
1219
1220 $basketCodeList = $this->getBasketCodes(false);
1221 if (!empty($basketCodeList))
1222 {
1223 foreach ($basketCodeList as $basketCode)
1224 {
1225 if (!isset($this->forwardBasketTable[$basketCode]))
1226 continue;
1227 $basketId = $this->forwardBasketTable[$basketCode];
1228 if (!isset($basketData[$basketId]) || !is_array($basketData[$basketId]))
1229 continue;
1230 $this->addBasketItemValues($basketCode, $basketData[$basketId]);
1231 }
1232 unset($basketId, $basketCode);
1233 }
1234 unset($basketCodeList);
1235 unset($basketData, $storageClassName);
1236 unset($order);
1237
1238 return $result;
1239 }
1240
1249 protected function getBasketItemValue($code, $field)
1250 {
1251 if (!isset($this->basketItemsData[$code]))
1252 return null;
1253 return (isset($this->basketItemsData[$code][$field]) ? $this->basketItemsData[$code][$field] : null);
1254 }
1255
1264 protected function getBasketItemValueList($code, array $fields)
1265 {
1266 if (!isset($this->basketItemsData[$code]) || empty($fields))
1267 return null;
1268
1269 $result = array();
1270 foreach ($fields as $fieldName)
1271 {
1272 $result[$fieldName] = (
1273 isset($this->basketItemsData[$code][$fieldName])
1274 ? $this->basketItemsData[$code][$fieldName]
1275 : null
1276 );
1277 }
1278 unset($fieldName);
1279 return $result;
1280 }
1281
1287 protected function calculateFull()
1288 {
1289 $result = new Result;
1290 if (!$this->isBasketNotEmpty())
1291 return $result;
1292
1293 $this->discountIds = array();
1294 Discount\Actions::setUseMode(
1296 array(
1297 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
1298 'SITE_ID' => $this->orderData['SITE_ID'],
1299 'CURRENCY' => $this->orderData['CURRENCY']
1300 )
1301 );
1302
1303 $this->fillEmptyDiscountResult();
1304 $this->getRoundForBasePrices();
1305 $this->checkBasketDiscounts();
1306
1308 $couponClassName = $this->getDiscountCouponClassName();
1309 $couponClassName::clearApply();
1310 $basketDiscountResult = $this->calculateFullBasketDiscount();
1311 if (!$basketDiscountResult->isSuccess())
1312 $result->addErrors($basketDiscountResult->getErrors());
1313 unset($basketDiscountResult);
1314 if (!$result->isSuccess())
1315 return $result;
1316
1317 if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT))
1318 $this->roundFullBasketPrices();
1319
1320 if (!$this->isBasketLastDiscount())
1321 {
1322 $this->loadDiscountByUserGroups();
1323 $this->loadDiscountList();
1324 $executeResult = $this->executeDiscountList();
1325 if (!$executeResult->isSuccess())
1326 $result->addErrors($executeResult->getErrors());
1327 unset($executeResult);
1328 if (!$result->isSuccess())
1329 return $result;
1330 }
1331
1332 if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE))
1333 $this->roundFullBasketPrices();
1334
1335 return $result;
1336 }
1337
1343 protected function calculateApply()
1344 {
1345 $result = new Result;
1346 if (!$this->isOrderExists())
1347 return $result;
1348
1349 if (!$this->isValidState())
1350 return $result;
1351
1352 if (!empty($this->discountResult['APPLY_BLOCKS']))
1353 {
1354 Discount\Actions::setUseMode(
1356 array(
1357 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
1358 'SITE_ID' => $this->orderData['SITE_ID'],
1359 'CURRENCY' => $this->orderData['CURRENCY']
1360 )
1361 );
1362
1363 $currentCounter = $this->discountResultCounter;
1364
1365 foreach (array_keys($this->discountResult['APPLY_BLOCKS']) as $counter)
1366 {
1367 $this->discountResultCounter = $counter;
1368 $blockResult = $this->calculateApplyDiscountBlock();
1369 if (!$blockResult->isSuccess())
1370 {
1371 $result->addErrors($blockResult->getErrors());
1372 unset($blockResult);
1373
1374 return $result;
1375 }
1376 unset($blockResult);
1377 }
1378
1379 $this->discountResultCounter = $currentCounter;
1380 unset($currentCounter);
1381 }
1382
1383 if ($result->isSuccess())
1384 {
1385 Discount\Actions::setUseMode(
1387 array(
1388 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
1389 'SITE_ID' => $this->orderData['SITE_ID'],
1390 'CURRENCY' => $this->orderData['CURRENCY']
1391 )
1392 );
1393
1394 $this->clearCurrentApplyBlock();
1395
1396 $couponsResult = $this->calculateApplyAdditionalCoupons();
1397 if (!$couponsResult->isSuccess())
1398 {
1399 $result->addErrors($couponsResult->getErrors());
1400 unset($couponsResult);
1401 return $result;
1402 }
1403 unset($couponsResult);
1404
1405 if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE))
1406 $this->roundChangedBasketPrices();
1407 }
1408
1409 return $result;
1410 }
1411
1417 protected function calculateMixed()
1418 {
1419 $result = new Result;
1420
1421 if (!$this->isOrderExists())
1422 return $result;
1423
1424 if (!$this->isValidState())
1425 return $result;
1426
1427 if (!empty($this->discountResult['APPLY_BLOCKS']))
1428 {
1429 Discount\Actions::setUseMode(
1431 array(
1432 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
1433 'SITE_ID' => $this->orderData['SITE_ID'],
1434 'CURRENCY' => $this->orderData['CURRENCY']
1435 )
1436 );
1437
1438 $currentCounter = $this->discountResultCounter;
1439
1440 foreach (array_keys($this->discountResult['APPLY_BLOCKS']) as $counter)
1441 {
1442 $this->discountResultCounter = $counter;
1443 $blockResult = $this->calculateApplyDiscountBlock();
1444 if (!$blockResult->isSuccess())
1445 {
1446 $result->addErrors($blockResult->getErrors());
1447 unset($blockResult);
1448
1449 return $result;
1450 }
1451 unset($blockResult);
1452 }
1453
1454 $this->discountResultCounter = $currentCounter;
1455 unset($currentCounter);
1456 }
1457
1458 if ($result->isSuccess())
1459 {
1460 Discount\Actions::setUseMode(
1462 array(
1463 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
1464 'SITE_ID' => $this->orderData['SITE_ID'],
1465 'CURRENCY' => $this->orderData['CURRENCY']
1466 )
1467 );
1468
1469 $this->clearCurrentApplyBlock();
1470
1471 $this->getRoundForBasePrices();
1472 $this->checkBasketDiscounts();
1473
1474 $basketDiscountResult = $this->calculateFullBasketDiscount();
1475 if (!$basketDiscountResult->isSuccess())
1476 {
1477 $result->addErrors($basketDiscountResult->getErrors());
1478 unset($basketDiscountResult);
1479 return $result;
1480 }
1481 unset($basketDiscountResult);
1482
1483 if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT))
1484 $this->roundFullBasketPrices();
1485
1486 $couponsResult = $this->calculateApplyAdditionalCoupons();
1487 if (!$couponsResult->isSuccess())
1488 {
1489 $result->addErrors($couponsResult->getErrors());
1490 unset($couponsResult);
1491 return $result;
1492 }
1493 unset($couponsResult);
1494
1495 if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE))
1496 $this->roundChangedBasketPrices();
1497 }
1498
1499 return $result;
1500 }
1501
1507 protected function saveFull()
1508 {
1509 $result = new Result;
1510
1511 $process = true;
1512 $orderId = $this->getOrder()->getId();
1513
1515 $storageClassName = $this->getOrderDiscountClassName();
1517 $couponClassName = $this->getDiscountCouponClassName();
1519 $entityMarkerClassName = $this->getEntityMarkerClassName();
1520
1521 if (!Compatible\DiscountCompatibility::isUsed() || !Compatible\DiscountCompatibility::isInited())
1522 {
1523 $basketResult = $this->getBasketTables();
1524 if (!$basketResult->isSuccess())
1525 {
1526 $process = false;
1527 $result->addErrors($basketResult->getErrors());
1528 }
1529 }
1530
1531 if ($process)
1532 {
1533 $couponClassName::finalApply();
1534 $couponsResult = $couponClassName::saveApplied();
1535 if (!$couponsResult->isSuccess())
1536 {
1537 $process = false;
1538 $error = new Main\Error(
1539 $this->prepareCouponsResult($couponsResult)
1540 );
1541 $result->addError($error);
1542 $markerResult = new Result();
1543 $markerResult->addWarning($error);
1544 $markerResult->addWarning(new Main\Error(
1545 Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_PRICES')
1546 ));
1547 $entityMarkerClassName::addMarker(
1548 $this->getOrder(),
1549 $this->getOrder(),
1550 $markerResult
1551 );
1552 unset($markerResult, $error);
1553 }
1554 }
1555 if ($process)
1556 {
1557 $couponsResult = $this->saveCoupons();
1558 if (!$couponsResult->isSuccess())
1559 {
1560 $process = false;
1561 $result->addErrors($couponsResult->getErrors());
1562 }
1563 }
1564
1565 if ($process)
1566 {
1567 $storageClassName::deleteByOrder($orderId);
1568
1569 $lastApplyBlockResult = $this->saveLastApplyBlock();
1570 if (!$lastApplyBlockResult->isSuccess())
1571 {
1572 $process = false;
1573 $result->addErrors($lastApplyBlockResult->getErrors());
1574 }
1575 unset($lastApplyBlockResult);
1576 }
1577
1578 if ($process)
1579 {
1580 $config = $this->getOrderConfig();
1581 $dataResult = $storageClassName::saveOrderStoredData(
1582 $orderId,
1583 $storageClassName::STORAGE_TYPE_ORDER_CONFIG,
1584 $config
1585 );
1586 if (!$dataResult->isSuccess())
1587 {
1588 $result->addError(new Main\Entity\EntityError(
1589 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
1590 self::ERROR_ID
1591 ));
1592 }
1593 unset($dataResult, $config);
1594
1595 $config = array(
1596 'MODE' => $this->roundApplyMode,
1597 'CONFIG' => $this->roundApplyConfig
1598 );
1599 $dataResult = $storageClassName::saveOrderStoredData(
1600 $orderId,
1601 $storageClassName::STORAGE_TYPE_ROUND_CONFIG,
1602 $config,
1603 array('ALLOW_UPDATE' => 'Y')
1604 );
1605 if (!$dataResult->isSuccess())
1606 {
1607 $result->addError(new Main\Entity\EntityError(
1608 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
1609 self::ERROR_ID
1610 ));
1611 }
1612 unset($dataResult, $config);
1613
1614 if (!empty($this->discountStoredActionData))
1615 {
1616 $dataResult = $storageClassName::saveOrderStoredData(
1617 $orderId,
1618 $storageClassName::STORAGE_TYPE_DISCOUNT_ACTION_DATA,
1619 $this->discountStoredActionData,
1620 array('ALLOW_UPDATE' => 'Y')
1621 );
1622 if (!$dataResult->isSuccess())
1623 {
1624 $result->addError(new Main\Entity\EntityError(
1625 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
1626 self::ERROR_ID
1627 ));
1628 }
1629 unset($dataResult);
1630 }
1631
1632 $dataResult = $this->saveBasketStoredData($this->getBasketCodes(true));
1633 if (!$dataResult->isSuccess())
1634 {
1635 $result->addErrors($dataResult->getErrors());
1636 }
1637 unset($dataResult);
1638 }
1639
1640 if ($process)
1641 {
1642 if ($couponClassName::usedByManager())
1643 $couponClassName::clear(true);
1644 }
1645
1646 return $result;
1647 }
1648
1649 protected function prepareCouponsResult(Main\Result $couponsResult): string
1650 {
1651 $commonList = [];
1652 $errorList = $couponsResult->getErrors();
1653 $error = reset($errorList);
1654
1656 $list = $error->getCustomData();
1657 foreach (array_keys($list) as $coupon)
1658 {
1659 $commonList[] = $coupon.' - '.$list[$coupon];
1660 }
1661 return $error->getMessage().': '.implode(', ', $commonList);
1662
1663 }
1664
1670 protected function getOrderConfig()
1671 {
1672 return array(
1673 'OPTIONS' => $this->saleOptions,
1674 );
1675 }
1676
1683 protected function saveBasketStoredData(array $basketCodeList)
1684 {
1685 $result = new Result();
1686 if (empty($basketCodeList))
1687 return $result;
1688 $useMode = $this->getUseMode();
1689 if ($useMode != self::USE_MODE_FULL && $useMode != self::USE_MODE_MIXED)
1690 return $result;
1691
1692 $itemsData = [];
1693 foreach ($basketCodeList as $basketCode)
1694 {
1695 if (!isset($this->basketItemsData[$basketCode]))
1696 continue;
1697 $data = $this->prepareBasketItemStoredData($basketCode);
1698 if ($data === null)
1699 continue;
1700 $basketId = $this->forwardBasketTable[$basketCode];
1701 $itemsData[$basketId] = [
1702 'ENTITY_ID' => $basketId,
1703 'ENTITY_VALUE' => $basketId,
1704 'ENTITY_DATA' => $data
1705 ];
1706 }
1707 unset($data, $basketCode);
1708 if (!empty($itemsData))
1709 {
1710 $orderId = $this->getOrder()->getId();
1712 $storageClassName = $this->getOrderDiscountClassName();
1713 $dataResult = $storageClassName::saveStoredDataBlock(
1714 $orderId,
1715 $storageClassName::STORAGE_TYPE_BASKET_ITEM,
1716 $itemsData,
1717 ['ALLOW_UPDATE' => 'Y', 'DELETE_MISSING' => 'Y']
1718 );
1719 if (!$dataResult->isSuccess())
1720 {
1721 $result->addError(new Main\Entity\EntityError(
1722 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
1723 self::ERROR_ID
1724 ));
1725 }
1726 unset($dataResult, $storageClassName);
1727 }
1728 unset($itemsData);
1729
1730 return $result;
1731 }
1732
1738 protected function saveApply()
1739 {
1740 $result = new Result;
1741
1742 $process = true;
1743 $orderId = $this->getOrder()->getId();
1744
1745 if (!$this->isValidState())
1746 return $result;
1747
1748 $basketResult = $this->getBasketTables();
1749 if (!$basketResult->isSuccess())
1750 {
1751 $process = false;
1752 $result->addErrors($basketResult->getErrors());
1753 }
1754
1756 $storageClassName = $this->getOrderDiscountClassName();
1758 $couponClassName = $this->getDiscountCouponClassName();
1759
1760 $rulesList = array();
1761 $roundList = array();
1762 foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock)
1763 {
1764 if ($counter == $this->discountResultCounter)
1765 continue;
1766
1767 if (!empty($applyBlock['BASKET']))
1768 {
1769 foreach ($applyBlock['BASKET'] as $basketCode => $discountList)
1770 {
1771 foreach ($discountList as $discount)
1772 {
1773 if (!isset($discount['RULE_ID']) || (int)$discount['RULE_ID'] < 0)
1774 {
1775 $process = false;
1776 $result->addError(new Main\Entity\EntityError(
1777 Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_EXT_DISCOUNT'),
1778 self::ERROR_ID
1779 ));
1780 continue;
1781 }
1782 $rulesList[] = array(
1783 'RULE_ID' => $discount['RULE_ID'],
1784 'APPLY' => $discount['RESULT']['APPLY'],
1785 'DESCR_ID' => (isset($discount['RULE_DESCR_ID']) ? (int)$discount['RULE_DESCR_ID'] : 0),
1786 'DESCR' => $discount['RESULT']['DESCR_DATA']['BASKET'],
1787 );
1788 }
1789 unset($discount);
1790 }
1791 unset($basketCode, $discountList);
1792 }
1793
1794 if (!empty($applyBlock['ORDER']))
1795 {
1796 foreach ($applyBlock['ORDER'] as $discount)
1797 {
1798 if (!empty($discount['RESULT']['BASKET']))
1799 {
1800 foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyData)
1801 {
1802 if (!isset($applyData['RULE_ID']) || (int)$applyData['RULE_ID'] < 0)
1803 {
1804 $process = false;
1805 $result->addError(new Main\Entity\EntityError(
1806 Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_SALE_DISCOUNT'),
1807 self::ERROR_ID
1808 ));
1809 continue;
1810 }
1811 $ruleData = array(
1812 'RULE_ID' => $applyData['RULE_ID'],
1813 'APPLY' => $applyData['APPLY'],
1814 'DESCR_ID' => (isset($applyData['RULE_DESCR_ID']) ? (int)$applyData['RULE_DESCR_ID'] : 0),
1815 'DESCR' => $applyData['DESCR_DATA'],
1816 );
1817 if (!$discount['ACTION_BLOCK_LIST'])
1818 $ruleData['ACTION_BLOCK_LIST'] = $applyData['ACTION_BLOCK_LIST'];
1819 $rulesList[] = $ruleData;
1820 unset($ruleData);
1821 }
1822 unset($basketCode, $applyData);
1823 }
1824 if (!empty($discount['RESULT']['DELIVERY']))
1825 {
1826 if (!isset($discount['RESULT']['DELIVERY']['RULE_ID']) || (int)$discount['RESULT']['DELIVERY']['RULE_ID'] < 0)
1827 {
1828 $process = false;
1829 $result->addError(new Main\Entity\EntityError(
1830 Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_SALE_DISCOUNT'),
1831 self::ERROR_ID
1832 ));
1833 continue;
1834 }
1835 $ruleData = array(
1836 'RULE_ID' => $discount['RESULT']['DELIVERY']['RULE_ID'],
1837 'APPLY' => $discount['RESULT']['DELIVERY']['APPLY'],
1838 'DESCR_ID' => (isset($discount['RESULT']['DELIVERY']['RULE_DESCR_ID']) ? (int)$discount['RESULT']['DELIVERY']['RULE_DESCR_ID'] : 0),
1839 'DESCR' => $discount['RESULT']['DELIVERY']['DESCR_DATA']
1840 );
1841 $rulesList[] = $ruleData;
1842 unset($ruleData);
1843 }
1844 }
1845 unset($discount);
1846 }
1847
1848 if (!empty($applyBlock['BASKET_ROUND']))
1849 {
1850 foreach ($applyBlock['BASKET_ROUND'] as $row)
1851 {
1852 $roundList[] = array(
1853 'RULE_ID' => $row['RULE_ID'],
1854 'APPLY' => 'Y'
1855 );
1856 }
1857 unset($row);
1858 }
1859 }
1860
1861 if ($process)
1862 {
1863 $ruleResult = $storageClassName::updateResultBlock($orderId, $rulesList);
1864 if (!$ruleResult->isSuccess())
1865 {
1866
1867 }
1868 unset($ruleResult);
1869 $roundResult = $storageClassName::updateRoundBlock($orderId, $roundList);
1870 if (!$roundResult->isSuccess())
1871 {
1872
1873 }
1874 unset($roundResult);
1875 }
1876
1877 if ($process)
1878 {
1879 if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]))
1880 {
1881 $couponClassName::finalApply();
1882 $couponClassName::saveApplied();
1883 $couponsResult = $this->saveCoupons();
1884 if (!$couponsResult->isSuccess())
1885 {
1886 $process = false;
1887 $result->addErrors($couponsResult->getErrors());
1888 }
1889
1890 if ($process)
1891 {
1892 $lastApplyBlockResult = $this->saveLastApplyBlock();
1893 if (!$lastApplyBlockResult->isSuccess())
1894 {
1895 $process = false;
1896 $result->addErrors($lastApplyBlockResult->getErrors());
1897 }
1898 unset($lastApplyBlockResult);
1899 }
1900 }
1901 }
1902
1903 if ($process)
1904 {
1905 if (!empty($this->discountStoredActionData))
1906 {
1907 $dataResult = $storageClassName::saveOrderStoredData(
1908 $orderId,
1909 $storageClassName::STORAGE_TYPE_DISCOUNT_ACTION_DATA,
1910 $this->discountStoredActionData,
1911 array('ALLOW_UPDATE' => 'Y')
1912 );
1913 if (!$dataResult->isSuccess())
1914 {
1915 $result->addError(new Main\Entity\EntityError(
1916 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
1917 self::ERROR_ID
1918 ));
1919 }
1920 unset($dataResult);
1921 }
1922 }
1923
1924 if ($process)
1925 {
1926 if ($couponClassName::usedByManager())
1927 $couponClassName::clear(true);
1928 }
1929
1930 return $result;
1931 }
1932
1938 protected function saveMixed()
1939 {
1940 $result = $this->saveApply();
1941
1942 if ($result->isSuccess())
1943 {
1944 $basketCodeList = array_merge(
1945 $this->getBasketCodes(false),
1946 $this->getBasketCodes(true)
1947 );
1948 $dataResult = $this->saveBasketStoredData($basketCodeList);
1949 if (!$dataResult->isSuccess())
1950 {
1951 $result->addErrors($dataResult->getErrors());
1952 }
1953 unset($dataResult, $basketCodeList);
1954 }
1955
1956 return $result;
1957 }
1958
1964 protected function saveCoupons()
1965 {
1966 $result = new Result;
1967 if (!$this->isOrderExists())
1968 return $result;
1969 if (!empty($this->couponsCache))
1970 {
1972 $storageClassName = $this->getOrderDiscountClassName();
1973
1974 $orderId = $this->getOrder()->getId();
1975 foreach ($this->couponsCache as $orderCouponId => $couponData)
1976 {
1977 if ($couponData['ID'] > 0)
1978 continue;
1979 $fields = $couponData;
1980 $fields['ORDER_ID'] = $orderId;
1981 $couponResult = $storageClassName::saveCoupon($fields);
1982 if (!$couponResult->isSuccess())
1983 {
1984 $result->addErrors($couponResult->getErrors());
1985 unset($couponResult);
1986 continue;
1987 }
1988 $this->couponsCache[$orderCouponId]['ID'] = $couponResult->getId();
1989 unset($couponResult);
1990 }
1991 unset($orderId);
1992 unset($storageClassName);
1993 }
1994 return $result;
1995 }
1996
2003 protected function saveLastApplyBlock()
2004 {
2005 $result = new Result;
2006
2007 $orderId = $this->getOrder()->getId();
2008
2009 $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter];
2011 $storageClassName = $this->getOrderDiscountClassName();
2012
2013 $rulesList = array();
2014 if (!empty($applyBlock['BASKET']))
2015 {
2016 foreach ($applyBlock['BASKET'] as $basketCode => $discountList)
2017 {
2018 $commonFields = $this->getEntitySaveIdentifier([
2019 'ENTITY_TYPE' => self::ENTITY_BASKET_ITEM,
2020 'ENTITY_CODE' => $basketCode
2021 ]);
2022 if ($commonFields === null)
2023 continue;
2024 foreach ($discountList as $discount)
2025 {
2026 $rulesList[] = $commonFields + [
2027 'ORDER_DISCOUNT_ID' => $discount['DISCOUNT_ID'],
2028 'COUPON_ID' => $discount['COUPON_ID'],
2029 'APPLY' => $discount['RESULT']['APPLY'],
2030 'DESCR' => $discount['RESULT']['DESCR_DATA']
2031 ];
2032 }
2033 unset($discount);
2034 }
2035 unset($discount, $commonFields, $discountList, $basketCode);
2036 }
2037 if (!empty($applyBlock['ORDER']))
2038 {
2039 foreach ($applyBlock['ORDER'] as $discount)
2040 {
2041 if (!empty($discount['RESULT']['BASKET']))
2042 {
2043 foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyData)
2044 {
2045 $commonFields = $this->getEntitySaveIdentifier([
2046 'ENTITY_TYPE' => self::ENTITY_BASKET_ITEM,
2047 'ENTITY_CODE' => $basketCode
2048 ]);
2049 if ($commonFields === null)
2050 continue;
2051 $rulesList[] = $commonFields + [
2052 'ORDER_DISCOUNT_ID' => $discount['DISCOUNT_ID'],
2053 'COUPON_ID' => $discount['COUPON_ID'],
2054 'APPLY' => $applyData['APPLY'],
2055 'ACTION_BLOCK_LIST' => $applyData['ACTION_BLOCK_LIST'],
2056 'DESCR' => $applyData['DESCR_DATA']
2057 ];
2058 }
2059 unset($commonFields, $basketCode, $applyData);
2060 }
2061 if (!empty($discount['RESULT']['DELIVERY']))
2062 {
2063 $commonFields = $this->getEntitySaveIdentifier([
2064 'ENTITY_TYPE' => self::ENTITY_DELIVERY,
2065 'ENTITY_CODE' => $discount['RESULT']['DELIVERY']['DELIVERY_ID']
2066 ]);
2067 if ($commonFields === null)
2068 continue;
2069 $rulesList[] = $commonFields + [
2070 'ORDER_DISCOUNT_ID' => $discount['DISCOUNT_ID'],
2071 'COUPON_ID' => $discount['COUPON_ID'],
2072 'APPLY' => $discount['RESULT']['DELIVERY']['APPLY'],
2073 'DESCR' => $discount['RESULT']['DELIVERY']['DESCR_DATA']
2074 ];
2075 unset($commonFields);
2076 }
2077 }
2078 unset($discount);
2079 }
2080
2081 if (!empty($rulesList))
2082 {
2083 $this->normalizeNewResultRows($rulesList);
2084 $blockResult = $storageClassName::addResultBlock($orderId, $rulesList);
2085 if (!$blockResult->isSuccess())
2086 {
2087 $result->addError(new Main\Entity\EntityError(
2088 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
2089 self::ERROR_ID
2090 ));
2091 }
2092 unset($blockResult);
2093 }
2094 unset($rulesList);
2095
2096 if (!empty($applyBlock['BASKET_ROUND']))
2097 {
2098 $roundList = array();
2099 foreach ($applyBlock['BASKET_ROUND'] as $basketCode => $roundData)
2100 {
2101 $commonFields = $this->getEntitySaveIdentifier([
2102 'ENTITY_TYPE' => self::ENTITY_BASKET_ITEM,
2103 'ENTITY_CODE' => $basketCode
2104 ]);
2105 if ($commonFields === null)
2106 continue;
2107 $roundList[] = $commonFields + [
2108 'APPLY' => $roundData['APPLY'],
2109 'ROUND_RULE' => $roundData['ROUND_RULE']
2110 ];
2111 }
2112 unset($commonFields, $roundData, $basketCode);
2113 if (!empty($roundList))
2114 {
2115 $this->normalizeNewResultRows($roundList);
2116 $blockResult = $storageClassName::addRoundBlock($orderId, $roundList);
2117 if (!$blockResult->isSuccess())
2118 {
2119 $result->addError(new Main\Entity\EntityError(
2120 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'),
2121 self::ERROR_ID
2122 ));
2123 }
2124 unset($blockResult);
2125 }
2126 unset($roundList);
2127 }
2128
2129 unset($applyBlock);
2130
2131 return $result;
2132 }
2133
2140 protected function normalizeNewResultRows(array &$rows)
2141 {
2142 if (empty($rows))
2143 return;
2144
2145 foreach (array_keys($rows) as $index)
2146 {
2147 $rows[$index]['APPLY_BLOCK_COUNTER'] = $this->discountResultCounter;
2148 if (isset($rows[$index]['ORDER_DISCOUNT_ID']))
2149 $rows[$index]['MODULE_ID'] = $this->discountsCache[$rows[$index]['ORDER_DISCOUNT_ID']]['MODULE_ID'];
2150 if (isset($rows[$index]['COUPON_ID']))
2151 $rows[$index]['COUPON_ID'] = ($rows[$index]['COUPON_ID'] != '' ? $this->couponsCache[$rows[$index]['COUPON_ID']]['ID'] : 0);
2152 }
2153 unset($index);
2154 }
2155
2161 protected function checkDiscountConditions()
2162 {
2163 if (
2164 !isset($this->currentStep['cacheIndex'])
2165 || !isset($this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']])
2166 )
2167 return false;
2168
2169 $key = $this->getConditionField();
2170 $executeKey = self::getExecuteFieldName($key);
2171
2172 if (empty($this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']][$key]))
2173 return false;
2174
2175 $discountLink = &$this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']];
2176
2177 if (!array_key_exists($executeKey, $discountLink))
2178 {
2179 $checkOrder = null;
2180
2181 $evalCode = '$checkOrder='.$discountLink[$key].';';
2182 if (PHP_MAJOR_VERSION >= 7)
2183 {
2184 try
2185 {
2186 eval($evalCode);
2187 }
2188 catch (\ParseError $e)
2189 {
2190 $this->showAdminError();
2191 }
2192 }
2193 else
2194 {
2195 eval($evalCode);
2196 }
2197 unset($evalCode);
2198
2199 if (!is_callable($checkOrder))
2200 return false;
2201 $result = $checkOrder($this->orderData);
2202 unset($checkOrder);
2203 }
2204 else
2205 {
2206 if (!is_callable($discountLink[$executeKey]))
2207 return false;
2208
2209 $result = $discountLink[$executeKey]($this->orderData);
2210 }
2211 unset($discountLink);
2212 return $result;
2213 }
2214
2220 protected function applySaleDiscount()
2221 {
2222 $result = new Result;
2223
2224 Discount\Actions::clearApplyCounter();
2225
2226 $discount = (
2227 isset($this->currentStep['discountIndex'])
2228 ? $this->discountsCache[$this->currentStep['discountId']]
2229 : $this->currentStep['discount']
2230 );
2231 if (isset($this->currentStep['discountIndex']))
2232 {
2233 if (!empty($discount['APPLICATION']) && !$this->loadDiscountModules($this->discountsCache[$this->currentStep['discountId']]['MODULES']))
2234 {
2235 $discount['APPLICATION'] = null;
2236 $result->addError(new Main\Entity\EntityError(
2237 Loc::getMessage('BX_SALE_DISCOUNT_ERR_SALE_DISCOUNT_MODULES_ABSENT'),
2238 self::ERROR_ID
2239 ));
2240 }
2241 }
2242
2243 if (!empty($discount['APPLICATION']))
2244 {
2245 $executeKey = self::getExecuteFieldName('APPLICATION');
2246 if (!array_key_exists($executeKey, $discount))
2247 {
2248 $discount[$executeKey] = null;
2249
2250 $evalCode = '$discount["'.$executeKey.'"] = '.$discount['APPLICATION'].';';
2251 if (PHP_MAJOR_VERSION >= 7)
2252 {
2253 try
2254 {
2255 eval($evalCode);
2256 }
2257 catch (\ParseError $e)
2258 {
2259 $this->showAdminError();
2260 }
2261 }
2262 else
2263 {
2264 eval($evalCode);
2265 }
2266 unset($evalCode);
2267 }
2268 if (is_callable($discount[$executeKey]))
2269 {
2270 $currentUseMode = $this->getUseMode();
2271 $this->currentStep['oldData'] = $this->orderData;
2272 if (
2273 $currentUseMode == self::USE_MODE_APPLY
2274 || $currentUseMode == self::USE_MODE_MIXED
2275 )
2276 {
2277 $discountStoredActionData = $this->getDiscountStoredActionData($this->currentStep['discountId']);
2279 Discount\Actions::setStoredData($discountStoredActionData);
2281 }
2282 $discount[$executeKey]($this->orderData);
2283 switch ($currentUseMode)
2284 {
2287 $actionsResult = $this->calculateFullSaleDiscountResult();
2288 break;
2291 $actionsResult = $this->calculateApplySaleDiscountResult();
2292 break;
2293 default:
2294 $actionsResult = new Result;
2295
2296 }
2297 if (!$actionsResult->isSuccess())
2298 $result->addErrors($actionsResult->getErrors());
2299 unset($actionsResult);
2300 unset($currentUseMode);
2301 }
2302 }
2303 unset($discount);
2304 Discount\Actions::clearAction();
2305
2306 return $result;
2307 }
2308
2315 protected function checkBasketDiscounts()
2316 {
2317 $useMode = $this->getUseMode();
2318 if (
2319 $useMode === self::USE_MODE_FULL
2320 || $useMode == self::USE_MODE_MIXED
2321 )
2322 {
2323 $basketCodeList = $this->getBasketCodes(true);
2324 if (!empty($basketCodeList))
2325 {
2326 $basket = $this->getBasket();
2327 foreach ($basketCodeList as $code)
2328 {
2329 $basketItem = $basket->getItemByBasketCode($code);
2330 if ($basketItem instanceof BasketItemBase)
2331 {
2332 if (!isset($this->basketDiscountList[$code]))
2333 {
2334 $this->basketDiscountList[$code] = $basketItem->getField('DISCOUNT_LIST');
2335 if ($this->basketDiscountList[$code] === null)
2336 unset($this->basketDiscountList[$code]);
2337 }
2338 }
2339 }
2340 unset($basketItem, $code, $basket);
2341 }
2342 unset($basketCodeList);
2343 }
2344 unset($useMode);
2345 }
2346
2352 protected function calculateFullBasketDiscount()
2353 {
2354 $result = new Result;
2355
2356 if ((string)Main\Config\Option::get('sale', 'use_sale_discount_only') == 'Y')
2357 return $result;
2358 if (empty($this->basketDiscountList))
2359 return $result;
2360
2362 $couponClassName = $this->getDiscountCouponClassName();
2363
2364 $applyExist = $this->isBasketApplyResultExist();
2365
2366 $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET'];
2367
2368 foreach ($this->getBasketCodes(true) as $basketCode)
2369 {
2370 if ($this->isOrderNew() && array_key_exists($basketCode, $applyBlock))
2371 unset($applyBlock[$basketCode]);
2372 if (empty($this->basketDiscountList[$basketCode]))
2373 continue;
2374
2375 $itemData = array(
2376 'MODULE_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['MODULE'],
2377 'PRODUCT_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'],
2378 'BASKET_ID' => $basketCode
2379 );
2380 foreach ($this->basketDiscountList[$basketCode] as $index => $discount)
2381 {
2382 $discountResult = $this->convertDiscount($discount);
2383 if (!$discountResult->isSuccess())
2384 {
2385 $result->addErrors($discountResult->getErrors());
2386 unset($discountResult);
2387 return $result;
2388 }
2389 $orderDiscountId = $discountResult->getId();
2390 $discountData = $discountResult->getData();
2391 $orderCouponId = '';
2392 $this->basketDiscountList[$basketCode][$index]['ORDER_DISCOUNT_ID'] = $orderDiscountId;
2393 if ($discountData['USE_COUPONS'] == 'Y')
2394 {
2395 if (empty($discount['COUPON']))
2396 {
2397 $result->addError(new Main\Entity\EntityError(
2398 Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'),
2399 self::ERROR_ID
2400 ));
2401 return $result;
2402 }
2403 $couponResult = $this->convertCoupon($discount['COUPON'], $orderDiscountId);
2404 if (!$couponResult->isSuccess())
2405 {
2406 $result->addErrors($couponResult->getErrors());
2407 unset($couponResult);
2408 return $result;
2409 }
2410 $orderCouponId = $couponResult->getId();
2411
2412 $couponClassName::setApplyByProduct($itemData, array($orderCouponId));
2413 unset($couponResult);
2414 }
2415 unset($discountData, $discountResult);
2416 if (!isset($applyBlock[$basketCode]))
2417 $applyBlock[$basketCode] = array();
2418 $applyBlock[$basketCode][$index] = array(
2419 'DISCOUNT_ID' => $orderDiscountId,
2420 'COUPON_ID' => $orderCouponId,
2421 'RESULT' => array(
2422 'APPLY' => 'Y',
2423 'DESCR' => false,
2424 'DESCR_DATA' => false
2425 )
2426 );
2427
2428 $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode];
2429 $orderApplication = (
2430 !empty($this->discountsCache[$orderDiscountId]['APPLICATION'])
2431 ? $this->discountsCache[$orderDiscountId]['APPLICATION']
2432 : null
2433 );
2434 if (!empty($orderApplication))
2435 {
2436 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = (
2437 !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'])
2438 ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']
2439 : false
2440 );
2441
2442 $applyProduct = null;
2443 eval('$applyProduct='.$orderApplication.';');
2444 if (is_callable($applyProduct))
2445 $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]);
2446 unset($applyProduct);
2447
2448 if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']))
2449 {
2450 $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']['BASKET'];
2451 $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = $this->formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
2452 }
2453 unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
2454 }
2455 unset($orderApplication);
2456
2457 if ($applyExist && !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId))
2458 {
2459 $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct;
2460 $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N';
2461 }
2462 unset($disable, $currentProduct);
2463 if ($applyBlock[$basketCode][$index]['RESULT']['APPLY'] == 'Y')
2464 $this->orderData['BASKET_ITEMS'][$basketCode]['ACTION_APPLIED'] = 'Y';
2465 }
2466 unset($discount, $index);
2467 }
2468 unset($basketCode);
2469
2470 unset($applyBlock);
2471
2472 return $result;
2473 }
2474
2480 protected function calculateApplyBasketDiscount()
2481 {
2482 $result = new Result;
2483
2484 if ($this->useOnlySaleDiscounts())
2485 return $result;
2486 if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET']))
2487 return $result;
2488
2489 $applyExist = $this->isBasketApplyResultExist();
2490
2491 $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET'];
2492
2493 foreach ($this->getBasketCodes(false) as $basketCode)
2494 {
2495 if ($this->isCustomPriceByCode($basketCode))
2496 {
2497 if (isset($applyBlock[$basketCode]))
2498 unset($applyBlock[$basketCode]);
2499 continue;
2500 }
2501 if (empty($applyBlock[$basketCode]))
2502 continue;
2503
2504 foreach ($applyBlock[$basketCode] as $index => $discount)
2505 {
2506 $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode];
2507 $orderDiscountId = $discount['DISCOUNT_ID'];
2508 $orderCouponId = $discount['COUPON_ID'];
2509
2510 if (!isset($this->discountsCache[$orderDiscountId]))
2511 {
2512 $result->addError(new Main\Entity\EntityError(
2513 Loc::getMessage('BX_SALE_DISCOUNT_ERR_APPLY_WITHOUT_EXT_DISCOUNT'),
2514 self::ERROR_ID
2515 ));
2516 return $result;
2517 }
2518
2519 $orderApplication = (
2520 !empty($this->discountsCache[$orderDiscountId]['APPLICATION'])
2521 ? $this->discountsCache[$orderDiscountId]['APPLICATION']
2522 : null
2523 );
2524 if (!empty($orderApplication) && !$this->loadDiscountModules($this->discountsCache[$orderDiscountId]['MODULES']))
2525 $orderApplication = null;
2526
2527 if (!empty($orderApplication))
2528 {
2529 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = (
2530 !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'])
2531 ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']
2532 : false
2533 );
2534
2535 $applyProduct = null;
2536 eval('$applyProduct='.$orderApplication.';');
2537 if (is_callable($applyProduct))
2538 $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]);
2539 unset($applyProduct);
2540
2541 if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']))
2542 {
2543 $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'];
2544 $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = $this->formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
2545 }
2546 unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
2547 }
2548 unset($orderApplication);
2549
2550 $disable = ($applyBlock[$basketCode][$index]['RESULT']['APPLY'] == 'N');
2551 if ($applyExist)
2552 {
2553 $applyDisable = !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId);
2554 if ($applyDisable != $disable)
2555 $disable = $applyDisable;
2556 unset($applyDisable);
2557 }
2558 if ($disable)
2559 {
2560 $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct;
2561 $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N';
2562 }
2563 else
2564 {
2565 $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'Y';
2566 $this->orderData['BASKET_ITEMS'][$basketCode]['ACTION_APPLIED'] = 'Y';
2567 }
2568 unset($disable, $currentProduct);
2569
2570 }
2571 unset($index, $discount);
2572 }
2573 unset($basketCode);
2574
2575 unset($applyBlock);
2576
2577 return $result;
2578 }
2579
2585 protected function calculateApplyDiscountBlock()
2586 {
2587 $result = new Result;
2588
2589 $basketDiscountResult = $this->calculateApplyBasketDiscount();
2590 if (!$basketDiscountResult->isSuccess())
2591 {
2592 $result->addErrors($basketDiscountResult->getErrors());
2593 unset($basketDiscountResult);
2594
2595 return $result;
2596 }
2597 unset($basketDiscountResult);
2598
2599 $roundApply = false;
2600 if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT))
2601 {
2602 $roundApply = true;
2603 $this->roundApplyBasketPrices();
2604 }
2605
2606 if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply)
2607 {
2608 $this->roundApplyBasketPricesByIndex(array(
2609 'DISCOUNT_INDEX' => -1,
2610 'DISCOUNT_ID' => 0
2611 ));
2612 }
2613 if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER']))
2614 {
2615 $index = -1;
2616 foreach ($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'] as $indexDiscount => $discount)
2617 {
2618 $index++;
2619 $orderDiscountId = $discount['DISCOUNT_ID'];
2620 if (!isset($this->discountsCache[$orderDiscountId]))
2621 {
2622 $result->addError(new Main\Entity\EntityError(
2623 Loc::getMessage('BX_SALE_DISCOUNT_ERR_APPLY_WITHOUT_SALE_DISCOUNT'),
2624 self::ERROR_ID
2625 ));
2626 return $result;
2627 }
2628 Discount\Actions::clearAction();
2629 if (!empty($discount['RESULT']['BASKET']))
2630 {
2631 if ($discount['ACTION_BLOCK_LIST'])
2632 {
2633 $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_COUNTER;
2634 $blockList = array();
2635 foreach ($discount['RESULT']['BASKET'] as $basketCode => $basketItem)
2636 $blockList[$basketCode] = $basketItem['ACTION_BLOCK_LIST'];
2637 unset($basketCode, $basketItem);
2638 }
2639 else
2640 {
2641 if ($this->discountsCache[$orderDiscountId]['SIMPLE_ACTION'])
2642 {
2643 $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_SIMPLE;
2644 $blockList = array_fill_keys(array_keys($discount['RESULT']['BASKET']), true);
2645 }
2646 else
2647 {
2648 $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_DESCR;
2649 $blockList = array();
2650 foreach ($discount['RESULT']['BASKET'] as $basketCode => $basketItem)
2651 $blockList[$basketCode] = $basketItem['DESCR_DATA'];
2652 unset($basketCode, $basketItem);
2653 }
2654 }
2655 Discount\Actions::setApplyResultMode($applyResultMode);
2656 Discount\Actions::setApplyResult(array('BASKET' => $blockList));
2657 unset($blockList, $applyResultMode);
2658 }
2659 if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply)
2660 {
2661 $this->roundApplyBasketPricesByIndex(array(
2662 'DISCOUNT_INDEX' => $index,
2663 'DISCOUNT_ID' => $orderDiscountId
2664 ));
2665 }
2666 $this->fillCurrentStep(array(
2667 'discountIndex' => $indexDiscount,
2668 'discountId' => $orderDiscountId,
2669 ));
2670 $actionsResult = $this->applySaleDiscount();
2671 if (!$actionsResult->isSuccess())
2672 {
2673 $result->addErrors($actionsResult->getErrors());
2674 unset($actionsResult);
2675 return $result;
2676 }
2677 unset($orderDiscountId);
2678 }
2679 if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply)
2680 {
2681 $index++;
2682 $this->roundApplyBasketPricesByIndex(array(
2683 'DISCOUNT_INDEX' => $index,
2684 'DISCOUNT_ID' => 0
2685 ));
2686 $roundConfig = $this->getRoundIndex('BASKET_ROUND');
2687 if (is_array($roundConfig))
2688 {
2689 if ($roundConfig['DISCOUNT_INDEX'] > $index && $roundConfig['DISCOUNT_ID'] == 0)
2690 {
2691 $this->roundApplyBasketPricesByIndex(array(
2692 'DISCOUNT_INDEX' => $roundConfig['DISCOUNT_INDEX'],
2693 'DISCOUNT_ID' => 0
2694 ));
2695 }
2696 }
2697 unset($roundConfig);
2698 }
2699 unset($discount, $indexDiscount, $currentList);
2700 }
2701
2702 if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE))
2703 $this->roundApplyBasketPrices();
2704
2705 $this->fillEmptyCurrentStep();
2706
2707 return $result;
2708 }
2709
2715 protected function calculateApplyAdditionalCoupons()
2716 {
2717 $result = new Result;
2718
2719 $useMode = $this->getUseMode();
2720 if ($useMode != self::USE_MODE_APPLY && $useMode != self::USE_MODE_MIXED)
2721 return $result;
2722
2723 if (!$this->useOnlySaleDiscounts())
2724 {
2725 $couponList = $this->getAdditionalCoupons(array('!MODULE_ID' => 'sale'));
2726 if (!empty($couponList))
2727 {
2728 $params = array(
2729 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'],
2730 'USER_ID' => $this->orderData['USER_ID'],
2731 'SITE_ID' => $this->orderData['SITE_ID']
2732 );
2733 $couponsByModule = array();
2734 foreach ($couponList as &$coupon)
2735 {
2736 if (!isset($couponsByModule[$coupon['MODULE_ID']]))
2737 $couponsByModule[$coupon['MODULE_ID']] = array();
2738 $couponsByModule[$coupon['MODULE_ID']][] = array(
2739 'DISCOUNT_ID' => $coupon['DISCOUNT_ID'],
2740 'COUPON' => $coupon['COUPON']
2741 );
2742 }
2743 unset($coupon);
2744 if (!empty($couponsByModule))
2745 {
2747 $storageClassName = $this->getOrderDiscountClassName();
2748 foreach ($couponsByModule as $moduleId => $moduleCoupons)
2749 {
2750 if ($useMode == self::USE_MODE_APPLY)
2751 {
2752 $currentBasket = $this->orderData['BASKET_ITEMS'];
2753 }
2754 else
2755 {
2756 $currentBasket = array();
2757 $basketCodeList = $this->getBasketCodes(false);
2758 foreach ($basketCodeList as $basketCode)
2759 $currentBasket[$basketCode] = $this->orderData['BASKET_ITEMS'][$basketCode];
2760 unset($basketCode, $basketCodeList);
2761 }
2762 if (empty($currentBasket))
2763 continue;
2764 $couponsApply = $storageClassName::calculateApplyCoupons(
2765 $moduleId,
2766 $moduleCoupons,
2767 $currentBasket,
2768 $params
2769 );
2770 unset($currentBasket);
2771 if (!empty($couponsApply))
2772 {
2773 $couponsApplyResult = $this->calculateApplyBasketAdditionalCoupons($couponsApply);
2774 if (!$couponsApplyResult->isSuccess())
2775 $result->addErrors($couponsApplyResult->getErrors());
2776 unset($couponsApplyResult);
2777 }
2778 unset($couponsApply);
2779 }
2780 unset($moduleId, $moduleCoupons);
2781 }
2782 unset($couponsByModule, $params);
2783 }
2784 unset($couponList);
2785 }
2786
2787 $couponList = $this->getAdditionalCoupons(array('MODULE_ID' => 'sale'));
2788 if (!empty($couponList))
2789 {
2790 $couponsApplyResult = $this->calculateApplySaleAdditionalCoupons($couponList);
2791 if (!$couponsApplyResult->isSuccess())
2792 $result->addErrors($couponsApplyResult->getErrors());
2793 unset($couponsApplyResult);
2794 }
2795 unset($couponList);
2796
2797 return $result;
2798 }
2799
2805 protected function calculateFullSaleDiscountResult()
2806 {
2807 $result = new Result;
2808
2810 $couponClassName = $this->getDiscountCouponClassName();
2811
2812 $this->orderData['DISCOUNT_RESULT'] = Discount\Actions::getActionResult();
2813 $this->orderData['DISCOUNT_DESCR'] = Discount\Actions::getActionDescription();
2814 if (!empty($this->orderData['DISCOUNT_RESULT']) && is_array($this->orderData['DISCOUNT_RESULT']))
2815 {
2816 $stepResult = $this->getStepResult($this->orderData);
2817 }
2818 else
2819 {
2820 $stepResult = Discount\Result\CompatibleFormat::getStepResult(
2821 $this->orderData,
2822 $this->currentStep['oldData']
2823 );
2824 if (!empty($stepResult))
2825 {
2826 if (empty($this->orderData['DISCOUNT_DESCR']) || !is_array($this->orderData['DISCOUNT_DESCR']))
2827 $this->orderData['DISCOUNT_DESCR'] = Discount\Result\CompatibleFormat::getDiscountDescription($stepResult);
2828 }
2829 }
2830
2831 Discount\Actions::fillCompatibleFields($this->orderData);
2832 $applied = !empty($stepResult);
2833
2834 $orderDiscountId = 0;
2835 $orderCouponId = '';
2836
2837 if ($applied)
2838 {
2839 $this->correctStepResult($stepResult, $this->currentStep['discount']);
2840
2841 $this->currentStep['discount']['ACTIONS_DESCR'] = $this->orderData['DISCOUNT_DESCR'];
2842 $discountResult = $this->convertDiscount($this->currentStep['discount']);
2843 if (!$discountResult->isSuccess())
2844 {
2845 $result->addErrors($discountResult->getErrors());
2846 return $result;
2847 }
2848 $orderDiscountId = $discountResult->getId();
2849 $discountData = $discountResult->getData();
2850
2851 $this->currentStep['discount']['ORDER_DISCOUNT_ID'] = $orderDiscountId;
2852
2853 if ($discountData['USE_COUPONS'] == 'Y')
2854 {
2855 if (empty($this->currentStep['discount']['COUPON']))
2856 {
2857 $result->addError(new Main\Entity\EntityError(
2858 Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'),
2859 self::ERROR_ID
2860 ));
2861
2862 return $result;
2863 }
2864 $couponResult = $this->convertCoupon($this->currentStep['discount']['COUPON']['COUPON'], $orderDiscountId);
2865 if (!$couponResult->isSuccess())
2866 {
2867 $result->addErrors($couponResult->getErrors());
2868 unset($couponResult);
2869
2870 return $result;
2871 }
2872 $orderCouponId = $couponResult->getId();
2873 $couponClassName::setApply($orderCouponId, $stepResult);
2874 unset($couponResult);
2875 }
2876 $this->setDiscountStoredActionData($orderDiscountId, Discount\Actions::getStoredData());
2877 }
2878 unset($this->orderData['DISCOUNT_DESCR'], $this->orderData['DISCOUNT_RESULT']);
2879
2880 if ($applied)
2881 {
2882 if (
2883 (
2884 !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId])
2885 && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N'
2886 )
2887 ||
2888 (
2889 $orderCouponId != ''
2890 && !empty($this->applyResult['COUPON_LIST'][$orderCouponId])
2891 && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N'
2892 )
2893 )
2894 {
2895 $this->orderData = $this->currentStep['oldData'];
2896 if (!empty($stepResult['BASKET']))
2897 {
2898 foreach ($stepResult['BASKET'] as &$basketItem)
2899 $basketItem['APPLY'] = 'N';
2900 unset($basketItem);
2901 }
2902 if (!empty($stepResult['DELIVERY']))
2903 $stepResult['DELIVERY']['APPLY'] = 'N';
2904 }
2905 else
2906 {
2907 if (!empty($this->applyResult['BASKET']) && is_array($this->applyResult['BASKET']))
2908 {
2909 foreach ($this->applyResult['BASKET'] as $basketCode => $discountList)
2910 {
2911 if (
2912 is_array($discountList) && !empty($discountList[$orderDiscountId]) && $discountList[$orderDiscountId] == 'N'
2913 )
2914 {
2915 if (empty($stepResult['BASKET'][$basketCode]))
2916 continue;
2917 $stepResult['BASKET'][$basketCode]['APPLY'] = 'N';
2918 $this->orderData['BASKET_ITEMS'][$basketCode] = $this->currentStep['oldData']['BASKET_ITEMS'][$basketCode];
2919 }
2920 }
2921 unset($basketCode, $discountList);
2922 }
2923 if (!empty($this->applyResult['DELIVERY']))
2924 {
2925 if (
2926 is_array($this->applyResult['DELIVERY']) && !empty($this->applyResult['DELIVERY'][$orderDiscountId]) && $this->applyResult['DELIVERY'][$orderDiscountId] == 'N'
2927 )
2928 {
2929 $this->orderData['PRICE_DELIVERY'] = $this->currentStep['oldData']['PRICE_DELIVERY'];
2930 $this->orderData['PRICE_DELIVERY_DIFF'] = $this->currentStep['oldData']['PRICE_DELIVERY_DIFF'];
2931 $stepResult['DELIVERY']['APPLY'] = 'N';
2932 }
2933 }
2934 }
2935 }
2936
2937 if ($applied && $orderCouponId != '')
2938 {
2939 $couponApply = $couponClassName::setApply($this->couponsCache[$orderCouponId]['COUPON'], $stepResult);
2940 unset($couponApply);
2941 }
2942
2943 if ($applied)
2944 {
2945 $this->tryToRevertApplyStatusInBlocks($stepResult);
2946
2947 if (!empty($stepResult['BASKET']))
2948 {
2949 foreach ($stepResult['BASKET'] as $basketCode => $itemResult)
2950 {
2951 if ($itemResult['APPLY'] == 'Y')
2952 $this->orderData['BASKET_ITEMS'][$basketCode]['ACTION_APPLIED'] = 'Y';
2953 }
2954 unset($basketCode, $itemResult);
2955 }
2956
2957 $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][] = array(
2958 'DISCOUNT_ID' => $orderDiscountId,
2959 'COUPON_ID' => $orderCouponId,
2960 'RESULT' => $stepResult
2961 );
2962 if ($this->currentStep['discount']['LAST_DISCOUNT'] == 'Y')
2963 $this->currentStep['stop'] = true;
2964
2965 if ($this->currentStep['discount']['LAST_LEVEL_DISCOUNT'] == 'Y')
2966 $this->currentStep['stopLevel'] = true;
2967 }
2968
2969 return $result;
2970 }
2971
2979 protected function tryToRevertApplyStatusInBlocks(array $stepResult)
2980 {
2981 if (empty($stepResult['BASKET']))
2982 {
2983 return;
2984 }
2985
2986 foreach ($stepResult['BASKET'] as $basketItemId => $item)
2987 {
2988 if ($item['APPLY'] !== 'Y')
2989 {
2990 continue;
2991 }
2992
2993 if (empty($item['DESCR_DATA']))
2994 {
2995 continue;
2996 }
2997
2998 foreach ($item['DESCR_DATA'] as $rowDescription)
2999 {
3000 //TODO: remove this hack
3001 if (
3002 !empty($rowDescription['REVERT_APPLY']) &&
3003 $rowDescription['VALUE_ACTION'] === Discount\Formatter::VALUE_ACTION_CUMULATIVE
3004 )
3005 {
3006 $this->revertApplyBlockForBasketItem($basketItemId);
3007 }
3008 }
3009 }
3010 }
3011
3018 protected function revertApplyBlockForBasketItem($basketItemId)
3019 {
3020 if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]))
3021 {
3022 return;
3023 }
3024
3025 $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter];
3026 foreach ($applyBlock['ORDER'] as &$orderBlock)
3027 {
3028 foreach ($orderBlock['RESULT']['BASKET'] as $bid => &$basketItem)
3029 {
3030 if ($bid != $basketItemId)
3031 {
3032 continue;
3033 }
3034
3035 $basketItem['APPLY'] = 'N';
3036 }
3037 }
3038 }
3039
3045 protected function calculateApplySaleDiscountResult()
3046 {
3047 $result = new Result;
3048
3050 $couponClassName = $this->getDiscountCouponClassName();
3051
3052 $this->orderData['DISCOUNT_RESULT'] = Discount\Actions::getActionResult();
3053 if (!empty($this->orderData['DISCOUNT_RESULT']) && is_array($this->orderData['DISCOUNT_RESULT']))
3054 $stepResult = $this->getStepResult($this->orderData);
3055 else
3056 $stepResult = Discount\Result\CompatibleFormat::getStepResult(
3057 $this->orderData, $this->currentStep['oldData']
3058 );
3059 $applied = !empty($stepResult);
3060
3061 $orderDiscountId = 0;
3062 $orderCouponId = '';
3063
3064 if ($applied)
3065 {
3066 $this->correctStepResult($stepResult, $this->discountsCache[$this->currentStep['discountId']]);
3067
3068 $orderDiscountId = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['DISCOUNT_ID'];
3069 $orderCouponId = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['COUPON_ID'];
3070 }
3071
3072 unset($this->orderData['DISCOUNT_RESULT']);
3073
3074 if ($applied)
3075 {
3076 if (
3077 (
3078 !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId])
3079 && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N'
3080 )
3081 ||
3082 (
3083 $orderCouponId != ''
3084 && !empty($this->applyResult['COUPON_LIST'][$orderCouponId])
3085 && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N'
3086 )
3087 )
3088 {
3089 $this->orderData = $this->currentStep['oldData'];
3090 if (!empty($stepResult['BASKET']))
3091 {
3092 foreach ($stepResult['BASKET'] as &$basketItem)
3093 $basketItem['APPLY'] = 'N';
3094 unset($basketItem);
3095 }
3096 if (!empty($stepResult['DELIVERY']))
3097 $stepResult['DELIVERY']['APPLY'] = 'N';
3098 }
3099 else
3100 {
3101 if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT']))
3102 {
3103 $existDiscountResult = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT'];
3104 if (!empty($existDiscountResult['BASKET']))
3105 {
3106 $basketCodeList = $this->getBasketCodes(false);
3107 if (!empty($basketCodeList))
3108 {
3109 foreach ($basketCodeList as &$basketCode)
3110 {
3111 if ($this->isCustomPriceByCode($basketCode))
3112 continue;
3113 if (isset($existDiscountResult['BASKET'][$basketCode]))
3114 {
3115 $disable = ($existDiscountResult['BASKET'][$basketCode]['APPLY'] == 'N');
3116 if (isset($this->applyResult['BASKET'][$basketCode][$orderDiscountId]))
3117 {
3118 $applyDisable = ($this->applyResult['BASKET'][$basketCode][$orderDiscountId] == 'N');
3119 if ($disable != $applyDisable)
3120 $disable = $applyDisable;
3121 unset($applyDisable);
3122 }
3123 if ($disable)
3124 {
3125 $stepResult['BASKET'][$basketCode]['APPLY'] = 'N';
3126 $this->orderData['BASKET_ITEMS'][$basketCode] = $this->currentStep['oldData']['BASKET_ITEMS'][$basketCode];
3127 }
3128 else
3129 {
3130 $stepResult['BASKET'][$basketCode]['APPLY'] = 'Y';
3131 $this->orderData['BASKET_ITEMS'][$basketCode]['ACTION_APPLIED'] = 'Y';
3132 }
3133 }
3134 }
3135 unset($disable, $basketCode);
3136 }
3137 }
3138 if (!empty($existDiscountResult['DELIVERY']))
3139 {
3140 $disable = ($existDiscountResult['DELIVERY']['APPLY'] == 'N');
3141 if (!empty($this->applyResult['DELIVERY'][$orderDiscountId]))
3142 {
3143 $applyDisable = ($this->applyResult['DELIVERY'][$orderDiscountId] == 'N');
3144 if ($disable != $applyDisable)
3145 $disable = $applyDisable;
3146 unset($applyDisable);
3147 }
3148 if ($disable)
3149 {
3150 $this->orderData['PRICE_DELIVERY'] = $this->currentStep['oldData']['PRICE_DELIVERY'];
3151 $this->orderData['PRICE_DELIVERY_DIFF'] = $this->currentStep['oldData']['PRICE_DELIVERY_DIFF'];
3152 $stepResult['DELIVERY']['APPLY'] = 'N';
3153 }
3154 else
3155 {
3156 $stepResult['DELIVERY']['APPLY'] = 'Y';
3157 }
3158 unset($disable);
3159 }
3160 }
3161 }
3162 }
3163
3164 if ($applied && $orderCouponId != '')
3165 {
3166 $couponApply = $couponClassName::setApply($this->couponsCache[$orderCouponId]['COUPON'], $stepResult);
3167 unset($couponApply);
3168 }
3169
3170 if ($applied)
3171 {
3172 $this->mergeDiscountActionResult($this->currentStep['discountIndex'], $stepResult);
3173 }
3174 else
3175 {
3176 if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]))
3177 $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT'] = array();
3178 }
3179
3180 return $result;
3181 }
3182
3183 /* rounding tools */
3184
3191 protected function getRoundMode()
3192 {
3193 return $this->roundApplyMode;
3194 }
3195
3203 protected function isRoundMode($mode)
3204 {
3205 return $this->roundApplyMode == $mode;
3206 }
3207
3214 protected function loadRoundConfig()
3215 {
3216 $defaultApplyMode = self::ROUND_MODE_FINAL_PRICE;
3217 $this->roundApplyMode = $defaultApplyMode;
3218 $this->roundApplyConfig = array();
3219
3220 if ($this->isOrderExists() && !$this->isOrderNew() && $this->getUseMode() != self::USE_MODE_FULL)
3221 {
3222 $orderId = $this->getOrder()->getId();
3224 $storageClassName = $this->getOrderDiscountClassName();
3225 $entityData = $storageClassName::loadOrderStoredDataFromDb(
3226 $orderId,
3227 $storageClassName::STORAGE_TYPE_ROUND_CONFIG
3228 );
3229 unset($orderId);
3230
3231 if (
3232 is_array($entityData)
3233 && isset($entityData['MODE'])
3234 )
3235 {
3236 $this->roundApplyMode = (int)$entityData['MODE'];
3237 if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
3238 $this->roundApplyConfig = (isset($entityData['CONFIG']) ? $entityData['CONFIG'] : array());
3239 }
3240 }
3241
3242 if (
3243 $this->roundApplyMode != self::ROUND_MODE_BASKET_DISCOUNT
3244 && $this->roundApplyMode != self::ROUND_MODE_SALE_DISCOUNT
3245 && $this->roundApplyMode != self::ROUND_MODE_FINAL_PRICE
3246 )
3247 $this->roundApplyMode = null;
3248 if (!is_array($this->roundApplyConfig))
3249 $this->roundApplyConfig = array();
3250 unset($defaultApplyMode);
3251 }
3252
3261 protected function setRoundIndex($entity, array $index)
3262 {
3263 if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
3264 return;
3265 if (!isset($index['DISCOUNT_INDEX']) || !isset($index['DISCOUNT_ID']))
3266 return;
3267 if (!isset($this->roundApplyConfig[$this->discountResultCounter]))
3268 $this->roundApplyConfig[$this->discountResultCounter] = array();
3269 $this->roundApplyConfig[$this->discountResultCounter][$entity] = $index;
3270 }
3271
3280 protected function getRoundIndex($entity, $applyCounter = null)
3281 {
3282 if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
3283 return null;
3284 if ($applyCounter === null)
3285 $applyCounter = $this->discountResultCounter;
3286 return (isset($this->roundApplyConfig[$applyCounter][$entity]) ? $this->roundApplyConfig[$applyCounter][$entity] : null);
3287 }
3288
3294 protected function roundFullBasketPrices()
3295 {
3296 $basketCodeList = $this->getBasketCodes(true);
3297 if (!empty($basketCodeList))
3298 {
3299 $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND'];
3301 unset($orderData['BASKET_ITEMS']);
3302 $basket = array_intersect_key(
3303 $this->orderData['BASKET_ITEMS'],
3304 array_fill_keys($basketCodeList, true)
3305 );
3306
3308 $storageClassName = $this->getOrderDiscountClassName();
3309
3310 $result = $storageClassName::roundBasket(
3311 $basket,
3312 array(),
3314 );
3315 foreach ($result as $basketCode => $roundResult)
3316 {
3317 if (empty($roundResult) || !is_array($roundResult))
3318 continue;
3319 if (!$this->isExistBasketItem($basketCode))
3320 continue;
3321 $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE'];
3322 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE'];
3323
3324 $roundBlock[$basketCode] = array(
3325 'APPLY' => 'Y',
3326 'ROUND_RULE' => $roundResult['ROUND_RULE']
3327 );
3328 }
3329 unset($basketCode, $roundResult, $result);
3330 unset($storageClassName);
3331 unset($basket, $orderData);
3332 unset($roundBlock);
3333 }
3334 unset($basketCodeList);
3335 }
3336
3342 protected function roundApplyBasketPrices()
3343 {
3344 if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND']))
3345 return;
3346
3347 $basketCodeList = $this->getBasketCodes(false);
3348 if (!empty($basketCodeList))
3349 {
3350 $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND'];
3351 $basket = array();
3352 $roundData = array();
3353 foreach ($basketCodeList as $basketCode)
3354 {
3355 if (empty($roundBlock[$basketCode]))
3356 continue;
3357 if ($roundBlock[$basketCode]['APPLY'] != 'Y')
3358 continue;
3359 $basket[$basketCode] = $this->orderData['BASKET_ITEMS'][$basketCode];
3360 $roundData[$basketCode] = $roundBlock[$basketCode]['ROUND_RULE'];
3361 }
3362 unset($basketCode);
3363
3364 if (!empty($basket))
3365 {
3367 unset($orderData['BASKET_ITEMS']);
3368
3370 $storageClassName = $this->getOrderDiscountClassName();
3371
3372 $result = $storageClassName::roundBasket(
3373 $basket,
3374 $roundData,
3376 );
3377 foreach ($result as $basketCode => $roundResult)
3378 {
3379 if (empty($roundResult) || !is_array($roundResult))
3380 continue;
3381 if (!$this->isExistBasketItem($basketCode))
3382 continue;
3383 $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE'];
3384 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE'];
3385 }
3386 unset($basketCode, $roundResult, $result);
3387 unset($orderData);
3388 }
3389 unset($roundData, $basket);
3390
3391 unset($roundBlock);
3392 }
3393 unset($basketCodeList);
3394 }
3395
3401 protected function roundChangedBasketPrices()
3402 {
3403 $basketCodeList = array();
3404 $applyBlock = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter];
3405 switch ($this->getUseMode())
3406 {
3408 if (!empty($applyBlock['BASKET']))
3409 {
3410 foreach (array_keys($applyBlock['BASKET']) as $basketCode)
3411 {
3412 $basketCodeList[$basketCode] = $basketCode;
3413 }
3414 unset($basketCode);
3415 }
3416 if (!empty($applyBlock['ORDER']))
3417 {
3418 foreach ($applyBlock['ORDER'] as $discount)
3419 {
3420 if (empty($discount['RESULT']['BASKET']))
3421 continue;
3422 foreach (array_keys($discount['RESULT']['BASKET']) as $basketCode)
3423 {
3424 $basketCodeList[$basketCode] = $basketCode;
3425 }
3426 }
3427 unset($basketCode, $discount);
3428 }
3429 break;
3431 if (!empty($applyBlock['BASKET']))
3432 {
3433 foreach (array_keys($applyBlock['BASKET']) as $basketCode)
3434 {
3435 $basketCodeList[$basketCode] = $basketCode;
3436 }
3437 unset($basketCode);
3438 }
3439 if (!empty($applyBlock['ORDER']))
3440 {
3441 foreach ($applyBlock['ORDER'] as $discount)
3442 {
3443 if (empty($discount['RESULT']['BASKET']))
3444 continue;
3445 foreach (array_keys($discount['RESULT']['BASKET']) as $basketCode)
3446 {
3447 $basketCodeList[$basketCode] = $basketCode;
3448 }
3449 }
3450 unset($basketCode, $discount);
3451 }
3452 foreach ($this->getBasketCodes(true) as $basketCode)
3453 $basketCodeList[$basketCode] = $basketCode;
3454 break;
3455 }
3456
3457 if (!empty($basketCodeList))
3458 {
3459 $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND'];
3461 unset($orderData['BASKET_ITEMS']);
3462 $basket = array_intersect_key(
3463 $this->orderData['BASKET_ITEMS'],
3464 array_fill_keys($basketCodeList, true)
3465 );
3466
3468 $storageClassName = $this->getOrderDiscountClassName();
3469
3470 $result = $storageClassName::roundBasket(
3471 $basket,
3472 array(),
3474 );
3475
3476 foreach ($result as $basketCode => $roundResult)
3477 {
3478 if (empty($roundResult) || !is_array($roundResult))
3479 continue;
3480 if (!$this->isExistBasketItem($basketCode))
3481 continue;
3482 $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE'];
3483 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE'];
3484
3485 $roundBlock[$basketCode] = array(
3486 'APPLY' => 'Y',
3487 'ROUND_RULE' => $roundResult['ROUND_RULE']
3488 );
3489 }
3490 unset($basketCode, $roundResult, $result);
3491 unset($storageClassName);
3492 unset($basket, $orderData);
3493 unset($roundBlock);
3494 }
3495 unset($basketCodeList);
3496 }
3497
3505 protected function roundFullBasketPriceByIndex(array $index)
3506 {
3507 if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
3508 return;
3509 if ($this->getUseMode() != self::USE_MODE_FULL)
3510 return;
3511
3512 $this->roundFullBasketPrices();
3513 if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND']))
3514 $this->setRoundIndex('BASKET_ROUND', $index);
3515 }
3516
3524 protected function roundApplyBasketPricesByIndex(array $index)
3525 {
3526 if (!isset($index['DISCOUNT_INDEX']))
3527 return;
3528 if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
3529 return;
3530 if ($this->getUseMode() != self::USE_MODE_APPLY && $this->getUseMode() != self::USE_MODE_MIXED)
3531 return;
3532
3533 $roundConfig = $this->getRoundIndex('BASKET_ROUND');
3534 if ($roundConfig === null)
3535 return;
3536 if ($roundConfig['DISCOUNT_INDEX'] != $index['DISCOUNT_INDEX'])
3537 return;
3538 $this->roundApplyBasketPrices();
3539 }
3540
3541 /* rounding tools finish */
3542
3549 protected function convertDiscount($discount)
3550 {
3551 $result = new Result;
3552
3554 $storageClassName = $this->getOrderDiscountClassName();
3555
3556 $discountResult = $storageClassName::saveDiscount($discount, false);
3557 if (!$discountResult->isSuccess())
3558 {
3559 $result->addErrors($discountResult->getErrors());
3560 unset($discountResult);
3561 return $result;
3562 }
3563 $orderDiscountId = $discountResult->getId();
3564 $discountData = $discountResult->getData();
3565 $resultData = array(
3566 'ORDER_DISCOUNT_ID' => $orderDiscountId,
3567 'USE_COUPONS' => $discountData['USE_COUPONS'],
3568 'MODULE_ID' => $discountData['MODULE_ID'],
3569 'DISCOUNT_ID' => $discountData['DISCOUNT_ID']
3570 );
3571 if (!isset($this->discountsCache[$orderDiscountId]))
3572 {
3573 $discountData['ACTIONS_DESCR_DATA'] = false;
3574 if (!empty($discountData['ACTIONS_DESCR']) && is_array($discountData['ACTIONS_DESCR']))
3575 {
3576 $discountData['ACTIONS_DESCR_DATA'] = $discountData['ACTIONS_DESCR'];
3577 $discountData['ACTIONS_DESCR'] = $this->formatDescription($discountData['ACTIONS_DESCR']);
3578 }
3579 else
3580 {
3581 $discountData['ACTIONS_DESCR'] = false;
3582 }
3583 if (empty($discountData['ACTIONS_DESCR']))
3584 {
3585 $discountData['ACTIONS_DESCR'] = false;
3586 $discountData['ACTIONS_DESCR_DATA'] = false;
3587 }
3588 $this->discountsCache[$orderDiscountId] = $discountData;
3589 }
3590
3591 $result->setId($orderDiscountId);
3592 $result->setData($resultData);
3593 unset($discountData, $resultData, $orderDiscountId);
3594
3595 return $result;
3596 }
3597
3605 protected function convertCoupon($coupon, $discount)
3606 {
3607 $result = new Result;
3608
3610 $couponClassName = $this->getDiscountCouponClassName();
3611
3612 if (!is_array($coupon))
3613 {
3614 $couponData = $couponClassName::getEnteredCoupon($coupon, true);
3615 if (empty($couponData))
3616 {
3617 $result->addError(new Main\Entity\EntityError(
3618 Loc::getMessage('BX_SALE_DISCOUNT_ERR_COUPON_NOT_FOUND'),
3619 self::ERROR_ID
3620 ));
3621 return $result;
3622 }
3623 $coupon = array(
3624 'COUPON' => $couponData['COUPON'],
3625 'TYPE' => $couponData['TYPE'],
3626 'COUPON_ID' => $couponData['ID'],
3627 'DATA' => $couponData
3628 );
3629 unset($couponData);
3630 }
3631 $coupon['ORDER_DISCOUNT_ID'] = $discount;
3632 $coupon['ID'] = 0;
3633
3634 $orderCouponId = $coupon['COUPON'];
3635 if (!isset($this->couponsCache[$orderCouponId]))
3636 $this->couponsCache[$orderCouponId] = $coupon;
3637 $result->setId($orderCouponId);
3638 $result->setData($coupon);
3639 unset($coupon, $orderCouponId);
3640 return $result;
3641 }
3642
3649 protected static function getStepResult(array $order)
3650 {
3651 $result = array();
3652 $stepResult = &$order['DISCOUNT_RESULT'];
3653 if (!empty($stepResult['BASKET']) && is_array($stepResult['BASKET']))
3654 {
3655 if (!isset($result['BASKET']))
3656 $result['BASKET'] = array();
3657 foreach ($stepResult['BASKET'] as $basketCode => $basketResult)
3658 {
3659 $result['BASKET'][$basketCode] = array(
3660 'APPLY' => 'Y',
3661 'DESCR' => Discount\Formatter::formatList($basketResult),
3662 'DESCR_DATA' => $basketResult,
3663 'MODULE' => $order['BASKET_ITEMS'][$basketCode]['MODULE'],
3664 'PRODUCT_ID' => $order['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'],
3665 'BASKET_ID' => (isset($order['BASKET_ITEMS'][$basketCode]['ID']) ? $order['BASKET_ITEMS'][$basketCode]['ID'] : $basketCode),
3666 'ACTION_BLOCK_LIST' => array_keys($basketResult)
3667 );
3668 if (is_array($result['BASKET'][$basketCode]['DESCR']))
3669 $result['BASKET'][$basketCode]['DESCR'] = implode(', ', $result['BASKET'][$basketCode]['DESCR']);
3670 }
3671 unset($basketCode, $basketResult);
3672 }
3673 unset($stepResult);
3674
3675 return $result;
3676 }
3677
3685 protected function correctStepResult(&$stepResult, $discount)
3686 {
3687 if ($discount['USE_COUPONS'] == 'Y' && !empty($discount['COUPON']))
3688 {
3689 if (
3690 $discount['COUPON']['TYPE'] == Internals\DiscountCouponTable::TYPE_BASKET_ROW &&
3691 (!empty($stepResult['BASKET']) && count($stepResult['BASKET']) > 1)
3692 )
3693 {
3694 $maxPrice = 0;
3695 $maxKey = -1;
3696 $basketKeys = array();
3697 foreach ($stepResult['BASKET'] as $key => $row)
3698 {
3699 $basketKeys[$key] = $key;
3700 if ($maxPrice < $this->currentStep['oldData']['BASKET_ITEMS'][$key]['PRICE'])
3701 {
3702 $maxPrice = $this->currentStep['oldData']['BASKET_ITEMS'][$key]['PRICE'];
3703 $maxKey = $key;
3704 }
3705 }
3706 unset($basketKeys[$maxKey]);
3707 foreach ($basketKeys as $key => $row)
3708 {
3709 unset($stepResult['BASKET'][$key]);
3710 $this->orderData['BASKET_ITEMS'][$row] = $this->currentStep['oldData']['BASKET_ITEMS'][$row];
3711 }
3712 unset($row, $key);
3713 }
3714 }
3715 }
3716
3717 /* discount action reference tools */
3718
3727 protected function setDiscountStoredActionData($orderDiscountId, array $data)
3728 {
3729 $orderDiscountId = (int)$orderDiscountId;
3730 if (!isset($this->discountsCache[$orderDiscountId]))
3731 return;
3732 if (empty($data))
3733 return;
3734 if (!isset($this->discountStoredActionData[$this->discountResultCounter]))
3735 $this->discountStoredActionData[$this->discountResultCounter] = array();
3736 $this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId] = $data;
3737 }
3738
3745 protected function getDiscountStoredActionData($orderDiscountId)
3746 {
3747 $orderDiscountId = (int)$orderDiscountId;
3748 if (isset($this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId]))
3749 return $this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId];
3750 return null;
3751 }
3752
3753 /* discount action reference tools finish */
3754
3760 protected function isBasketApplyResultExist()
3761 {
3762 return (
3763 !empty($this->applyResult['DISCOUNT_LIST'])
3764 || !empty($this->applyResult['COUPON_LIST'])
3765 || !empty($this->applyResult['BASKET'])
3766 );
3767 }
3768
3774 protected function getApplyDiscounts()
3775 {
3776 $discountApply = array();
3777 $couponApply = array();
3778
3779 if (!empty($this->discountsCache))
3780 {
3781 foreach ($this->discountsCache as $id => $discount)
3782 {
3783 $this->discountResult['DISCOUNT_LIST'][$id] = array(
3784 'ID' => $id,
3785 'NAME' => $discount['NAME'],
3786 'MODULE_ID' => $discount['MODULE_ID'],
3787 'DISCOUNT_ID' => $discount['ID'],
3788 'REAL_DISCOUNT_ID' => $discount['DISCOUNT_ID'],
3789 'USE_COUPONS' => $discount['USE_COUPONS'],
3790 'ACTIONS_DESCR' => $discount['ACTIONS_DESCR'],
3791 'ACTIONS_DESCR_DATA' => $discount['ACTIONS_DESCR_DATA'],
3792 'APPLY' => 'N',
3793 'EDIT_PAGE_URL' => $discount['EDIT_PAGE_URL']
3794 );
3795 $discountApply[$id] = &$this->discountResult['DISCOUNT_LIST'][$id];
3796 }
3797 unset($id, $discount);
3798 }
3799
3800 if (!empty($this->couponsCache))
3801 {
3802 foreach ($this->couponsCache as $id => $coupon)
3803 {
3804 $this->discountResult['COUPON_LIST'][$id] = $coupon;
3805 $this->discountResult['COUPON_LIST'][$id]['APPLY'] = 'N';
3806 $couponApply[$id] = &$this->discountResult['COUPON_LIST'][$id];
3807 }
3808 unset($id, $coupon);
3809 }
3810
3811 if (empty($this->discountResult['APPLY_BLOCKS']))
3812 {
3813 unset($discountApply, $couponApply);
3814 return;
3815 }
3816
3817 foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock)
3818 {
3819 if (!empty($applyBlock['BASKET']))
3820 {
3821 foreach ($applyBlock['BASKET'] as $basketCode => $discountList)
3822 {
3823 foreach ($discountList as $discount)
3824 {
3825 if ($discount['RESULT']['APPLY'] == 'Y')
3826 {
3827 if (isset($discountApply[$discount['DISCOUNT_ID']]))
3828 $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y';
3829 if (isset($couponApply[$discount['COUPON_ID']]))
3830 $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y';
3831 }
3832 }
3833 unset($discount);
3834 }
3835 unset($basketCode, $discountList);
3836 }
3837
3838 if (!empty($applyBlock['ORDER']))
3839 {
3840 foreach ($applyBlock['ORDER'] as $discount)
3841 {
3842 if (!empty($discount['RESULT']['BASKET']))
3843 {
3844 foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyList)
3845 {
3846 if ($applyList['APPLY'] == 'Y')
3847 {
3848 if (isset($discountApply[$discount['DISCOUNT_ID']]))
3849 $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y';
3850 if (isset($couponApply[$discount['COUPON_ID']]))
3851 $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y';
3852 }
3853 }
3854 unset($basketCode, $applyList);
3855 }
3856 if (!empty($discount['RESULT']['DELIVERY']) && $discount['RESULT']['DELIVERY']['APPLY'] == 'Y')
3857 {
3858 if (isset($discountApply[$discount['DISCOUNT_ID']]))
3859 $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y';
3860 if (isset($couponApply[$discount['COUPON_ID']]))
3861 $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y';
3862 }
3863 }
3864 unset($discount);
3865 }
3866 }
3867 unset($counter, $applyBlock);
3868
3869 unset($discountApply, $couponApply);
3870 }
3871
3877 protected function getApplyPrices()
3878 {
3879 $this->normalizeDiscountResult();
3880
3881 $basket = [];
3882 if (!empty($this->orderData['BASKET_ITEMS']))
3883 {
3884 foreach ($this->orderData['BASKET_ITEMS'] as $basketCode => $basketItem)
3885 {
3886 $basket[$basketCode] = [
3887 'BASE_PRICE' => $basketItem['BASE_PRICE'],
3888 'PRICE' => $basketItem['PRICE'],
3889 'DISCOUNT' => $basketItem['DISCOUNT_PRICE']
3890 ];
3891 }
3892 unset($basketCode, $basketItem);
3893 }
3894
3895 $this->discountResult['PRICES'] = [
3896 'BASKET' => $basket
3897 ];
3898 unset($basket);
3899 }
3900
3906 protected function remakingDiscountResult()
3907 {
3908 $basket = [];
3909 if (!empty($this->discountResult['APPLY_BLOCKS']))
3910 {
3911 foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock)
3912 {
3913 if (!empty($applyBlock['BASKET']))
3914 {
3915 foreach ($applyBlock['BASKET'] as $basketCode => $discountList)
3916 {
3917 if (!isset($basket[$basketCode]))
3918 $basket[$basketCode] = [];
3919 foreach ($discountList as $discount)
3920 {
3921 $basket[$basketCode][] = [
3922 'DISCOUNT_ID' => $discount['DISCOUNT_ID'],
3923 'COUPON_ID' => $discount['COUPON_ID'],
3924 'APPLY' => $discount['RESULT']['APPLY'],
3925 'DESCR' => $discount['RESULT']['DESCR']
3926 ];
3927 }
3928 unset($discount);
3929 }
3930 unset($basketCode, $discountList);
3931 }
3932
3933 if (!empty($applyBlock['ORDER']))
3934 {
3935 foreach ($applyBlock['ORDER'] as $discount)
3936 {
3937 if (!empty($discount['RESULT']['BASKET']))
3938 {
3939 foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyList)
3940 {
3941 if (!isset($basket[$basketCode]))
3942 $basket[$basketCode] = [];
3943 $basket[$basketCode][] = [
3944 'DISCOUNT_ID' => $discount['DISCOUNT_ID'],
3945 'COUPON_ID' => $discount['COUPON_ID'],
3946 'APPLY' => $applyList['APPLY'],
3947 'DESCR' => $applyList['DESCR']
3948 ];
3949 }
3950 unset($basketCode, $applyList);
3951 }
3952 }
3953 unset($discount);
3954 }
3955 }
3956 unset($counter, $applyBlock);
3957 }
3958
3959 $this->discountResult['RESULT'] = [
3960 'BASKET' => $basket
3961 ];
3962 unset($basket);
3963 }
3964
3965 /* entities id tools */
3966
3972 protected function getBasketTables()
3973 {
3974 $result = new Result;
3975
3976 $this->forwardBasketTable = array();
3977 $this->reverseBasketTable = array();
3978
3979 if (!$this->isBasketNotEmpty())
3980 return $result;
3981
3982 $basket = $this->getBasket();
3984 foreach ($basket as $basketItem)
3985 {
3986 $code = $basketItem->getBasketCode();
3987 $id = $basketItem->getField('ID');
3988 $this->forwardBasketTable[$code] = $id;
3989 $this->reverseBasketTable[$id] = $code;
3990 unset($id, $code);
3991
3992 if ($basketItem->isBundleParent())
3993 {
3994 $bundle = $basketItem->getBundleCollection();
3995 if (empty($bundle))
3996 {
3997 $result->addError(new Main\Entity\EntityError(
3998 Loc::getMessage('BX_SALE_DISCOUNT_ERR_BASKET_BUNDLE_EMPTY'),
3999 self::ERROR_ID
4000 ));
4001 break;
4002 }
4004 foreach ($bundle as $bundleItem)
4005 {
4006 $code = $bundleItem->getBasketCode();
4007 $id = $bundleItem->getField('ID');
4008 $this->forwardBasketTable[$code] = $id;
4009 $this->reverseBasketTable[$id] = $code;
4010 unset($id, $code);
4011 }
4012 unset($bundle, $bundleItem);
4013 }
4014 }
4015 unset($basketItem, $basket);
4016
4017 return $result;
4018 }
4019
4026 protected function getEntitySaveIdentifier(array $entity)
4027 {
4028 $result = null;
4029
4030 switch ($entity['ENTITY_TYPE'])
4031 {
4033 $basketCode = $entity['ENTITY_CODE'];
4034 if (isset($this->forwardBasketTable[$basketCode]))
4035 {
4036 $result = [
4037 'ENTITY_TYPE' => self::ENTITY_BASKET_ITEM,
4038 'ENTITY_ID' => (int)$this->forwardBasketTable[$basketCode],
4039 'ENTITY_VALUE' => (string)$this->forwardBasketTable[$basketCode]
4040 ];
4041 }
4042 unset($basketCode);
4043 break;
4044 case self::ENTITY_ORDER:
4045 $result = [
4046 'ENTITY_TYPE' => self::ENTITY_ORDER,
4047 'ENTITY_ID' => (int)$entity['ENTITY_CODE'],
4048 'ENTITY_VALUE' => (string)$entity['ENTITY_CODE']
4049 ];
4050 break;
4051 }
4052
4053 return $result;
4054 }
4055
4056 /* entities id tools finish */
4057
4064 protected function isCustomPriceByCode($code)
4065 {
4066 if (!$this->isExistBasketItem($code))
4067 return false;
4068 return $this->isCustomPrice($this->orderData['BASKET_ITEMS'][$code]);
4069 }
4070
4077 protected static function isCustomPrice(array $item)
4078 {
4079 if (!empty($item['CUSTOM_PRICE']) && $item['CUSTOM_PRICE'] == 'Y')
4080 return true;
4081 return false;
4082 }
4083
4090 protected function isInSetByCode($code)
4091 {
4092 if (!$this->isExistBasketItem($code))
4093 return false;
4094 return $this->isInSet($this->orderData['BASKET_ITEMS'][$code]);
4095 }
4096
4103 protected static function isInSet(array $item)
4104 {
4105 if (!empty($item['IN_SET']) && $item['IN_SET'] == 'Y')
4106 return true;
4107 return false;
4108 }
4109
4116 protected function isNewBasketItemByCode($code)
4117 {
4118 return (
4119 $this->getUseMode() == self::USE_MODE_FULL
4120 || !isset($this->orderData['BASKET_ITEMS'][$code]['ID'])
4121 || $this->orderData['BASKET_ITEMS'][$code]['ID'] <= 0
4122 );
4123 }
4124
4131 protected static function isNewBasketItem(array $item)
4132 {
4133 return (
4134 !isset($item['ID'])
4135 || $item['ID'] <= 0
4136 );
4137 }
4138
4145 protected function isFreezedBasketItemByCode($code)
4146 {
4147 if (!$this->isExistBasketItem($code))
4148 return false;
4149 return $this->isFreezedBasketItem($this->orderData['BASKET_ITEMS'][$code]);
4150 }
4151
4158 protected static function isFreezedBasketItem(array $item)
4159 {
4160 return (static::isCustomPrice($item) || static::isInSet($item));
4161 }
4162
4169 protected function isBasketItemChanged($code)
4170 {
4171 $result = false;
4172 if ($this->isOrderExists() && !$this->isOrderNew() && $this->isBasketNotEmpty())
4173 {
4174 $basketItem = $this->getBasket()->getItemByBasketCode($code);
4175 if ($basketItem instanceof BasketItem)
4176 {
4178 if (in_array('PRODUCT_ID', $basketItem->getFields()->getChangedKeys()))
4179 $result = true;
4180 }
4181 }
4182 return $result;
4183 }
4184
4191 protected function isExistBasketItem($code)
4192 {
4193 if (!$this->isLoaded())
4194 return false;
4195 return isset($this->orderData['BASKET_ITEMS'][$code]);
4196 }
4197
4203 protected function isOrderChanged()
4204 {
4205 return $this->isMixedBasket();
4206 }
4207
4213 protected function isMixedBasket()
4214 {
4215 $result = false;
4216 if (empty($this->orderData['BASKET_ITEMS']))
4217 return $result;
4218
4219 foreach ($this->orderData['BASKET_ITEMS'] as $basketItem)
4220 {
4221 if (!isset($basketItem['ID']) || (int)$basketItem['ID'] <= 0)
4222 {
4223 $result = true;
4224 break;
4225 }
4226 }
4227 unset($basketItem);
4228
4229 if (!$result)
4230 {
4231 if ($this->isOrderedBasketChanged())
4232 $result = true;
4233 }
4234
4235 return $result;
4236 }
4237
4243 protected function isOrderedBasketChanged()
4244 {
4245 $result = false;
4246 if ($this->isOrderExists() && !$this->isOrderNew() && $this->isBasketNotEmpty())
4247 {
4248 $basket = $this->getBasket();
4250 foreach ($basket as $basketItem)
4251 {
4252 if (!$basketItem->isChanged())
4253 continue;
4255 if (in_array('PRODUCT_ID', $basketItem->getFields()->getChangedKeys()))
4256 {
4257 $result = true;
4258 break;
4259 }
4260 }
4261 unset($basketItem, $basket);
4262 }
4263 return $result;
4264 }
4265
4272 protected function getBasketCodes($full = true)
4273 {
4274 $result = [];
4275 if (empty($this->orderData['BASKET_ITEMS']))
4276 return $result;
4277 switch ($this->getUseMode())
4278 {
4281 foreach ($this->orderData['BASKET_ITEMS'] as $code => $item)
4282 {
4283 if ($this->isFreezedBasketItem($item))
4284 continue;
4285 $result[] = $code;
4286 }
4287 unset($code, $item);
4288 break;
4290 foreach ($this->orderData['BASKET_ITEMS'] as $code => $item)
4291 {
4292 if (
4293 $this->isFreezedBasketItem($item)
4294 || $this->isNewBasketItem($item)
4295 || $this->isBasketItemChanged($code)
4296 )
4297 continue;
4298 $result[] = $code;
4299 }
4300 unset($code, $item);
4301 break;
4303 $full = ($full === true);
4304 if ($full)
4305 {
4306 foreach ($this->orderData['BASKET_ITEMS'] as $code => $item)
4307 {
4308 if (
4309 !$this->isFreezedBasketItem($item)
4310 && ($this->isNewBasketItem($item) || $this->isBasketItemChanged($code))
4311 )
4312 $result[] = $code;
4313 }
4314 unset($code, $item);
4315 }
4316 else
4317 {
4318 foreach ($this->orderData['BASKET_ITEMS'] as $code => $item)
4319 {
4320 if (
4321 $this->isFreezedBasketItem($item)
4322 || $this->isNewBasketItem($item)
4323 || $this->isBasketItemChanged($code)
4324 )
4325 continue;
4326 $result[] = $code;
4327 }
4328 unset($code, $item);
4329 }
4330 break;
4331 }
4332
4333 return $result;
4334 }
4335
4336 protected function getAllowedBasketCodeList()
4337 {
4338 $result = [];
4339 if (empty($this->orderData['BASKET_ITEMS']))
4340 return $result;
4341
4342 foreach ($this->orderData['BASKET_ITEMS'] as $code => $item)
4343 {
4344 if ($this->isFreezedBasketItem($item))
4345 continue;
4346 $result[] = $code;
4347 }
4348 unset($code, $item);
4349
4350 return $result;
4351 }
4352
4360 protected function mergeDiscountActionResult($index, $stepResult)
4361 {
4362 if (!isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]))
4363 return;
4364 if (empty($stepResult) || !is_array($stepResult))
4365 return;
4366 $basketKeys = array_keys($this->orderData['BASKET_ITEMS']);
4367 foreach ($basketKeys as &$basketCode)
4368 {
4369 if (!$this->isCustomPriceByCode($basketCode))
4370 continue;
4371 if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['BASKET'][$basketCode]))
4372 unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['BASKET'][$basketCode]);
4373 }
4374 unset($basketCode);
4375 if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR_DATA']))
4376 unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR_DATA']);
4377 if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR']))
4378 unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR']);
4379 self::recursiveMerge($stepResult, $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']);
4380 $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT'] = $stepResult;
4381 }
4382
4388 protected function fillEmptyDiscountResult()
4389 {
4390 $this->discountResultCounter = 0;
4391 $this->discountResult = [
4392 'APPLY_BLOCKS' => [],
4393 'DISCOUNT_LIST' => [],
4394 'COUPON_LIST' => []
4395 ];
4396 $this->clearCurrentApplyBlock();
4397 $this->discountStoredActionData = [];
4398 $this->basketItemsData = [];
4399 $this->entityResultCache = [];
4400 }
4401
4407 protected function fillDiscountResult()
4408 {
4409 $this->normalizeDiscountResult();
4410 $basketKeys = ['PRICE', 'DISCOUNT_PRICE', 'VAT_RATE', 'VAT_VALUE', 'CURRENCY'];
4411 $result = [
4412 'BASKET_ITEMS' => [],
4413 'CURRENCY' => $this->orderData['CURRENCY']
4414 ];
4415 foreach ($this->orderData['BASKET_ITEMS'] as $index => $basketItem)
4416 {
4417 $result['BASKET_ITEMS'][$index] = [];
4418 foreach ($basketKeys as $key)
4419 {
4420 if (isset($basketItem[$key]))
4421 $result['BASKET_ITEMS'][$index][$key] = $basketItem[$key];
4422 }
4423 unset($key);
4424 }
4425 unset($index, $basketItem);
4426
4427 return $result;
4428 }
4429
4435 protected function clearCurrentApplyBlock()
4436 {
4437 $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter] = $this->getEmptyApplyBlock();
4438 }
4444 protected function fillEmptyCurrentStep()
4445 {
4446 $this->currentStep = array(
4447 'oldData' => array(),
4448 'discount' => array(),
4449 'discountIndex' => null,
4450 'discountId' => 0,
4451 'result' => array(),
4452 'stop' => false,
4453 );
4454 }
4455
4462 protected function fillCurrentStep($data)
4463 {
4464 $this->fillEmptyCurrentStep();
4465 if (!empty($data) && is_array($data))
4466 {
4467 foreach ($data as $key => $value)
4468 $this->currentStep[$key] = $value;
4469 unset($value, $key);
4470 }
4471 }
4472
4478 protected function extendOrderData()
4479 {
4480 if (empty($this->discountIds))
4481 return;
4482
4483 $entityCacheKey = md5(serialize($this->discountIds));
4484 if (!isset($this->entityResultCache[$entityCacheKey]))
4485 {
4486 $this->entityResultCache[$entityCacheKey] = array();
4487 $this->entityList = RuntimeCache\DiscountCache::getInstance()->getDiscountEntities($this->discountIds);
4488 if (empty($this->entityList))
4489 return;
4490
4491 $event = new Main\Event(
4492 'sale',
4493 self::EVENT_EXTEND_ORDER_DATA,
4494 array(
4495 'ORDER' => $this->orderData,
4496 'ENTITY' => $this->entityList
4497 )
4498 );
4499 $event->send();
4500 $this->entityResultCache[$entityCacheKey] = $event->getResults();
4501 }
4502 $resultList = $this->entityResultCache[$entityCacheKey];
4503 if (empty($resultList) || !is_array($resultList))
4504 return;
4506 foreach ($resultList as &$eventResult)
4507 {
4508 if ($eventResult->getType() != Main\EventResult::SUCCESS)
4509 continue;
4510
4511 $newData = $eventResult->getParameters();
4512 if (empty($newData) || !is_array($newData))
4513 continue;
4514
4515 $this->modifyOrderData($newData);
4516 }
4517 unset($newData, $eventResult, $resultList);
4518 }
4519
4526 protected function modifyOrderData(&$newData)
4527 {
4528 if (!empty($newData) && is_array($newData))
4529 self::recursiveMerge($this->orderData, $newData);
4530 }
4531
4538 protected static function formatDescription($descr)
4539 {
4540 $result = array();
4541 if (empty($descr) || !is_array($descr))
4542 return $result;
4543 if (isset($descr['DELIVERY']))
4544 {
4545 $result['DELIVERY'] = array();
4546 foreach ($descr['DELIVERY'] as $index => $value)
4547 {
4548 if (!is_array($value))
4549 continue;
4550 $result['DELIVERY'][$index] = Discount\Formatter::formatRow($value);
4551 if ($result['DELIVERY'][$index] === null)
4552 unset($result['DELIVERY'][$index]);
4553 }
4554 unset($value, $index);
4555 if (!empty($result['DELIVERY']))
4556 $result['DELIVERY'] = implode(', ', $result['DELIVERY']);
4557 }
4558 if (isset($descr['BASKET']))
4559 {
4560 $result['BASKET'] = array();
4561 foreach ($descr['BASKET'] as $index => $value)
4562 {
4563 if (!is_array($value))
4564 continue;
4565 $result['BASKET'][$index] = Discount\Formatter::formatRow($value);
4566 if ($result['BASKET'][$index] === null)
4567 unset($result['BASKET'][$index]);
4568 }
4569 unset($value, $index);
4570 if (!empty($result['BASKET']))
4571 $result['BASKET'] = implode(', ', $result['BASKET']);
4572 }
4573 return $result;
4574 }
4575
4581 protected function resetOrderState()
4582 {
4583 $this->resetPrices();
4584 $this->resetOrderPrice();
4585 $this->resetDiscountAppliedFlag();
4586 }
4587
4593 protected function resetPrices()
4594 {
4595 $this->resetBasketPrices();
4596 }
4597
4603 protected function resetOrderPrice(): void
4604 {
4605 if ($this->isOrderExists())
4606 {
4607 $order = $this->getOrder();
4608 $this->orderData['ORDER_PRICE'] = $order->getBasePrice();
4609 unset($order);
4610 }
4611 else
4612 {
4613 $basket = $this->getBasket();
4614 $this->orderData['ORDER_PRICE'] = $basket->getBasePrice();
4615 unset($basket);
4616 }
4617 }
4618
4624 protected function resetBasketPrices()
4625 {
4626 foreach ($this->orderData['BASKET_ITEMS'] as &$basketItem)
4627 {
4628 if ($this->isFreezedBasketItem($basketItem))
4629 continue;
4630 $basketItem = self::resetBasketItemPrice($basketItem);
4631 }
4632 unset($basketItem);
4633 }
4634
4640 protected function resetDiscountAppliedFlag()
4641 {
4642 foreach ($this->orderData['BASKET_ITEMS'] as &$basketItem)
4643 {
4644 if ($this->isFreezedBasketItem($basketItem))
4645 continue;
4646 $basketItem['ACTION_APPLIED'] = 'N';
4647 }
4648 }
4649
4655 protected function executeDiscountList()
4656 {
4657 $result = new Result;
4658
4659 $roundApply = true;
4660 $saleDiscountOnly = $this->useOnlySaleDiscounts();
4661 $useMode = $this->getUseMode();
4662 if ($saleDiscountOnly)
4663 {
4664 if ($useMode == self::USE_MODE_FULL && $this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT))
4665 $roundApply = false;
4666 }
4667
4668 $this->discountIds = array();
4669 if (empty($this->saleDiscountCacheKey) || empty($this->saleDiscountCache[$this->saleDiscountCacheKey]))
4670 {
4671 if (!$roundApply)
4672 {
4673 $this->roundFullBasketPriceByIndex(array(
4674 'DISCOUNT_INDEX' => -1,
4675 'DISCOUNT_ID' => 0
4676 ));
4677 }
4678 return $result;
4679 }
4680
4681 $currentList = $this->saleDiscountCache[$this->saleDiscountCacheKey];
4682 $this->discountIds = array_keys($currentList);
4683 $this->extendOrderData();
4684
4685 Discount\Actions::clearAction();
4686
4687 $blackList = array(
4688 self::getExecuteFieldName('UNPACK') => true,
4689 self::getExecuteFieldName('APPLICATION') => true,
4690 self::getExecuteFieldName('PREDICTIONS_APP') => true
4691 );
4692
4693 $index = -1;
4694 $skipPriorityLevel = null;
4695 foreach ($currentList as $discountIndex => $discount)
4696 {
4697 if($skipPriorityLevel == $discount['PRIORITY'])
4698 {
4699 continue;
4700 }
4701 $skipPriorityLevel = null;
4702
4703 $this->fillCurrentStep(array(
4704 'discount' => $discount,
4705 'cacheIndex' => $discountIndex
4706 ));
4707 if (!$this->checkDiscountConditions())
4708 continue;
4709
4710 $index++;
4711 if (!$roundApply && $discount['EXECUTE_MODULE'] == 'sale')
4712 {
4713 $this->roundFullBasketPriceByIndex(array(
4714 'DISCOUNT_INDEX' => $index,
4715 'DISCOUNT_ID' => $discount['ID']
4716 ));
4717 $roundApply = true;
4718 }
4719
4720 if ($useMode == self::USE_MODE_FULL && !isset($this->fullDiscountList[$discount['ID']]))
4721 $this->fullDiscountList[$discount['ID']] = array_diff_key($discount, $blackList);
4722
4723 $actionsResult = $this->applySaleDiscount();
4724 if (!$actionsResult->isSuccess())
4725 {
4726 $result->addErrors($actionsResult->getErrors());
4727 unset($actionsResult);
4728 return $result;
4729 }
4730
4731 if ($this->currentStep['stop'])
4732 break;
4733
4734 if (isset($this->currentStep['stopLevel']) && $this->currentStep['stopLevel'])
4735 {
4736 $skipPriorityLevel = $discount['PRIORITY'];
4737 }
4738 }
4739 unset($discount, $currentList);
4740 $this->fillEmptyCurrentStep();
4741
4742 if (!$roundApply)
4743 {
4744 $index++;
4745 $this->roundFullBasketPriceByIndex(array(
4746 'DISCOUNT_INDEX' => $index,
4747 'DISCOUNT_ID' => 0
4748 ));
4749 }
4750
4751 return $result;
4752 }
4753
4759 protected function fillBasketLastDiscount()
4760 {
4761 if ($this->getUseMode() != self::USE_MODE_FULL)
4762 return;
4763 $applyMode = self::getApplyMode();
4764 if ($applyMode == self::APPLY_MODE_ADD)
4765 return;
4766
4767 $codeList = array_keys($this->orderData['BASKET_ITEMS']);
4768 switch ($applyMode)
4769 {
4772 foreach ($codeList as &$code)
4773 {
4774 if (isset($this->basketDiscountList[$code]) && !empty($this->basketDiscountList[$code]))
4775 $this->orderData['BASKET_ITEMS'][$code]['LAST_DISCOUNT'] = 'Y';
4776 }
4777 unset($code);
4778 break;
4781 foreach ($codeList as &$code)
4782 {
4783 if (!isset($this->basketDiscountList[$code]) || empty($this->basketDiscountList[$code]))
4784 continue;
4785 $lastDiscount = end($this->basketDiscountList[$code]);
4786 if (!empty($lastDiscount['LAST_DISCOUNT']) && $lastDiscount['LAST_DISCOUNT'] == 'Y')
4787 $this->orderData['BASKET_ITEMS'][$code]['LAST_DISCOUNT'] = 'Y';
4788 }
4789 unset($code);
4790 break;
4791 }
4792 unset($codeList, $applyMode);
4793 }
4794
4800 protected function isBasketLastDiscount()
4801 {
4802 $result = false;
4803
4804 if ($this->getUseMode() != self::USE_MODE_FULL)
4805 return $result;
4806
4807 $this->fillBasketLastDiscount();
4808 $applyMode = self::getApplyMode();
4809 if ($applyMode == self::APPLY_MODE_FULL_LAST || $applyMode == self::APPLY_MODE_FULL_DISABLE)
4810 {
4811 foreach ($this->orderData['BASKET_ITEMS'] as $basketItem)
4812 {
4813 if (isset($basketItem['LAST_DISCOUNT']) && $basketItem['LAST_DISCOUNT'] == 'Y')
4814 {
4815 $result = true;
4816 break;
4817 }
4818 }
4819 unset($basketItem);
4820 }
4821 unset($applyMode);
4822
4823 return $result;
4824 }
4825
4826 /* additional coupons tools */
4827
4835 protected function clearAdditionalCoupons(array $coupons)
4836 {
4837 if (empty($coupons))
4838 return array();
4839
4840 if (empty($this->discountsCache))
4841 return $coupons;
4842
4843 $result = array();
4844
4845 foreach ($coupons as $couponCode => $couponData)
4846 {
4847 $found = false;
4848 foreach ($this->discountsCache as &$discount)
4849 {
4850 if (
4851 $discount['MODULE_ID'] == $couponData['MODULE_ID']
4852 && $discount['DISCOUNT_ID'] == $couponData['DISCOUNT_ID']
4853 && $discount['USE_COUPONS'] == 'N'
4854 )
4855 {
4856 $found = true;
4857 }
4858 }
4859 unset($discount);
4860
4861 if (!$found && !empty($this->couponsCache))
4862 {
4863 foreach ($this->couponsCache as $existCouponCode => $existCouponData)
4864 {
4865 $discount = $this->discountsCache[$existCouponData['ORDER_DISCOUNT_ID']];
4866 if (
4867 $discount['MODULE_ID'] != $couponData['MODULE_ID']
4868 || $discount['DISCOUNT_ID'] != $couponData['DISCOUNT_ID']
4869 )
4870 continue;
4871 if ($couponCode == $existCouponCode)
4872 {
4873 if (
4874 $existCouponData['ID'] > 0 || $existCouponData['TYPE'] == Internals\DiscountCouponTable::TYPE_BASKET_ROW
4875 )
4876 $found = true;
4877 }
4878 else
4879 {
4880 if (
4881 $existCouponData['TYPE'] != Internals\DiscountCouponTable::TYPE_BASKET_ROW
4882 || $couponData['TYPE'] != Internals\DiscountCouponTable::TYPE_BASKET_ROW
4883 )
4884 {
4885 $found = true;
4886 }
4887 else
4888 {
4889 if ($discount['MODULE_ID'] == 'sale')
4890 $found = true;
4891 }
4892 }
4893 unset($discount);
4894 if ($found)
4895 break;
4896 }
4897 unset($existCouponCode, $existCouponData);
4898 }
4899
4900 if (!$found)
4901 $result[$couponCode] = $couponData;
4902 unset($found);
4903 }
4904 unset($couponCode, $couponData);
4905
4906 return $result;
4907 }
4908
4916 protected function getAdditionalCoupons(array $filter = array())
4917 {
4918 if ($this->useOnlySaleDiscounts())
4919 {
4920 if (isset($filter['MODULE_ID']) && $filter['MODULE_ID'] != 'sale')
4921 return array();
4922 if (isset($filter['!MODULE_ID']) && $filter['!MODULE_ID'] == 'sale')
4923 return array();
4924 $filter['MODULE_ID'] = 'sale';
4925 }
4926
4928 $couponClassName = $this->getDiscountCouponClassName();
4929
4930 $useOrderCoupons = $couponClassName::isUsedOrderCouponsForApply();
4931 $couponClassName::useSavedCouponsForApply(false);
4932 $coupons = $couponClassName::getForApply($filter, array(), true);
4933 $couponClassName::useSavedCouponsForApply($useOrderCoupons);
4934 unset($useOrderCoupons);
4935
4936 if (empty($coupons))
4937 return array();
4938
4939 return $this->clearAdditionalCoupons($coupons);
4940 }
4941
4948 protected function calculateApplyBasketAdditionalCoupons(array $applyCoupons)
4949 {
4950 $result = new Result;
4951
4952 if ($this->useOnlySaleDiscounts())
4953 return $result;
4954 if (empty($applyCoupons))
4955 return $result;
4956
4958 $couponClassName = $this->getDiscountCouponClassName();
4959
4960 $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET'];
4961
4962 $applyExist = $this->isBasketApplyResultExist();
4963
4964 $basketCodeList = $this->getBasketCodes(false);
4965 foreach ($basketCodeList as &$basketCode)
4966 {
4967 if (array_key_exists($basketCode, $applyBlock))
4968 unset($applyBlock[$basketCode]);
4969 if (empty($applyCoupons[$basketCode]))
4970 continue;
4971
4972 $itemData = array(
4973 'MODULE_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['MODULE'],
4974 'PRODUCT_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'],
4975 'BASKET_ID' => $basketCode
4976 );
4977 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'];
4978 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'];
4979 foreach ($applyCoupons[$basketCode] as $index => $discount)
4980 {
4981 $discountResult = $this->convertDiscount($discount);
4982 if (!$discountResult->isSuccess())
4983 {
4984 $result->addErrors($discountResult->getErrors());
4985 unset($discountResult);
4986 return $result;
4987 }
4988 $orderDiscountId = $discountResult->getId();
4989 $discountData = $discountResult->getData();
4990 $applyCoupons[$basketCode][$index]['ORDER_DISCOUNT_ID'] = $orderDiscountId;
4991
4992 if (empty($discount['COUPON']))
4993 {
4994 $result->addError(new Main\Entity\EntityError(
4995 Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'),
4996 self::ERROR_ID
4997 ));
4998 return $result;
4999 }
5000 $couponResult = $this->convertCoupon($discount['COUPON'], $orderDiscountId);
5001 if (!$couponResult->isSuccess())
5002 {
5003 $result->addErrors($couponResult->getErrors());
5004 unset($couponResult);
5005 return $result;
5006 }
5007 $orderCouponId = $couponResult->getId();
5008
5009 $couponClassName::setApplyByProduct($itemData, array($orderCouponId));
5010 unset($couponResult);
5011
5012 unset($discountData, $discountResult);
5013 if (!isset($applyBlock[$basketCode]))
5014 $applyBlock[$basketCode] = array();
5015 $applyBlock[$basketCode][$index] = array(
5016 'DISCOUNT_ID' => $orderDiscountId,
5017 'COUPON_ID' => $orderCouponId,
5018 'RESULT' => array(
5019 'APPLY' => 'Y',
5020 'DESCR' => false,
5021 'DESCR_DATA' => false
5022 )
5023 );
5024
5025 $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode];
5026 $orderApplication = (
5027 !empty($this->discountsCache[$orderDiscountId]['APPLICATION'])
5028 ? $this->discountsCache[$orderDiscountId]['APPLICATION']
5029 : null
5030 );
5031 if (!empty($orderApplication))
5032 {
5033 $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = (
5034 !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'])
5035 ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']
5036 : false
5037 );
5038
5039 $applyProduct = null;
5040 eval('$applyProduct='.$orderApplication.';');
5041 if (is_callable($applyProduct))
5042 $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]);
5043 unset($applyProduct);
5044
5045 if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']))
5046 {
5047 $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']['BASKET'];
5048 $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = $this->formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
5049 }
5050 unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']);
5051 }
5052 unset($orderApplication);
5053
5054 if ($applyExist && !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId))
5055 {
5056 $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct;
5057 $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N';
5058 }
5059 unset($currentProduct);
5060 if ($applyBlock[$basketCode][$index]['RESULT']['APPLY'] == 'Y')
5061 $this->orderData['BASKET_ITEMS'][$basketCode]['ACTION_APPLIED'] = 'Y';
5062 }
5063 unset($discount, $index);
5064 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'];
5065 unset($this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']);
5066 }
5067 unset($basketCode, $basketCodeList);
5068
5069 unset($applyBlock);
5070
5071 return $result;
5072 }
5073
5080 protected function calculateApplySaleAdditionalCoupons(array $applyCoupons)
5081 {
5082 $result = new Result;
5083
5084 if (empty($applyCoupons))
5085 return $result;
5086
5087 $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'] = array();
5088
5089 $discountId = array();
5090 foreach ($applyCoupons as $coupon)
5091 $discountId[] = $coupon['DISCOUNT_ID'];
5092 unset($coupon);
5093
5094 $currentUseMode = $this->getUseMode();
5095 $this->setUseMode(self::USE_MODE_COUPONS);
5096
5097 $this->loadDiscountByUserGroups(array('@DISCOUNT_ID' => $discountId));
5098 unset($discountId);
5099
5100 $basketCodeList = $this->getBasketCodes(false);
5101 foreach ($basketCodeList as $basketCode)
5102 {
5103 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'];
5104 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'];
5105 }
5106 unset($basketCode);
5107
5108 $this->loadDiscountList();
5109 $executeResult = $this->executeDiscountList();
5110 if (!$executeResult->isSuccess())
5111 $result->addErrors($executeResult->getErrors());
5112 unset($executeResult);
5113 $this->setUseMode($currentUseMode);
5114 unset($currentUseMode);
5115
5116 foreach ($basketCodeList as $basketCode)
5117 {
5118 $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'];
5119 unset($this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']);
5120 }
5121 unset($basketCode);
5122 unset($basketCodeList);
5123
5124 return $result;
5125 }
5126
5127 /* additional coupons tools finish */
5128
5129 /* apply flag tools */
5130
5140 protected function getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId)
5141 {
5142 $disable = false;
5143 if (
5144 $orderCouponId != ''
5145 && !empty($this->applyResult['COUPON_LIST'][$orderCouponId])
5146 && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N'
5147 )
5148 {
5149 $disable = true;
5150 }
5151 else
5152 {
5153 if (
5154 !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId])
5155 && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N'
5156 )
5157 {
5158 $disable = true;
5159 }
5160 if (!empty($this->applyResult['BASKET'][$basketCode]))
5161 {
5162 if (is_string($this->applyResult['BASKET'][$basketCode]))
5163 $disable = ($this->applyResult['BASKET'][$basketCode] == 'N');
5164 elseif (!empty($this->applyResult['BASKET'][$basketCode][$orderDiscountId]))
5165 $disable = ($this->applyResult['BASKET'][$basketCode][$orderDiscountId] == 'N');
5166 }
5167 }
5168 return !$disable;
5169 }
5170
5177 protected function normalizeDiscountResult()
5178 {
5179 if (!empty($this->orderData['BASKET_ITEMS']))
5180 {
5181 foreach (array_keys($this->orderData['BASKET_ITEMS']) as $basketCode)
5182 {
5183 $customPrice = $this->isCustomPriceByCode($basketCode);
5184 $basketItem = $this->orderData['BASKET_ITEMS'][$basketCode];
5185 $basketItem['DISCOUNT_PRICE'] = (!$customPrice
5186 ? PriceMaths::roundPrecision($basketItem['DISCOUNT_PRICE'])
5187 : 0
5188 );
5189 if (!$customPrice)
5190 {
5191 if ($basketItem['DISCOUNT_PRICE'] > 0)
5192 $basketItem['PRICE'] = $basketItem['BASE_PRICE'] - $basketItem['DISCOUNT_PRICE'];
5193 else
5194 $basketItem['PRICE'] = PriceMaths::roundPrecision($basketItem['PRICE']);
5195 }
5196 $this->orderData['BASKET_ITEMS'][$basketCode] = $basketItem;
5197 }
5198 unset($basketItem, $customPrice, $basketCode);
5199 }
5200 }
5201
5202 /* Instances methods */
5203
5210 protected static function instanceExists($index)
5211 {
5212 $className = get_called_class();
5213 if (!isset(self::$instances[$className]))
5214 return false;
5215 return isset(self::$instances[$className][$index]);
5216 }
5217
5224 protected static function getInstance($index)
5225 {
5226 $className = get_called_class();
5227 if (!isset(self::$instances[$className]))
5228 self::$instances[$className] = array();
5229 if (!isset(self::$instances[$className][$index]))
5230 self::$instances[$className][$index] = self::createObject();
5231
5232 return self::$instances[$className][$index];
5233 }
5234
5235 protected static function migrateInstance($oldIndex, $newIndex)
5236 {
5237 $className = get_called_class();
5238 if (!isset(self::$instances[$className]))
5239 return;
5240 if (isset(self::$instances[$className][$oldIndex]) && !isset(self::$instances[$className][$newIndex]))
5241 {
5242 self::$instances[$className][$newIndex] = self::$instances[$className][$oldIndex];
5243 unset(self::$instances[$className][$oldIndex]);
5244 }
5245 }
5246
5247 protected static function removeInstance($index)
5248 {
5249 $className = get_called_class();
5250 if (!isset(self::$instances[$className]))
5251 return;
5252 if (isset(self::$instances[$className][$index]))
5253 unset(self::$instances[$className][$index]);
5254 }
5255
5263 public static function getRegistryType()
5264 {
5265 throw new Main\NotImplementedException();
5266 }
5267
5275 protected static function getInstanceIndexByOrder(OrderBase $order)
5276 {
5277 return $order->getInternalId().'|0'.'|'.$order->getSiteId();
5278 }
5279
5291 {
5292 if (!$context)
5293 return '0|'.$basket->getFUserId(false).'|'.$basket->getSiteId();
5294 return '0|-1|'.$basket->getSiteId().'|'.$context->getUserGroupsHash();
5295 }
5296
5297 /* Instances methods end */
5298
5304 public function getOrder()
5305 {
5306 return $this->order;
5307 }
5308
5314 public function isOrderExists()
5315 {
5316 return ($this->order instanceof OrderBase);
5317 }
5318
5325 protected function getSiteId()
5326 {
5327 if ($this->isOrderExists())
5328 return $this->order->getSiteId();
5329 if ($this->isBasketExist())
5330 return $this->basket->getSiteId();
5331 return null;
5332 }
5333
5340 protected function getCurrency()
5341 {
5342 if ($this->isOrderExists())
5343 return $this->order->getCurrency();
5344 $result = null;
5345 if ($this->isBasketExist())
5346 {
5347 if ($this->isBasketNotEmpty())
5348 {
5350 $basketItem = $this->basket->rewind();
5351 $result = $basketItem->getCurrency();
5352 unset($basketItem);
5353 }
5354 else
5355 {
5356 $result = $this->getSiteCurrency();
5357 }
5358 }
5359 return $result;
5360 }
5361
5368 protected function getSiteCurrency()
5369 {
5370 $result = Internals\SiteCurrencyTable::getCurrency($this->getSiteId());;
5371 if (is_array($result))
5372 return $result['CURRENCY'];
5373 $result = (string)Main\Config\Option::get('sale', 'default_currency');
5374 if ($result !== '')
5375 return $result;
5376 $result = Currency\CurrencyManager::getBaseCurrency();
5377 if ($result !== '')
5378 return $result;
5379 return null;
5380 }
5381
5382 /* Sale disocunt hit cache methods */
5391 final protected static function getExecuteFieldName($fieldName)
5392 {
5393 return self::EXECUTE_FIELD_PREFIX.$fieldName;
5394 }
5395
5403 protected function getExecuteFieldList()
5404 {
5405 return ['UNPACK', 'APPLICATION'];
5406 }
5407
5415 protected function getConditionField()
5416 {
5417 return 'UNPACK';
5418 }
5419
5429 protected function loadDiscountByUserGroups(array $filter = array())
5430 {
5431 $this->discountIds = array();
5432 $userGroups = $this->context->getUserGroups();
5433 if (empty($userGroups))
5434 {
5435 return;
5436 }
5437 $customFilter = array_diff_key(
5438 $filter,
5439 [
5440 '@GROUP_ID' => true,
5441 '=ACTIVE' => true,
5442 ]
5443 );
5444
5445 $filter['@GROUP_ID'] = $userGroups;
5446 $filter['=ACTIVE'] = 'Y';
5447
5448 //RuntimeCache works only with basic filter.
5449 if (empty($customFilter))
5450 {
5451 $this->discountIds = Discount\RuntimeCache\DiscountCache::getInstance()->getDiscountIds($userGroups);
5452 }
5453 else
5454 {
5455 $discountCache = array();
5456 $groupDiscountIterator = Internals\DiscountGroupTable::getList(array(
5457 'select' => array('DISCOUNT_ID'),
5458 'filter' => $filter,
5459 'order' => array('DISCOUNT_ID' => 'ASC')
5460 ));
5461 while ($groupDiscount = $groupDiscountIterator->fetch())
5462 {
5463 $groupDiscount['DISCOUNT_ID'] = (int)$groupDiscount['DISCOUNT_ID'];
5464 if ($groupDiscount['DISCOUNT_ID'] > 0)
5465 $discountCache[$groupDiscount['DISCOUNT_ID']] = $groupDiscount['DISCOUNT_ID'];
5466 }
5467 unset($groupDiscount, $groupDiscountIterator);
5468 $this->discountIds = $discountCache;
5469 unset($discountCache);
5470 }
5471 unset($userGroups);
5472 }
5473
5483 protected function loadDiscountModules(array $modules)
5484 {
5485 $result = true;
5486 if (empty($modules))
5487 return $result;
5488
5489 foreach ($modules as $moduleId)
5490 {
5491 if (!isset($this->loadedModules[$moduleId]))
5492 $this->loadedModules[$moduleId] = Main\Loader::includeModule($moduleId);
5493 if (!$this->loadedModules[$moduleId])
5494 {
5495 $result = false;
5496 break;
5497 }
5498 }
5499 unset($moduleId);
5500
5501 return $result;
5502 }
5503
5511 protected function loadDiscountList()
5512 {
5513 if (empty($this->discountIds))
5514 return;
5515
5517 array('MODULE_ID' => 'sale', 'DISCOUNT_ID' => $this->discountIds),
5518 array(),
5519 true
5520 );
5521
5522 $this->saleDiscountCacheKey = md5('D'.implode('_', $this->discountIds));
5523 if (!empty($couponList))
5524 $this->saleDiscountCacheKey .= '-C'.implode('_', array_keys($couponList));
5525
5526 $this->saleDiscountCacheKey .= '-MF'.implode('_', $this->executeModuleFilter);
5527
5528 if (!isset($this->saleDiscountCache[$this->saleDiscountCacheKey]))
5529 {
5530 $currentList = Discount\RuntimeCache\DiscountCache::getInstance()->getDiscounts(
5531 $this->discountIds,
5532 $this->executeModuleFilter,
5533 $this->getSiteId(),
5534 $couponList?: array()
5535 );
5536
5537 if (!empty($currentList))
5538 {
5539 $evalCode = '';
5540 $executeFields = $this->getExecuteFieldList();
5541 foreach (array_keys($currentList) as $index)
5542 {
5543 $discount = $currentList[$index];
5544 if (!$this->loadDiscountModules($discount['MODULES']))
5545 {
5546 unset($currentList[$index]);
5547 continue;
5548 }
5549
5550 foreach ($executeFields as $field)
5551 {
5552 if (!empty($discount[$field]))
5553 $evalCode .= '$currentList['.$index.'][\''.self::getExecuteFieldName($field).'\'] = '.$discount[$field].";\n";
5554 }
5555 }
5556 unset($field, $code, $discount, $index, $executeFields);
5557
5558 if ($evalCode !== '')
5559 {
5560 if (PHP_MAJOR_VERSION >= 7)
5561 {
5562 try
5563 {
5564 eval($evalCode);
5565 }
5566 catch (\ParseError $e)
5567 {
5568 $this->showAdminError();
5569 }
5570 }
5571 else
5572 {
5573 eval($evalCode);
5574 }
5575 }
5576 unset($evalCode);
5577 }
5578
5579 $this->saleDiscountCache[$this->saleDiscountCacheKey] = $currentList;
5580 }
5581 unset($couponList);
5582 }
5583 /* Sale disocunt hit cache methods end */
5584
5588 private static function createObject()
5589 {
5590 $registry = Registry::getInstance(static::getRegistryType());
5591 $discountClassName = $registry->getDiscountClassName();
5592
5593 return new $discountClassName();
5594 }
5595
5604 protected function getOrderDiscountClassName()
5605 {
5606 $registry = Registry::getInstance(static::getRegistryType());
5607 return $registry->getOrderDiscountClassName();
5608 }
5609
5618 protected function getDiscountCouponClassName()
5619 {
5620 $registry = Registry::getInstance(static::getRegistryType());
5621 return $registry->getDiscountCouponClassName();
5622 }
5623
5632 protected function getShipmentClassName()
5633 {
5634 $registry = Registry::getInstance(static::getRegistryType());
5635 return $registry->getShipmentClassName();
5636 }
5637
5644 protected function getEntityMarkerClassName(): string
5645 {
5646 $registry = Registry::getInstance(static::getRegistryType());
5647 return $registry->getEntityMarkerClassName();
5648 }
5649
5650 private function showAdminError()
5651 {
5652 $iterator = \CAdminNotify::GetList(
5653 array(),
5654 array('MODULE_ID' => 'sale', 'TAG' => self::ERROR_ID)
5655 );
5656 $notify = $iterator->Fetch();
5657 unset($iterator);
5658 if (empty($notify))
5659 {
5660 $defaultLang = '';
5661 $messages = array();
5662 $languages = Main\Localization\LanguageTable::getList(array(
5663 'select' => array('ID', 'DEF'),
5664 'filter' => array('=ACTIVE' => 'Y')
5665 ));
5666 while ($row = $languages->fetch())
5667 {
5668 if ($row['DEF'] == 'Y')
5669 $defaultLang = $row['ID'];
5670 $languageId = $row['ID'];
5671 Main\Localization\Loc::loadLanguageFile(
5672 __FILE__,
5673 $languageId
5674 );
5675 $messages[$languageId] = Main\Localization\Loc::getMessage(
5676 'BX_SALE_DISCOUNT_ERR_PARSE_ERROR',
5677 array('#LINK#' => '/bitrix/admin/settings.php?lang='.$languageId.'&mid=sale'),
5678 $languageId
5679 );
5680 }
5681 unset($row, $languages);
5682
5683 \CAdminNotify::Add(array(
5684 'MODULE_ID' => 'sale',
5685 'TAG' => self::ERROR_ID,
5686 'ENABLE_CLOSE' => 'N',
5687 'NOTIFY_TYPE' => \CAdminNotify::TYPE_ERROR,
5688 'MESSAGE' => $messages[$defaultLang],
5689 'LANG' => $messages
5690 ));
5691 unset($messages, $defaultLang);
5692 }
5693 unset($notify);
5694 }
5695
5701 protected static function getOrderPropertyCodes()
5702 {
5703 return [
5704 'DELIVERY_LOCATION' => 'IS_LOCATION',
5705 'USER_EMAIL' => 'IS_EMAIL',
5706 'PAYER_NAME' => 'IS_PAYER',
5707 'PROFILE_NAME' => 'IS_PROFILE_NAME',
5708 'DELIVERY_LOCATION_ZIP' => 'IS_ZIP'
5709 ];
5710 }
5711
5719 protected static function recursiveMerge(&$dest, $src)
5720 {
5721 if (!is_array($dest) || !is_array($src))
5722 return;
5723 if (empty($dest))
5724 {
5725 $dest = $src;
5726 return;
5727 }
5728 foreach ($src as $key => $value)
5729 {
5730 if (!isset($dest[$key]))
5731 {
5732 $dest[$key] = $value;
5733 continue;
5734 }
5735 if (is_array($dest[$key]))
5736 self::recursiveMerge($dest[$key], $value);
5737 }
5738 unset($value, $key);
5739 }
5740
5746 public static function getEmptyApplyBlock()
5747 {
5748 return array(
5749 'BASKET' => array(),
5750 'BASKET_ROUND' => array(),
5751 'ORDER' => array()
5752 );
5753 }
5754
5762 public static function calculateDiscountPercent($basePrice, $discount)
5763 {
5764 $basePrice = (float)$basePrice;
5765 if ($basePrice <= 0)
5766 return null;
5767 $discount = (float)$discount;
5768 if ($discount > $basePrice)
5769 return null;
5770
5771 $result = round(100*$discount/$basePrice, 0);
5772 if ($result < 0)
5773 $result = 0;
5774 return $result;
5775 }
5776
5782 public function getShowPrices()
5783 {
5784 if (!$this->isOrderNew() && !$this->isLoaded())
5785 {
5786 $this->initUseMode();
5787 $this->loadOrderData();
5788 }
5789
5790 $result = [
5791 'BASKET' => []
5792 ];
5793
5794 $checkRound = true;
5795 $useMode = $this->getUseMode();
5796 switch ($useMode)
5797 {
5798 case self::USE_MODE_APPLY:
5799 case self::USE_MODE_MIXED:
5800 if (!$this->isValidState())
5801 $checkRound = false;
5802 break;
5803 }
5804
5805 if (!empty($this->orderData['BASKET_ITEMS']))
5806 {
5808 $storageClassName = $this->getOrderDiscountClassName();
5809
5810 $basket = $this->orderData['BASKET_ITEMS'];
5811 $order = $this->orderData;
5812 unset($order['BASKET_ITEMS']);
5813
5814 switch ($useMode)
5815 {
5816 case self::USE_MODE_FULL:
5817 case self::USE_MODE_APPLY:
5818 if ($checkRound)
5819 {
5820 $basketCodeList = $this->getBasketCodes(true);
5821 $existRound = array();
5822 $existRoundRules = array();
5823 foreach ($basketCodeList as $basketCode)
5824 {
5825 if (!empty($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']))
5826 {
5827 $existRound[$basketCode] = self::resetBasketItemPrice($basket[$basketCode]);
5828 $existRoundRules[$basketCode] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'];
5829 }
5830 }
5831 if (!empty($existRound))
5832 {
5833 $roundResult = $storageClassName::roundBasket(
5834 $existRound,
5835 $existRoundRules,
5836 $order
5837 );
5838 foreach ($roundResult as $basketCode => $row)
5839 {
5840 if (empty($row) || !is_array($row))
5841 continue;
5842 if (!isset($existRound[$basketCode]))
5843 continue;
5844 $basket[$basketCode]['BASE_PRICE'] = $row['PRICE'];
5845 $basket[$basketCode]['DISCOUNT_PRICE'] = $basket[$basketCode]['BASE_PRICE'] - $basket[$basketCode]['PRICE'];
5846 }
5847 }
5848 unset($existRoundRules, $existRound);
5849 }
5850 break;
5851 case self::USE_MODE_MIXED:
5852 if ($checkRound)
5853 {
5854 $existRound = array();
5855 $existRoundRules = array();
5856 foreach ($basket as $basketCode => $item)
5857 {
5858 if ($this->isFreezedBasketItem($item))
5859 continue;
5860 if (!empty($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']))
5861 {
5862 $existRound[$basketCode] = self::resetBasketItemPrice($basket[$basketCode]);
5863 $existRoundRules[$basketCode] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'];
5864 }
5865 }
5866 unset($code, $item);
5867 if (!empty($existRound))
5868 {
5869 $roundResult = $storageClassName::roundBasket(
5870 $existRound,
5871 $existRoundRules,
5872 $order
5873 );
5874 foreach ($roundResult as $basketCode => $row)
5875 {
5876 if (empty($row) || !is_array($row))
5877 continue;
5878 if (!isset($existRound[$basketCode]))
5879 continue;
5880 $basket[$basketCode]['BASE_PRICE'] = $row['PRICE'];
5881 $basket[$basketCode]['DISCOUNT_PRICE'] = $basket[$basketCode]['BASE_PRICE'] - $basket[$basketCode]['PRICE'];
5882 }
5883 }
5884 unset($existRoundRules, $existRound);
5885 }
5886 break;
5887 }
5888
5889 foreach ($basket as $basketCode => $basketItem)
5890 {
5891 $result['BASKET'][$basketCode] = $this->getShowBasketItemPrice($basketCode, $basketItem);
5892 $result['BASKET'][$basketCode]['REAL_BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'];
5893 $result['BASKET'][$basketCode]['REAL_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'];
5894 $result['BASKET'][$basketCode]['REAL_DISCOUNT'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'];
5895 }
5896 unset($basketCode, $basketItem);
5897 }
5898
5899 return $result;
5900 }
5901
5908 private function getRoundForBasePrices()
5909 {
5910 $mode = $this->getUseMode();
5911 if ($mode != self::USE_MODE_FULL && $mode != self::USE_MODE_MIXED)
5912 return;
5913
5914 $basketCodeList = $this->getBasketCodes(true);
5915 if (empty($basketCodeList))
5916 return;
5917
5918 $basket = array_intersect_key(
5919 $this->orderData['BASKET_ITEMS'],
5920 array_fill_keys($basketCodeList, true)
5921 );
5922
5923 if (empty($basket))
5924 return;
5925
5926 foreach ($basketCodeList as $basketCode)
5927 {
5928 $this->addBasketItemValues(
5929 $basketCode,
5930 ['BASE_PRICE_ROUND' => $basket[$basketCode]['BASE_PRICE'], 'BASE_PRICE_ROUND_RULE' => []]
5931 );
5932 }
5933 unset($basketCode);
5934
5935 foreach ($basket as &$basketItem)
5936 $basketItem = self::resetBasketItemPrice($basketItem);
5937 unset($basketItem);
5938
5939 $orderData = $this->orderData;
5940 unset($orderData['BASKET_ITEMS']);
5941
5943 $storageClassName = $this->getOrderDiscountClassName();
5944
5945 $result = $storageClassName::roundBasket(
5946 $basket,
5947 array(),
5948 $orderData
5949 );
5950 foreach ($basketCodeList as $basketCode)
5951 {
5952 if (empty($result[$basketCode]) || !is_array($result[$basketCode]))
5953 continue;
5954 $this->addBasketItemValues(
5955 $basketCode,
5956 [
5957 'BASE_PRICE_ROUND' => $result[$basketCode]['PRICE'],
5958 'BASE_PRICE_ROUND_RULE' => $result[$basketCode]['ROUND_RULE']
5959 ]
5960 );
5961 }
5962 unset($basketCode, $result);
5963 unset($storageClassName);
5964 unset($basket, $orderData);
5965 }
5966
5974 private function getShowBasketItemPrice($basketCode, array $item)
5975 {
5976 if ($this->isFreezedBasketItem($item))
5977 {
5978 if ($item['BASE_PRICE'] <= $item['PRICE'])
5979 $result = $this->getShowPriceWithZeroDiscountPercent($item);
5980 else
5981 $result = $this->getShowPriceWithDiscountPercent($item);
5982 return $result;
5983 }
5984
5985 if ($item['BASE_PRICE'] <= $item['PRICE'])
5986 return $this->getShowPriceWithZeroDiscountPercent($item);
5987
5988 if ($this->isExistBasketItemDiscount($basketCode))
5989 return $this->getShowPriceWithDiscountPercent($item);
5990
5991 return $this->getShowPriceWithZeroDiscountPercent($item);
5992 }
5993
6000 private function getShowPriceWithDiscountPercent(array $item)
6001 {
6002 return [
6003 'SHOW_BASE_PRICE' => $item['BASE_PRICE'],
6004 'SHOW_PRICE' => $item['PRICE'],
6005 'SHOW_DISCOUNT' => $item['DISCOUNT_PRICE'],
6006 'SHOW_DISCOUNT_PERCENT' => $this->calculateDiscountPercent(
6007 $item['BASE_PRICE'],
6008 $item['DISCOUNT_PRICE']
6009 )
6010 ];
6011 }
6012
6019 private function getShowPriceWithZeroDiscountPercent(array $item)
6020 {
6021 return [
6022 'SHOW_BASE_PRICE' => $item['PRICE'],
6023 'SHOW_PRICE' => $item['PRICE'],
6024 'SHOW_DISCOUNT' => 0,
6025 'SHOW_DISCOUNT_PERCENT' => 0
6026 ];
6027 }
6028
6035 private function isExistBasketItemDiscount($basketCode)
6036 {
6037 $result = false;
6038
6039 if (!empty($this->discountResult['APPLY_BLOCKS']))
6040 {
6041 foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock)
6042 {
6043 if (isset($applyBlock['BASKET'][$basketCode]))
6044 {
6045 foreach ($applyBlock['BASKET'][$basketCode] as $discount)
6046 {
6047 if ($discount['RESULT']['APPLY'] == 'Y')
6048 $result = true;
6049 }
6050 unset($discount);
6051 }
6052
6053 if (!empty($applyBlock['ORDER']))
6054 {
6055 foreach ($applyBlock['ORDER'] as $discount)
6056 {
6057 if (!empty($discount['RESULT']['BASKET']))
6058 {
6059 if (!isset($discount['RESULT']['BASKET'][$basketCode]))
6060 continue;
6061 if ($discount['RESULT']['BASKET'][$basketCode]['APPLY'] == 'Y')
6062 $result = true;
6063 }
6064 }
6065 unset($discount);
6066 }
6067 }
6068 unset($counter, $applyBlock);
6069 }
6070
6071 return $result;
6072 }
6073
6080 private function prepareBasketItemStoredData($basketCode)
6081 {
6082 $result = [];
6083 if (isset($this->basketItemsData[$basketCode]))
6084 {
6085 if (isset($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']))
6086 $result['BASE_PRICE_ROUND_RULE'] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'];
6087 }
6088
6089 return (!empty($result) ? $result : null);
6090 }
6091
6098 private static function resetBasketItemPrice(array $item)
6099 {
6100 $item['PRICE'] = $item['BASE_PRICE'];
6101 $item['DISCOUNT_PRICE'] = 0;
6102
6103 return $item;
6104 }
6105
6113 private function addBasketItemValues($basketCode, array $values)
6114 {
6115 if (empty($values))
6116 return;
6117
6118 if (!isset($this->basketItemsData[$basketCode]))
6119 {
6120 $this->basketItemsData[$basketCode] = $values;
6121 }
6122 else
6123 {
6124 foreach ($values as $index => $value)
6125 $this->basketItemsData[$basketCode][$index] = $value;
6126 unset($index, $value);
6127 }
6128 }
6129}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static formatList(array $actionList)
static isCustomPrice(array $item)
getBasketItemFields(BasketItemBase $basketItem)
revertApplyBlockForBasketItem($basketItemId)
createClone(\SplObjectStorage $cloneEntity)
static recursiveMerge(&$dest, $src)
static formatDescription($descr)
static getExecuteFieldName($fieldName)
loadDiscountModules(array $modules)
static isInSet(array $item)
static removeInstance($index)
correctStepResult(&$stepResult, $discount)
setDiscountStoredActionData($orderDiscountId, array $data)
normalizeNewResultRows(array &$rows)
getEntitySaveIdentifier(array $entity)
getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId)
roundApplyBasketPricesByIndex(array $index)
getApplyResult($extMode=false)
loadDiscountByUserGroups(array $filter=array())
static isFreezedBasketItem(array $item)
roundFullBasketPriceByIndex(array $index)
setRoundIndex($entity, array $index)
static getInstanceIndexByOrder(OrderBase $order)
static instanceExists($index)
clearAdditionalCoupons(array $coupons)
getRoundIndex($entity, $applyCounter=null)
getDiscountStoredActionData($orderDiscountId)
static isNewBasketItem(array $item)
mergeDiscountActionResult($index, $stepResult)
setExecuteModuleFilter(array $moduleList)
static buildFromBasket(BasketBase $basket, Context\BaseContext $context)
getBasketItemValueList($code, array $fields)
static getInstanceIndexByBasket(BasketBase $basket, Context\BaseContext $context=null)
applyLoadedOrderConfig(array $data)
static getStepResult(array $order)
static migrateInstance($oldIndex, $newIndex)
tryToRevertApplyStatusInBlocks(array $stepResult)
calculateApplySaleAdditionalCoupons(array $applyCoupons)
static getApplyModeList($extendedMode=false)
getBasketItemValue($code, $field)
static getForApply($filter, $product=[], $uniqueDiscount=false)
static roundPrecision($value)
static getInstance($type)
Definition registry.php:183