Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
actions.php
1<?php
3
7
8Loc::loadMessages(__FILE__);
9
11{
12 const VALUE_TYPE_FIX = 'F';
13 const VALUE_TYPE_PERCENT = 'P';
14 const VALUE_TYPE_SUMM = 'S';
16
17 const GIFT_SELECT_TYPE_ONE = 'one';
18 const GIFT_SELECT_TYPE_ALL = 'all';
19
20 const BASKET_APPLIED_FIELD = 'DISCOUNT_APPLIED';
21
22 const VALUE_EPS = 1E-5;
23
24 const MODE_CALCULATE = 0x0001;
25 const MODE_MANUAL = 0x0002;
26 const MODE_MIXED = 0x0004;
27
29
32
33 const RESULT_ENTITY_BASKET = 0x0001;
34 const RESULT_ENTITY_DELIVERY = 0x0002;
35 const RESULT_ENTITY_ORDER = 0x0004;
36
40
42 const ACTION_TYPE_EXTRA = 'E';
43
44 protected static $useMode = self::MODE_CALCULATE;
46 protected static $actionResult = array();
47 protected static $actionDescription = array();
48 protected static $applyResult = array();
50 protected static $storedData = array();
51
52 protected static $useBasketFilter = true;
53
55 protected static $currencyId = '';
56 protected static $siteId = '';
57
58 private static $compatibleBasketFields = array('DISCOUNT_PRICE', 'PRICE', 'VAT_VALUE', 'PRICE_DEFAULT');
59
66 public static function roundZeroValue($value)
67 {
68 return (abs($value) <= self::VALUE_EPS ? 0 : $value);
69 }
70
78 public static function roundValue($value, $currency)
79 {
81 return Sale\PriceMaths::roundPrecision($value);
82 }
83
91 public static function setUseMode($mode, array $config = array())
92 {
93 $mode = (int)$mode;
94 if ($mode !== self::MODE_CALCULATE && $mode !== self::MODE_MANUAL && $mode !== self::MODE_MIXED)
95 return;
96 self::$useMode = $mode;
97 switch (self::$useMode)
98 {
100 $percentOption = (string)Main\Config\Option::get('sale', 'get_discount_percent_from_base_price');
101 self::$percentValueMode = ($percentOption == 'Y' ? self::PERCENT_FROM_BASE_PRICE : self::PERCENT_FROM_CURRENT_PRICE);
102 unset($percentOption);
103 if (isset($config['CURRENCY']))
104 self::$currencyId = $config['CURRENCY'];
105 if (isset($config['SITE_ID']))
106 {
107 self::$siteId = $config['SITE_ID'];
108 if (self::$currencyId == '')
109 self::$currencyId = Sale\Internals\SiteCurrencyTable::getSiteCurrency(self::$siteId);
110 }
111 break;
113 case self::MODE_MIXED:
114 $percentOption = '';
115 if (isset($config['USE_BASE_PRICE']))
116 $percentOption = $config['USE_BASE_PRICE'];
117 if ($percentOption == '')
118 $percentOption = (string)Main\Config\Option::get('sale', 'get_discount_percent_from_base_price');
119 self::$percentValueMode = ($percentOption == 'Y' ? self::PERCENT_FROM_BASE_PRICE : self::PERCENT_FROM_CURRENT_PRICE);
120 unset($percentOption);
121 if (isset($config['CURRENCY']))
122 self::$currencyId = $config['CURRENCY'];
123 if (isset($config['SITE_ID']))
124 {
125 self::$siteId = $config['SITE_ID'];
126 if (self::$currencyId == '')
127 self::$currencyId = Sale\Internals\SiteCurrencyTable::getSiteCurrency(self::$siteId);
128 }
129 break;
130 }
131 static::clearApplyCounter();
132 static::enableBasketFilter();
133 }
134
140 public static function getUseMode()
141 {
142 return self::$useMode;
143 }
144
150 public static function isCalculateMode()
151 {
152 return self::$useMode === self::MODE_CALCULATE;
153 }
154
160 public static function isManualMode()
161 {
162 return self::$useMode === self::MODE_MANUAL;
163 }
164
170 public static function isMixedMode()
171 {
172 return self::$useMode === self::MODE_MIXED;
173 }
174
181 public static function checkUseMode(array $list)
182 {
183 return (in_array(self::$useMode, $list, true));
184 }
185
191 public static function getPercentMode()
192 {
194 }
195
201 public static function getCurrency()
202 {
203 return self::$currencyId;
204 }
205
211 public static function clearApplyCounter()
212 {
213 self::$applyCounter = self::APPLY_COUNTER_START;
214 }
215
221 public static function getApplyCounter()
222 {
223 return self::$applyCounter;
224 }
225
231 public static function increaseApplyCounter()
232 {
233 self::$applyCounter++;
234 }
235
241 public static function disableBasketFilter()
242 {
243 if (!static::isMixedMode())
244 return;
245 self::$useBasketFilter = false;
246 }
247
253 public static function enableBasketFilter()
254 {
255 if (!static::isMixedMode())
256 return;
257 self::$useBasketFilter = true;
258 }
259
265 public static function usedBasketFilter()
266 {
268 }
269
276 public static function fillCompatibleFields(array &$order)
277 {
278 $adminSection = Main\Context::getCurrent()->getRequest()->isAdminSection();
279 if (empty($order) || !is_array($order))
280 return;
281 if (!empty($order['BASKET_ITEMS']) && is_array($order['BASKET_ITEMS']))
282 {
283 foreach ($order['BASKET_ITEMS'] as &$item)
284 {
285 if (isset($item['PRICE_DEFAULT']))
286 $item['PRICE_DEFAULT'] = $item['PRICE'];
287 if ($adminSection)
288 continue;
289
290 foreach (self::$compatibleBasketFields as &$fieldName)
291 {
292 if (array_key_exists($fieldName, $item) && !is_array($item[$fieldName]))
293 $item['~'.$fieldName] = $item[$fieldName];
294 }
295 unset($fieldName);
296 }
297 unset($item);
298 }
299 }
300
307 public static function filterBasketForAction(array $item)
308 {
309 return (
310 (!isset($item['CUSTOM_PRICE']) || $item['CUSTOM_PRICE'] != 'Y') &&
311 (
312 (isset($item['TYPE']) && (int)$item['TYPE'] == Sale\BasketItem::TYPE_SET) ||
313 (!isset($item['SET_PARENT_ID']) || (int)$item['SET_PARENT_ID'] <= 0)
314 ) &&
315 (!isset($item['ITEM_FIX']) || $item['ITEM_FIX'] != 'Y') &&
316 (!isset($item['LAST_DISCOUNT']) || $item['LAST_DISCOUNT'] != 'Y') &&
317 (!isset($item['IN_SET']) || $item['IN_SET'] != 'Y')
318 );
319 }
320
326 public static function getActionDescription()
327 {
329 }
330
336 public static function getActionResult()
337 {
338 return self::$actionResult;
339 }
340
347 public static function setApplyResultMode($mode)
348 {
349 $mode = (int)$mode;
350 if ($mode != self::APPLY_RESULT_MODE_COUNTER && $mode != self::APPLY_RESULT_MODE_DESCR && $mode != self::APPLY_RESULT_MODE_SIMPLE)
351 return;
352 self::$applyResultMode = $mode;
353 self::$applyResult = array();
354 }
355
361 public static function getApplyResultMode()
362 {
364 }
365
372 public static function setApplyResult(array $applyResult)
373 {
374 self::$applyResult = $applyResult;
375 }
376
383 public static function setStoredData(array $data)
384 {
385 self::$storedData = $data;
386 }
387
393 public static function getStoredData()
394 {
395 return self::$storedData;
396 }
397
404 public static function setActionStoredData(array $data)
405 {
406 if (!static::isCalculateMode())
407 return;
408 if (empty($data))
409 return;
410 self::$storedData[static::getApplyCounter()] = $data;
411 }
412
418 public static function getActionStoredData()
419 {
420 $counter = static::getApplyCounter();
421 if (isset(self::$storedData[$counter]))
422 return self::$storedData[$counter];
423 return null;
424 }
425
431 public static function clearAction()
432 {
434 self::$applyResult = array();
435 self::$actionResult = array();
436 self::$actionDescription = array();
437 self::$storedData = array();
438 }
439
455 public static function applyToBasket(array &$order, array $action, $filter)
456 {
457 static::increaseApplyCounter();
458
459 if (!isset($action['VALUE']) || !isset($action['UNIT']))
460 return;
461
462 $orderCurrency = static::getCurrency();
463 $value = (float)$action['VALUE'];
464 $limitValue = (int)$action['LIMIT_VALUE'];
465 $unit = (string)$action['UNIT'];
466 $currency = (isset($action['CURRENCY']) ? $action['CURRENCY'] : $orderCurrency);
467 $maxBound = false;
468 if ($unit == self::VALUE_TYPE_FIX && $value < 0)
469 $maxBound = (isset($action['MAX_BOUND']) && $action['MAX_BOUND'] == 'Y');
470 $valueAction = (
471 $value < 0
474 );
475
476 $actionDescription = array(
477 'ACTION_TYPE' => Formatter::TYPE_VALUE,
478 'VALUE' => abs($value),
479 'VALUE_ACTION' => $valueAction
480 );
481 switch ($unit)
482 {
485 'ACTION_TYPE' => Formatter::TYPE_VALUE,
486 'VALUE' => abs($value),
487 'VALUE_ACTION' => ($value < 0 ? Formatter::VALUE_ACTION_DISCOUNT : Formatter::VALUE_ACTION_EXTRA),
488 'VALUE_TYPE' => Formatter::VALUE_TYPE_SUMM,
489 'VALUE_UNIT' => $currency
490 ];
491 break;
494 'ACTION_TYPE' => Formatter::TYPE_VALUE,
495 'VALUE' => abs($value),
496 'VALUE_ACTION' => ($value < 0 ? Formatter::VALUE_ACTION_DISCOUNT : Formatter::VALUE_ACTION_EXTRA),
497 'VALUE_TYPE' => Formatter::VALUE_TYPE_PERCENT
498 ];
499 break;
502 'ACTION_TYPE' => ($maxBound ? Formatter::TYPE_MAX_BOUND : Formatter::TYPE_VALUE),
503 'VALUE' => abs($value),
504 'VALUE_ACTION' => ($value < 0 ? Formatter::VALUE_ACTION_DISCOUNT : Formatter::VALUE_ACTION_EXTRA),
505 'VALUE_TYPE' => Formatter::VALUE_TYPE_CURRENCY,
506 'VALUE_UNIT' => $currency
507 ];
508 break;
511 'ACTION_TYPE' => Formatter::TYPE_FIXED,
512 'VALUE' => abs($value),
513 'VALUE_ACTION' => Formatter::VALUE_ACTION_DISCOUNT,
514 'VALUE_TYPE' => Formatter::VALUE_TYPE_CURRENCY,
515 'VALUE_UNIT' => $currency
516 ];
517 break;
518 default:
519 return;
520 break;
521 }
522 $valueAction = $actionDescription['VALUE_ACTION'];
523
524 if(!empty($limitValue))
525 {
528 $actionDescription['LIMIT_UNIT'] = $orderCurrency;
529 $actionDescription['LIMIT_VALUE'] = $limitValue;
530 }
531
532 static::setActionDescription(self::RESULT_ENTITY_BASKET, $actionDescription);
533
534 if (empty($order['BASKET_ITEMS']) || !is_array($order['BASKET_ITEMS']))
535 return;
536
537 static::enableBasketFilter();
538 $filteredBasket = static::getBasketForApply($order['BASKET_ITEMS'], $filter, $action);
539 if (empty($filteredBasket))
540 return;
541
542 $applyBasket = array_filter($filteredBasket, '\Bitrix\Sale\Discount\Actions::filterBasketForAction');
543 unset($filteredBasket);
544 if (empty($applyBasket))
545 return;
546
547 if ($unit == self::VALUE_TYPE_SUMM || $unit == self::VALUE_TYPE_FIX)
548 {
549 if ($currency != $orderCurrency)
550 $value = \CCurrencyRates::ConvertCurrency($value, $currency, $orderCurrency);
551 if ($unit == self::VALUE_TYPE_SUMM)
552 {
553 $value = static::getPercentByValue($applyBasket, $value);
554 if (
555 ($valueAction == Formatter::VALUE_ACTION_DISCOUNT && ($value >= 0 || $value < -100))
556 ||
557 ($valueAction == Formatter::VALUE_ACTION_EXTRA && $value <= 0)
558 )
559 return;
561 }
562 }
563 $value = static::roundZeroValue($value);
564 if ($value == 0)
565 return;
566
567 foreach ($applyBasket as $basketCode => $basketRow)
568 {
569 list($calculateValue, $result) = self::calculateDiscountPrice(
570 $value,
571 $unit,
572 $basketRow,
573 $limitValue,
574 $maxBound
575 );
576 if ($result >= 0)
577 {
578 self::fillDiscountPrice($basketRow, $result, -$calculateValue);
579
580 $order['BASKET_ITEMS'][$basketCode] = $basketRow;
581
582 $rowActionDescription = $actionDescription;
583 $rowActionDescription['BASKET_CODE'] = $basketCode;
584 $rowActionDescription['RESULT_VALUE'] = abs($calculateValue);
585 $rowActionDescription['RESULT_UNIT'] = $orderCurrency;
586
587 if(!empty($limitValue))
588 {
589 $rowActionDescription['ACTION_TYPE'] = Formatter::TYPE_LIMIT_VALUE;
590 $rowActionDescription['LIMIT_TYPE'] = Formatter::LIMIT_MAX;
591 $rowActionDescription['LIMIT_UNIT'] = $orderCurrency;
592 $rowActionDescription['LIMIT_VALUE'] = $limitValue;
593 }
594
595 static::setActionResult(self::RESULT_ENTITY_BASKET, $rowActionDescription);
596 unset($rowActionDescription);
597 }
598 unset($result);
599 }
600 unset($basketCode, $basketRow);
601 }
602
613 public static function applyCumulativeToBasket(array &$order, array $ranges, array $configuration = array(), $filter = null)
614 {
615 static::increaseApplyCounter();
616
617 Main\Type\Collection::sortByColumn($ranges, 'sum');
618
619 $sumConfiguration = $configuration['sum']?: array();
620 $applyIfMoreProfitable = $configuration['apply_if_more_profitable'] === 'Y';
621
622 if (static::checkUseMode(array(self::MODE_MANUAL, self::MODE_MIXED)))
623 {
624 $actionStoredData = self::getActionStoredData();
625 $cumulativeOrderUserValue = $actionStoredData['cumulative_value'];
626 }
627 else
628 {
629 $cumulativeCalculator = new CumulativeCalculator((int)$order['USER_ID'], $order['SITE_ID']);
630 $cumulativeCalculator->setSumConfiguration(
631 array(
632 'type_sum_period' => $sumConfiguration['type_sum_period'],
633 'sum_period_data' => array(
634 'order_start' => $sumConfiguration['sum_period_data']['discount_sum_order_start'],
635 'order_end' => $sumConfiguration['sum_period_data']['discount_sum_order_end'],
636 'period_value' => $sumConfiguration['sum_period_data']['discount_sum_period_value'],
637 'period_type' => $sumConfiguration['sum_period_data']['discount_sum_period_type'],
638 ),
639 )
640 );
641 $cumulativeOrderUserValue = $cumulativeCalculator->calculate();
642 }
643
644 $rangeToApply = null;
645 foreach ($ranges as $range)
646 {
647 if ($cumulativeOrderUserValue >= $range['sum'])
648 {
649 $rangeToApply = $range;
650 }
651 }
652
653 if (!$rangeToApply)
654 {
655 return;
656 }
657
658 $action = array(
659 'VALUE' => -$rangeToApply['value'],
660 'UNIT' => $rangeToApply['type'],
661 );
662
663 if (!isset($action['VALUE']) || !isset($action['UNIT']))
664 return;
665
666 $orderCurrency = static::getCurrency();
667 $value = (float)$action['VALUE'];
668 $limitValue = (int)$action['LIMIT_VALUE'];
669 $unit = (string)$action['UNIT'];
670 $currency = (isset($action['CURRENCY']) ? $action['CURRENCY'] : $orderCurrency);
671 $maxBound = false;
672 if ($unit == self::VALUE_TYPE_FIX && $value < 0)
673 $maxBound = (isset($action['MAX_BOUND']) && $action['MAX_BOUND'] == 'Y');
675
676 $actionDescription = array(
677 'ACTION_TYPE' => Formatter::TYPE_VALUE,
678 'VALUE' => abs($value),
679 'VALUE_ACTION' => $valueAction
680 );
681 switch ($unit)
682 {
685 break;
688 $actionDescription['VALUE_UNIT'] = $currency;
689 if ($maxBound)
691 break;
692 default:
693 return;
694 }
695
696 if ($unit == self::VALUE_TYPE_FIX && $currency != $orderCurrency)
697 {
698 $value = \CCurrencyRates::ConvertCurrency($value, $currency, $orderCurrency);
699 }
700
701 $value = static::roundZeroValue($value);
702 if ($value == 0)
703 {
704 return;
705 }
706
707 if(!empty($limitValue))
708 {
711 $actionDescription['LIMIT_UNIT'] = $orderCurrency;
712 $actionDescription['LIMIT_VALUE'] = $limitValue;
713 }
714
715 static::setActionDescription(self::RESULT_ENTITY_BASKET, $actionDescription);
716
717 if (empty($order['BASKET_ITEMS']) || !is_array($order['BASKET_ITEMS']))
718 return;
719
720 static::enableBasketFilter();
721
722 if ($applyIfMoreProfitable)
723 {
724 if ($filter === null)
725 {
726 $filter = function(){
727 return true;
728 };
729 }
730 $filter = self::wrapFilterToFindMoreProfitableForCumulative($filter, $unit, $value, $limitValue, $maxBound);
731 }
732
733 $filteredBasket = static::getBasketForApply($order['BASKET_ITEMS'], $filter, $action);
734 if (empty($filteredBasket))
735 return;
736
737
738 $applyBasket = array_filter($filteredBasket, '\Bitrix\Sale\Discount\Actions::filterBasketForAction');
739 unset($filteredBasket);
740 if (empty($applyBasket))
741 return;
742
743 foreach ($applyBasket as $basketCode => $basketRow)
744 {
745 if ($applyIfMoreProfitable)
746 {
747 $basketRow['PRICE'] = $basketRow['BASE_PRICE'];
748 $basketRow['DISCOUNT_PRICE'] = 0;
749 }
750
751 list($calculateValue, $result) = self::calculateDiscountPrice(
752 $value,
753 $unit,
754 $basketRow,
755 $limitValue,
756 $maxBound
757 );
758 if ($result >= 0)
759 {
760 self::fillDiscountPrice($basketRow, $result, -$calculateValue);
761
762 $order['BASKET_ITEMS'][$basketCode] = $basketRow;
763
764 $rowActionDescription = $actionDescription;
765 $rowActionDescription['BASKET_CODE'] = $basketCode;
766 $rowActionDescription['RESULT_VALUE'] = abs($calculateValue);
767 $rowActionDescription['RESULT_UNIT'] = $orderCurrency;
768
769 if(!empty($limitValue))
770 {
771 $rowActionDescription['ACTION_TYPE'] = Formatter::TYPE_LIMIT_VALUE;
772 $rowActionDescription['LIMIT_TYPE'] = Formatter::LIMIT_MAX;
773 $rowActionDescription['LIMIT_UNIT'] = $orderCurrency;
774 $rowActionDescription['LIMIT_VALUE'] = $limitValue;
775 }
776
777 if ($applyIfMoreProfitable)
778 {
779 //TODO: remove this hack
780 //revert apply on affected basket items
781 $rowActionDescription['REVERT_APPLY'] = true;
782 }
783
784 static::setActionResult(self::RESULT_ENTITY_BASKET, $rowActionDescription);
785 unset($rowActionDescription);
786 }
787 unset($result);
788 }
789 unset($basketCode, $basketRow);
790
791 if (self::getUseMode() == self::MODE_CALCULATE)
792 {
794 array(
795 'cumulative_value' => $cumulativeOrderUserValue,
796 )
797 );
798 }
799 }
800
801 private static function wrapFilterToFindMoreProfitableForCumulative($filter, $unit, $value, $limitValue, $maxBound)
802 {
803 if (!is_callable($filter))
804 {
805 return null;
806 }
807
808 return function($basketItem) use ($filter, $unit, $value, $limitValue, $maxBound) {
809 if (empty($basketItem['BASE_PRICE']))
810 {
811 return false;
812 }
813
814 if (empty($basketItem['DISCOUNT_PRICE']))
815 {
816 return true;
817 }
818
819 if (!$filter($basketItem))
820 {
821 return false;
822 }
823
824 $prevPrice = $basketItem['PRICE'];
825 $basketItem['PRICE'] = $basketItem['BASE_PRICE'];
826 list(, $newPrice) = self::calculateDiscountPrice(
827 $value,
828 $unit,
829 $basketItem,
830 $limitValue,
831 $maxBound
832 );
833
834 return $newPrice < $prevPrice;
835 };
836 }
837
852 public static function applyToDelivery(array &$order, array $action)
853 {
854 static::increaseApplyCounter();
855
856 if (!isset($action['VALUE']) || !isset($action['UNIT']))
857 return;
858 if ($action['UNIT'] != self::VALUE_TYPE_PERCENT && $action['UNIT'] != self::VALUE_TYPE_FIX)
859 return;
860
861 $orderCurrency = static::getCurrency();
862 $unit = (string)$action['UNIT'];
863 $value = (float)$action['VALUE'];
864 $currency = (isset($action['CURRENCY']) ? $action['CURRENCY'] : $orderCurrency);
865 $maxBound = false;
866 if ($unit == self::VALUE_TYPE_FIX && $value < 0)
867 $maxBound = (isset($action['MAX_BOUND']) && $action['MAX_BOUND'] == 'Y');
868
869 $actionDescription = array(
870 'ACTION_TYPE' => Formatter::TYPE_VALUE,
871 'VALUE' => abs($value),
872 'VALUE_ACTION' => (
873 $value < 0
876 )
877 );
878 if ($maxBound)
880
881 switch ($unit)
882 {
885 $value = ($order['PRICE_DELIVERY'] * $value) / 100;
886 break;
889 $actionDescription['VALUE_UNIT'] = $currency;
890 if ($currency != $orderCurrency)
891 $value = \CCurrencyRates::ConvertCurrency($value, $currency, $orderCurrency);
892 break;
893 }
894 static::setActionDescription(self::RESULT_ENTITY_DELIVERY, $actionDescription);
895
896 if (isset($order['CUSTOM_PRICE_DELIVERY']) && $order['CUSTOM_PRICE_DELIVERY'] == 'Y')
897 return;
898 if (
899 !isset($order['PRICE_DELIVERY'])
900 || (
901 static::roundZeroValue($order['PRICE_DELIVERY']) == 0
903 )
904 )
905 return;
906
907 $value = static::roundValue($value, $order['CURRENCY']);
908 $value = static::roundZeroValue($value);
909 if ($value == 0)
910 return;
911
912 $resultValue = static::roundZeroValue($order['PRICE_DELIVERY'] + $value);
913 if ($maxBound && $resultValue < 0)
914 {
915 $resultValue = 0;
916 $value = -$order['PRICE_DELIVERY'];
917 }
918
919 if ($resultValue < 0)
920 return;
921
922 if (!isset($order['PRICE_DELIVERY_DIFF']))
923 $order['PRICE_DELIVERY_DIFF'] = 0;
924 $order['PRICE_DELIVERY_DIFF'] -= $value;
925 $order['PRICE_DELIVERY'] = $resultValue;
926
927 $actionDescription['RESULT_VALUE'] = abs($value);
928 $actionDescription['RESULT_UNIT'] = $orderCurrency;
929
930 static::setActionResult(self::RESULT_ENTITY_DELIVERY, $actionDescription);
931 unset($actionDescription);
932 }
933
942 public static function applySimpleGift(array &$order, $filter)
943 {
944 static::increaseApplyCounter();
945
946 $actionDescription = array(
947 'ACTION_TYPE' => Formatter::TYPE_SIMPLE_GIFT
948 );
949 static::setActionDescription(self::RESULT_ENTITY_BASKET, $actionDescription);
950
951 if (!is_callable($filter))
952 return;
953
954 if (empty($order['BASKET_ITEMS']) || !is_array($order['BASKET_ITEMS']))
955 return;
956
957 static::disableBasketFilter();
958
959 $itemsCopy = $order['BASKET_ITEMS'];
960 Main\Type\Collection::sortByColumn($itemsCopy, 'PRICE', null, null, true);
961 $filteredBasket = static::getBasketForApply(
962 $itemsCopy,
963 $filter,
964 array(
965 'GIFT_TITLE' => Loc::getMessage('BX_SALE_DISCOUNT_ACTIONS_SIMPLE_GIFT_DESCR')
966 )
967 );
968 unset($itemsCopy);
969
970 static::enableBasketFilter();
971
972 if (empty($filteredBasket))
973 return;
974
975 $applyBasket = array_filter($filteredBasket, '\Bitrix\Sale\Discount\Actions::filterBasketForAction');
976 unset($filteredBasket);
977 if (empty($applyBasket))
978 return;
979
980 foreach ($applyBasket as $basketCode => $basketRow)
981 {
982 self::fillDiscountPrice($basketRow, 0, $basketRow['PRICE']);
983
984 $order['BASKET_ITEMS'][$basketCode] = $basketRow;
985
986 $rowActionDescription = $actionDescription;
987 $rowActionDescription['BASKET_CODE'] = $basketCode;
988 static::setActionResult(self::RESULT_ENTITY_BASKET, $rowActionDescription);
989 unset($rowActionDescription);
990 }
991 unset($basketCode, $basketRow);
992 }
993
1002 public static function getBasketForApply(array $basket, $filter, $action = array())
1003 {
1004 $result = array();
1005 switch (static::getUseMode())
1006 {
1008 $result = (is_callable($filter) ? array_filter($basket, $filter) : $basket);
1009 break;
1010 case self::MODE_MANUAL:
1011 case self::MODE_MIXED:
1012 switch (static::getApplyResultMode())
1013 {
1015 $currentCounter = static::getApplyCounter();
1016 $basketCodeList = array_keys($basket);
1017 foreach ($basketCodeList as &$code)
1018 {
1019 if (empty(self::$applyResult['BASKET'][$code]) || !is_array(self::$applyResult['BASKET'][$code]))
1020 continue;
1021 if (!in_array($currentCounter, self::$applyResult['BASKET'][$code]))
1022 continue;
1023 $result[$code] = $basket[$code];
1024 }
1025 unset($code, $basketCodeList, $currentCounter);
1026 break;
1028 $basketCodeList = array_keys($basket);
1029 foreach ($basketCodeList as &$code)
1030 {
1031 if (empty(self::$applyResult['BASKET'][$code]) || !is_array(self::$applyResult['BASKET'][$code]))
1032 continue;
1033 foreach (self::$applyResult['BASKET'][$code] as $descr)
1034 {
1035 if (static::compareBasketResultDescr($action, $descr))
1036 {
1037 $result[$code] = $basket[$code];
1038 break;
1039 }
1040 }
1041 unset($descr);
1042 // only for old format simple gifts
1043 if (!isset($result[$code]))
1044 {
1045 if (isset($action['GIFT_TITLE']))
1046 {
1047 end(self::$applyResult['BASKET'][$code]);
1048 $descr = current(self::$applyResult['BASKET'][$code]);
1049 if (
1050 $descr['TYPE'] == Formatter::TYPE_SIMPLE
1051 && $descr['DESCR'] == $action['GIFT_TITLE']
1052 )
1053 $result[$code] = $basket[$code];
1054 unset($descr);
1055 }
1056 }
1057 }
1058 unset($code, $basketCodeList);
1059 break;
1061 $basketCodeList = array_keys($basket);
1062 foreach ($basketCodeList as &$code)
1063 {
1064 if (isset(self::$applyResult['BASKET'][$code]))
1065 $result[$code] = $basket[$code];
1066 }
1067 unset($code, $basketCodeList);
1068 break;
1069 }
1070 break;
1071 }
1072
1073 return $result;
1074 }
1075
1083 public static function setActionDescription($type, $description)
1084 {
1085 if (!static::isCalculateMode())
1086 return;
1087 if (empty($description) || !is_array($description) || !isset($description['ACTION_TYPE']))
1088 return;
1089 $actionType = $description['ACTION_TYPE'];
1090 if ($actionType == Formatter::TYPE_SIMPLE)
1091 $description = (isset($description['ACTION_DESCRIPTION']) ? $description['ACTION_DESCRIPTION'] : '');
1092
1093 $prepareResult = Sale\Discount\Formatter::prepareRow($actionType, $description);
1094 unset($actionType);
1095
1096 if ($prepareResult !== null)
1097 {
1098 switch ($type)
1099 {
1101 if (!isset(self::$actionDescription['BASKET']))
1102 self::$actionDescription['BASKET'] = array();
1103 self::$actionDescription['BASKET'][static::getApplyCounter()] = $prepareResult;
1104 break;
1106 if (!isset(self::$actionDescription['DELIVERY']))
1107 self::$actionDescription['DELIVERY'] = array();
1108 self::$actionDescription['DELIVERY'][static::getApplyCounter()] = $prepareResult;
1109 break;
1110 }
1111 }
1112 unset($prepareResult);
1113 }
1114
1122 public static function setActionResult($entity, array $actionResult)
1123 {
1124 if (empty($actionResult) || !isset($actionResult['ACTION_TYPE']))
1125 return;
1126
1127 $actionType = $actionResult['ACTION_TYPE'];
1128 if ($actionType == Formatter::TYPE_SIMPLE)
1129 $actionDescription = (isset($actionResult['ACTION_DESCRIPTION']) ? $actionResult['ACTION_DESCRIPTION'] : '');
1130 else
1132 $prepareResult = Sale\Discount\Formatter::prepareRow($actionType, $actionDescription);
1133 unset($actionDescription, $actionType);
1134
1135 if ($prepareResult !== null)
1136 {
1137 switch ($entity)
1138 {
1140 if (!isset(self::$actionResult['BASKET']))
1141 self::$actionResult['BASKET'] = array();
1142 $basketCode = $actionResult['BASKET_CODE'];
1143 if (!isset(self::$actionResult['BASKET'][$basketCode]))
1144 self::$actionResult['BASKET'][$basketCode] = array();
1145 //TODO: remove this hack
1146 if (isset($actionResult['REVERT_APPLY']))
1147 $prepareResult['REVERT_APPLY'] = $actionResult['REVERT_APPLY'];
1148 self::$actionResult['BASKET'][$basketCode][static::getApplyCounter()] = $prepareResult;
1149 unset($basketCode);
1150 break;
1152 if (!isset(self::$actionResult['DELIVERY']))
1153 self::$actionResult['DELIVERY'] = array();
1154 self::$actionResult['DELIVERY'][static::getApplyCounter()] = $prepareResult;
1155 break;
1156 }
1157 }
1158 unset($prepareResult);
1159 }
1160
1166 public static function clearEntityActionResult($entity, array $entityParams = array())
1167 {
1168 switch ($entity)
1169 {
1171 if (empty($entityParams))
1172 {
1173 if (array_key_exists('BASKET', self::$actionResult))
1174 unset(self::$actionResult['BASKET']);
1175 }
1176 else
1177 {
1178 if (isset($entityParams['BASKET_CODE']) && array_key_exists($entityParams['BASKET_CODE'], self::$actionResult['BASKET']))
1179 unset(self::$actionResult['BASKET'][$entityParams['BASKET_CODE']]);
1180 }
1181 break;
1183 if (array_key_exists('DELIVERY', self::$actionResult))
1184 unset(self::$actionResult['DELIVERY']);
1185 break;
1186 }
1187 }
1188
1196 public static function getPercentByValue($basket, $value)
1197 {
1198 $summ = 0;
1199 switch (static::getPercentMode())
1200 {
1202 foreach ($basket as $basketRow)
1203 $summ += (float)$basketRow['BASE_PRICE'] * (float)$basketRow['QUANTITY'];
1204 unset($basketRow);
1205 break;
1207 foreach ($basket as $basketRow)
1208 $summ += (float)$basketRow['PRICE'] * (float)$basketRow['QUANTITY'];
1209 unset($basketRow);
1210 break;
1211 }
1212
1213 return static::roundZeroValue($summ > 0 ? ($value * 100) / $summ : 0);
1214 }
1215
1223 public static function percentToValue($basketRow, $percent)
1224 {
1225 $value = 0.0;
1226 switch (static::getPercentMode())
1227 {
1229 $value = ((float)$basketRow['BASE_PRICE'] * $percent) / 100;
1230 break;
1232 $value = ((float)$basketRow['PRICE'] * $percent) / 100;
1233 break;
1234 }
1235
1236 return $value;
1237 }
1238
1239 public static function getActionConfiguration(array $discount)
1240 {
1241 $actionStructure = self::getActionStructure($discount);
1242
1243 if(!$actionStructure || !is_array($actionStructure))
1244 {
1245 return null;
1246 }
1247
1248 if($actionStructure['CLASS_ID'] != 'CondGroup')
1249 {
1250 return null;
1251 }
1252
1253 if(count($actionStructure['CHILDREN']) > 1)
1254 {
1255 return null;
1256 }
1257
1258 $action = reset($actionStructure['CHILDREN']);
1259 if($action['CLASS_ID'] != 'ActSaleBsktGrp')
1260 {
1261 return null;
1262 }
1263
1264 $actionData = $action['DATA'];
1265
1266 $configuration = array(
1267 'TYPE' => $actionData['Type'],
1268 'VALUE' => $actionData['Value'],
1269 'LIMIT_VALUE' => $actionData['Max']?: 0,
1270 );
1271 switch ($actionData['Unit'])
1272 {
1273 case 'CurEach':
1274 $configuration['VALUE_TYPE'] = Sale\Discount\Actions::VALUE_TYPE_FIX;
1275 break;
1276 case 'CurAll':
1277 $configuration['VALUE_TYPE'] = Sale\Discount\Actions::VALUE_TYPE_SUMM;
1278 break;
1279 default:
1280 $configuration['VALUE_TYPE'] = Sale\Discount\Actions::VALUE_TYPE_PERCENT;
1281 break;
1282 }
1283
1284 return $configuration;
1285 }
1286
1287 protected static function getActionStructure(array $discount)
1288 {
1289 $actionStructure = null;
1290 if (isset($discount['ACTIONS']) && !empty($discount['ACTIONS']))
1291 {
1292 $actionStructure = false;
1293 if (!is_array($discount['ACTIONS']))
1294 {
1295 if (CheckSerializedData($discount['ACTIONS']))
1296 {
1297 $actionStructure = unserialize($discount['ACTIONS'], ['allowed_classes' => false]);
1298 }
1299 }
1300 else
1301 {
1302 $actionStructure = $discount['ACTIONS'];
1303 }
1304 }
1305 elseif(isset($discount['ACTIONS_LIST']) && is_array($discount['ACTIONS_LIST']))
1306 {
1307 $actionStructure = $discount['ACTIONS_LIST'];
1308 }
1309
1310 return $actionStructure;
1311 }
1312
1320 protected static function compareBasketResultDescr(array $action, $resultDescr)
1321 {
1322 $result = false;
1323
1324 if (empty($action))
1325 return $result;
1326 if (!is_array($resultDescr) || !isset($resultDescr['TYPE']))
1327 return $result;
1328
1329 $currency = (isset($action['CURRENCY']) ? $action['CURRENCY'] : static::getCurrency());
1330 $value = abs($action['VALUE']);
1331 $valueAction = (
1332 $action['VALUE'] < 0
1335 );
1336
1337 switch ($resultDescr['TYPE'])
1338 {
1340 if (
1341 $resultDescr['VALUE'] == $value
1342 && $resultDescr['VALUE_ACTION'] = $valueAction
1343 )
1344 {
1345 switch($action['UNIT'])
1346 {
1348 $result = (
1349 (
1350 $resultDescr['VALUE_TYPE'] == Formatter::VALUE_TYPE_SUMM_BASKET
1351 || $resultDescr['VALUE_TYPE'] == Formatter::VALUE_TYPE_SUMM
1352 )
1353 && $resultDescr['VALUE_UNIT'] == $currency
1354 );
1355 break;
1357 $result = ($resultDescr['VALUE_TYPE'] == Formatter::VALUE_TYPE_PERCENT);
1358 break;
1360 $result = (
1361 $resultDescr['VALUE_TYPE'] == Formatter::VALUE_TYPE_CURRENCY
1362 && $resultDescr['VALUE_UNIT'] == $currency
1363 );
1364 break;
1365 }
1366 }
1367 break;
1369 $result = (
1370 $resultDescr['VALUE'] == $value
1371 && $resultDescr['VALUE_ACTION'] == $valueAction
1372 && $resultDescr['VALUE_TYPE'] == Formatter::VALUE_TYPE_CURRENCY
1373 && $resultDescr['VALUE_UNIT'] == $currency
1374 );
1375 break;
1376 }
1377
1378 unset($valueAction, $value, $currency);
1379
1380 return $result;
1381 }
1382
1394 protected static function calculateDiscountPrice($value, $unit, array $basketRow, $limitValue, $maxBound)
1395 {
1396 $calculateValue = $value;
1397 if ($unit == self::VALUE_TYPE_PERCENT)
1398 $calculateValue = static::percentToValue($basketRow, $calculateValue);
1399 $calculateValue = static::roundValue($calculateValue, $basketRow['CURRENCY']);
1400
1401 if ($unit == self::VALUE_TYPE_CLOSEOUT)
1402 {
1403 if ($calculateValue < $basketRow['PRICE'])
1404 {
1405 $result = $calculateValue;
1406 $calculateValue = $result - $basketRow['PRICE'];
1407 }
1408 else
1409 {
1410 $result = -1;
1411 }
1412 }
1413 else
1414 {
1415 if (!empty($limitValue) && $limitValue + $calculateValue <= 0)
1416 $calculateValue = -$limitValue;
1417
1418 $result = static::roundZeroValue($basketRow['PRICE'] + $calculateValue);
1419 if ($maxBound && $result < 0)
1420 {
1421 $result = 0;
1422 $calculateValue = -$basketRow['PRICE'];
1423 }
1424 }
1425
1426 return [$calculateValue, $result];
1427 }
1428
1437 protected static function fillDiscountPrice(array &$basketRow, $price, $discount)
1438 {
1439 if (!isset($basketRow['DISCOUNT_PRICE']))
1440 $basketRow['DISCOUNT_PRICE'] = 0;
1441 $basketRow['PRICE'] = $price;
1442 $basketRow['DISCOUNT_PRICE'] += $discount;
1443 }
1444}
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29
static filterBasketForAction(array $item)
Definition actions.php:307
static fillCompatibleFields(array &$order)
Definition actions.php:276
static setActionDescription($type, $description)
Definition actions.php:1083
static setActionStoredData(array $data)
Definition actions.php:404
static clearEntityActionResult($entity, array $entityParams=array())
Definition actions.php:1166
static applyToDelivery(array &$order, array $action)
Definition actions.php:852
static percentToValue($basketRow, $percent)
Definition actions.php:1223
static getActionConfiguration(array $discount)
Definition actions.php:1239
static calculateDiscountPrice($value, $unit, array $basketRow, $limitValue, $maxBound)
Definition actions.php:1394
static getActionStructure(array $discount)
Definition actions.php:1287
static applySimpleGift(array &$order, $filter)
Definition actions.php:942
static compareBasketResultDescr(array $action, $resultDescr)
Definition actions.php:1320
static roundZeroValue($value)
Definition actions.php:66
static setUseMode($mode, array $config=array())
Definition actions.php:91
static applyToBasket(array &$order, array $action, $filter)
Definition actions.php:455
static setActionResult($entity, array $actionResult)
Definition actions.php:1122
static setApplyResult(array $applyResult)
Definition actions.php:372
static applyCumulativeToBasket(array &$order, array $ranges, array $configuration=array(), $filter=null)
Definition actions.php:613
static getPercentByValue($basket, $value)
Definition actions.php:1196
static fillDiscountPrice(array &$basketRow, $price, $discount)
Definition actions.php:1437
static roundValue($value, $currency)
Definition actions.php:78
static getBasketForApply(array $basket, $filter, $action=array())
Definition actions.php:1002
static setApplyResultMode($mode)
Definition actions.php:347
static checkUseMode(array $list)
Definition actions.php:181
static setStoredData(array $data)
Definition actions.php:383