1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
base.php
См. документацию.
1<?php
3
12
17
18Loc::loadMessages(__FILE__);
19
20abstract class Base extends \CBitrixComponent
21{
22 public const ACTION_BUY = 'BUY';
23 public const ACTION_ADD_TO_BASKET = 'ADD2BASKET';
24 public const ACTION_SUBSCRIBE = 'SUBSCRIBE_PRODUCT';
25 public const ACTION_ADD_TO_COMPARE = 'ADD_TO_COMPARE_LIST';
26 public const ACTION_DELETE_FROM_COMPARE = 'DELETE_FROM_COMPARE_LIST';
27
28 public const ERROR_TEXT = 1;
29 public const ERROR_404 = 2;
30
31 public const PARAM_TITLE_MASK = '/^[A-Za-z_][A-Za-z01-9_]*$/';
32 public const SORT_ORDER_MASK = '/^(asc|desc|nulls)(,asc|,desc|,nulls)?$/i';
33
34 private $action = '';
35 private $cacheUsage = true;
36 private $extendedMode = true;
39
40 protected $separateLoading = false;
41
42 protected $selectFields = array();
43 protected $filterFields = array();
44 protected $sortFields = array();
45
47 protected $productIds = array();
48
49 protected $productIdMap = array();
50 protected $iblockProducts = array();
51 protected $elements = array();
52 protected $elementLinks = array();
53
56
57 protected $globalFilter = array();
58 protected $navParams = false;
59
60 protected $useCatalog = false;
61 protected $isIblockCatalog = false;
62 protected $useDiscountCache = false;
63
65 protected $compatibleMode = false;
66
67 protected $oldData = array();
69 protected $prices = array();
70 protected $calculatePrices = array();
71
72 protected $measures = array();
73 protected $ratios = array();
74 protected $quantityRanges = array();
75
76 protected $storage = array();
78
83 public function __construct($component = null)
84 {
85 parent::__construct($component);
86 $this->errorCollection = new ErrorCollection();
87 }
88
94 public function getAction()
95 {
96 return $this->action;
97 }
98
105 protected function setAction($action)
106 {
107 $this->action = $action;
108 }
109
115 protected function hasErrors()
116 {
117 return (bool)count($this->errorCollection);
118 }
119
125 protected function processErrors()
126 {
127 if (!empty($this->errorCollection))
128 {
130 foreach ($this->errorCollection as $error)
131 {
132 $code = $error->getCode();
133
134 if ($code == self::ERROR_404)
135 {
137 $this->arParams['MESSAGE_404'] ?: $error->getMessage(),
138 true,
139 $this->arParams['SET_STATUS_404'] === 'Y',
140 $this->arParams['SHOW_404'] === 'Y',
141 $this->arParams['FILE_404']
142 );
143 }
144 elseif ($code == self::ERROR_TEXT)
145 {
146 ShowError($error->getMessage());
147 }
148 }
149 }
150
151 return false;
152 }
153
160 protected function setCacheUsage($state)
161 {
162 $this->cacheUsage = (bool)$state;
163
164 return $this;
165 }
166
172 public function isCacheDisabled()
173 {
174 return !$this->cacheUsage;
175 }
176
185 protected function setExtendedMode($state)
186 {
187 $this->extendedMode = (bool)$state;
188
189 return $this;
190 }
191
197 public function isExtendedMode()
198 {
199 return $this->extendedMode;
200 }
201
208 protected function setCompatibleMode($state)
209 {
210 $this->compatibleMode = (bool)$state;
211 }
212
218 public function isEnableCompatible()
219 {
220 return $this->compatibleMode;
221 }
222
227 protected function setSeparateLoading($state)
228 {
229 $this->separateLoading = (bool)$state;
230 }
231
235 protected function isSeparateLoading()
236 {
237 return $this->separateLoading;
238 }
239
247 public static function getSettingsScript($componentPath, $settingsName)
248 {
249 if ($settingsName === 'filter_conditions')
250 {
251 if (Loader::includeModule('catalog'))
252 {
253 \CJSCore::Init(['core_condtree']);
254 }
255 }
256 $path = $componentPath.'/settings/'.$settingsName.'/script.js';
257 $file = new Main\IO\File(Main\Application::getDocumentRoot().$path);
258
259 return $path.'?'.$file->getModificationTime();
260 }
261
269 {
270 if (!is_array($params))
271 {
272 $params = [];
273 }
274
275 if (!isset($params['CURRENT_BASE_PAGE']))
276 {
277 $uri = new Main\Web\Uri($this->request->getRequestUri());
279 $params['CURRENT_BASE_PAGE'] = $uri->getUri();
280 }
281
282 // parent component params for correct template load through ajax
283 if (!isset($params['PARENT_NAME']) && $parent = $this->getParent())
284 {
285 $params['PARENT_NAME'] = $parent->getName();
286 $params['PARENT_TEMPLATE_NAME'] = $parent->getTemplateName();
287 $params['PARENT_TEMPLATE_PAGE'] = $parent->getTemplatePage();
288 }
289
290 // save original parameters for further ajax requests
291 $this->arResult['ORIGINAL_PARAMETERS'] = $params;
292
293 if (isset($params['CUSTOM_SITE_ID']) && is_string($params['CUSTOM_SITE_ID']))
294 {
295 $this->setSiteId($params['CUSTOM_SITE_ID']);
296 }
297
298 // for AJAX_MODE set original ajax_id from initial load
299 if (isset($params['AJAX_MODE']) && $params['AJAX_MODE'] === 'Y')
300 {
301 $ajaxId = $this->request->get('AJAX_ID');
302 if (!empty($ajaxId))
303 {
304 $params['AJAX_ID'] = $ajaxId;
305 }
306 unset($ajaxId);
307 }
308 $params['AJAX_ID'] = trim((string)($params['AJAX_ID'] ?? ''));
309
310 $params['CACHE_TIME'] = (int)($params['CACHE_TIME'] ?? 36000000);
311
312 $params['IBLOCK_ID'] = (int)($params['IBLOCK_ID'] ?? 0);
313 $params['SECTION_ID'] = (int)($params['SECTION_ID'] ?? 0);
314
315 $params['SECTION_CODE'] = trim((string)($params['SECTION_CODE'] ?? ''));
316 $params['SECTION_URL'] = trim((string)($params['SECTION_URL'] ?? ''));
317 $params['STRICT_SECTION_CHECK'] = isset($params['STRICT_SECTION_CHECK']) && $params['STRICT_SECTION_CHECK'] === 'Y';
318
319 $params['CHECK_LANDING_PRODUCT_SECTION'] = (
320 isset($params['CHECK_LANDING_PRODUCT_SECTION'])
321 && $params['CHECK_LANDING_PRODUCT_SECTION'] === 'Y'
322 );
323
324 $params['DETAIL_URL'] = trim((string)($params['DETAIL_URL'] ?? ''));
325 $params['BASKET_URL'] = trim((string)($params['BASKET_URL'] ?? ''));
326 if ($params['BASKET_URL'] === '')
327 {
328 $params['BASKET_URL'] = '/personal/basket.php';
329 }
330
331 $params['SHOW_SKU_DESCRIPTION'] = $params['SHOW_SKU_DESCRIPTION'] ?? 'N';
332
333 $params['HIDE_DETAIL_URL'] = isset($params['HIDE_DETAIL_URL']) && $params['HIDE_DETAIL_URL'] === 'Y';
334
335 $params['ACTION_VARIABLE'] = trim((string)($params['ACTION_VARIABLE'] ?? ''));
336 if ($params['ACTION_VARIABLE'] === '' || !preg_match(self::PARAM_TITLE_MASK, $params['ACTION_VARIABLE']))
337 {
338 $params['ACTION_VARIABLE'] = 'action';
339 }
340
341 $params['PRODUCT_ID_VARIABLE'] = trim((string)($params['PRODUCT_ID_VARIABLE'] ?? ''));
342 if (
343 $params['PRODUCT_ID_VARIABLE'] === ''
344 || !preg_match(self::PARAM_TITLE_MASK, $params['PRODUCT_ID_VARIABLE'])
345 )
346 {
347 $params['PRODUCT_ID_VARIABLE'] = 'id';
348 }
349
350 $params['ACTION_COMPARE_VARIABLE'] = trim((string)($params['ACTION_COMPARE_VARIABLE'] ?? ''));
351 if (
352 $params['ACTION_COMPARE_VARIABLE'] === ''
353 || !preg_match(self::PARAM_TITLE_MASK, $params['ACTION_COMPARE_VARIABLE'])
354 )
355 {
356 $params['ACTION_COMPARE_VARIABLE'] = $params['ACTION_VARIABLE'];
357 }
358
359 $params['PRODUCT_QUANTITY_VARIABLE'] = trim((string)($params['PRODUCT_QUANTITY_VARIABLE'] ?? ''));
360 if (
361 $params['PRODUCT_QUANTITY_VARIABLE'] === ''
362 || !preg_match(self::PARAM_TITLE_MASK, $params['PRODUCT_QUANTITY_VARIABLE'])
363 )
364 {
365 $params['PRODUCT_QUANTITY_VARIABLE'] = 'quantity';
366 }
367
368 $params['PRODUCT_PROPS_VARIABLE'] = trim((string)($params['PRODUCT_PROPS_VARIABLE'] ?? ''));
369 if (
370 $params['PRODUCT_PROPS_VARIABLE'] === ''
371 || !preg_match(self::PARAM_TITLE_MASK, $params['PRODUCT_PROPS_VARIABLE'])
372 )
373 {
374 $params['PRODUCT_PROPS_VARIABLE'] = 'prop';
375 }
376
377 // landing mode
378 if (
379 isset($params['ALLOW_SEO_DATA'])
380 && ($params['ALLOW_SEO_DATA'] === 'Y' || $params['ALLOW_SEO_DATA'] === 'N')
381 )
382 {
383 $params['SET_TITLE'] = $params['ALLOW_SEO_DATA'] === 'Y';
384 $params['SET_BROWSER_TITLE'] = $params['ALLOW_SEO_DATA'];
385 $params['SET_META_KEYWORDS'] = $params['ALLOW_SEO_DATA'];
386 $params['SET_META_DESCRIPTION'] = $params['ALLOW_SEO_DATA'];
387 }
388 else
389 {
390 $params['SET_TITLE'] = ($params['SET_TITLE'] ?? '') !== 'N';
391 $params['SET_BROWSER_TITLE'] = isset($params['SET_BROWSER_TITLE']) && $params['SET_BROWSER_TITLE'] === 'N' ? 'N' : 'Y';
392 $params['SET_META_KEYWORDS'] = isset($params['SET_META_KEYWORDS']) && $params['SET_META_KEYWORDS'] === 'N' ? 'N' : 'Y';
393 $params['SET_META_DESCRIPTION'] = isset($params['SET_META_DESCRIPTION']) && $params['SET_META_DESCRIPTION'] === 'N' ? 'N' : 'Y';
394 }
395 $params['SET_LAST_MODIFIED'] = isset($params['SET_LAST_MODIFIED']) && $params['SET_LAST_MODIFIED'] === 'Y';
396 $params['ADD_SECTIONS_CHAIN'] = isset($params['ADD_SECTIONS_CHAIN']) && $params['ADD_SECTIONS_CHAIN'] === 'Y';
397
398 $params['DISPLAY_COMPARE'] = isset($params['DISPLAY_COMPARE']) && $params['DISPLAY_COMPARE'] === 'Y';
399 $params['COMPARE_PATH'] = trim((string)($params['COMPARE_PATH'] ?? ''));
400 $params['COMPARE_NAME'] = trim((string)($params['COMPARE_NAME'] ?? ''));
401 if ($params['COMPARE_NAME'] === '')
402 {
403 $params['COMPARE_NAME'] = 'CATALOG_COMPARE_LIST';
404 }
405 $params['USE_COMPARE_LIST'] = (isset($params['USE_COMPARE_LIST']) && $params['USE_COMPARE_LIST'] === 'Y' ? 'Y' : 'N');
406
407 $params['USE_PRICE_COUNT'] = isset($params['USE_PRICE_COUNT']) && $params['USE_PRICE_COUNT'] === 'Y';
408 $params['SHOW_PRICE_COUNT'] = (int)($params['SHOW_PRICE_COUNT'] ?? 1);
409 if ($params['SHOW_PRICE_COUNT'] <= 0)
410 {
411 $params['SHOW_PRICE_COUNT'] = 1;
412 }
413 $params['FILL_ITEM_ALL_PRICES'] = isset($params['FILL_ITEM_ALL_PRICES']) && $params['FILL_ITEM_ALL_PRICES'] === 'Y';
414
415 $params['USE_PRODUCT_QUANTITY'] = isset($params['USE_PRODUCT_QUANTITY']) && $params['USE_PRODUCT_QUANTITY'] === 'Y';
416
417 $params['ADD_PROPERTIES_TO_BASKET'] = isset($params['ADD_PROPERTIES_TO_BASKET']) && $params['ADD_PROPERTIES_TO_BASKET'] === 'N' ? 'N' : 'Y';
418 if (Iblock\Model\PropertyFeature::isEnabledFeatures())
419 $params['ADD_PROPERTIES_TO_BASKET'] = 'Y';
420 if ($params['ADD_PROPERTIES_TO_BASKET'] === 'N')
421 {
422 $params['PRODUCT_PROPERTIES'] = array();
423 $params['OFFERS_CART_PROPERTIES'] = array();
424 }
425
426 $params['PARTIAL_PRODUCT_PROPERTIES'] = isset($params['PARTIAL_PRODUCT_PROPERTIES']) && $params['PARTIAL_PRODUCT_PROPERTIES'] === 'Y' ? 'Y' : 'N';
427
428 $params['OFFERS_SORT_FIELD'] = trim((string)($params['OFFERS_SORT_FIELD'] ?? ''));
429 if ($params['OFFERS_SORT_FIELD'] === '')
430 {
431 $params['OFFERS_SORT_FIELD'] = 'sort';
432 }
433
434 $params['OFFERS_SORT_ORDER'] = trim((string)($params['OFFERS_SORT_ORDER'] ?? ''));
435 if (
436 $params['OFFERS_SORT_ORDER'] === ''
437 || !preg_match(self::SORT_ORDER_MASK, $params['OFFERS_SORT_ORDER'])
438 )
439 {
440 $params['OFFERS_SORT_ORDER'] = 'asc';
441 }
442
443 $params['OFFERS_SORT_FIELD2'] = trim((string)($params['OFFERS_SORT_FIELD2'] ?? ''));
444 if ($params['OFFERS_SORT_FIELD2'] === '')
445 {
446 $params['OFFERS_SORT_FIELD2'] = 'id';
447 }
448
449 $params['OFFERS_SORT_ORDER2'] = trim((string)($params['OFFERS_SORT_ORDER2'] ?? ''));
450 if (
451 $params['OFFERS_SORT_ORDER2'] === ''
452 || !preg_match(self::SORT_ORDER_MASK, $params['OFFERS_SORT_ORDER2'])
453 )
454 {
455 $params['OFFERS_SORT_ORDER2'] = 'desc';
456 }
457
458 $params['PRICE_VAT_INCLUDE'] = !(isset($params['PRICE_VAT_INCLUDE']) && $params['PRICE_VAT_INCLUDE'] === 'N');
459
460 $params['CONVERT_CURRENCY'] = isset($params['CONVERT_CURRENCY']) && $params['CONVERT_CURRENCY'] === 'Y' ? 'Y' : 'N';
461 $params['CURRENCY_ID'] ??= '';
462 if (!is_scalar($params['CURRENCY_ID']))
463 {
464 $params['CURRENCY_ID'] = '';
465 }
466 $params['CURRENCY_ID'] = trim((string)$params['CURRENCY_ID']);
467 if ($params['CURRENCY_ID'] === '' || $params['CONVERT_CURRENCY'] === 'N')
468 {
469 $params['CONVERT_CURRENCY'] = 'N';
470 $params['CURRENCY_ID'] = '';
471 }
472
473 $params['OFFERS_LIMIT'] = (int)($params['OFFERS_LIMIT'] ?? 0);
474 if ($params['OFFERS_LIMIT'] < 0)
475 {
476 $params['OFFERS_LIMIT'] = 0;
477 }
478
479 $params['CACHE_GROUPS'] = trim((string)($params['CACHE_GROUPS'] ?? ''));
480 if ($params['CACHE_GROUPS'] !== 'N')
481 {
482 $params['CACHE_GROUPS'] = 'Y';
483 }
484
485 if (isset($params['~PRICE_CODE']))
486 {
487 $params['PRICE_CODE'] = $params['~PRICE_CODE'];
488 }
489 $params['PRICE_CODE'] ??= [];
490 if (!is_array($params['PRICE_CODE']))
491 {
492 $params['PRICE_CODE'] = [];
493 }
494
495 $params['SHOW_FROM_SECTION'] = isset($params['SHOW_FROM_SECTION']) && $params['SHOW_FROM_SECTION'] === 'Y' ? 'Y' : 'N';
496 if ($params['SHOW_FROM_SECTION'] === 'Y')
497 {
498 $params['SECTION_ELEMENT_ID'] = (int)($params['SECTION_ELEMENT_ID'] ?? 0);
499 $params['SECTION_ELEMENT_CODE'] = trim((string)($params['SECTION_ELEMENT_CODE'] ?? ''));
500 $params['DEPTH'] = (int)($params['DEPTH'] ?? 0);
501
502 if (empty($params['SECTION_ID']))
503 {
504 if ($params['SECTION_CODE'] !== '')
505 {
506 $sectionId = $this->getSectionIdByCode($params['SECTION_CODE'], $params['IBLOCK_ID']);
507 }
508 else
509 {
510 $sectionId = $this->getSectionIdByElement(
511 $params['SECTION_ELEMENT_ID'],
512 $params['SECTION_ELEMENT_CODE'],
513 $params['IBLOCK_ID']
514 );
515 }
516
517 $params['SECTION_ID'] = $sectionId;
518 }
519 }
520
521 $params['FILTER_IDS'] ??= [];
522 if (!is_array($params['FILTER_IDS']))
523 {
524 $params['FILTER_IDS'] = [$params['FILTER_IDS']];
525 }
526
527 $params['MESSAGE_404'] = trim((string)($params['MESSAGE_404'] ?? ''));
528 $params['SET_STATUS_404'] = ($params['SET_STATUS_404'] ?? 'N') === 'Y' ? 'Y' : 'N';
529 $params['SHOW_404'] = ($params['SHOW_404'] ?? 'N') === 'Y' ? 'Y' : 'N';
530 $params['FILE_404'] = trim((string)($params['FILE_404'] ?? ''));
531
532 return $params;
533 }
534
540 protected function checkModules()
541 {
542 $this->useCatalog = Loader::includeModule('catalog');
543 $this->storage['MODULES'] = array(
544 'iblock' => true,
545 'catalog' => $this->useCatalog,
546 'currency' => $this->useCatalog
547 );
548
549 return true;
550 }
551
557 protected function initCatalogDiscountCache()
558 {
559 if ($this->useCatalog && $this->useDiscountCache && !empty($this->elementLinks))
560 {
561 foreach ($this->iblockProducts as $iblock => $products)
562 {
563 if ($this->storage['USE_SALE_DISCOUNTS'])
564 {
565 Catalog\Discount\DiscountManager::preloadPriceData($products, $this->storage['PRICES_ALLOW']);
567 }
568 else
569 {
571 \CCatalogDiscount::SetDiscountProductCache($products, array('IBLOCK_ID' => $iblock, 'GET_BY_ID' => 'Y'));
572 }
573 }
574 }
575 }
576
582 protected function clearCatalogDiscountCache()
583 {
584 if ($this->useCatalog && $this->useDiscountCache)
585 {
587 'PRODUCT' => true,
588 'SECTIONS' => true,
589 'PROPERTIES' => true
590 ));
591 }
592 }
593
599 protected function initCurrencyConvert()
600 {
601 $this->storage['CONVERT_CURRENCY'] = array();
602
603 if ($this->arParams['CONVERT_CURRENCY'] === 'Y')
604 {
605 $correct = false;
606 if (Loader::includeModule('currency'))
607 {
608 $this->storage['MODULES']['currency'] = true;
609 $correct = Currency\CurrencyManager::isCurrencyExist($this->arParams['CURRENCY_ID']);
610 }
611 if ($correct)
612 {
613 $this->storage['CONVERT_CURRENCY'] = array(
614 'CURRENCY_ID' => $this->arParams['CURRENCY_ID']
615 );
616 }
617 else
618 {
619 $this->arParams['CONVERT_CURRENCY'] = 'N';
620 $this->arParams['CURRENCY_ID'] = '';
621 }
622 unset($correct);
623 }
624 }
625
632 protected function offerIblockExist($iblockId)
633 {
634 if (empty($this->storage['CATALOGS'][$iblockId]))
635 return false;
636
637 $catalog = $this->storage['CATALOGS'][$iblockId];
638
639 if (empty($catalog['CATALOG_TYPE']))
640 return false;
641
642 return $catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_FULL || $catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_PRODUCT;
643 }
644
650 protected function initCatalogInfo()
651 {
652 $catalogs = array();
653
654 if ($this->useCatalog)
655 {
656 $this->storage['SHOW_CATALOG_WITH_OFFERS'] = Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y';
657 $this->storage['USE_SALE_DISCOUNTS'] = Main\Config\Option::get('sale', 'use_sale_discount_only') === 'Y';
658 foreach (array_keys($this->iblockProducts) as $iblockId)
659 {
660 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
661 if (!empty($catalog) && is_array($catalog))
662 {
663 $this->isIblockCatalog = $this->isIblockCatalog || $catalog['CATALOG_TYPE'] != \CCatalogSku::TYPE_PRODUCT;
664 $catalogs[$iblockId] = $catalog;
665 }
666 }
667 }
668
669 $this->storage['CATALOGS'] = $catalogs;
670 }
671
672 protected function getProductInfo($productId)
673 {
674 if (!$this->useCatalog)
675 return null;
676
677 $productId = (int)$productId;
678 if ($productId <= 0)
679 return null;
680
681 $iblockId = (int)\CIBlockElement::GetIBlockByID($productId);
682 if ($iblockId <= 0)
683 return null;
684
686 'select' => ['ID', 'TYPE'],
687 'filter' => ['=ID' => $productId]
688 ]);
689 $row = $iterator->fetch();
690 unset($iterator);
691 if (empty($row))
692 return null;
693
694 $row['ID'] = (int)$row['ID'];
695 $row['TYPE'] = (int)$row['TYPE'];
696 if (
697 $row['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU
698 || $row['TYPE'] == Catalog\ProductTable::TYPE_FREE_OFFER
699 )
700 return null;
701
702 $row['ELEMENT_IBLOCK_ID'] = $iblockId;
703 $row['PRODUCT_IBLOCK_ID'] = 0;
704
705 if (isset($this->storage['CATALOGS'][$iblockId]))
706 {
707 if ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] == \CCatalogSku::TYPE_CATALOG)
708 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
709 else
710 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['PRODUCT_IBLOCK_ID'];
711 return $row;
712 }
713
714 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
715 if (empty($catalog) || !is_array($catalog))
716 return null;
717
718 if ($catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_PRODUCT)
719 return null;
720
721 if ($catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_OFFERS)
722 {
723 $iblockId = $catalog['PRODUCT_IBLOCK_ID'];
724 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
725 }
726 if (!isset($this->storage['CATALOGS']))
727 $this->storage['CATALOGS'] = [];
728 $this->storage['CATALOGS'][$iblockId] = $catalog;
729 unset($catalog);
730
731 if ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] == \CCatalogSku::TYPE_CATALOG)
732 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
733 else
734 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['PRODUCT_IBLOCK_ID'];
735 return $row;
736 }
737
743 protected function initPrices()
744 {
745 // This function returns array with prices description and access rights
746 // in case catalog module n/a prices get values from element properties
747 $this->storage['PRICES'] = \CIBlockPriceTools::GetCatalogPrices(
748 isset($this->arParams['IBLOCK_ID']) && $this->arParams['IBLOCK_ID'] > 0 ? $this->arParams['IBLOCK_ID'] : false,
749 $this->arParams['PRICE_CODE']
750 );
751 $this->storage['PRICES_ALLOW'] = \CIBlockPriceTools::GetAllowCatalogPrices($this->storage['PRICES']);
752 $this->storage['PRICES_CAN_BUY'] = array();
753 $this->storage['PRICES_MAP'] = array();
754 foreach ($this->storage['PRICES'] as $priceType)
755 {
756 $this->storage['PRICES_MAP'][$priceType['ID']] = $priceType['CODE'];
757 if ($priceType['CAN_BUY'])
758 $this->storage['PRICES_CAN_BUY'][$priceType['ID']] = $priceType['ID'];
759 }
760
761 $this->storage['PRICE_TYPES'] = array();
762 if ($this->useCatalog)
763 $this->storage['PRICE_TYPES'] = \CCatalogGroup::GetListArray();
764
765 $this->useDiscountCache = false;
766 if ($this->useCatalog)
767 {
768 if (!empty($this->storage['CATALOGS']) && !empty($this->storage['PRICES_ALLOW']))
769 $this->useDiscountCache = true;
770 }
771
772 if ($this->useCatalog && $this->useDiscountCache)
773 {
774 $this->useDiscountCache = \CIBlockPriceTools::SetCatalogDiscountCache(
775 $this->storage['PRICES_ALLOW'],
776 $this->getUserGroups()
777 );
778 }
779
780 if ($this->useCatalog)
781 Catalog\Product\Price::loadRoundRules($this->storage['PRICES_ALLOW']);
782 }
783
789 protected function initVats()
790 {
791 $this->storage['VATS'] = [];
792 $this->storage['IBLOCKS_VAT'] = [];
793 if ($this->useCatalog)
794 {
796 'select' => ['ID', 'RATE'],
797 'order' => ['ID' => 'ASC']
798 ]);
799 while ($row = $iterator->fetch())
800 $this->storage['VATS'][(int)$row['ID']] = (float)$row['RATE'];
801 unset($row, $iterator);
802
803 if (!empty($this->storage['CATALOGS']))
804 {
805 foreach ($this->storage['CATALOGS'] as $catalog)
806 {
807 $this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']] = 0;
808 if ($catalog['PRODUCT_IBLOCK_ID'] > 0)
809 $this->storage['IBLOCKS_VAT'][$catalog['PRODUCT_IBLOCK_ID']] = 0;
810 }
811 unset($catalog);
812
814 'select' => ['IBLOCK_ID', 'VAT_ID'],
815 'filter' => ['@IBLOCK_ID' => array_keys($this->storage['IBLOCKS_VAT'])]
816 ]);
817 while ($row = $iterator->fetch())
818 $this->storage['IBLOCKS_VAT'][(int)$row['IBLOCK_ID']] = (int)$row['VAT_ID'];
819 unset($row, $iterator);
820 }
821 }
822 }
823
827 protected function initIblockPropertyFeatures()
828 {
829
830 }
831
837 protected function initElementList()
838 {
839 $this->storage['CURRENCY_LIST'] = array();
840 $this->storage['DEFAULT_MEASURE'] = $this->getDefaultMeasure();
841
842 $this->initQueryFields();
843
844 foreach ($this->iblockProducts as $iblock => $products)
845 {
846 $elementIterator = $this->getElementList($iblock, $products);
847 $iblockElements = $this->getIblockElements($elementIterator);
848
849 if (!empty($iblockElements) && !$this->hasErrors())
850 {
851 $this->modifyDisplayProperties($iblock, $iblockElements);
852 $this->elements = array_merge($this->elements, array_values($iblockElements));
853 $this->iblockProducts[$iblock] = array_keys($iblockElements);
854 }
855
856 unset($elementIterator, $iblockElements, $element);
857 }
858 }
859
866 abstract protected function getIblockElements($elementIterator);
867
873 protected function sortElementList()
874 {
875 if (!empty($this->productIdMap) && is_array($this->productIdMap))
876 {
877 $sortedElements = array();
878
879 foreach (array_keys($this->productIdMap) as $productId)
880 {
881 $parentId = $this->productIdMap[$productId];
882
883 foreach ($this->elements as $element)
884 {
885 if ($element['ID'] == $parentId)
886 {
887 $sortedElements[$productId] = $element;
888 break;
889 }
890 }
891 }
892
893 $this->elements = array_values($sortedElements);
894 }
895 }
896
902 protected function makeElementLinks()
903 {
904 if (!empty($this->elements))
905 {
906 foreach ($this->elements as $index => $element)
907 {
908 $this->elementLinks[$element['ID']] =& $this->elements[$index];
909 }
910 }
911 }
912
918 protected function getProductIds()
919 {
920 return false;
921 }
922
928 protected function getBigDataProductIds()
929 {
930 $shownIds = $this->request->get('shownIds');
931 if (!empty($shownIds) && is_array($shownIds))
932 {
933 $this->arParams['FILTER_IDS'] += $shownIds;
934 }
935
936 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->request->get('count') ?: 20;
937 $this->arParams['FILTER'] ??= [];
938 $this->arParams['FILTER'] = $this->arParams['FILTER'] ?: ['PAYED'];
939 $this->arParams['BY'] ??= '';
940 $this->arParams['BY'] = $this->arParams['BY'] ?: 'AMOUNT';
941 $this->arParams['PERIOD'] ??= 0;
942 $this->arParams['PERIOD'] = (int)$this->arParams['PERIOD'] ?: 30;
943 $this->arParams['DEPTH'] ??= 0;
944 $this->arParams['DEPTH'] = (int)$this->arParams['DEPTH'] ?: 2;
945
946 // general filter
947 $this->filterFields = $this->getFilter();
948 $this->filterFields['IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
950
951 // try cloud
952 $ids = $this->request->get('items') ?: array();
953 if (!empty($ids))
954 {
955 $recommendationId = $this->request->get('rid');
956 $ids = $this->filterByParams($ids, $this->arParams['FILTER_IDS']);
957
958 foreach ($ids as $id)
959 {
960 $this->recommendationIdToProduct[$id] = $recommendationId;
961 }
962 }
963
964 // try bestsellers
965 if (Main\Loader::includeModule('sale') && count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
966 {
967 $ids = $this->getBestSellersRecommendation($ids);
968 }
969
970 // try most viewed
971 if ($this->useCatalog && count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
972 {
973 $ids = $this->getMostViewedRecommendation($ids);
974 }
975
976 // try random
977 if (count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
978 {
979 $ids = $this->getRandomRecommendation($ids);
980 }
981
982 // limit
983 return array_slice($ids, 0, $this->arParams['PAGE_ELEMENT_COUNT']);
984 }
985
992 protected function getBestSellersRecommendation($ids)
993 {
994 // increase element count
995 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->arParams['PAGE_ELEMENT_COUNT'] * 10;
996 $bestsellers = $this->getBestSellersProductIds();
997 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->arParams['PAGE_ELEMENT_COUNT'] / 10;
998
999 if (!empty($bestsellers))
1000 {
1001 $recommendationId = 'bestsellers';
1002 $bestsellers = Main\Analytics\Catalog::getProductIdsByOfferIds($bestsellers);
1003 $bestsellers = $this->filterByParams($bestsellers, $this->arParams['FILTER_IDS']);
1004
1005 foreach ($bestsellers as $id)
1006 {
1007 if (!isset($this->recommendationIdToProduct[$id]))
1008 {
1009 $this->recommendationIdToProduct[$id] = $recommendationId;
1010 }
1011 }
1012
1013 $ids = array_unique(array_merge($ids, $bestsellers));
1014 }
1015
1016 return $ids;
1017 }
1018
1025 protected function getMostViewedRecommendation($ids)
1026 {
1027 $mostViewed = array();
1028 $recommendationId = 'mostviewed';
1029
1030 $result = Catalog\CatalogViewedProductTable::getList(array(
1031 'select' => array(
1032 'ELEMENT_ID',
1033 new Main\Entity\ExpressionField('SUM_HITS', 'SUM(%s)', 'VIEW_COUNT')
1034 ),
1035 'filter' => array(
1036 '=SITE_ID' => $this->getSiteId(),
1037 '>ELEMENT_ID' => 0,
1038 '>DATE_VISIT' => new Main\Type\DateTime(date('Y-m-d H:i:s', strtotime('-30 days')), 'Y-m-d H:i:s')
1039 ),
1040 'order' => array('SUM_HITS' => 'DESC'),
1041 'limit' => $this->arParams['PAGE_ELEMENT_COUNT'] * 10
1042 ));
1043 while ($row = $result->fetch())
1044 {
1045 $mostViewed[] = $row['ELEMENT_ID'];
1046 }
1047 unset($row, $result);
1048
1049 $mostViewed = $this->filterByParams($mostViewed, $this->arParams['FILTER_IDS']);
1050
1051 foreach ($mostViewed as $id)
1052 {
1053 if (!isset($this->recommendationIdToProduct[$id]))
1054 {
1055 $this->recommendationIdToProduct[$id] = $recommendationId;
1056 }
1057 }
1058
1059 return array_unique(array_merge($ids, $mostViewed));
1060 }
1061
1068 protected function getRandomRecommendation($ids)
1069 {
1070 $limit = $this->getRecommendationLimit($ids);
1071
1072 if ($limit <= 0)
1073 {
1074 return $ids;
1075 }
1076
1077 $randomIds = array();
1078 $recommendationId = 'random';
1080
1081 $filterIds = array_merge($ids, $this->arParams['FILTER_IDS']);
1082 if (!empty($filterIds))
1083 {
1084 $filter['!ID'] = $filterIds;
1085 }
1086
1087 if ($this->arParams['SHOW_FROM_SECTION'] === 'Y' && !empty($this->arParams['SECTION_ID']))
1088 {
1089 $filter['SECTION_ID'] = $this->arParams['SECTION_ID'];
1090 }
1091
1092 $elementIterator = \CIBlockElement::GetList(array('RAND' => 'ASC'), $filter, false, array('nTopCount' => $limit), array('ID'));
1093 while ($element = $elementIterator->Fetch())
1094 {
1095 $randomIds[] = $element['ID'];
1096 }
1097
1098 if (!empty($randomIds))
1099 {
1100 $this->setCacheUsage(false);
1101 }
1102
1103 foreach ($randomIds as $id)
1104 {
1105 if (!isset($this->recommendationIdToProduct[$id]))
1106 {
1107 $this->recommendationIdToProduct[$id] = $recommendationId;
1108 }
1109 }
1110
1111 return array_merge($ids, $randomIds);
1112 }
1113
1122 protected function filterByParams($ids, $filterIds = array(), $useSectionFilter = true)
1123 {
1124 if (empty($ids))
1125 {
1126 return array();
1127 }
1128
1129 $ids = array_values(array_unique($ids));
1130 // remove duplicates of already showed items
1131 if (!empty($filterIds))
1132 {
1133 $ids = array_diff($ids, $filterIds);
1134 }
1135
1136 if (!empty($ids))
1137 {
1139 $filter['ID'] = $ids;
1140
1141 $correctIds = array();
1142 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
1143 while ($element = $elementIterator->Fetch())
1144 {
1145 $correctIds[] = $element['ID'];
1146 }
1147
1148 if ($useSectionFilter && !empty($correctIds) && $this->arParams['SHOW_FROM_SECTION'] === 'Y')
1149 {
1150 $correctIds = $this->filterIdBySection(
1151 $correctIds,
1152 $this->arParams['IBLOCK_ID'],
1153 $this->arParams['SECTION_ID'],
1154 $this->arParams['PAGE_ELEMENT_COUNT'],
1155 $this->arParams['DEPTH']
1156 );
1157 }
1158
1159 $correctIds = array_flip($correctIds);
1160 // remove invalid items
1161 foreach ($ids as $key => $id)
1162 {
1163 if (!isset($correctIds[$id]))
1164 {
1165 unset($ids[$key]);
1166 }
1167 }
1168
1169 return array_values($ids);
1170 }
1171 else
1172 {
1173 return array();
1174 }
1175 }
1176
1183 protected function getSectionIdByCode($sectionCode = '', int $iblockId = 0)
1184 {
1185 $sectionId = 0;
1186 $sectionCode = (string)$sectionCode;
1187
1188 if ($sectionCode === '')
1189 {
1190 return $sectionId;
1191 }
1192
1193 $sectionFilter = [];
1194 if ($iblockId > 0)
1195 {
1196 $sectionFilter['=IBLOCK_ID'] = $iblockId;
1197 }
1198 elseif (!empty($this->arParams['IBLOCK_ID']))
1199 {
1200 $sectionFilter['@IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
1201 }
1202 if (empty($sectionFilter))
1203 {
1204 return $sectionId;
1205 }
1206
1207 $sectionFilter['=IBLOCK.ACTIVE'] = 'Y';
1208 $sectionFilter['=CODE'] = $sectionCode;
1209
1211 'select' => array('ID'),
1212 'filter' => $sectionFilter
1213 ))->fetch();
1214 if (!empty($section))
1215 {
1216 $sectionId = (int)$section['ID'];
1217 }
1218
1219 return $sectionId;
1220 }
1221
1229 protected function getSectionIdByElement($elementId, $elementCode = '', int $iblockId = 0)
1230 {
1231 $sectionId = 0;
1232 $elementId = (int)$elementId;
1233 $elementCode = (string)$elementCode;
1234 $filter = [];
1235
1236 if ($iblockId > 0)
1237 {
1238 $filter['=IBLOCK_ID'] = $iblockId;
1239 }
1240 elseif (!empty($this->arParams['IBLOCK_ID']))
1241 {
1242 $filter['=IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
1243 }
1244 if (empty($filter))
1245 {
1246 return $sectionId;
1247 }
1248
1249 if ($elementId > 0)
1250 {
1251 $filter['=ID'] = $elementId;
1252 }
1253 elseif ($elementCode !== '')
1254 {
1255 $filter['=CODE'] = $elementCode;
1256 }
1257 else
1258 {
1259 return $sectionId;
1260 }
1261
1262 $itemIterator = Iblock\ElementTable::getList(array(
1263 'select' => array('ID', 'IBLOCK_SECTION_ID'),
1264 'filter' => $filter
1265 ));
1266 if ($item = $itemIterator->fetch())
1267 {
1268 $sectionId = (int)$item['IBLOCK_SECTION_ID'];
1269 }
1270
1271 return $sectionId;
1272 }
1273
1274 protected function filterIdBySection($elementIds, $iblockId, $sectionId, $limit, $depth = 0)
1275 {
1276 $map = array();
1277
1279
1280 if (empty($elementIds))
1281 return $map;
1282
1283 $iblockId = (int)$iblockId;
1284 $sectionId = (int)$sectionId;
1285 $limit = (int)$limit;
1286 $depth = (int)$depth;
1287
1288 if ($iblockId <= 0 ||$depth < 0)
1289 return $map;
1290
1291 $subSections = array();
1292 if ($depth > 0)
1293 {
1294 $parentSectionId = Catalog\Product\Viewed::getParentSection($sectionId, $depth);
1295 if ($parentSectionId !== null)
1296 {
1297 $subSections[$parentSectionId] = $parentSectionId;
1298 }
1299 unset($parentSectionId);
1300 }
1301
1302 if (empty($subSections) && $sectionId <= 0)
1303 {
1305 'select' => array('ID'),
1306 'filter' => array(
1307 '@ID' => $elementIds,
1308 '=IBLOCK_ID' => $iblockId,
1309 '=WF_STATUS_ID' => 1,
1310 '=WF_PARENT_ELEMENT_ID' => null
1311 ),
1312 );
1313 if ($limit > 0)
1314 {
1315 $getListParams['limit'] = $limit;
1316 }
1317
1319 }
1320 else
1321 {
1322 if (empty($subSections))
1323 {
1324 $subSections[$sectionId] = $sectionId;
1325 }
1326
1327 $sectionQuery = new Main\Entity\Query(Iblock\SectionTable::getEntity());
1328 $sectionQuery->setTableAliasPostfix('_parent');
1329 $sectionQuery->setSelect(array('ID', 'LEFT_MARGIN', 'RIGHT_MARGIN'));
1330 $sectionQuery->setFilter(array('@ID' => $subSections));
1331
1332 $subSectionQuery = new Main\Entity\Query(Iblock\SectionTable::getEntity());
1333 $subSectionQuery->setTableAliasPostfix('_sub');
1334 $subSectionQuery->setSelect(array('ID'));
1335 $subSectionQuery->setFilter(array('=IBLOCK_ID' => $iblockId));
1336 $subSectionQuery->registerRuntimeField(
1337 '',
1338 new Main\Entity\ReferenceField(
1339 'BS',
1340 Main\Entity\Base::getInstanceByQuery($sectionQuery),
1341 array('>=this.LEFT_MARGIN' => 'ref.LEFT_MARGIN', '<=this.RIGHT_MARGIN' => 'ref.RIGHT_MARGIN'),
1342 array('join_type' => 'INNER')
1343 )
1344 );
1345
1346 $sectionElementQuery = new Main\Entity\Query(Iblock\SectionElementTable::getEntity());
1347 $sectionElementQuery->setSelect(array('IBLOCK_ELEMENT_ID'));
1348 $sectionElementQuery->setGroup(array('IBLOCK_ELEMENT_ID'));
1349 $sectionElementQuery->setFilter(array('=ADDITIONAL_PROPERTY_ID' => null));
1350 $sectionElementQuery->registerRuntimeField(
1351 '',
1352 new Main\Entity\ReferenceField(
1353 'BSUB',
1354 Main\Entity\Base::getInstanceByQuery($subSectionQuery),
1355 array('=this.IBLOCK_SECTION_ID' => 'ref.ID'),
1356 array('join_type' => 'INNER')
1357 )
1358 );
1359
1360 $elementQuery = new Main\Entity\Query(Iblock\ElementTable::getEntity());
1361 $elementQuery->setSelect(array('ID'));
1362 $elementQuery->setFilter(array('=IBLOCK_ID' => $iblockId, '=WF_STATUS_ID' => 1, '=WF_PARENT_ELEMENT_ID' => null));
1363 $elementQuery->registerRuntimeField(
1364 '',
1365 new Main\Entity\ReferenceField(
1366 'BSE',
1367 Main\Entity\Base::getInstanceByQuery($sectionElementQuery),
1368 array('=this.ID' => 'ref.IBLOCK_ELEMENT_ID'),
1369 array('join_type' => 'INNER')
1370 )
1371 );
1372 if ($limit > 0)
1373 {
1374 $elementQuery->setLimit($limit);
1375 }
1376
1377 $iterator = $elementQuery->exec();
1378
1379 unset($elementQuery, $sectionElementQuery, $subSectionQuery, $sectionQuery);
1380 }
1381
1382 while ($row = $iterator->fetch())
1383 {
1384 $map[] = $row['ID'];
1385 }
1386 unset($row, $iterator);
1387
1388 return $map;
1389 }
1390
1398 protected function getRecommendationLimit($ids)
1399 {
1400 $limit = 0;
1401 $idsCount = count($ids);
1402 $rowsRange = $this->request->get('rowsRange');
1403
1404 if (!empty($rowsRange))
1405 {
1406 foreach ($rowsRange as $range)
1407 {
1408 $range = (int)$range;
1409
1410 if ($range > $idsCount)
1411 {
1412 $limit = $range - $idsCount;
1413 break;
1414 }
1415 }
1416 }
1417 else
1418 {
1419 $limit = $this->arParams['PAGE_ELEMENT_COUNT'] - $idsCount;
1420 }
1421
1422 return $limit;
1423 }
1424
1425 protected function getBigDataServiceRequestParams($type = '')
1426 {
1427 $params = array(
1428 'uid' => ($_COOKIE['BX_USER_ID'] ?? ''),
1429 'aid' => Main\Analytics\Counter::getAccountId(),
1430 'count' => max($this->arParams['PAGE_ELEMENT_COUNT'] * 2, 30)
1431 );
1432
1433 // random choices
1434 if ($type === 'any_similar')
1435 {
1436 $possible = array('similar_sell', 'similar_view', 'similar');
1437 $type = $possible[array_rand($possible)];
1438 }
1439 elseif ($type === 'any_personal')
1440 {
1441 $possible = array('bestsell', 'personal');
1442 $type = $possible[array_rand($possible)];
1443 }
1444 elseif ($type === 'any')
1445 {
1446 $possible = array('similar_sell', 'similar_view', 'similar', 'bestsell', 'personal');
1447 $type = $possible[array_rand($possible)];
1448 }
1449
1450 // configure
1451 switch ($type)
1452 {
1453 case 'bestsell':
1454 $params['op'] = 'sim_domain_items';
1455 $params['type'] = 'order';
1456 $params['domain'] = Main\Context::getCurrent()->getServer()->getHttpHost();
1457 break;
1458 case 'personal':
1459 $params['op'] = 'recommend';
1460 break;
1461 case 'similar_sell':
1462 $params['op'] = 'simitems';
1463 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1464 $params['type'] = 'order';
1465 break;
1466 case 'similar_view':
1467 $params['op'] = 'simitems';
1468 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1469 $params['type'] = 'view';
1470 break;
1471 case 'similar':
1472 $params['op'] = 'simitems';
1473 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1474 break;
1475 default:
1476 $params['op'] = 'recommend';
1477 }
1478
1479 $iblocks = array();
1480
1481 if (!empty($this->storage['IBLOCK_PARAMS']))
1482 {
1483 $iblocks = array_keys($this->storage['IBLOCK_PARAMS']);
1484 }
1485 else
1486 {
1487 $iblockList = array();
1488 /* catalog */
1490 'select' => array('IBLOCK_ID', 'PRODUCT_IBLOCK_ID')
1491 ));
1492 while ($iblock = $iblockIterator->fetch())
1493 {
1494 $iblock['IBLOCK_ID'] = (int)$iblock['IBLOCK_ID'];
1495 $iblock['PRODUCT_IBLOCK_ID'] = (int)$iblock['PRODUCT_IBLOCK_ID'];
1496 $iblockList[$iblock['IBLOCK_ID']] = $iblock['IBLOCK_ID'];
1497
1498 if ($iblock['PRODUCT_IBLOCK_ID'] > 0)
1499 {
1500 $iblockList[$iblock['PRODUCT_IBLOCK_ID']] = $iblock['PRODUCT_IBLOCK_ID'];
1501 }
1502 }
1503
1504 /* iblock */
1505 $iblockIterator = Iblock\IblockSiteTable::getList(array(
1506 'select' => array('IBLOCK_ID'),
1507 'filter' => array('@IBLOCK_ID' => $iblockList, '=SITE_ID' => $this->getSiteId())
1508 ));
1509 while ($iblock = $iblockIterator->fetch())
1510 {
1511 $iblocks[] = $iblock['IBLOCK_ID'];
1512 }
1513 }
1514
1515 $params['ib'] = join('.', $iblocks);
1516
1517 return $params;
1518 }
1519
1525 protected function getBestSellersProductIds()
1526 {
1527 $productIds = array();
1528 $filter = $this->getBestSellersFilter();
1529
1530 if (!empty($filter))
1531 {
1532 $productIterator = \CSaleProduct::GetBestSellerList(
1533 $this->arParams['BY'],
1534 array(),
1535 $filter,
1536 $this->arParams['PAGE_ELEMENT_COUNT']
1537 );
1538 while($product = $productIterator->fetch())
1539 {
1540 $productIds[] = $product['PRODUCT_ID'];
1541 }
1542 }
1543
1544 return $productIds;
1545 }
1546
1547 protected function getBestSellersFilter()
1548 {
1549 $filter = array();
1550
1551 if (!empty($this->arParams['FILTER']))
1552 {
1553 $filter = array('=LID' => $this->getSiteId());
1554 $subFilter = array('LOGIC' => 'OR');
1555
1556 $statuses = array(
1557 'CANCELED' => true,
1558 'ALLOW_DELIVERY' => true,
1559 'PAYED' => true,
1560 'DEDUCTED' => true
1561 );
1562
1563 if ($this->arParams['PERIOD'] > 0)
1564 {
1565 $date = ConvertTimeStamp(AddToTimeStamp(array('DD' => '-'.$this->arParams['PERIOD'])));
1566 if (!empty($date))
1567 {
1568 foreach ($this->arParams['FILTER'] as $field)
1569 {
1570 if (isset($statuses[$field]))
1571 {
1572 $subFilter[] = array(
1573 '>=DATE_'.$field => $date,
1574 '='.$field => 'Y'
1575 );
1576 }
1577 else
1578 {
1579 if (empty($this->storage['ORDER_STATUS']) || in_array($field, $this->storage['ORDER_STATUS']))
1580 {
1581 $subFilter[] = array(
1582 '=STATUS_ID' => $field,
1583 '>=DATE_UPDATE' => $date,
1584 );
1585 }
1586 }
1587 }
1588 unset($field);
1589 }
1590 }
1591 else
1592 {
1593 foreach ($this->arParams['FILTER'] as $field)
1594 {
1595 if (isset($statuses[$field]))
1596 {
1597 $subFilter[] = array(
1598 '='.$field => 'Y'
1599 );
1600 }
1601 else
1602 {
1603 if (empty($this->storage['ORDER_STATUS']) || in_array($field, $this->storage['ORDER_STATUS']))
1604 {
1605 $subFilter[] = array(
1606 '=STATUS_ID' => $field,
1607 );
1608 }
1609 }
1610 }
1611 unset($field);
1612 }
1613
1614 $filter[] = $subFilter;
1615 }
1616
1617 return $filter;
1618 }
1619
1625 protected function getDeferredProductIds()
1626 {
1627 return array();
1628 }
1629
1630 protected function getProductIdMap($productIds)
1631 {
1632 if ($productIds === false)
1633 {
1634 return false;
1635 }
1636
1637 return $this->useCatalog ? static::getProductsMap($productIds) : $productIds;
1638 }
1639
1646 public static function getProductsMap(array $originalIds = array())
1647 {
1648 if (empty($originalIds))
1649 {
1650 return array();
1651 }
1652
1653 $result = array();
1654 $productList = \CCatalogSku::getProductList($originalIds);
1655 if ($productList === false)
1656 {
1657 $productList = array();
1658 }
1659
1660 foreach ($originalIds as $id)
1661 {
1662 $result[$id] = isset($productList[$id]) ? $productList[$id]['ID'] : (int)$id;
1663 }
1664
1665 return $result;
1666 }
1667
1678 protected function getProductsSeparatedByIblock()
1679 {
1680 $iblockItems = array();
1681
1682 if (!empty($this->productIdMap) && is_array($this->productIdMap))
1683 {
1684 $itemsIterator = Iblock\ElementTable::getList(array(
1685 'select' => array('ID', 'IBLOCK_ID'),
1686 'filter' => array('@ID' => $this->productIdMap)
1687 ));
1688 while ($item = $itemsIterator->fetch())
1689 {
1690 $item['ID'] = (int)$item['ID'];
1691 $item['IBLOCK_ID'] = (int)$item['IBLOCK_ID'];
1692
1693 if (!isset($iblockItems[$item['IBLOCK_ID']]))
1694 {
1695 $iblockItems[$item['IBLOCK_ID']] = array();
1696 }
1697
1698 $iblockItems[$item['IBLOCK_ID']][] = $item['ID'];
1699 }
1700 unset($item, $itemsIterator);
1701 }
1702 elseif ($this->productIdMap === false)
1703 {
1704 $iblockItems[$this->arParams['IBLOCK_ID']] = $this->arParams['ELEMENT_ID'] ?? 0;
1705 }
1706
1707 return $iblockItems;
1708 }
1709
1715 protected function getDefaultMeasure()
1716 {
1717 $defaultMeasure = array();
1718
1719 if ($this->useCatalog)
1720 {
1721 $defaultMeasure = \CCatalogMeasure::getDefaultMeasure(true, true);
1722 }
1723
1724 return $defaultMeasure;
1725 }
1726
1734 protected function getElementList($iblockId, $products)
1735 {
1737
1739 if ($iblockId > 0)
1740 {
1741 $filterFields['IBLOCK_ID'] = $iblockId;
1742 }
1743 if (!empty($products))
1744 {
1745 $filterFields['ID'] = $products;
1746 }
1747
1748 $globalFilter = [];
1749 if (!empty($this->globalFilter))
1750 $globalFilter = $this->convertFilter($this->globalFilter);
1751
1752 $iteratorParams = [
1753 'select' => $selectFields,
1754 'filter' => array_merge($globalFilter, $filterFields),
1755 'order' => $this->sortFields,
1756 'navigation' => $this->navParams
1757 ];
1758 if ($this->isSeparateLoading() && $iblockId > 0)
1759 {
1760 $elementIterator = $this->getSeparateList($iteratorParams);
1761 }
1762 else
1763 {
1764 $elementIterator = $this->getFullIterator($iteratorParams);
1765 }
1766 unset($iteratorParams);
1767
1768 $elementIterator->SetUrlTemplates($this->arParams['DETAIL_URL']);
1769
1770 return $elementIterator;
1771 }
1772
1777 protected function getSeparateList(array $params)
1778 {
1779 $list = [];
1780
1781 $selectFields = ['ID', 'IBLOCK_ID'];
1782 if (!empty($params['order']))
1783 {
1784 $selectFields = array_unique(array_merge(
1786 array_keys($params['order'])
1787 ));
1788 }
1789
1790 $iterator = \CIBlockElement::GetList(
1791 $params['order'],
1792 $params['filter'],
1793 false,
1794 $params['navigation'],
1796 );
1797 while ($row = $iterator->Fetch())
1798 {
1799 $id = (int)$row['ID'];
1800 $list[$id] = [
1801 'ID' => $row['ID'],
1802 'IBLOCK_ID' => $row['IBLOCK_ID'],
1803 ];
1804 }
1805 unset($row);
1806
1807 if (!empty($list))
1808 {
1809 $fullIterator = \CIBlockElement::GetList(
1810 [],
1811 ['IBLOCK_ID' => $params['filter']['IBLOCK_ID'], 'ID' => array_keys($list), 'SITE_ID' => $this->getSiteId()],
1812 false,
1813 false,
1814 $params['select']
1815 );
1816 while ($row = $fullIterator->Fetch())
1817 {
1818 $id = (int)$row['ID'];
1819 $list[$id] = $list[$id] + $row;
1820 }
1821 unset($row, $fullIterator);
1822
1823 $iterator->InitFromArray(array_values($list));
1824 }
1825
1826 return $iterator;
1827 }
1828
1833 protected function getFullIterator(array $params)
1834 {
1835 return \CIBlockElement::GetList(
1836 $params['order'],
1837 $params['filter'],
1838 false,
1839 $params['navigation'],
1840 $params['select']
1841 );
1842 }
1843
1849 protected function initQueryFields()
1850 {
1851 $this->selectFields = $this->getSelect();
1852 $this->filterFields = $this->getFilter();
1853 $this->sortFields = $this->getSort();
1855 }
1856
1862 protected function getSelect()
1863 {
1864 $result = [
1865 'ID', 'IBLOCK_ID', 'CODE', 'XML_ID', 'NAME', 'ACTIVE', 'DATE_ACTIVE_FROM', 'DATE_ACTIVE_TO', 'SORT',
1866 'PREVIEW_TEXT', 'PREVIEW_TEXT_TYPE', 'DETAIL_TEXT', 'DETAIL_TEXT_TYPE', 'DATE_CREATE', 'CREATED_BY', 'TAGS',
1867 'TIMESTAMP_X', 'MODIFIED_BY', 'IBLOCK_SECTION_ID', 'DETAIL_PAGE_URL', 'DETAIL_PICTURE', 'PREVIEW_PICTURE'
1868 ];
1869
1870 $checkPriceProperties = (
1871 !$this->useCatalog
1872 || (
1873 isset($this->arParams['IBLOCK_ID'])
1874 && $this->arParams['IBLOCK_ID'] > 0
1875 && !isset($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
1876 )
1877 );
1878
1879 if ($checkPriceProperties && !empty($this->storage['PRICES']))
1880 {
1881 foreach ($this->storage['PRICES'] as $row)
1882 {
1883 if (!empty($row['SELECT']))
1884 $result[] = $row['SELECT'];
1885 }
1886 }
1887
1888 return $result;
1889 }
1890
1896 protected function getFilter()
1897 {
1898 return array(
1899 'IBLOCK_LID' => $this->getSiteId(),
1900 'ACTIVE_DATE' => 'Y',
1901 'CHECK_PERMISSIONS' => 'Y',
1902 'MIN_PERMISSION' => 'R'
1903 );
1904 }
1905
1911 protected function getSort()
1912 {
1913 return array();
1914 }
1915
1921 protected function prepareElementQueryFields()
1922 {
1923 $result = $this->prepareQueryFields($this->selectFields, $this->filterFields, $this->sortFields);
1924 $this->selectFields = $result['SELECT'];
1925 $this->filterFields = $result['FILTER'];
1926 $this->sortFields = $result['ORDER'];
1927 if (!empty($this->globalFilter))
1928 {
1929 $result = $this->prepareQueryFields([], $this->globalFilter, []);
1930 $this->globalFilter = $result['FILTER'];
1931 }
1932 unset($result);
1933 }
1934
1944 {
1945 if ($this->useCatalog)
1946 {
1947 $select = $this->convertSelect($select);
1948 $order = $this->convertOrder($order);
1949 $filter = $this->convertFilter($filter);
1951 $filter,
1952 $order,
1953 ['QUANTITY' => $this->arParams['SHOW_PRICE_COUNT']]
1954 );
1955 }
1956
1957 if (!empty($select))
1958 {
1959 $select = array_unique($select);
1960 }
1961
1962 return [
1963 'SELECT' => $select,
1964 'FILTER' => $filter,
1965 'ORDER' => $order
1966 ];
1967 }
1968
1973 protected function initPricesQuery()
1974 {
1976 }
1977
1986 {
1987 if (!$this->useCatalog)
1988 return $selectFields;
1989
1990 $additionalFields = $this->getProductFields($iblockId);
1992
1993 if (!empty($additionalFields))
1994 {
1995 $result = array_merge($result, $additionalFields);
1996 $result = array_unique($result);
1997 }
1998 unset($additionalFields);
1999
2000 return $result;
2001 }
2002
2009 protected function getProductFields($iblockId)
2010 {
2011 if (!$this->isIblockCatalog && !$this->offerIblockExist($iblockId))
2012 return [];
2013
2014 $result = [
2015 'TYPE', 'AVAILABLE', 'BUNDLE',
2016 'QUANTITY', 'QUANTITY_TRACE', 'CAN_BUY_ZERO', 'MEASURE',
2017 'SUBSCRIBE',
2018 'VAT_ID', 'VAT_INCLUDED',
2019 'WEIGHT', 'WIDTH', 'LENGTH', 'HEIGHT',
2020 'PAYMENT_TYPE', 'RECUR_SCHEME_LENGTH', 'RECUR_SCHEME_TYPE',
2021 'TRIAL_PRICE_ID'
2022 ];
2023
2024 if ($this->isEnableCompatible())
2025 {
2026 $result = array_merge(
2027 $result,
2028 [
2029 'QUANTITY_TRACE_RAW', 'CAN_BUY_ZERO_RAW', 'SUBSCRIBE_RAW',
2030 'PURCHASING_PRICE', 'PURCHASING_CURRENCY',
2031 'BARCODE_MULTI',
2032 'WITHOUT_ORDER'
2033 ]
2034 );
2035 }
2036
2037 return $result;
2038 }
2039
2046 protected function convertSelect(array $select)
2047 {
2048 if (!$this->useCatalog)
2049 return $select;
2050 return \CProductQueryBuilder::convertOldSelect($select);
2051 }
2052
2059 protected function convertFilter(array $filter)
2060 {
2061 if (!$this->useCatalog)
2062 return $filter;
2063 return \CProductQueryBuilder::convertOldFilter($filter);
2064 }
2065
2072 protected function convertOrder(array $order)
2073 {
2074 if (!$this->useCatalog)
2075 return $order;
2076 return \CProductQueryBuilder::convertOldOrder($order);
2077 }
2078
2080 {
2081 if (!$this->useCatalog)
2082 return $this->selectFields;
2083 return $this->getProductSelect($iblockId, $this->selectFields);
2084 }
2085
2093 protected function parseCondition($condition, $params)
2094 {
2095 $result = array();
2096
2097 if (!empty($condition) && is_array($condition))
2098 {
2099 if ($condition['CLASS_ID'] === 'CondGroup')
2100 {
2101 if (!empty($condition['CHILDREN']))
2102 {
2103 foreach ($condition['CHILDREN'] as $child)
2104 {
2105 $childResult = $this->parseCondition($child, $params);
2106
2107 // is group
2108 if ($child['CLASS_ID'] === 'CondGroup')
2109 {
2110 $result[] = $childResult;
2111 }
2112 // same property names not overrides each other
2113 elseif (isset($result[key($childResult)]))
2114 {
2115 $fieldName = key($childResult);
2116
2117 if (!isset($result['LOGIC']))
2118 {
2119 $result = array(
2120 'LOGIC' => $condition['DATA']['All'],
2121 array($fieldName => $result[$fieldName])
2122 );
2123 }
2124
2125 $result[][$fieldName] = $childResult[$fieldName];
2126 }
2127 else
2128 {
2129 $result += $childResult;
2130 }
2131 }
2132
2133 if (!empty($result))
2134 {
2135 $this->parsePropertyCondition($result, $condition, $params);
2136
2137 if (count($result) > 1)
2138 {
2139 $result['LOGIC'] = $condition['DATA']['All'];
2140 }
2141 }
2142 }
2143 }
2144 else
2145 {
2146 $result += $this->parseConditionLevel($condition, $params);
2147 }
2148 }
2149
2150 return $result;
2151 }
2152
2153 protected function parseConditionLevel($condition, $params)
2154 {
2155 $result = array();
2156
2157 if (!empty($condition) && is_array($condition))
2158 {
2159 $name = $this->parseConditionName($condition);
2160 if (!empty($name))
2161 {
2162 $operator = $this->parseConditionOperator($condition);
2163 $value = $this->parseConditionValue($condition, $name);
2164 $result[$operator.$name] = $value;
2165
2166 if ($name === 'SECTION_ID')
2167 {
2168 $result['INCLUDE_SUBSECTIONS'] = isset($params['INCLUDE_SUBSECTIONS']) && $params['INCLUDE_SUBSECTIONS'] === 'N' ? 'N' : 'Y';
2169
2170 if (isset($params['INCLUDE_SUBSECTIONS']) && $params['INCLUDE_SUBSECTIONS'] === 'A')
2171 {
2172 $result['SECTION_GLOBAL_ACTIVE'] = 'Y';
2173 }
2174
2176 }
2177 }
2178 }
2179
2180 return $result;
2181 }
2182
2183 protected function parseConditionName(array $condition)
2184 {
2185 $name = '';
2186 $conditionNameMap = array(
2187 'CondIBXmlID' => 'XML_ID',
2188 'CondIBSection' => 'SECTION_ID',
2189 'CondIBDateActiveFrom' => 'DATE_ACTIVE_FROM',
2190 'CondIBDateActiveTo' => 'DATE_ACTIVE_TO',
2191 'CondIBSort' => 'SORT',
2192 'CondIBDateCreate' => 'DATE_CREATE',
2193 'CondIBCreatedBy' => 'CREATED_BY',
2194 'CondIBTimestampX' => 'TIMESTAMP_X',
2195 'CondIBModifiedBy' => 'MODIFIED_BY',
2196 'CondIBTags' => 'TAGS',
2197 'CondCatQuantity' => 'QUANTITY',
2198 'CondCatWeight' => 'WEIGHT'
2199 );
2200
2201 if (isset($conditionNameMap[$condition['CLASS_ID']]))
2202 {
2203 $name = $conditionNameMap[$condition['CLASS_ID']];
2204 }
2205 elseif (mb_strpos($condition['CLASS_ID'], 'CondIBProp') !== false)
2206 {
2207 $name = $condition['CLASS_ID'];
2208 }
2209
2210 return $name;
2211 }
2212
2213 protected function parseConditionOperator($condition)
2214 {
2215 $operator = '';
2216
2217 switch ($condition['DATA']['logic'])
2218 {
2219 case 'Equal':
2220 $operator = '';
2221 break;
2222 case 'Not':
2223 $operator = '!';
2224 break;
2225 case 'Contain':
2226 $operator = '%';
2227 break;
2228 case 'NotCont':
2229 $operator = '!%';
2230 break;
2231 case 'Great':
2232 $operator = '>';
2233 break;
2234 case 'Less':
2235 $operator = '<';
2236 break;
2237 case 'EqGr':
2238 $operator = '>=';
2239 break;
2240 case 'EqLs':
2241 $operator = '<=';
2242 break;
2243 }
2244
2245 return $operator;
2246 }
2247
2248 protected function parseConditionValue($condition, $name)
2249 {
2250 $value = $condition['DATA']['value'];
2251
2252 switch ($name)
2253 {
2254 case 'DATE_ACTIVE_FROM':
2255 case 'DATE_ACTIVE_TO':
2256 case 'DATE_CREATE':
2257 case 'TIMESTAMP_X':
2258 $value = ConvertTimeStamp($value, 'FULL');
2259 break;
2260 }
2261
2262 return $value;
2263 }
2264
2265 protected function parsePropertyCondition(array &$result, array $condition, $params)
2266 {
2267 if (!empty($result))
2268 {
2269 $subFilter = array();
2270
2271 foreach ($result as $name => $value)
2272 {
2273 if (!empty($result[$name]) && is_array($result[$name]))
2274 {
2275 $this->parsePropertyCondition($result[$name], $condition, $params);
2276 }
2277 else
2278 {
2279 if (($ind = mb_strpos($name, 'CondIBProp')) !== false)
2280 {
2281 [$prefix, $iblock, $propertyId] = explode(':', $name);
2282 $operator = $ind > 0? mb_substr($prefix, 0, $ind) : '';
2283
2284 $catalogInfo = \CCatalogSku::GetInfoByIBlock($iblock);
2285 if (!empty($catalogInfo))
2286 {
2287 if (
2288 $catalogInfo['CATALOG_TYPE'] != \CCatalogSku::TYPE_CATALOG
2289 && $catalogInfo['IBLOCK_ID'] == $iblock
2290 )
2291 {
2292 $subFilter[$operator.'PROPERTY_'.$propertyId] = $value;
2293 }
2294 else
2295 {
2296 $result[$operator.'PROPERTY_'.$propertyId] = $value;
2297 }
2298 }
2299
2300 unset($result[$name]);
2301 }
2302 }
2303 }
2304
2305 if (!empty($subFilter) && !empty($catalogInfo))
2306 {
2307 $offerPropFilter = array(
2308 'IBLOCK_ID' => $catalogInfo['IBLOCK_ID'],
2309 'ACTIVE_DATE' => 'Y',
2310 'ACTIVE' => 'Y'
2311 );
2312
2313 if ($params['HIDE_NOT_AVAILABLE_OFFERS'] === 'Y')
2314 {
2315 $offerPropFilter['HIDE_NOT_AVAILABLE'] = 'Y';
2316 }
2317 elseif ($params['HIDE_NOT_AVAILABLE_OFFERS'] === 'L')
2318 {
2319 $offerPropFilter[] = array(
2320 'LOGIC' => 'OR',
2321 'AVAILABLE' => 'Y',
2322 'SUBSCRIBE' => 'Y'
2323 );
2324 }
2325
2326 if (count($subFilter) > 1)
2327 {
2328 $subFilter['LOGIC'] = $condition['DATA']['All'];
2329 $subFilter = array($subFilter);
2330 }
2331
2332 $result['=ID'] = \CIBlockElement::SubQuery(
2333 'PROPERTY_'.$catalogInfo['SKU_PROPERTY_ID'],
2334 $offerPropFilter + $subFilter
2335 );
2336 }
2337 }
2338 }
2339
2346 protected function processElement(array &$element)
2347 {
2348 $this->modifyElementCommonData($element);
2349 $this->modifyElementPrices($element);
2350 $this->setElementPanelButtons($element);
2351 }
2352
2359 protected function modifyElementCommonData(array &$element)
2360 {
2361 $element['ID'] = (int)$element['ID'];
2362 $element['IBLOCK_ID'] = (int)$element['IBLOCK_ID'];
2363
2364 if ($this->arParams['HIDE_DETAIL_URL'])
2365 {
2366 $element['DETAIL_PAGE_URL'] = $element['~DETAIL_PAGE_URL'] = '';
2367 }
2368
2369 if ($this->isEnableCompatible())
2370 {
2371 $element['ACTIVE_FROM'] = ($element['DATE_ACTIVE_FROM'] ?? null);
2372 $element['ACTIVE_TO'] = ($element['DATE_ACTIVE_TO'] ?? null);
2373 }
2374
2375 $ipropValues = new Iblock\InheritedProperty\ElementValues($element['IBLOCK_ID'], $element['ID']);
2376 $element['IPROPERTY_VALUES'] = $ipropValues->getValues();
2377
2379 $element,
2380 array('PREVIEW_PICTURE', 'DETAIL_PICTURE'),
2382 'IPROPERTY_VALUES'
2383 );
2384
2385 if (isset($element['~TYPE']))
2386 {
2387 $productFields = $this->getProductFields($element['IBLOCK_ID']);
2388 $translateFields = $this->getCompatibleProductFields();
2389
2390 $element['PRODUCT'] = array(
2391 'TYPE' => (int)$element['~TYPE'],
2392 'AVAILABLE' => $element['~AVAILABLE'],
2393 'BUNDLE' => $element['~BUNDLE'],
2394 'QUANTITY' => $element['~QUANTITY'],
2395 'QUANTITY_TRACE' => $element['~QUANTITY_TRACE'],
2396 'CAN_BUY_ZERO' => $element['~CAN_BUY_ZERO'],
2397 'MEASURE' => (int)$element['~MEASURE'],
2398 'SUBSCRIBE' => $element['~SUBSCRIBE'],
2399 'VAT_ID' => (int)$element['~VAT_ID'],
2400 'VAT_RATE' => 0,
2401 'VAT_INCLUDED' => $element['~VAT_INCLUDED'],
2402 'WEIGHT' => (float)$element['~WEIGHT'],
2403 'WIDTH' => (float)$element['~WIDTH'],
2404 'LENGTH' => (float)$element['~LENGTH'],
2405 'HEIGHT' => (float)$element['~HEIGHT'],
2406 'PAYMENT_TYPE' => $element['~PAYMENT_TYPE'],
2407 'RECUR_SCHEME_TYPE' => $element['~RECUR_SCHEME_TYPE'],
2408 'RECUR_SCHEME_LENGTH' => (int)$element['~RECUR_SCHEME_LENGTH'],
2409 'TRIAL_PRICE_ID' => (int)$element['~TRIAL_PRICE_ID']
2410 );
2411
2412 $vatId = 0;
2413 $vatRate = 0;
2414 if ($element['PRODUCT']['VAT_ID'] > 0)
2415 $vatId = $element['PRODUCT']['VAT_ID'];
2416 elseif ($this->storage['IBLOCKS_VAT'][$element['IBLOCK_ID']] > 0)
2417 $vatId = $this->storage['IBLOCKS_VAT'][$element['IBLOCK_ID']];
2418 if ($vatId > 0 && isset($this->storage['VATS'][$vatId]))
2419 $vatRate = $this->storage['VATS'][$vatId];
2420 $element['PRODUCT']['VAT_RATE'] = $vatRate;
2421 unset($vatRate, $vatId);
2422 $element['PRODUCT']['USE_OFFERS'] = $element['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_SKU;
2423
2424 if ($this->isEnableCompatible())
2425 {
2426 foreach ($translateFields as $currentKey => $oldKey)
2427 $element[$oldKey] = $element[$currentKey];
2428 unset($currentKey, $oldKey);
2429 $element['~CATALOG_VAT'] = $element['PRODUCT']['VAT_RATE'];
2430 $element['CATALOG_VAT'] = $element['PRODUCT']['VAT_RATE'];
2431 }
2432 else
2433 {
2434 // temporary (compatibility custom templates)
2435 $element['~CATALOG_TYPE'] = $element['PRODUCT']['TYPE'];
2436 $element['CATALOG_TYPE'] = $element['PRODUCT']['TYPE'];
2437 $element['~CATALOG_QUANTITY'] = $element['PRODUCT']['QUANTITY'];
2438 $element['CATALOG_QUANTITY'] = $element['PRODUCT']['QUANTITY'];
2439 $element['~CATALOG_QUANTITY_TRACE'] = $element['PRODUCT']['QUANTITY_TRACE'];
2440 $element['CATALOG_QUANTITY_TRACE'] = $element['PRODUCT']['QUANTITY_TRACE'];
2441 $element['~CATALOG_CAN_BUY_ZERO'] = $element['PRODUCT']['CAN_BUY_ZERO'];
2442 $element['CATALOG_CAN_BUY_ZERO'] = $element['PRODUCT']['CAN_BUY_ZERO'];
2443 $element['~CATALOG_SUBSCRIBE'] = $element['PRODUCT']['SUBSCRIBE'];
2444 $element['CATALOG_SUBSCRIBE'] = $element['PRODUCT']['SUBSCRIBE'];
2445 }
2446
2447 foreach ($productFields as $field)
2448 unset($element[$field], $element['~'.$field]);
2449 unset($field);
2450 }
2451 else
2452 {
2453 $element['PRODUCT'] = array(
2454 'TYPE' => null,
2455 'AVAILABLE' => null,
2456 'USE_OFFERS' => false
2457 );
2458 }
2459
2460 $element['PROPERTIES'] = array();
2461 $element['DISPLAY_PROPERTIES'] = array();
2462 $element['PRODUCT_PROPERTIES'] = array();
2463 $element['PRODUCT_PROPERTIES_FILL'] = array();
2464 $element['OFFERS'] = array();
2465 $element['OFFER_ID_SELECTED'] = 0;
2466
2467 if (!empty($this->storage['CATALOGS'][$element['IBLOCK_ID']]))
2468 $element['CHECK_QUANTITY'] = $this->isNeedCheckQuantity($element['PRODUCT']);
2469
2470 if ($this->getAction() === 'bigDataLoad')
2471 {
2472 $element['RCM_ID'] = $this->recommendationIdToProduct[$element['ID']];
2473 }
2474 }
2475
2482 protected function setElementPanelButtons(&$element)
2483 {
2484 $buttons = \CIBlock::GetPanelButtons(
2485 $element['IBLOCK_ID'],
2486 $element['ID'],
2487 $element['IBLOCK_SECTION_ID'],
2488 array('SECTION_BUTTONS' => false, 'SESSID' => false, 'CATALOG' => true)
2489 );
2490 $element['EDIT_LINK'] = ($buttons['edit']['edit_element']['ACTION_URL'] ?? null);
2491 $element['DELETE_LINK'] = ($buttons['edit']['delete_element']['ACTION_URL'] ?? null);
2492 }
2493
2501 protected function modifyDisplayProperties($iblock, &$iblockElements)
2502 {
2503 }
2504
2505 protected function getPropertyList($iblock, $propertyCodes)
2506 {
2507 $propertyList = array();
2508 if (empty($propertyCodes))
2509 return $propertyList;
2510
2511 $propertyCodes = array_fill_keys($propertyCodes, true);
2512
2513 $propertyIterator = Iblock\PropertyTable::getList(array(
2514 'select' => array('ID', 'CODE', 'SORT'),
2515 'filter' => array('=IBLOCK_ID' => $iblock, '=ACTIVE' => 'Y'),
2516 'order' => array('SORT' => 'ASC', 'ID' => 'ASC')
2517 ));
2518 while ($property = $propertyIterator->fetch())
2519 {
2520 $code = (string)$property['CODE'];
2521
2522 if ($code == '')
2523 {
2524 $code = $property['ID'];
2525 }
2526
2527 if (!isset($propertyCodes[$code]))
2528 continue;
2529
2530 $propertyList[] = $code;
2531 }
2532
2533 return $propertyList;
2534 }
2535
2541 protected function clearItems()
2542 {
2543 $this->prices = array();
2544 $this->measures = array();
2545 $this->ratios = array();
2546 $this->quantityRanges = array();
2547 $this->oldData = array();
2548 }
2549
2557 protected function loadMeasureRatios(array $itemIds)
2558 {
2559 if (empty($itemIds))
2560 return;
2562 if (empty($itemIds))
2563 return;
2564 $emptyRatioIds = array_fill_keys($itemIds, true);
2565
2566 $iterator = Catalog\MeasureRatioTable::getList(array(
2567 'select' => array('ID', 'RATIO', 'IS_DEFAULT', 'PRODUCT_ID'),
2568 'filter' => array('@PRODUCT_ID' => $itemIds),
2569 'order' => array('PRODUCT_ID' => 'ASC')// not add 'RATIO' => 'ASC' - result will be resorted after load prices
2570 ));
2571 while ($row = $iterator->fetch())
2572 {
2573 $ratio = max((float)$row['RATIO'], (int)$row['RATIO']);
2574 if ($ratio > CATALOG_VALUE_EPSILON)
2575 {
2576 $row['RATIO'] = $ratio;
2577 $row['ID'] = (int)$row['ID'];
2578 $id = (int)$row['PRODUCT_ID'];
2579 if (!isset($this->ratios[$id]))
2580 $this->ratios[$id] = array();
2581 $this->ratios[$id][$row['ID']] = $row;
2582 unset($emptyRatioIds[$id]);
2583 unset($id);
2584 }
2585 unset($ratio);
2586 }
2587 unset($row, $iterator);
2588 if (!empty($emptyRatioIds))
2589 {
2590 $emptyRatio = $this->getEmptyRatio();
2591 foreach (array_keys($emptyRatioIds) as $id)
2592 {
2593 $this->ratios[$id] = array(
2594 $emptyRatio['ID'] => $emptyRatio
2595 );
2596 }
2597 unset($id, $emptyRatio);
2598 }
2599 unset($emptyRatioIds);
2600 }
2601
2607 protected function getEmptyRatio()
2608 {
2609 return array(
2610 'ID' => 0,
2611 'RATIO' => 1,
2612 'IS_DEFAULT' => 'Y'
2613 );
2614 }
2615
2622 protected function initItemsMeasure(array &$items)
2623 {
2624 if (empty($items))
2625 return;
2626
2627 foreach (array_keys($items) as $index)
2628 {
2629 if (!isset($items[$index]['PRODUCT']['MEASURE']))
2630 continue;
2631 if ($items[$index]['PRODUCT']['MEASURE'] > 0)
2632 {
2633 $items[$index]['ITEM_MEASURE'] = array(
2634 'ID' => $items[$index]['PRODUCT']['MEASURE'],
2635 'TITLE' => '',
2636 '~TITLE' => ''
2637 );
2638 }
2639 else
2640 {
2641 $items[$index]['ITEM_MEASURE'] = array(
2642 'ID' => null,
2643 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
2644 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS'],
2645 );
2646 }
2647 }
2648 unset($index);
2649 }
2650
2657 protected function getMeasureIds(array $items)
2658 {
2659 $result = array();
2660
2661 if (!empty($items))
2662 {
2663 foreach (array_keys($items) as $itemId)
2664 {
2665 if (!isset($items[$itemId]['ITEM_MEASURE']))
2666 continue;
2667 $measureId = (int)$items[$itemId]['ITEM_MEASURE']['ID'];
2668 if ($measureId > 0)
2669 $result[$measureId] = $measureId;
2670 unset($measureId);
2671 }
2672 unset($itemId);
2673 }
2674
2675 return $result;
2676 }
2677
2684 protected function loadMeasures(array $measureIds)
2685 {
2686 if (empty($measureIds))
2687 return;
2689 if (empty($measureIds))
2690 return;
2691
2692 $measureIterator = \CCatalogMeasure::getList(
2693 array(),
2694 array('@ID' => $measureIds),
2695 false,
2696 false,
2697 array('ID', 'SYMBOL_RUS')
2698 );
2699 while ($measure = $measureIterator->GetNext())
2700 {
2701 $measure['ID'] = (int)$measure['ID'];
2702 $measure['TITLE'] = $measure['SYMBOL_RUS'];
2703 $measure['~TITLE'] = $measure['~SYMBOL_RUS'];
2704 unset($measure['SYMBOL_RUS'], $measure['~SYMBOL_RUS']);
2705 $this->measures[$measure['ID']] = $measure;
2706 }
2707 unset($measure, $measureIterator);
2708 }
2709
2716 protected function loadPrices(array $itemIds)
2717 {
2718 if (empty($itemIds))
2719 return;
2721 if (empty($itemIds))
2722 return;
2723
2724 $this->loadMeasureRatios($itemIds);
2725
2726 if (empty($this->storage['PRICES_ALLOW']))
2727 return;
2728
2729 $enableCompatible = $this->isEnableCompatible();
2730
2731 $quantityList = array_fill_keys($itemIds, array());
2732
2733 $select = array(
2734 'ID', 'PRODUCT_ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY',
2735 'QUANTITY_FROM', 'QUANTITY_TO', 'PRICE_SCALE'
2736 );
2737 if ($enableCompatible)
2738 $select[] = 'EXTRA_ID';
2739
2740 $pagedItemIds = array_chunk($itemIds, 500);
2741 foreach ($pagedItemIds as $pageIds)
2742 {
2743 if (empty($pageIds))
2744 continue;
2745
2747 'select' => $select,
2748 'filter' => array('@PRODUCT_ID' => $pageIds, '@CATALOG_GROUP_ID' => $this->storage['PRICES_ALLOW']),
2749 'order' => array('PRODUCT_ID' => 'ASC', 'CATALOG_GROUP_ID' => 'ASC')
2750 ));
2751 while ($row = $iterator->fetch())
2752 {
2753 $id = (int)$row['PRODUCT_ID'];
2754 unset($row['PRODUCT_ID']);
2755 if (!isset($this->prices[$id]))
2756 {
2757 $this->prices[$id] = array(
2758 'RATIO' => array(),
2759 'QUANTITY' => array(),
2760 'SIMPLE' => array()
2761 );
2762 }
2763
2764 if ($row['QUANTITY_FROM'] !== null || $row['QUANTITY_TO'] !== null)
2765 {
2766 $hash = $this->getQuantityRangeHash($row);
2767 if (!isset($quantityList[$id][$hash]))
2768 {
2769 $quantityList[$id][$hash] = array(
2770 'HASH' => $hash,
2771 'QUANTITY_FROM' => $row['QUANTITY_FROM'],
2772 'QUANTITY_TO' => $row['QUANTITY_TO'],
2773 'SORT_FROM' => (int)$row['QUANTITY_FROM'],
2774 'SORT_TO' => ($row['QUANTITY_TO'] === null ? INF : (int)$row['QUANTITY_TO'])
2775 );
2776 }
2777 if (!isset($this->prices[$id]['QUANTITY'][$hash]))
2778 {
2779 $this->prices[$id]['QUANTITY'][$hash] = array();
2780 }
2781 $this->prices[$id]['QUANTITY'][$hash][$row['CATALOG_GROUP_ID']] = $row;
2782 unset($hash);
2783 }
2784 elseif (!isset($row['MEASURE_RATIO_ID']))
2785 {
2786 $this->prices[$id]['SIMPLE'][$row['CATALOG_GROUP_ID']] = $row;
2787 }
2788 $this->storage['CURRENCY_LIST'][$row['CURRENCY']] = $row['CURRENCY'];
2789
2790 unset($id);
2791 }
2792 unset($row, $iterator);
2793 }
2794 unset($pageIds, $pagedItemIds);
2795
2796 foreach ($itemIds as $id)
2797 {
2798 if (isset($this->prices[$id]))
2799 {
2800 foreach ($this->prices[$id] as $key => $data)
2801 {
2802 if (empty($data))
2803 unset($this->prices[$id][$key]);
2804 }
2805 unset($key, $data);
2806
2807 if (count($this->prices[$id]) !== 1)
2808 {
2809 unset($this->prices[$id]);
2810 }
2811 else
2812 {
2813 if (!empty($this->prices[$id]['QUANTITY']))
2814 {
2815 $productQuantity = $quantityList[$id];
2817 $productQuantity,
2818 array('SORT_FROM' => SORT_ASC, 'SORT_TO' => SORT_ASC),
2819 '', null, true
2820 );
2821 $this->quantityRanges[$id] = $productQuantity;
2822 unset($productQuantity);
2823
2824 if (count($this->ratios[$id]) > 1)
2825 $this->compactItemRatios($id);
2826 }
2827 if (!empty($this->prices[$id]['SIMPLE']))
2828 {
2829 $range = $this->getFullQuantityRange();
2830 $this->quantityRanges[$id] = array(
2831 $range['HASH'] => $range
2832 );
2833 unset($range);
2834 if (count($this->ratios[$id]) > 1)
2835 $this->compactItemRatios($id);
2836 }
2837 }
2838 }
2839 }
2840 unset($id);
2841
2842 unset($quantityList);
2843
2844 unset($enableCompatible);
2845 }
2846
2847 protected function calculateItemPrices(array &$items)
2848 {
2849 if (empty($items))
2850 return;
2851
2852 $enableCompatible = $this->isEnableCompatible();
2853
2854 if ($enableCompatible)
2856
2857 foreach (array_keys($items) as $index)
2858 {
2859 $id = $items[$index]['ID'];
2860 $items[$index]['CAN_BUY'] = false;
2861 if (!isset($this->calculatePrices[$id]))
2862 continue;
2863 if (empty($this->prices[$id]))
2864 continue;
2865 $productPrices = $this->prices[$id];
2866 $result = array(
2867 'ITEM_PRICE_MODE' => null,
2868 'ITEM_PRICES' => array(),
2869 'ITEM_PRICES_CAN_BUY' => false
2870 );
2871 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2872 $result['ITEM_ALL_PRICES'] = array();
2873 $priceBlockIndex = 0;
2874 if (!empty($productPrices['QUANTITY']))
2875 {
2877 $ratio = current($this->ratios[$id]);
2878 foreach ($this->quantityRanges[$id] as $range)
2879 {
2880 $priceBlock = $this->calculatePriceBlock(
2881 $items[$index],
2882 $productPrices['QUANTITY'][$range['HASH']],
2883 $ratio['RATIO'],
2884 $this->arParams['USE_PRICE_COUNT'] || $this->checkQuantityRange($range)
2885 );
2886 if (!empty($priceBlock))
2887 {
2888 $minimalPrice = ($this->arParams['FILL_ITEM_ALL_PRICES']
2889 ? $priceBlock['MINIMAL_PRICE']
2890 : $priceBlock
2891 );
2892 if ($minimalPrice['QUANTITY_FROM'] === null)
2893 {
2894 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'];
2895 }
2896 else
2897 {
2898 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'] * ((int)($minimalPrice['QUANTITY_FROM']/$ratio['RATIO']));
2899 if ($minimalPrice['MIN_QUANTITY'] < $minimalPrice['QUANTITY_FROM'])
2900 $minimalPrice['MIN_QUANTITY'] += $ratio['RATIO'];
2901 }
2902 $result['ITEM_PRICES'][$priceBlockIndex] = $minimalPrice;
2903 if (isset($this->storage['PRICES_CAN_BUY'][$minimalPrice['PRICE_TYPE_ID']]))
2904 $result['ITEM_PRICES_CAN_BUY'] = true;
2905 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2906 {
2907 $priceBlock['ALL_PRICES']['MIN_QUANTITY'] = $minimalPrice['MIN_QUANTITY'];
2908 $result['ITEM_ALL_PRICES'][$priceBlockIndex] = $priceBlock['ALL_PRICES'];
2909 }
2910 unset($minimalPrice);
2911 $priceBlockIndex++;
2912 }
2913 unset($priceBlock);
2914 }
2915 unset($range);
2916 unset($ratio);
2917 }
2918 if (!empty($productPrices['SIMPLE']))
2919 {
2921 $ratio = current($this->ratios[$id]);
2922 $priceBlock = $this->calculatePriceBlock(
2923 $items[$index],
2924 $productPrices['SIMPLE'],
2925 $ratio['RATIO'],
2926 true
2927 );
2928 if (!empty($priceBlock))
2929 {
2930 $minimalPrice = ($this->arParams['FILL_ITEM_ALL_PRICES']
2931 ? $priceBlock['MINIMAL_PRICE']
2932 : $priceBlock
2933 );
2934 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'];
2935 $result['ITEM_PRICES'][$priceBlockIndex] = $minimalPrice;
2936 if (isset($this->storage['PRICES_CAN_BUY'][$minimalPrice['PRICE_TYPE_ID']]))
2937 $result['ITEM_PRICES_CAN_BUY'] = true;
2938 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2939 {
2940 $priceBlock['ALL_PRICES']['MIN_QUANTITY'] = $minimalPrice['MIN_QUANTITY'];
2941 $result['ITEM_ALL_PRICES'][$priceBlockIndex] = $priceBlock['ALL_PRICES'];
2942 }
2943 unset($minimalPrice);
2944 $priceBlockIndex++;
2945 }
2946 unset($priceBlock);
2947 unset($ratio);
2948 }
2949 $this->prices[$id] = $result;
2950
2951 if (isset($items[$index]['ACTIVE']) && $items[$index]['ACTIVE'] === 'N')
2952 {
2953 $items[$index]['CAN_BUY'] = false;
2954 }
2955 else
2956 {
2957 $items[$index]['CAN_BUY'] = $result['ITEM_PRICES_CAN_BUY'] && $items[$index]['PRODUCT']['AVAILABLE'] === 'Y';
2958 }
2959
2960 unset($priceBlockIndex, $result);
2961 unset($productPrices);
2962
2963 if ($enableCompatible)
2964 $this->resortOldPrices($id);
2965 }
2966 unset($index);
2967 }
2968
2969 protected function transferItems(array &$items)
2970 {
2971 if (empty($items))
2972 return;
2973
2974 $enableCompatible = $this->isEnableCompatible();
2975 $urls = $this->storage['URLS'];
2976
2977 foreach (array_keys($items) as $index)
2978 {
2979 $itemId = $items[$index]['ID'];
2980 // measure
2981 if (!empty($items[$index]['ITEM_MEASURE']))
2982 {
2983 $id = (int)$items[$index]['ITEM_MEASURE']['ID'];
2984 if (isset($this->measures[$id]))
2985 {
2986 $items[$index]['ITEM_MEASURE']['TITLE'] = $this->measures[$id]['TITLE'];
2987 $items[$index]['ITEM_MEASURE']['~TITLE'] = $this->measures[$id]['~TITLE'];
2988 }
2989 unset($id);
2990 }
2991 // prices
2992 $items[$index]['ITEM_MEASURE_RATIOS'] = $this->ratios[$itemId] ?? [];
2993 $items[$index]['ITEM_MEASURE_RATIO_SELECTED'] = $this->searchItemSelectedRatioId($itemId);
2994 $items[$index]['ITEM_QUANTITY_RANGES'] = $this->quantityRanges[$itemId] ?? [];
2995 $items[$index]['ITEM_QUANTITY_RANGE_SELECTED'] = $this->searchItemSelectedQuantityRangeHash($itemId);
2996 if (!empty($this->prices[$itemId]))
2997 {
2998 $items[$index] = array_merge($items[$index], $this->prices[$itemId]);
2999 if (!empty($items[$index]['ITEM_PRICES']))
3000 {
3001 switch ($items[$index]['ITEM_PRICE_MODE'])
3002 {
3004 $items[$index]['ITEM_PRICE_SELECTED'] = 0;
3005 break;
3007 foreach (array_keys($items[$index]['ITEM_PRICES']) as $priceIndex)
3008 {
3009 if ($items[$index]['ITEM_PRICES'][$priceIndex]['QUANTITY_HASH'] == $items[$index]['ITEM_QUANTITY_RANGE_SELECTED'])
3010 {
3011 $items[$index]['ITEM_PRICE_SELECTED'] = $priceIndex;
3012 break;
3013 }
3014 }
3015 break;
3017 foreach (array_keys($items[$index]['ITEM_PRICES']) as $priceIndex)
3018 {
3019 if ($items[$index]['ITEM_PRICES'][$priceIndex]['MEASURE_RATIO_ID'] == $items[$index]['ITEM_MEASURE_RATIO_SELECTED'])
3020 {
3021 $items[$index]['ITEM_PRICE_SELECTED'] = $priceIndex;
3022 break;
3023 }
3024 }
3025 break;
3026 }
3027 }
3028 }
3029
3030 // compatibility
3031 if ($enableCompatible)
3032 {
3033 // old links to buy, add to basket, etc
3034 $id = $items[$index]['ID'];
3035 $items[$index]['~BUY_URL'] = str_replace('#ID#', $id, $urls['~BUY_URL_TEMPLATE']);
3036 $items[$index]['BUY_URL'] = str_replace('#ID#', $id, $urls['BUY_URL_TEMPLATE']);
3037 $items[$index]['~ADD_URL'] = str_replace('#ID#', $id, $urls['~ADD_URL_TEMPLATE']);
3038 $items[$index]['ADD_URL'] = str_replace('#ID#', $id, $urls['ADD_URL_TEMPLATE']);
3039 $items[$index]['~SUBSCRIBE_URL'] = str_replace('#ID#', $id, $urls['~SUBSCRIBE_URL_TEMPLATE']);
3040 $items[$index]['SUBSCRIBE_URL'] = str_replace('#ID#', $id, $urls['SUBSCRIBE_URL_TEMPLATE']);
3041 if ($this->arParams['DISPLAY_COMPARE'])
3042 {
3043 $items[$index]['~COMPARE_URL'] = str_replace('#ID#', $id, $urls['~COMPARE_URL_TEMPLATE']);
3044 $items[$index]['COMPARE_URL'] = str_replace('#ID#', $id, $urls['COMPARE_URL_TEMPLATE']);
3045 $items[$index]['~COMPARE_DELETE_URL'] = str_replace('#ID#', $id, $urls['~COMPARE_DELETE_URL_TEMPLATE']);
3046 $items[$index]['COMPARE_DELETE_URL'] = str_replace('#ID#', $id, $urls['COMPARE_DELETE_URL_TEMPLATE']);
3047 }
3048 unset($id);
3049
3050 // old measure
3051 $items[$index]['CATALOG_MEASURE_NAME'] = $items[$index]['ITEM_MEASURE']['TITLE'];
3052 $items[$index]['~CATALOG_MEASURE_NAME'] = $items[$index]['ITEM_MEASURE']['~TITLE'];
3053
3054 // old measure ratio
3055 $items[$index]['CATALOG_MEASURE_RATIO'] = $items[$index]['ITEM_MEASURE_RATIOS'][$items[$index]['ITEM_MEASURE_RATIO_SELECTED']]['RATIO'] ?? 1;
3056
3057 // old fields
3058 if (!empty($this->oldData[$itemId]))
3059 $items[$index] = array_merge($this->oldData[$itemId], $items[$index]);
3060 }
3061 unset($itemId);
3062 }
3063 unset($index);
3064 unset($urls, $enableCompatible);
3065 }
3066
3076 protected function calculatePriceBlock(array $product, array $priceBlock, $ratio, $defaultBlock = false)
3077 {
3078 if (empty($product) || empty($priceBlock))
3079 return null;
3080
3081 $enableCompatible = $defaultBlock && $this->isEnableCompatible();
3082
3083 if ($enableCompatible && !$this->arParams['USE_PRICE_COUNT'])
3084 $this->fillCompatibleRawPriceFields($product['ID'], $priceBlock);
3085
3086 $userGroups = $this->getUserGroups();
3087
3088 $baseCurrency = Currency\CurrencyManager::getBaseCurrency();
3090 $minimalPrice = null;
3092 $minimalBuyerPrice = null;
3093 $fullPrices = array();
3094
3095 $currencyConvert = $this->arParams['CONVERT_CURRENCY'] === 'Y';
3096 $resultCurrency = ($currencyConvert ? $this->storage['CONVERT_CURRENCY']['CURRENCY_ID'] : null);
3097
3098 $vatRate = (float)$product['PRODUCT']['VAT_RATE'];
3099 $percentVat = $vatRate * 0.01;
3100 $percentPriceWithVat = 1 + $percentVat;
3101 $vatInclude = $product['PRODUCT']['VAT_INCLUDED'] === 'Y';
3102
3103 $oldPrices = array();
3104 $oldMinPrice = false;
3105 $oldMatrix = false;
3106 if ($enableCompatible && $this->arParams['USE_PRICE_COUNT'])
3107 {
3108 $oldMatrix = $this->getCompatibleFieldValue($product['ID'], 'PRICE_MATRIX');
3109 if (empty($oldMatrix))
3110 {
3111 $oldMatrix = $this->getEmptyPriceMatrix();
3112 $oldMatrix['AVAILABLE'] = $product['PRODUCT']['AVAILABLE'];
3113 }
3114 }
3115
3116 foreach ($priceBlock as $rawPrice)
3117 {
3118 $priceType = (int)$rawPrice['CATALOG_GROUP_ID'];
3119 $price = (float)$rawPrice['PRICE'];
3120 if (!$vatInclude)
3121 $price *= $percentPriceWithVat;
3122 $currency = $rawPrice['CURRENCY'];
3123
3124 $changeCurrency = $currencyConvert && $currency !== $resultCurrency;
3125 if ($changeCurrency)
3126 {
3127 $price = \CCurrencyRates::ConvertCurrency($price, $currency, $resultCurrency);
3128 $currency = $resultCurrency;
3129 }
3130
3131 $discounts = array();
3133 {
3135 $discounts = \CCatalogDiscount::GetDiscount(
3136 $product['ID'],
3137 $product['IBLOCK_ID'],
3138 array($priceType),
3139 $userGroups,
3140 'N',
3141 $this->getSiteId(),
3142 array()
3143 );
3145 }
3146 $discountPrice = \CCatalogProduct::CountPriceWithDiscount(
3147 $price,
3148 $currency,
3149 $discounts
3150 );
3151 if ($discountPrice !== false)
3152 {
3153 $priceWithVat = array(
3154 'UNROUND_BASE_PRICE' => $price,
3155 'UNROUND_PRICE' => $discountPrice,
3156 'BASE_PRICE' => Catalog\Product\Price::roundPrice(
3157 $priceType,
3158 $price,
3159 $currency
3160 ),
3161 'PRICE' => Catalog\Product\Price::roundPrice(
3162 $priceType,
3163 $discountPrice,
3164 $currency
3165 )
3166 );
3167
3168 $price /= $percentPriceWithVat;
3169 $discountPrice /= $percentPriceWithVat;
3170
3171 $priceWithoutVat = array(
3172 'UNROUND_BASE_PRICE' => $price,
3173 'UNROUND_PRICE' => $discountPrice,
3174 'BASE_PRICE' => Catalog\Product\Price::roundPrice(
3175 $priceType,
3176 $price,
3177 $currency
3178 ),
3179 'PRICE' => Catalog\Product\Price::roundPrice(
3180 $priceType,
3181 $discountPrice,
3182 $currency
3183 )
3184 );
3185
3186 if ($this->arParams['PRICE_VAT_INCLUDE'])
3187 $priceRow = $priceWithVat;
3188 else
3189 $priceRow = $priceWithoutVat;
3190 $priceRow['ID'] = $rawPrice['ID'];
3191 $priceRow['PRICE_TYPE_ID'] = $rawPrice['CATALOG_GROUP_ID'];
3192 $priceRow['CURRENCY'] = $currency;
3193
3194 if (
3195 empty($discounts)
3196 || ($priceRow['BASE_PRICE'] <= $priceRow['PRICE'])
3197 )
3198 {
3199 $priceRow['BASE_PRICE'] = $priceRow['PRICE'];
3200 $priceRow['DISCOUNT'] = 0;
3201 $priceRow['PERCENT'] = 0;
3202 }
3203 else
3204 {
3205 $priceRow['DISCOUNT'] = $priceRow['BASE_PRICE'] - $priceRow['PRICE'];
3206 $priceRow['PERCENT'] = roundEx(100*$priceRow['DISCOUNT']/$priceRow['BASE_PRICE'], 0);
3207 }
3208 if (isset($this->arParams['PRICE_VAT_SHOW_VALUE']) && $this->arParams['PRICE_VAT_SHOW_VALUE'])
3209 $priceRow['VAT'] = ($vatRate > 0 ? $priceWithVat['PRICE'] - $priceWithoutVat['PRICE'] : 0);
3210
3211 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
3212 $fullPrices[$priceType] = $priceRow;
3213
3214 $priceRow['QUANTITY_FROM'] = $rawPrice['QUANTITY_FROM'];
3215 $priceRow['QUANTITY_TO'] = $rawPrice['QUANTITY_TO'];
3216 $priceRow['QUANTITY_HASH'] = $this->getQuantityRangeHash($rawPrice);
3217 $priceRow['MEASURE_RATIO_ID'] = $rawPrice['MEASURE_RATIO_ID'] ?? null;
3218 $priceRow['PRICE_SCALE'] = \CCurrencyRates::ConvertCurrency(
3219 $priceRow['PRICE'],
3220 $priceRow['CURRENCY'],
3221 $baseCurrency
3222 );
3223 $priceRow['BASE_PRICE_SCALE'] = $rawPrice['PRICE_SCALE'];
3224
3225 if (
3226 $minimalPrice === null
3227 || $minimalPrice['PRICE_SCALE'] > $priceRow['PRICE_SCALE']
3228 )
3229 {
3230 $minimalPrice = $priceRow;
3231 }
3232 elseif (
3233 $minimalPrice['PRICE_SCALE'] == $priceRow['PRICE_SCALE']
3234 && $minimalPrice['BASE_PRICE_SCALE'] > $priceRow['BASE_PRICE_SCALE']
3235 )
3236 {
3237 $minimalPrice = $priceRow;
3238 }
3239 if (isset($this->storage['PRICES_CAN_BUY'][$priceRow['PRICE_TYPE_ID']]))
3240 {
3241 if (
3242 $minimalBuyerPrice === null
3243 || $minimalBuyerPrice['PRICE_SCALE'] > $priceRow['PRICE_SCALE']
3244 )
3245 {
3246 $minimalBuyerPrice = $priceRow;
3247 }
3248 elseif (
3249 $minimalBuyerPrice['PRICE_SCALE'] == $priceRow['PRICE_SCALE']
3250 && $minimalBuyerPrice['BASE_PRICE_SCALE'] > $priceRow['BASE_PRICE_SCALE']
3251 )
3252 {
3253 $minimalBuyerPrice = $priceRow;
3254 }
3255 }
3256
3257 if ($enableCompatible)
3258 {
3259 if ($this->arParams['USE_PRICE_COUNT'])
3260 {
3261 $rowIndex = $this->getQuantityRangeHash($rawPrice);
3262 $oldMatrix['ROWS'][$rowIndex] = array(
3263 'QUANTITY_FROM' => (float)$rawPrice['QUANTITY_FROM'],
3264 'QUANTITY_TO' => (float)$rawPrice['QUANTITY_TO']
3265 );
3266 if (!isset($oldMatrix['MATRIX'][$priceType]))
3267 {
3268 $oldMatrix['MATRIX'][$priceType] = array();
3269 $oldMatrix['COLS'][$priceType] = $this->storage['PRICE_TYPES'][$priceType];
3270 }
3271 $oldMatrix['MATRIX'][$priceType][$rowIndex] = array(
3272 'ID' => $priceRow['ID'],
3273 'PRICE' => $priceRow['BASE_PRICE'],
3274 'DISCOUNT_PRICE' => $priceRow['PRICE'],
3275 'UNROUND_DISCOUNT_PRICE' => $priceRow['UNROUND_PRICE'],
3276 'CURRENCY' => $priceRow['CURRENCY'],
3277 'VAT_RATE' => $percentVat
3278 );
3279 if ($changeCurrency)
3280 {
3281 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_CURRENCY'] = $rawPrice['CURRENCY'];
3282 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_PRICE'] = \CCurrencyRates::ConvertCurrency(
3283 $priceRow['BASE_PRICE'],
3284 $priceRow['CURRENCY'],
3285 $rawPrice['CURRENCY']
3286 );
3287 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_DISCOUNT_PRICE'] = \CCurrencyRates::ConvertCurrency(
3288 $priceRow['PRICE'],
3289 $priceRow['CURRENCY'],
3290 $rawPrice['CURRENCY']
3291 );
3292 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_VAT_RATE'] = $percentVat; // crazy key, but above all the compatibility
3293 }
3294 }
3295 else
3296 {
3297 $priceCode = $this->storage['PRICES_MAP'][$priceType];
3298 $oldPriceRow = array(
3299 'PRICE_ID' => $priceRow['PRICE_TYPE_ID'],
3300 'ID' => $priceRow['ID'],
3301 'CAN_ACCESS' => ($this->storage['PRICES'][$priceCode]['CAN_VIEW'] ? 'Y' : 'N'),
3302 'CAN_BUY' => ($this->storage['PRICES'][$priceCode]['CAN_BUY'] ? 'Y' : 'N'),
3303 'MIN_PRICE' => 'N',
3304 'CURRENCY' => $priceRow['CURRENCY'],
3305 'VALUE_VAT' => $priceWithVat['UNROUND_BASE_PRICE'],
3306 'VALUE_NOVAT' => $priceWithoutVat['UNROUND_BASE_PRICE'],
3307 'DISCOUNT_VALUE_VAT' => $priceWithVat['UNROUND_PRICE'],
3308 'DISCOUNT_VALUE_NOVAT' => $priceWithoutVat['UNROUND_PRICE'],
3309 'ROUND_VALUE_VAT' => $priceWithVat['PRICE'],
3310 'ROUND_VALUE_NOVAT' => $priceWithoutVat['PRICE'],
3311 'VALUE' => $priceRow['BASE_PRICE'],
3312 'UNROUND_DISCOUNT_VALUE' => $priceRow['UNROUND_PRICE'],
3313 'DISCOUNT_VALUE' => $priceRow['PRICE'],
3314 'DISCOUNT_DIFF' => $priceRow['DISCOUNT'],
3315 'DISCOUNT_DIFF_PERCENT' => $priceRow['PERCENT']
3316 );
3317 $oldPriceRow['VATRATE_VALUE'] = $oldPriceRow['VALUE_VAT'] - $oldPriceRow['VALUE_NOVAT'];
3318 $oldPriceRow['DISCOUNT_VATRATE_VALUE'] = $oldPriceRow['DISCOUNT_VALUE_VAT'] - $oldPriceRow['DISCOUNT_VALUE_NOVAT'];
3319 $oldPriceRow['ROUND_VATRATE_VALUE'] = $oldPriceRow['ROUND_VALUE_VAT'] - $oldPriceRow['ROUND_VALUE_NOVAT'];
3320 if ($changeCurrency)
3321 $oldPriceRow['ORIG_CURRENCY'] = $rawPrice['CURRENCY'];
3322 $oldPrices[$priceCode] = $oldPriceRow;
3323 unset($oldPriceRow);
3324 }
3325 }
3326 }
3327 unset($discounts);
3328 unset($priceType);
3329 }
3330 unset($price);
3331
3332 $minimalPriceId = null;
3333 if (is_array($minimalBuyerPrice))
3334 $minimalPrice = $minimalBuyerPrice;
3335 if (is_array($minimalPrice))
3336 {
3337 unset($minimalPrice['PRICE_SCALE']);
3338 unset($minimalPrice['BASE_PRICE_SCALE']);
3339 $minimalPriceId = $minimalPrice['PRICE_TYPE_ID'];
3340 $prepareFields = array(
3341 'BASE_PRICE', 'PRICE', 'DISCOUNT'
3342 );
3343 if (isset($this->arParams['PRICE_VAT_SHOW_VALUE']) && $this->arParams['PRICE_VAT_SHOW_VALUE'])
3344 $prepareFields[] = 'VAT';
3345
3346 foreach ($prepareFields as $fieldName)
3347 {
3348 $minimalPrice['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3349 $minimalPrice[$fieldName],
3350 $minimalPrice['CURRENCY'],
3351 true
3352 );
3353 $minimalPrice['RATIO_'.$fieldName] = $minimalPrice[$fieldName]*$ratio;
3354 $minimalPrice['PRINT_RATIO_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3355 $minimalPrice['RATIO_'.$fieldName],
3356 $minimalPrice['CURRENCY'],
3357 true
3358 );
3359 }
3360 unset($fieldName);
3361
3362 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
3363 {
3364 foreach (array_keys($fullPrices) as $priceType)
3365 {
3366 foreach ($prepareFields as $fieldName)
3367 {
3368 $fullPrices[$priceType]['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3369 $fullPrices[$priceType][$fieldName],
3370 $fullPrices[$priceType]['CURRENCY'],
3371 true
3372 );
3373 $fullPrices[$priceType]['RATIO_'.$fieldName] = $fullPrices[$priceType][$fieldName]*$ratio;
3374 $fullPrices[$priceType]['PRINT_RATIO_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3375 $fullPrices['RATIO_'.$fieldName],
3376 $fullPrices['CURRENCY'],
3377 true
3378 );
3379 }
3380 unset($fieldName);
3381 }
3382 unset($priceType);
3383 }
3384
3385 unset($prepareFields);
3386 }
3387
3388 if ($enableCompatible)
3389 {
3390 if ($this->arParams['USE_PRICE_COUNT'])
3391 {
3392 $oldMatrix['CAN_BUY'] = array_values($this->storage['PRICES_CAN_BUY']);
3393 $this->oldData[$product['ID']]['PRICE_MATRIX'] = $oldMatrix;
3394 }
3395 else
3396 {
3397 $convertFields = array(
3398 'VALUE_NOVAT', 'VALUE_VAT', 'VATRATE_VALUE',
3399 'DISCOUNT_VALUE_NOVAT', 'DISCOUNT_VALUE_VAT', 'DISCOUNT_VATRATE_VALUE'
3400 );
3401
3402 $prepareFields = array(
3403 'VALUE_NOVAT', 'VALUE_VAT', 'VATRATE_VALUE',
3404 'DISCOUNT_VALUE_NOVAT', 'DISCOUNT_VALUE_VAT', 'DISCOUNT_VATRATE_VALUE',
3405 'VALUE', 'DISCOUNT_VALUE', 'DISCOUNT_DIFF'
3406 );
3407
3408 if (!empty($oldPrices))
3409 {
3410 foreach (array_keys($oldPrices) as $index)
3411 {
3412 foreach ($prepareFields as $fieldName)
3413 $oldPrices[$index]['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3414 $oldPrices[$index][$fieldName],
3415 $oldPrices[$index]['CURRENCY'],
3416 true
3417 );
3418 unset($fieldName);
3419 if (isset($oldPrices[$index]['ORIG_CURRENCY']))
3420 {
3421 foreach ($convertFields as $fieldName)
3422 $oldPrices[$index]['ORIG_' . $fieldName] = \CCurrencyRates::ConvertCurrency(
3423 $oldPrices[$index][$fieldName],
3424 $oldPrices[$index]['CURRENCY'],
3425 $oldPrices[$index]['ORIG_CURRENCY']
3426 );
3427 unset($fieldName);
3428 }
3429 if ($oldPrices[$index]['PRICE_ID'] === $minimalPriceId)
3430 {
3431 $oldPrices[$index]['MIN_PRICE'] = 'Y';
3432 $oldMinPrice = $oldPrices[$index];
3433 }
3434 }
3435 unset($index);
3436 }
3437 unset($prepareFields);
3438
3439 $this->oldData[$product['ID']]['PRICES'] = $oldPrices;
3440 $this->oldData[$product['ID']]['MIN_PRICE'] = $oldMinPrice;
3441 }
3442 }
3443 unset($oldMatrix, $oldMinPrice, $oldPrices);
3444
3445 if (!$this->arParams['FILL_ITEM_ALL_PRICES'])
3446 return $minimalPrice;
3447
3448 return array(
3449 'MINIMAL_PRICE' => $minimalPrice,
3450 'ALL_PRICES' => array(
3451 'QUANTITY_FROM' => $minimalPrice['QUANTITY_FROM'],
3452 'QUANTITY_TO' => $minimalPrice['QUANTITY_TO'],
3453 'QUANTITY_HASH' => $minimalPrice['QUANTITY_HASH'],
3454 'MEASURE_RATIO_ID' => $minimalPrice['MEASURE_RATIO_ID'],
3455 'PRICES' => $fullPrices
3456 )
3457 );
3458 }
3459
3460 protected function searchItemSelectedRatioId($id)
3461 {
3462 if (!isset($this->ratios[$id]))
3463 return null;
3464
3465 $minimal = null;
3466 $minimalRatio = null;
3467 $result = null;
3468 foreach ($this->ratios[$id] as $ratio)
3469 {
3470 if ($minimalRatio === null || $minimalRatio > $ratio['RATIO'])
3471 {
3472 $minimalRatio = $ratio['RATIO'];
3473 $minimal = $ratio['ID'];
3474 }
3475 if ($ratio['IS_DEFAULT'] === 'Y')
3476 {
3477 $result = $ratio['ID'];
3478 break;
3479 }
3480 }
3481 unset($ratio);
3482 return ($result === null ? $minimal : $result);
3483 }
3484
3485 protected function compactItemRatios($id)
3486 {
3487 $ratioId = $this->searchItemSelectedRatioId($id);
3488 if ($ratioId === null)
3489 return;
3490 $this->ratios[$id] = array(
3491 $ratioId => $this->ratios[$id][$ratioId]
3492 );
3493 }
3494
3495 protected function getQuantityRangeHash(array $range)
3496 {
3497 return ($range['QUANTITY_FROM'] === null ? 'ZERO' : $range['QUANTITY_FROM']).
3498 '-'.($range['QUANTITY_TO'] === null ? 'INF' : $range['QUANTITY_TO']);
3499 }
3500
3501 protected function getFullQuantityRange()
3502 {
3503 return array(
3504 'HASH' => $this->getQuantityRangeHash(array('QUANTITY_FROM' => null, 'QUANTITY_TO' => null)),
3505 'QUANTITY_FROM' => null,
3506 'QUANTITY_TO' => null,
3507 'SORT_FROM' => 0,
3508 'SORT_TO' => INF
3509 );
3510 }
3511
3513 {
3514 if (empty($this->quantityRanges[$id]))
3515 return null;
3516 foreach ($this->quantityRanges[$id] as $range)
3517 {
3518 if ($this->checkQuantityRange($range))
3519 return $range['HASH'];
3520 }
3521 reset($this->quantityRanges[$id]);
3522 $firsrRange = current($this->quantityRanges[$id]);
3523 return $firsrRange['HASH'];
3524 }
3525
3531 protected function initUrlTemplates()
3532 {
3533 $actionVar = $this->arParams['ACTION_VARIABLE'];
3534 $productIdVar = $this->arParams['PRODUCT_ID_VARIABLE'];
3535 $compareActionVar = $this->arParams['ACTION_COMPARE_VARIABLE'];
3536
3538 $clearParams[] = $actionVar;
3539 $clearParams[] = $productIdVar;
3540 $clearParams[] = $compareActionVar;
3541 $clearParams[] = '';
3542
3543 if (!empty($this->arParams['CUSTOM_CURRENT_PAGE']))
3544 {
3545 $pageUrl = $this->arParams['CUSTOM_CURRENT_PAGE'];
3546 }
3547 else
3548 {
3549 if ($this->request->isAjaxRequest())
3550 {
3551 $pageUrl = $this->arParams['CURRENT_BASE_PAGE'];
3552 }
3553 else
3554 {
3555 $pageUrl = Main\Application::getInstance()->getContext()->getRequest()->getDecodedUri();
3556
3557 }
3558 }
3559 $currentUri = new Main\Web\Uri($pageUrl);
3560
3561 if ($this->arParams['USE_COMPARE_LIST'] == 'N' && $this->arParams['COMPARE_PATH'] != '')
3562 {
3563 $compareUri = new Main\Web\Uri($this->arParams['COMPARE_PATH']);
3564 }
3565 else
3566 {
3567 $compareUri = $currentUri;
3568 }
3569
3570 $currentUri->deleteParams($clearParams);
3571 $compareUri->deleteParams($clearParams);
3572
3573 $urls = [];
3574 $urls['BUY_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_BUY, $productIdVar => '#ID#'])->getUri();
3575 $urls['ADD_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_ADD_TO_BASKET, $productIdVar => '#ID#'])->getUri();
3576 $urls['SUBSCRIBE_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_SUBSCRIBE, $productIdVar => '#ID#'])->getUri();
3577
3578 $urls['COMPARE_URL_TEMPLATE'] = $compareUri->addParams([$compareActionVar => self::ACTION_ADD_TO_COMPARE, $productIdVar => '#ID#'])->getUri();
3579 $urls['COMPARE_DELETE_URL_TEMPLATE'] = $compareUri->addParams([$compareActionVar => self::ACTION_DELETE_FROM_COMPARE, $productIdVar => '#ID#'])->getUri();
3580
3581 unset($compareUri, $currentUri, $clearParams);
3582
3583 foreach (array_keys($urls) as $index)
3584 {
3585 $value = str_replace('%23ID%23', '#ID#', $urls[$index]); // format compatibility
3586 $urls['~'.$index] = $value;
3587 $urls[$index] = Main\Text\HtmlFilter::encode($value, ENT_QUOTES);
3588 }
3589 unset($index);
3590
3591 $this->storage['URLS'] = $urls;
3592 }
3593
3600 protected function modifyElementPrices(&$element)
3601 {
3602 $enableCompatible = $this->isEnableCompatible();
3603 $id = $element['ID'];
3604 $iblockId = $element['IBLOCK_ID'];
3605 $catalog = !empty($this->storage['CATALOGS'][$element['IBLOCK_ID']])
3606 ? $this->storage['CATALOGS'][$element['IBLOCK_ID']]
3607 : array();
3608
3609 $element['ITEM_PRICE_MODE'] = null;
3610 $element['ITEM_PRICES'] = array();
3611 $element['ITEM_QUANTITY_RANGES'] = array();
3612 $element['ITEM_MEASURE_RATIOS'] = array();
3613 $element['ITEM_MEASURE'] = array();
3614 $element['ITEM_MEASURE_RATIO_SELECTED'] = null;
3615 $element['ITEM_QUANTITY_RANGE_SELECTED'] = null;
3616 $element['ITEM_PRICE_SELECTED'] = null;
3617
3618 if (!empty($catalog))
3619 {
3620 if (!isset($this->productWithOffers[$iblockId]))
3621 $this->productWithOffers[$iblockId] = array();
3622 if ($element['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_SKU)
3623 {
3624 $this->productWithOffers[$iblockId][$id] = $id;
3625 if ($this->storage['SHOW_CATALOG_WITH_OFFERS'] && $enableCompatible)
3626 {
3627 $this->productWithPrices[$id] = $id;
3628 $this->calculatePrices[$id] = $id;
3629 }
3630 }
3631
3632 if (in_array(
3633 $element['PRODUCT']['TYPE'],
3634 array(
3635 Catalog\ProductTable::TYPE_PRODUCT,
3636 Catalog\ProductTable::TYPE_SET,
3637 Catalog\ProductTable::TYPE_OFFER,
3638 Catalog\ProductTable::TYPE_SERVICE,
3639 )
3640 ))
3641 {
3642 $this->productWithPrices[$id] = $id;
3643 $this->calculatePrices[$id] = $id;
3644 }
3645
3646 if (isset($this->productWithPrices[$id]))
3647 {
3648 if ($element['PRODUCT']['MEASURE'] > 0)
3649 {
3650 $element['ITEM_MEASURE'] = array(
3651 'ID' => $element['PRODUCT']['MEASURE'],
3652 'TITLE' => '',
3653 '~TITLE' => ''
3654 );
3655 }
3656 else
3657 {
3658 $element['ITEM_MEASURE'] = array(
3659 'ID' => null,
3660 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
3661 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS']
3662 );
3663 }
3664 if ($enableCompatible)
3665 {
3666 $element['CATALOG_MEASURE'] = $element['ITEM_MEASURE']['ID'];
3667 $element['CATALOG_MEASURE_NAME'] = $element['ITEM_MEASURE']['TITLE'];
3668 $element['~CATALOG_MEASURE_NAME'] = $element['ITEM_MEASURE']['~TITLE'];
3669 }
3670 }
3671 }
3672 else
3673 {
3674 $element['PRICES'] = \CIBlockPriceTools::GetItemPrices(
3675 $element['IBLOCK_ID'],
3676 $this->storage['PRICES'],
3677 $element,
3678 $this->arParams['PRICE_VAT_INCLUDE'],
3679 $this->storage['CONVERT_CURRENCY']
3680 );
3681 if (!empty($element['PRICES']))
3682 {
3683 $element['MIN_PRICE'] = \CIBlockPriceTools::getMinPriceFromList($element['PRICES']);
3684 }
3685
3686 $element['CAN_BUY'] = !empty($element['PRICES']);
3687 }
3688 }
3689
3695 protected function processProducts()
3696 {
3697 $this->initItemsMeasure($this->elements);
3698 $this->loadMeasures($this->getMeasureIds($this->elements));
3699
3700 $this->loadPrices($this->productWithPrices);
3701 $this->calculateItemPrices($this->elements);
3702
3703 $this->transferItems($this->elements);
3704 }
3705
3712 protected function processOffers()
3713 {
3714 if ($this->useCatalog && !empty($this->iblockProducts))
3715 {
3716 $offers = array();
3717
3718 $paramStack = array();
3719 $enableCompatible = $this->isEnableCompatible();
3720 if ($enableCompatible)
3721 {
3722 $paramStack['USE_PRICE_COUNT'] = $this->arParams['USE_PRICE_COUNT'];
3723 $paramStack['SHOW_PRICE_COUNT'] = $this->arParams['SHOW_PRICE_COUNT'];
3724 $this->arParams['USE_PRICE_COUNT'] = false;
3725 $this->arParams['SHOW_PRICE_COUNT'] = 1;
3726 }
3727
3728 foreach (array_keys($this->iblockProducts) as $iblock)
3729 {
3730 if (!empty($this->productWithOffers[$iblock]))
3731 {
3732 $iblockOffers = $this->getIblockOffers($iblock);
3733 if (!empty($iblockOffers))
3734 {
3735 $offersId = array_keys($iblockOffers);
3736 $this->initItemsMeasure($iblockOffers);
3737 $this->loadMeasures($this->getMeasureIds($iblockOffers));
3738
3739 $this->loadPrices($offersId);
3740 $this->calculateItemPrices($iblockOffers);
3741
3742 $this->transferItems($iblockOffers);
3743
3744 $this->modifyOffers($iblockOffers);
3745 $this->chooseOffer($iblockOffers, $iblock);
3746
3747 $offers = array_merge($offers, $iblockOffers);
3748 }
3749 unset($iblockOffers);
3750 }
3751 }
3752 if ($enableCompatible)
3753 {
3754 $this->arParams['USE_PRICE_COUNT'] = $paramStack['USE_PRICE_COUNT'];
3755 $this->arParams['SHOW_PRICE_COUNT'] = $paramStack['SHOW_PRICE_COUNT'];
3756 }
3757 unset($enableCompatible, $paramStack);
3758 }
3759 }
3760
3767 protected function getIblockOffers($iblockId)
3768 {
3769 $offers = array();
3770 $iblockParams = $this->storage['IBLOCK_PARAMS'][$iblockId];
3771
3772 $enableCompatible = $this->isEnableCompatible();
3773
3774 if (
3775 $this->useCatalog
3776 && $this->offerIblockExist($iblockId)
3777 && !empty($this->productWithOffers[$iblockId])
3778 )
3779 {
3780 $catalog = $this->storage['CATALOGS'][$iblockId];
3781
3782 $productProperty = 'PROPERTY_'.$catalog['SKU_PROPERTY_ID'];
3783 $productPropertyValue = $productProperty.'_VALUE';
3784
3785 $offersFilter = $this->getOffersFilter($catalog['IBLOCK_ID']);
3786 $offersFilter[$productProperty] = $this->productWithOffers[$iblockId];
3787
3788 $offersSelect = array(
3789 'ID' => 1,
3790 'IBLOCK_ID' => 1,
3791 'CODE' => 1,
3792 $productProperty => 1,
3793 'PREVIEW_PICTURE' => 1,
3794 'DETAIL_PICTURE' => 1,
3795 );
3796
3797 if ($this->arParams['SHOW_SKU_DESCRIPTION'] === 'Y')
3798 {
3799 $offersSelect['PREVIEW_TEXT'] = 1;
3800 $offersSelect['DETAIL_TEXT'] = 1;
3801 $offersSelect['PREVIEW_TEXT_TYPE'] = 1;
3802 $offersSelect['DETAIL_TEXT_TYPE'] = 1;
3803 }
3804
3805 if (!empty($iblockParams['OFFERS_FIELD_CODE']))
3806 {
3807 foreach ($iblockParams['OFFERS_FIELD_CODE'] as $code)
3808 $offersSelect[$code] = 1;
3809 unset($code);
3810 }
3811
3812 $offersSelect = $this->getProductSelect($iblockId, array_keys($offersSelect));
3813
3814 $getListParams = $this->prepareQueryFields($offersSelect, $offersFilter, $this->getOffersSort());
3815 $offersSelect = $getListParams['SELECT'];
3816 $offersFilter = $getListParams['FILTER'];
3817 $offersOrder = $getListParams['ORDER'];
3818 unset($getListParams);
3819
3820 $checkFields = array();
3821 foreach (array_keys($offersOrder) as $code)
3822 {
3823 $code = mb_strtoupper($code);
3824 if ($code == 'ID' || $code == 'AVAILABLE')
3825 continue;
3826 $checkFields[] = $code;
3827 }
3828 unset($code);
3829
3831 $translateFields = $this->getCompatibleProductFields();
3832
3833 $offersId = array();
3834 $offersCount = array();
3835 $iterator = \CIBlockElement::GetList(
3836 $offersOrder,
3837 $offersFilter,
3838 false,
3839 false,
3840 $offersSelect
3841 );
3842 while($row = $iterator->GetNext())
3843 {
3844 $row['ID'] = (int)$row['ID'];
3845 $row['IBLOCK_ID'] = (int)$row['IBLOCK_ID'];
3846 $productId = (int)$row[$productPropertyValue];
3847
3848 if ($productId <= 0)
3849 continue;
3850
3851 if ($enableCompatible && $this->arParams['OFFERS_LIMIT'] > 0)
3852 {
3853 $offersCount[$productId]++;
3854 if($offersCount[$productId] > $this->arParams['OFFERS_LIMIT'])
3855 continue;
3856 }
3857
3858 $row['SORT_HASH'] = 'ID';
3859 if (!empty($checkFields))
3860 {
3861 $checkValues = '';
3862 foreach ($checkFields as $code)
3863 $checkValues .= ($row[$code] ?? '').'|';
3864 unset($code);
3865 if ($checkValues != '')
3866 $row['SORT_HASH'] = md5($checkValues);
3867 unset($checkValues);
3868 }
3869 $row['LINK_ELEMENT_ID'] = $productId;
3870 $row['PROPERTIES'] = array();
3871 $row['DISPLAY_PROPERTIES'] = array();
3872
3873 $row['PRODUCT'] = array(
3874 'TYPE' => (int)$row['~TYPE'],
3875 'AVAILABLE' => $row['~AVAILABLE'],
3876 'BUNDLE' => $row['~BUNDLE'],
3877 'QUANTITY' => $row['~QUANTITY'],
3878 'QUANTITY_TRACE' => $row['~QUANTITY_TRACE'],
3879 'CAN_BUY_ZERO' => $row['~CAN_BUY_ZERO'],
3880 'MEASURE' => (int)$row['~MEASURE'],
3881 'SUBSCRIBE' => $row['~SUBSCRIBE'],
3882 'VAT_ID' => (int)$row['~VAT_ID'],
3883 'VAT_RATE' => 0,
3884 'VAT_INCLUDED' => $row['~VAT_INCLUDED'],
3885 'WEIGHT' => (float)$row['~WEIGHT'],
3886 'WIDTH' => (float)$row['~WIDTH'],
3887 'LENGTH' => (float)$row['~LENGTH'],
3888 'HEIGHT' => (float)$row['~HEIGHT'],
3889 'PAYMENT_TYPE' => $row['~PAYMENT_TYPE'],
3890 'RECUR_SCHEME_TYPE' => $row['~RECUR_SCHEME_TYPE'],
3891 'RECUR_SCHEME_LENGTH' => (int)$row['~RECUR_SCHEME_LENGTH'],
3892 'TRIAL_PRICE_ID' => (int)$row['~TRIAL_PRICE_ID']
3893 );
3894
3895 $vatId = 0;
3896 $vatRate = 0;
3897 if ($row['PRODUCT']['VAT_ID'] > 0)
3898 $vatId = $row['PRODUCT']['VAT_ID'];
3899 elseif ($this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']] > 0)
3900 $vatId = $this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']];
3901 if ($vatId > 0 && isset($this->storage['VATS'][$vatId]))
3902 $vatRate = $this->storage['VATS'][$vatId];
3903 $row['PRODUCT']['VAT_RATE'] = $vatRate;
3904 unset($vatRate, $vatId);
3905
3906 if ($enableCompatible)
3907 {
3908 foreach ($translateFields as $currentKey => $oldKey)
3909 $row[$oldKey] = $row[$currentKey];
3910 unset($currentKey, $oldKey);
3911 $row['~CATALOG_VAT'] = $row['PRODUCT']['VAT_RATE'];
3912 $row['CATALOG_VAT'] = $row['PRODUCT']['VAT_RATE'];
3913 }
3914 else
3915 {
3916 // temporary (compatibility custom templates)
3917 $row['~CATALOG_TYPE'] = $row['PRODUCT']['TYPE'];
3918 $row['CATALOG_TYPE'] = $row['PRODUCT']['TYPE'];
3919 $row['~CATALOG_QUANTITY'] = $row['PRODUCT']['QUANTITY'];
3920 $row['CATALOG_QUANTITY'] = $row['PRODUCT']['QUANTITY'];
3921 $row['~CATALOG_QUANTITY_TRACE'] = $row['PRODUCT']['QUANTITY_TRACE'];
3922 $row['CATALOG_QUANTITY_TRACE'] = $row['PRODUCT']['QUANTITY_TRACE'];
3923 $row['~CATALOG_CAN_BUY_ZERO'] = $row['PRODUCT']['CAN_BUY_ZERO'];
3924 $row['CATALOG_CAN_BUY_ZERO'] = $row['PRODUCT']['CAN_BUY_ZERO'];
3925 $row['~CATALOG_SUBSCRIBE'] = $row['PRODUCT']['SUBSCRIBE'];
3926 $row['CATALOG_SUBSCRIBE'] = $row['PRODUCT']['SUBSCRIBE'];
3927 }
3928
3929 foreach ($productFields as $field)
3930 unset($row[$field], $row['~'.$field]);
3931 unset($field);
3932
3933 if ($row['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_OFFER)
3934 $this->calculatePrices[$row['ID']] = $row['ID'];
3935
3936 $row['ITEM_PRICE_MODE'] = null;
3937 $row['ITEM_PRICES'] = array();
3938 $row['ITEM_QUANTITY_RANGES'] = array();
3939 $row['ITEM_MEASURE_RATIOS'] = array();
3940 $row['ITEM_MEASURE'] = array();
3941 $row['ITEM_MEASURE_RATIO_SELECTED'] = null;
3942 $row['ITEM_QUANTITY_RANGE_SELECTED'] = null;
3943 $row['ITEM_PRICE_SELECTED'] = null;
3944 $row['CHECK_QUANTITY'] = $this->isNeedCheckQuantity($row['PRODUCT']);
3945
3946 if ($row['PRODUCT']['MEASURE'] > 0)
3947 {
3948 $row['ITEM_MEASURE'] = array(
3949 'ID' => $row['PRODUCT']['MEASURE'],
3950 'TITLE' => '',
3951 '~TITLE' => ''
3952 );
3953 }
3954 else
3955 {
3956 $row['ITEM_MEASURE'] = array(
3957 'ID' => null,
3958 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
3959 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS']
3960 );
3961 }
3962 if ($enableCompatible)
3963 {
3964 $row['CATALOG_MEASURE'] = $row['ITEM_MEASURE']['ID'];
3965 $row['CATALOG_MEASURE_NAME'] = $row['ITEM_MEASURE']['TITLE'];
3966 $row['~CATALOG_MEASURE_NAME'] = $row['ITEM_MEASURE']['~TITLE'];
3967 }
3968
3969 $row['PROPERTIES'] = array();
3970 $row['DISPLAY_PROPERTIES'] = array();
3971
3973 $row,
3974 array('PREVIEW_PICTURE', 'DETAIL_PICTURE'),
3976 ''
3977 );
3978
3979 $offersId[$row['ID']] = $row['ID'];
3980 $offers[$row['ID']] = $row;
3981 }
3982 unset($row, $iterator);
3983
3984 if (!empty($offersId))
3985 {
3986 $loadPropertyCodes = ($iblockParams['OFFERS_PROPERTY_CODE'] ?? []);
3987 if (Iblock\Model\PropertyFeature::isEnabledFeatures())
3988 {
3989 $loadPropertyCodes = array_merge($loadPropertyCodes, $iblockParams['OFFERS_TREE_PROPS'] ?? []);
3990 }
3991
3992 $propertyList = $this->getPropertyList($catalog['IBLOCK_ID'], $loadPropertyCodes);
3993 unset($loadPropertyCodes);
3994
3995 if (!empty($propertyList) || $this->useDiscountCache)
3996 {
3997 \CIBlockElement::GetPropertyValuesArray($offers, $catalog['IBLOCK_ID'], $offersFilter);
3998 foreach ($offers as &$row)
3999 {
4000 if ($this->useDiscountCache)
4001 {
4002 if ($this->storage['USE_SALE_DISCOUNTS'])
4004 else
4005 \CCatalogDiscount::SetProductPropertiesCache($row['ID'], $row["PROPERTIES"]);
4006 }
4007
4008 if (!empty($propertyList))
4009 {
4010 foreach ($propertyList as $pid)
4011 {
4012 if (!isset($row["PROPERTIES"][$pid]))
4013 continue;
4014 $prop = &$row["PROPERTIES"][$pid];
4015 $boolArr = is_array($prop["VALUE"]);
4016 if (
4017 ($boolArr && !empty($prop["VALUE"])) ||
4018 (!$boolArr && (string)$prop["VALUE"] !== '')
4019 )
4020 {
4021 $row["DISPLAY_PROPERTIES"][$pid] = \CIBlockFormatProperties::GetDisplayValue($row, $prop);
4022 }
4023 unset($boolArr, $prop);
4024 }
4025 unset($pid);
4026 }
4027 }
4028 unset($row);
4029 }
4030 if (!empty($propertyList))
4031 {
4033 }
4034
4035 if ($this->useDiscountCache)
4036 {
4037 if ($this->storage['USE_SALE_DISCOUNTS'])
4038 {
4039 Catalog\Discount\DiscountManager::preloadPriceData($offersId, $this->storage['PRICES_ALLOW']);
4041 }
4042 else
4043 {
4045 \CCatalogDiscount::SetDiscountProductCache($offersId, array('IBLOCK_ID' => $catalog['IBLOCK_ID'], 'GET_BY_ID' => 'Y'));
4046 }
4047 }
4048 }
4049 unset($offersId);
4050 }
4051
4052 return $offers;
4053 }
4054
4055 protected function getOffersFilter($iblockId)
4056 {
4057 $offersFilter = array(
4058 'IBLOCK_ID' => $iblockId,
4059 'ACTIVE' => 'Y',
4060 'ACTIVE_DATE' => 'Y',
4061 'CHECK_PERMISSIONS' => 'N'
4062 );
4063
4064 if ($this->arParams['HIDE_NOT_AVAILABLE_OFFERS'] === 'Y')
4065 {
4066 $offersFilter['AVAILABLE'] = 'Y';
4067 }
4068 elseif ($this->arParams['HIDE_NOT_AVAILABLE_OFFERS'] === 'L')
4069 {
4070 $offersFilter['CUSTOM_FILTER'] = array(
4071 'LOGIC' => 'OR',
4072 'AVAILABLE' => 'Y',
4073 'SUBSCRIBE' => 'Y'
4074 );
4075 }
4076
4077 if (!$this->arParams['USE_PRICE_COUNT'])
4078 {
4079 $offersFilter['SHOW_PRICE_COUNT'] = $this->arParams['SHOW_PRICE_COUNT'];
4080 }
4081
4082 return $offersFilter;
4083 }
4084
4090 protected function getOffersSort()
4091 {
4092 $offersOrder = array(
4093 mb_strtoupper($this->arParams['OFFERS_SORT_FIELD']) => $this->arParams['OFFERS_SORT_ORDER'],
4094 mb_strtoupper($this->arParams['OFFERS_SORT_FIELD2']) => $this->arParams['OFFERS_SORT_ORDER2']
4095 );
4096 if (!isset($offersOrder['ID']))
4097 $offersOrder['ID'] = 'DESC';
4098
4099 return $offersOrder;
4100 }
4101
4102 protected function modifyOffers($offers)
4103 {
4104 //$urls = $this->storage['URLS'];
4105
4106 foreach ($offers as &$offer)
4107 {
4108 $elementId = $offer['LINK_ELEMENT_ID'];
4109
4110 if (!isset($this->elementLinks[$elementId]))
4111 continue;
4112
4113 $offer['CAN_BUY'] = $this->elementLinks[$elementId]['ACTIVE'] === 'Y' && $offer['CAN_BUY'];
4114
4115 $this->elementLinks[$elementId]['OFFERS'][] = $offer;
4116
4117 unset($elementId, $offer);
4118 }
4119 }
4120
4121 abstract protected function chooseOffer($offers, $iblockId);
4122
4123 protected function initResultCache()
4124 {
4125 if (
4126 $this->arParams['CONVERT_CURRENCY'] === 'Y'
4127 && !empty($this->storage['CURRENCY_LIST'])
4128 && defined('BX_COMP_MANAGED_CACHE')
4129 )
4130 {
4131 $this->storage['CURRENCY_LIST'][$this->storage['CONVERT_CURRENCY']['CURRENCY_ID']] = $this->storage['CONVERT_CURRENCY']['CURRENCY_ID'];
4132 $taggedCache = Main\Application::getInstance()->getTaggedCache();
4133 foreach ($this->storage['CURRENCY_LIST'] as $oneCurrency)
4134 {
4135 $taggedCache->registerTag('currency_id_'.$oneCurrency);
4136 }
4137
4138 unset($oneCurrency);
4139 unset($taggedCache);
4140 }
4141
4142 unset($this->storage['CURRENCY_LIST']);
4143
4144 $this->setResultCacheKeys($this->getCacheKeys());
4145 }
4146
4147 protected function getCacheKeys()
4148 {
4149 return array();
4150 }
4151
4156 protected function processResultData()
4157 {
4158 $this->iblockProducts = $this->getProductsSeparatedByIblock();
4159 $this->checkIblock();
4160
4161 if ($this->hasErrors())
4162 return;
4163
4164 $this->initCurrencyConvert();
4165 $this->initCatalogInfo();
4167 $this->initPrices();
4168 $this->initVats();
4169 $this->initUrlTemplates();
4170
4171 $this->initElementList();
4172 if (!$this->hasErrors())
4173 {
4174 $this->sortElementList();
4175 $this->makeElementLinks();
4176 $this->prepareData();
4177 $this->filterPureOffers();
4178 $this->makeOutputResult();
4179 }
4180 }
4181
4185 protected function checkIblock()
4186 {
4187 if (!empty($this->iblockProducts))
4188 {
4189 $iblocks = array();
4190 $iblockIterator = Iblock\IblockSiteTable::getList(array(
4191 'select' => array('IBLOCK_ID'),
4192 'filter' => array(
4193 '=IBLOCK_ID' => array_keys($this->iblockProducts),
4194 '=SITE_ID' => $this->getSiteId(),
4195 '=IBLOCK.ACTIVE' => 'Y'
4196 )
4197 ));
4198 while ($iblock = $iblockIterator->fetch())
4199 {
4200 $iblocks[$iblock['IBLOCK_ID']] = true;
4201 }
4202
4203 foreach ($this->iblockProducts as $iblock => $products)
4204 {
4205 if (!isset($iblocks[$iblock]))
4206 {
4207 unset($this->iblockProducts[$iblock]);
4208 }
4209 }
4210
4211 if (empty($this->iblockProducts))
4212 {
4213 $this->abortResultCache();
4214 $this->errorCollection->setError(new Error(Loc::getMessage('INVALID_IBLOCK'), self::ERROR_TEXT));
4215 }
4216 }
4217 }
4218
4219 protected function prepareData()
4220 {
4221 $this->clearItems();
4222 $this->initCatalogDiscountCache();
4223 $this->processProducts();
4224 $this->processOffers();
4225 $this->makeOutputResult();
4226 $this->clearItems();
4227 }
4228
4229 protected function filterPureOffers()
4230 {
4231 if (!empty($this->productIds) && is_array($this->productIds))
4232 {
4233 foreach ($this->productIds as $productId)
4234 {
4235 // check if it's element
4236 if ($this->productIdMap[$productId] == $productId)
4237 {
4238 continue;
4239 }
4240
4241 if (isset($this->elementLinks[$this->productIdMap[$productId]]) && !empty($this->elementLinks[$this->productIdMap[$productId]]['OFFERS']))
4242 {
4243 // clear all unwanted offers
4244 foreach ($this->elementLinks[$this->productIdMap[$productId]]['OFFERS'] as $key => $offer)
4245 {
4246 if ($offer['ID'] != $productId)
4247 {
4248 unset($this->elementLinks[$this->productIdMap[$productId]]['OFFERS'][$key]);
4249 }
4250 }
4251 }
4252 }
4253 }
4254 }
4255
4259 protected function makeOutputResult()
4260 {
4261 $this->arResult = array_merge($this->arResult, $this->storage['URLS']);
4262 $this->arResult['CONVERT_CURRENCY'] = $this->storage['CONVERT_CURRENCY'];
4263 $this->arResult['CATALOGS'] = $this->storage['CATALOGS'];
4264 $this->arResult['MODULES'] = $this->storage['MODULES'];
4265 $this->arResult['PRICES_ALLOW'] = $this->storage['PRICES_ALLOW'];
4266
4267 if ($this->isEnableCompatible())
4268 {
4269 if ($this->arParams['IBLOCK_ID'] > 0)
4270 {
4271 $this->arResult['CATALOG'] = false;
4272
4273 if (
4274 !empty($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
4275 && is_array($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
4276 )
4277 {
4278 $this->arResult['CATALOG'] = $this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']];
4279 }
4280 }
4281 }
4282 }
4283
4287 protected function processLinkAction()
4288 {
4289 global $APPLICATION;
4290
4291 if ($this->request->get($this->arParams['ACTION_VARIABLE'].self::ACTION_BUY) !== null)
4292 {
4293 $action = self::ACTION_BUY;
4294 }
4295 elseif ($this->request->get($this->arParams['ACTION_VARIABLE'].self::ACTION_ADD_TO_BASKET))
4296 {
4297 $action = self::ACTION_ADD_TO_BASKET;
4298 }
4299 else
4300 {
4301 $action = $this->request->get($this->arParams['ACTION_VARIABLE']);
4302 $action =
4303 is_string($action)
4304 ? mb_strtoupper(trim($action))
4305 : ''
4306 ;
4307 }
4308
4309 $productId = (int)$this->request->get($this->arParams['PRODUCT_ID_VARIABLE']);
4310
4311 if (
4312 ($action == self::ACTION_ADD_TO_BASKET || $action == self::ACTION_BUY || $action == self::ACTION_SUBSCRIBE)
4313 && Loader::includeModule('sale')
4314 && Loader::includeModule('catalog')
4315 )
4316 {
4317 $addByAjax = $this->request->get('ajax_basket') === 'Y';
4318 if ($addByAjax)
4319 {
4320 $this->request->set($this->request->toArray());
4321 }
4322
4323 [$successfulAdd, $errorMsg] = $this->addProductToBasket($productId, $action);
4324
4325 if ($addByAjax)
4326 {
4327 if ($successfulAdd)
4328 {
4329 $addResult = array(
4330 'STATUS' => 'OK',
4331 'MESSAGE' => Loc::getMessage('CATALOG_SUCCESSFUL_ADD_TO_BASKET')
4332 );
4333 }
4334 else
4335 {
4336 $addResult = array(
4337 'STATUS' => 'ERROR',
4338 'MESSAGE' => $errorMsg
4339 );
4340 }
4341
4342 $APPLICATION->RestartBuffer();
4343 header('Content-Type: application/json');
4344 \CMain::FinalActions(Main\Web\Json::encode($addResult));
4345 }
4346 else
4347 {
4348 if ($successfulAdd)
4349 {
4350 $pathRedirect = $action == self::ACTION_BUY
4351 ? $this->arParams['BASKET_URL']
4352 : $APPLICATION->GetCurPageParam('', array(
4353 $this->arParams['PRODUCT_ID_VARIABLE'],
4354 $this->arParams['ACTION_VARIABLE'],
4355 $this->arParams['PRODUCT_QUANTITY_VARIABLE'],
4356 $this->arParams['PRODUCT_PROPS_VARIABLE']
4357 ));
4358
4359 LocalRedirect($pathRedirect);
4360 }
4361 else
4362 {
4363 $this->errorCollection->setError(new Error($errorMsg, self::ERROR_TEXT));
4364 }
4365 }
4366 }
4367 }
4368
4369 protected function checkProductSection($productId, $sectionId = 0, $sectionCode = '')
4370 {
4371 $successfulAdd = true;
4372 $errorMsg = '';
4373
4374 if (!empty($productId) && ($sectionId > 0 || !empty($sectionCode)))
4375 {
4376 $productsMap = $this->getProductIdMap([$productId]);
4377
4378 if (!empty($productsMap[$productId]))
4379 {
4380 $sectionId = (int)$sectionId;
4381 $sectionCode = (string)$sectionCode;
4382
4383 $filter = ['ID' => $productsMap[$productId]];
4384
4385 $element = false;
4386 if ($sectionId > 0)
4387 {
4388 $filter['SECTION_ID'] = $sectionId;
4389 $filter['INCLUDE_SUBSECTIONS'] = 'Y';
4390 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
4391 $element = $elementIterator->Fetch();
4392 unset($elementIterator);
4393 }
4394 elseif ($sectionCode != '')
4395 {
4396 $iblockId = (int)\CIBlockElement::GetIBlockByID($productsMap[$productId]);
4397 if ($iblockId > 0)
4398 {
4399 $sectionIterator = \CIBlockSection::GetList(
4400 [],
4401 ['IBLOCK_ID' => $iblockId, '=CODE' => $sectionCode],
4402 false,
4403 ['ID', 'IBLOCK_ID']
4404 );
4405 $section = $sectionIterator->Fetch();
4406 unset($sectionIterator);
4407 if (!empty($section))
4408 {
4409 $filter['SECTION_ID'] = (int)$section['ID'];
4410 $filter['INCLUDE_SUBSECTIONS'] = 'Y';
4411 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
4412 $element = $elementIterator->Fetch();
4413 unset($elementIterator);
4414 }
4415 unset($section);
4416 }
4417 unset($iblockId);
4418 }
4419
4420 if (empty($element))
4421 {
4422 $successfulAdd = false;
4423 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4424 }
4425 }
4426 }
4427
4428 return [$successfulAdd, $errorMsg];
4429 }
4430
4431 protected function checkProductIblock(array $product): bool
4432 {
4433 return true;
4434 }
4435
4436 protected function addProductToBasket($productId, $action)
4437 {
4439 global $APPLICATION;
4440
4441 $successfulAdd = true;
4442 $errorMsg = '';
4443
4444 $quantity = 0;
4445 $productProperties = array();
4446
4447 $productId = (int)$productId;
4448 if ($productId <= 0)
4449 {
4450 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_ID_IS_ABSENT');
4451 $successfulAdd = false;
4452 }
4453 $product = [];
4454 if ($successfulAdd)
4455 {
4456 $product = $this->getProductInfo($productId);
4457 if (empty($product))
4458 {
4459 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4460 $successfulAdd = false;
4461 }
4462 }
4463 if ($successfulAdd)
4464 {
4465 if ($this->arParams['CHECK_LANDING_PRODUCT_SECTION'])
4466 {
4467 [$successfulAdd, $errorMsg] = $this->checkProductSection(
4468 $productId, $this->arParams['SECTION_ID'], $this->arParams['SECTION_CODE']
4469 );
4470 }
4471 }
4472 if ($successfulAdd)
4473 {
4474 if (!$this->checkProductIblock($product))
4475 {
4476 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4477 $successfulAdd = false;
4478 }
4479 }
4480 if ($successfulAdd)
4481 {
4482 if ($this->arParams['ADD_PROPERTIES_TO_BASKET'] === 'Y')
4483 {
4485 $iblockParams = $this->storage['IBLOCK_PARAMS'][$product['PRODUCT_IBLOCK_ID']];
4486 if ($product['TYPE'] == Catalog\ProductTable::TYPE_OFFER)
4487 {
4488 $skuAddProps = $this->request->get('basket_props') ?: '';
4489 if (!empty($iblockParams['OFFERS_CART_PROPERTIES']) || !empty($skuAddProps))
4490 {
4491 $productProperties = \CIBlockPriceTools::GetOfferProperties(
4492 $productId,
4493 $product['PRODUCT_IBLOCK_ID'],
4494 $iblockParams['OFFERS_CART_PROPERTIES'],
4495 $skuAddProps
4496 );
4497 }
4498 unset($skuAddProps);
4499 }
4500 else
4501 {
4502 if (!empty($iblockParams['CART_PROPERTIES']))
4503 {
4504 $productPropsVar = $this->request->get($this->arParams['PRODUCT_PROPS_VARIABLE']);
4505 if (is_array($productPropsVar))
4506 {
4507 $productProperties = \CIBlockPriceTools::CheckProductProperties(
4508 $product['PRODUCT_IBLOCK_ID'],
4509 $productId,
4510 $iblockParams['CART_PROPERTIES'],
4511 $productPropsVar,
4512 $this->arParams['PARTIAL_PRODUCT_PROPERTIES'] === 'Y'
4513 );
4514 if (!is_array($productProperties))
4515 {
4516 $errorMsg = Loc::getMessage('CATALOG_PARTIAL_BASKET_PROPERTIES_ERROR');
4517 $successfulAdd = false;
4518 }
4519 }
4520 else
4521 {
4522 if ($this->arParams['PARTIAL_PRODUCT_PROPERTIES'] !== 'Y')
4523 {
4524 $errorMsg = Loc::getMessage('CATALOG_EMPTY_BASKET_PROPERTIES_ERROR');
4525 $successfulAdd = false;
4526 }
4527 }
4528 unset($productPropsVar);
4529 }
4530 }
4531 unset($iblockParams);
4532 }
4533 }
4534
4535 if ($successfulAdd)
4536 {
4537 if ($this->arParams['USE_PRODUCT_QUANTITY'])
4538 {
4539 $quantity = (float)$this->request->get($this->arParams['PRODUCT_QUANTITY_VARIABLE']);
4540 }
4541
4542 if ($quantity <= 0)
4543 {
4544 $ratioIterator = \CCatalogMeasureRatio::getList(
4545 array(),
4546 array('PRODUCT_ID' => $productId),
4547 false,
4548 false,
4549 array('PRODUCT_ID', 'RATIO')
4550 );
4551 if ($ratio = $ratioIterator->Fetch())
4552 {
4553 $intRatio = (int)$ratio['RATIO'];
4554 $floatRatio = (float)$ratio['RATIO'];
4555 $quantity = $floatRatio > $intRatio ? $floatRatio : $intRatio;
4556 }
4557 }
4558
4559 if ($quantity <= 0)
4560 {
4561 $quantity = 1;
4562 }
4563 }
4564
4565 if ($successfulAdd)
4566 {
4567 $rewriteFields = $this->getRewriteFields($action);
4568 if (isset($rewriteFields['SUBSCRIBE']) && $rewriteFields['SUBSCRIBE'] == 'Y')
4569 {
4570 // Deprecated. Only for compatibility with older customized templates.
4571 if (!SubscribeProduct($productId, $rewriteFields, $productProperties))
4572 {
4573 if ($ex = $APPLICATION->GetException())
4574 {
4575 $errorMsg = $ex->GetString();
4576 }
4577 else
4578 {
4579 $errorMsg = Loc::getMessage('CATALOG_ERROR2BASKET');
4580 }
4581
4582 $successfulAdd = false;
4583 }
4584 }
4585 else
4586 {
4587 $product = [
4588 'PRODUCT_ID' => $productId,
4589 'QUANTITY' => $quantity
4590 ];
4591 if (!empty($productProperties))
4592 {
4593 $product['PROPS'] = $productProperties;
4594 }
4595
4596 $basketResult = Catalog\Product\Basket::addProduct($product, $rewriteFields, [
4597 'USE_MERGE' => $this->isMergeProductWhenAddedBasket() ? 'Y' : 'N',
4598 ]);
4599 if (!$basketResult->isSuccess())
4600 {
4601 $errorMsg = implode('; ', $basketResult->getErrorMessages());
4602 $successfulAdd = false;
4603 }
4604 unset($basketResult);
4605 }
4606 }
4607
4608 return array($successfulAdd, $errorMsg);
4609 }
4610
4619 {
4620 return ($this->arParams['USE_MERGE_WHEN_ADD_PRODUCT_TO_BASKET'] ?? 'Y') !== 'N';
4621 }
4622
4623 protected function getRewriteFields($action)
4624 {
4625 $rewriteFields = [];
4626
4627 if ($action === self::ACTION_ADD_TO_BASKET || $action === self::ACTION_BUY)
4628 {
4629 $rewriteFields['DELAY'] = 'N';
4630 }
4631
4632 if ($action == self::ACTION_SUBSCRIBE)
4633 {
4634 $notify = unserialize(Main\Config\Option::get('sale', 'subscribe_prod', ''), ['allowed_classes' => false]);
4635 if (!empty($notify[$this->getSiteId()]) && $notify[$this->getSiteId()]['use'] === 'Y')
4636 {
4637 $rewriteFields['SUBSCRIBE'] = 'Y';
4638 $rewriteFields['CAN_BUY'] = 'N';
4639 }
4640 }
4641
4642 return $rewriteFields;
4643 }
4644
4649 protected function deferredLoadAction()
4650 {
4651 $this->productIds = $this->getDeferredProductIds();
4652
4653 // if no products - show empty response
4654 if (empty($this->productIds))
4655 {
4656 static::sendJsonAnswer();
4657 }
4658
4659 $this->productIdMap = $this->getProductIdMap($this->productIds);
4660 $this->loadData();
4661 }
4662
4666 protected function bigDataLoadAction()
4667 {
4668 $this->initBigDataLastUsage();
4669 $this->productIds = $this->getBigDataProductIds();
4670
4671 // if no products - show empty response
4672 if (empty($this->productIds))
4673 {
4674 static::sendJsonAnswer();
4675 }
4676
4677 $this->productIdMap = $this->getProductIdMap($this->productIds);
4678 $this->loadData();
4679 }
4680
4684 protected function initBigDataLastUsage()
4685 {
4686 $lastUsage = Main\Config\Option::get('main', 'rcm_component_usage', 0);
4687
4688 if ($lastUsage == 0 || (time() - $lastUsage) > 3600)
4689 {
4690 Main\Config\Option::set('main', 'rcm_component_usage', time());
4691 }
4692 }
4693
4697 protected function initialLoadAction()
4698 {
4699 $this->productIds = $this->getProductIds();
4700 $this->productIdMap = $this->getProductIdMap($this->productIds);
4701 $this->loadData();
4702 }
4703
4708 protected function loadData()
4709 {
4710 if ($this->isCacheDisabled() || $this->startResultCache(false, $this->getAdditionalCacheId(), $this->getComponentCachePath()))
4711 {
4712 $this->processResultData();
4713 if (!$this->hasErrors())
4714 {
4715 if ($this->isExtendedMode())
4716 {
4717 $this->initComponentTemplate();
4719 }
4720
4721 $this->initResultCache();
4722 $this->includeComponentTemplate();
4724 }
4725 }
4726 }
4727
4733 abstract protected function getAdditionalCacheId();
4734
4740 abstract protected function getComponentCachePath();
4741
4742 public function getTemplateEmptyPreview()
4743 {
4744 $emptyPreview = false;
4746 $emptyPreviewPath = $this->getTemplate()->GetFolder().'/images/no_photo.png';
4747
4748 $file = new Main\IO\File($documentRoot.$emptyPreviewPath);
4749 if ($file->isExists())
4750 {
4751 $size = getimagesize($documentRoot.$emptyPreviewPath);
4752 if (!empty($size))
4753 {
4754 $emptyPreview = array(
4755 'ID' => 0,
4756 'SRC' => $emptyPreviewPath,
4757 'FILE_NAME' => 'no_photo.png',
4758 'WIDTH' => (int)$size[0],
4759 'HEIGHT' => (int)$size[1]
4760 );
4761 }
4762 }
4763
4764 return $emptyPreview;
4765 }
4766
4767 protected function sliceItemsForSlider(&$items)
4768 {
4769 $rows = array();
4770
4771 while (!empty($items))
4772 {
4773 $rows[] = array_splice($items, 0, $this->arParams['LINE_ELEMENT_COUNT']);
4774 }
4775
4776 $items = $rows;
4777 }
4778
4779 protected function getTemplateCurrencies()
4780 {
4781 $currencies = array();
4782
4783 if (!empty($this->arResult['MODULES']['currency']))
4784 {
4785 if (isset($this->arResult['CONVERT_CURRENCY']['CURRENCY_ID']))
4786 {
4787 $currencyFormat = \CCurrencyLang::GetFormatDescription($this->arResult['CONVERT_CURRENCY']['CURRENCY_ID']);
4788 $currencies = array(
4789 array(
4790 'CURRENCY' => $this->arResult['CONVERT_CURRENCY']['CURRENCY_ID'],
4791 'FORMAT' => array(
4792 'FORMAT_STRING' => $currencyFormat['FORMAT_STRING'],
4793 'DEC_POINT' => $currencyFormat['DEC_POINT'],
4794 'THOUSANDS_SEP' => $currencyFormat['THOUSANDS_SEP'],
4795 'DECIMALS' => $currencyFormat['DECIMALS'],
4796 'THOUSANDS_VARIANT' => $currencyFormat['THOUSANDS_VARIANT'],
4797 'HIDE_ZERO' => $currencyFormat['HIDE_ZERO']
4798 )
4799 )
4800 );
4801 unset($currencyFormat);
4802 }
4803 else
4804 {
4805 $currencyIterator = Currency\CurrencyTable::getList(array(
4806 'select' => array('CURRENCY')
4807 ));
4808 while ($currency = $currencyIterator->fetch())
4809 {
4810 $currencyFormat = \CCurrencyLang::GetFormatDescription($currency['CURRENCY']);
4811 $currencies[] = array(
4812 'CURRENCY' => $currency['CURRENCY'],
4813 'FORMAT' => array(
4814 'FORMAT_STRING' => $currencyFormat['FORMAT_STRING'],
4815 'DEC_POINT' => $currencyFormat['DEC_POINT'],
4816 'THOUSANDS_SEP' => $currencyFormat['THOUSANDS_SEP'],
4817 'DECIMALS' => $currencyFormat['DECIMALS'],
4818 'THOUSANDS_VARIANT' => $currencyFormat['THOUSANDS_VARIANT'],
4819 'HIDE_ZERO' => $currencyFormat['HIDE_ZERO']
4820 )
4821 );
4822 }
4823 unset($currencyFormat, $currency, $currencyIterator);
4824 }
4825 }
4826
4827 return $currencies;
4828 }
4829
4836 public static function sendJsonAnswer(array $result = [])
4837 {
4838 global $APPLICATION;
4839
4840 if (!empty($result))
4841 {
4842 $result['JS'] = Main\Page\Asset::getInstance()->getJs();
4843 }
4844
4845 $APPLICATION->RestartBuffer();
4846
4847 /* don't change this block, because delayed \CFile::ResizeImageGet is not started in cloud */
4849 \CMain::FinalActions();
4850 /* block end */
4851 }
4852
4859 protected function prepareAction()
4860 {
4861 if (
4862 $this->request->get($this->arParams['ACTION_VARIABLE']) !== null
4863 && $this->request->get($this->arParams['PRODUCT_ID_VARIABLE']) !== null
4864 )
4865 {
4866 $action = 'processLink';
4867 }
4868 elseif ($this->request->isAjaxRequest() && $this->request->get('action') === 'deferredLoad')
4869 {
4870 $action = $this->request->get('bigData') === 'Y' ? 'bigDataLoad' : 'deferredLoad';
4871 }
4872 else
4873 {
4874 $action = 'initialLoad';
4875 }
4876
4877 return $action;
4878 }
4879
4883 protected function doAction()
4884 {
4885 $action = $this->getAction();
4886
4887 if (is_callable(array($this, $action.'Action')))
4888 {
4889 call_user_func(array($this, $action.'Action'));
4890 }
4891 }
4892
4896 public function executeComponent()
4897 {
4898 $this->checkModules();
4899
4900 if ($this->hasErrors())
4901 {
4902 return $this->processErrors();
4903 }
4904
4905 $action = $this->prepareAction();
4906 $this->setAction($action);
4907 $this->doAction();
4908
4909 if ($this->hasErrors())
4910 {
4911 return $this->processErrors();
4912 }
4913
4914 return $this->arResult['ID'] ?? false;
4915 }
4916
4923 {
4924 $this->prepareTemplateParams();
4925 $this->checkTemplateTheme();
4926 $this->editTemplateData();
4927
4928 return $this->arParams;
4929 }
4930
4931 protected function prepareTemplateParams()
4932 {
4934 $defaultParams = $this->getTemplateDefaultParams();
4935 $params = array_merge($defaultParams, $params);
4936
4937 $params['SHOW_OLD_PRICE'] = $params['SHOW_OLD_PRICE'] === 'Y' ? 'Y' : 'N';
4938 $params['SHOW_CLOSE_POPUP'] = $params['SHOW_CLOSE_POPUP'] === 'Y' ? 'Y' : 'N';
4939 $params['SHOW_DISCOUNT_PERCENT'] = $params['SHOW_DISCOUNT_PERCENT'] === 'Y' ? 'Y' : 'N';
4940 $params['DISCOUNT_PERCENT_POSITION'] = trim($params['DISCOUNT_PERCENT_POSITION']) ?: 'bottom-right';
4941 $params['LABEL_PROP_POSITION'] = trim($params['LABEL_PROP_POSITION']) ?: 'top-left';
4942 $params['PRODUCT_SUBSCRIPTION'] = $params['PRODUCT_SUBSCRIPTION'] === 'N' ? 'N' : 'Y';
4943 $params['MESS_BTN_BUY'] = trim($params['MESS_BTN_BUY']);
4944 $params['MESS_BTN_ADD_TO_BASKET'] = trim($params['MESS_BTN_ADD_TO_BASKET']);
4945 $params['MESS_BTN_SUBSCRIBE'] = trim($params['MESS_BTN_SUBSCRIBE']);
4946 $params['MESS_BTN_DETAIL'] = trim($params['MESS_BTN_DETAIL']);
4947 $params['MESS_NOT_AVAILABLE'] = trim($params['MESS_NOT_AVAILABLE']);
4948 $params['MESS_BTN_COMPARE'] = trim($params['MESS_BTN_COMPARE']);
4949 $params['SHOW_SLIDER'] = $params['SHOW_SLIDER'] === 'N' ? 'N' : 'Y';
4950 $params['SLIDER_INTERVAL'] = (int)$params['SLIDER_INTERVAL'] ?: 5000;
4951 $params['SLIDER_PROGRESS'] = $params['SLIDER_PROGRESS'] === 'Y' ? 'Y' : 'N';
4952 $params['USE_ENHANCED_ECOMMERCE'] = $params['USE_ENHANCED_ECOMMERCE'] === 'Y' ? 'Y' : 'N';
4953 $params['DATA_LAYER_NAME'] = trim($params['DATA_LAYER_NAME']);
4954 $params['BRAND_PROPERTY'] = $params['BRAND_PROPERTY'] !== '-' ? trim($params['BRAND_PROPERTY']) : '';
4955
4956 if (!isset($params['SHOW_MAX_QUANTITY']) || !in_array($params['SHOW_MAX_QUANTITY'], array('Y', 'M', 'N')))
4957 {
4958 $params['SHOW_MAX_QUANTITY'] = 'N';
4959 }
4960
4961 $params['RELATIVE_QUANTITY_FACTOR'] = (int)($params['RELATIVE_QUANTITY_FACTOR'] ?? 0) > 0 ? (int)$params['RELATIVE_QUANTITY_FACTOR'] : 5;
4962 }
4963
4964 protected function getTemplateDefaultParams()
4965 {
4966 return array(
4967 'TEMPLATE_THEME' => 'blue',
4968 'SHOW_MAX_QUANTITY' => 'N',
4969 'SHOW_OLD_PRICE' => 'N',
4970 'SHOW_CLOSE_POPUP' => 'N',
4971 'SHOW_DISCOUNT_PERCENT' => 'N',
4972 'DISCOUNT_PERCENT_POSITION' => 'bottom-right',
4973 'LABEL_PROP' => array(),
4974 'LABEL_PROP_MOBILE' => array(),
4975 'LABEL_PROP_POSITION' => 'top-left',
4976 'PRODUCT_SUBSCRIPTION' => 'Y',
4977 'MESS_BTN_BUY' => '',
4978 'MESS_BTN_ADD_TO_BASKET' => '',
4979 'MESS_BTN_SUBSCRIBE' => '',
4980 'MESS_BTN_DETAIL' => '',
4981 'MESS_NOT_AVAILABLE' => '',
4982 'MESS_BTN_COMPARE' => '',
4983 'SHOW_SLIDER' => 'N',
4984 'SLIDER_INTERVAL' => 5000,
4985 'SLIDER_PROGRESS' => 'N',
4986 'USE_ENHANCED_ECOMMERCE' => 'N',
4987 'DATA_LAYER_NAME' => 'dataLayer',
4988 'BRAND_PROPERTY' => ''
4989 );
4990 }
4991
4992 protected function checkTemplateTheme()
4993 {
4994 $theme =& $this->arParams['TEMPLATE_THEME'];
4995 $theme = (string)$theme;
4996
4997 if ($theme != '')
4998 {
4999 $theme = preg_replace('/[^a-zA-Z0-9_\-\‍(\‍)\!]/', '', $theme);
5000 if ($theme === 'site')
5001 {
5002 $siteId = $this->getSiteId();
5003 $templateId = Main\Config\Option::get('main', 'wizard_template_id', 'eshop_bootstrap', $siteId);
5004 $templateId = preg_match('/^eshop_adapt/', $templateId) ? 'eshop_adapt' : $templateId;
5005 $theme = Main\Config\Option::get('main', 'wizard_'.$templateId.'_theme_id', 'blue', $siteId);
5006 }
5007
5008 if ($theme != '')
5009 {
5011 $templateFolder = $this->getTemplate()->GetFolder();
5012
5013 $themesFolder = new Main\IO\Directory($documentRoot.$templateFolder.'/themes/');
5014
5015 if ($themesFolder->isExists())
5016 {
5017 $file = new Main\IO\File($documentRoot.$templateFolder.'/themes/'.$theme.'/style.css');
5018
5019 if (!$file->isExists())
5020 {
5021 $theme = '';
5022 }
5023 }
5024 }
5025 }
5026
5027 if ($theme == '')
5028 {
5029 $theme = 'blue';
5030 }
5031 }
5032
5033 protected function editTemplateData()
5034 {
5035 //
5036 }
5037
5045 public static function checkEnlargedData(&$item, $propertyCode)
5046 {
5047 if (!empty($item) && is_array($item))
5048 {
5049 $item['ENLARGED'] = 'N';
5050 $propertyCode = (string)$propertyCode;
5051
5052 if ($propertyCode !== '' && isset($item['PROPERTIES'][$propertyCode]))
5053 {
5054 $prop = $item['PROPERTIES'][$propertyCode];
5055 if (!empty($prop['VALUE']))
5056 {
5057 $item['ENLARGED'] = 'Y';
5058 }
5059 }
5060 }
5061 }
5062
5063 protected function editTemplateProductSlider(&$item, $iblock, $limit = 0, $addDetailToSlider = true, $default = array())
5064 {
5065 $propCode = $this->storage['IBLOCK_PARAMS'][$iblock]['ADD_PICT_PROP'];
5066
5067 $slider = \CIBlockPriceTools::getSliderForItem($item, $propCode, $addDetailToSlider);
5068 if (empty($slider))
5069 {
5070 $slider = $default;
5071 }
5072
5073 if ($limit > 0)
5074 {
5075 $slider = array_slice($slider, 0, $limit);
5076 }
5077
5078 $item['SHOW_SLIDER'] = true;
5079 $item['MORE_PHOTO'] = $slider;
5080 $item['MORE_PHOTO_COUNT'] = count($slider);
5081 }
5082
5083 protected function editTemplateOfferSlider(&$item, $iblock, $limit = 0, $addDetailToSlider = true, $default = array())
5084 {
5085 $propCode = $this->storage['IBLOCK_PARAMS'][$iblock]['OFFERS_ADD_PICT_PROP'];
5086
5087 $slider = \CIBlockPriceTools::getSliderForItem($item, $propCode, $addDetailToSlider);
5088 if (empty($slider))
5089 {
5090 $slider = $default;
5091 }
5092
5093 if ($limit > 0)
5094 {
5095 $slider = array_slice($slider, 0, $limit);
5096 }
5097
5098 $item['MORE_PHOTO'] = $slider;
5099 $item['MORE_PHOTO_COUNT'] = count($slider);
5100 }
5101
5102 protected function editTemplateCatalogInfo(&$item)
5103 {
5104 if ($this->arResult['MODULES']['catalog'])
5105 {
5106 $item['CATALOG'] = true;
5107 if ($this->isEnableCompatible())
5108 $item['CATALOG_TYPE'] = $item['PRODUCT']['TYPE'];
5109 }
5110 else
5111 {
5112 if ($this->isEnableCompatible())
5113 $item['CATALOG_TYPE'] = 0;
5114 $item['OFFERS'] = array();
5115 }
5116 }
5117
5118 protected function getTemplatePropCell($code, $offer, &$matrixFields, $skuPropList)
5119 {
5120 $cell = array(
5121 'VALUE' => 0,
5122 'SORT' => PHP_INT_MAX,
5123 'NA' => true
5124 );
5125
5126 if (isset($offer['DISPLAY_PROPERTIES'][$code]))
5127 {
5128 $matrixFields[$code] = true;
5129 $cell['NA'] = false;
5130
5131 if ($skuPropList[$code]['USER_TYPE'] === 'directory')
5132 {
5133 $intValue = $skuPropList[$code]['XML_MAP'][$offer['DISPLAY_PROPERTIES'][$code]['VALUE']] ?? 0;
5134 $cell['VALUE'] = $intValue;
5135 }
5136 elseif ($skuPropList[$code]['PROPERTY_TYPE'] === 'L')
5137 {
5138 $cell['VALUE'] = (int)$offer['DISPLAY_PROPERTIES'][$code]['VALUE_ENUM_ID'];
5139 }
5140 elseif ($skuPropList[$code]['PROPERTY_TYPE'] === 'E')
5141 {
5142 $cell['VALUE'] = (int)$offer['DISPLAY_PROPERTIES'][$code]['VALUE'];
5143 }
5144
5145 $cell['SORT'] = $skuPropList[$code]['VALUES'][$cell['VALUE']]['SORT'];
5146 }
5147
5148 return $cell;
5149 }
5150
5151 protected function getOffersIblockId($iblockId)
5152 {
5153 if (!$this->useCatalog)
5154 return null;
5155 if (!isset($this->storage['CATALOGS'][$iblockId]))
5156 return null;
5157 if (
5158 $this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] != \CCatalogSku::TYPE_PRODUCT
5159 && $this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] != \CCatalogSku::TYPE_FULL
5160 )
5161 return null;
5162 return $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
5163 }
5164
5170 {
5171
5172 }
5173
5175 {
5176 if (!$this->useCatalog)
5177 return;
5178 if (!isset($this->storage['CATALOGS'][$iblockId]))
5179 return;
5180
5181 switch ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'])
5182 {
5183 case \CCatalogSku::TYPE_CATALOG:
5184 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5185 $iblockId,
5186 ['CODE' => 'Y']
5187 );
5188 if ($list === null)
5189 $list = [];
5190 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = $list;
5191 unset($list);
5192 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = [];
5193 break;
5194 case \CCatalogSku::TYPE_PRODUCT:
5195 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = [];
5196 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5198 ['CODE' => 'Y']
5199 );
5200 if ($list === null)
5201 $list = [];
5202 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = $list;
5203 unset($list);
5204 break;
5205 case \CCatalogSku::TYPE_FULL:
5206 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5207 $iblockId,
5208 ['CODE' => 'Y']
5209 );
5210 if ($list === null)
5211 $list = [];
5212 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = $list;
5213 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5215 ['CODE' => 'Y']
5216 );
5217 if ($list === null)
5218 $list = [];
5219 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = $list;
5220 unset($list);
5221 break;
5222 case \CCatalogSku::TYPE_OFFERS:
5223 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = [];
5224 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = [];
5225 break;
5226 default:
5227 break;
5228 }
5229 }
5230
5232 {
5233 if (!$this->useCatalog)
5234 return;
5235 if (!isset($this->storage['CATALOGS'][$iblockId]))
5236 return;
5237
5238 switch ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'])
5239 {
5240 case \CCatalogSku::TYPE_CATALOG:
5241 case \CCatalogSku::TYPE_OFFERS:
5242 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_TREE_PROPS'] = [];
5243 break;
5244 case \CCatalogSku::TYPE_PRODUCT:
5245 case \CCatalogSku::TYPE_FULL:
5246 $list = Catalog\Product\PropertyCatalogFeature::getOfferTreePropertyCodes(
5247 $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'],
5248 ['CODE' => 'Y']
5249 );
5250 if ($list === null)
5251 $list = [];
5252 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_TREE_PROPS'] = $list;
5253 unset($list);
5254 break;
5255 default:
5256 break;
5257 }
5258 }
5259
5260 /* product tools */
5261
5268 protected function isNeedCheckQuantity(array $product)
5269 {
5270 return (
5271 $product['QUANTITY_TRACE'] === Catalog\ProductTable::STATUS_YES
5272 && $product['CAN_BUY_ZERO'] === Catalog\ProductTable::STATUS_NO
5273 );
5274 }
5275
5276 /* product tools end */
5277
5278 /* user tools */
5279
5285 protected function getUserGroups()
5286 {
5288 global $USER;
5289 $result = array(2);
5290 if (isset($USER) && $USER instanceof \CUser)
5291 {
5292 $result = $USER->GetUserGroupArray();
5294 }
5295 return $result;
5296 }
5297
5303 protected function getUserGroupsCacheId()
5304 {
5305 return implode(',', $this->getUserGroups());
5306 }
5307
5308 /* user tools end */
5309
5310 /* compatibility tools */
5311
5320 {
5321 if (empty($items))
5322 return;
5323
5324 $initFields = array(
5325 'PRICES' => array(),
5326 'PRICE_MATRIX' => false,
5327 'MIN_PRICE' => false
5328 );
5329 if (!$this->arParams['USE_PRICE_COUNT'] && !empty($this->storage['PRICES']))
5330 {
5331 foreach ($this->storage['PRICES'] as $value)
5332 {
5333 if (!$value['CAN_VIEW'] && !$value['CAN_BUY'])
5334 continue;
5335
5336 $priceType = $value['ID'];
5337 $initFields['CATALOG_GROUP_ID_'.$priceType] = $priceType;
5338 $initFields['~CATALOG_GROUP_ID_'.$priceType] = $priceType;
5339 $initFields['CATALOG_GROUP_NAME_'.$priceType] = $value['TITLE'];
5340 $initFields['~CATALOG_GROUP_NAME_'.$priceType] = $value['~TITLE'];
5341 $initFields['CATALOG_CAN_ACCESS_'.$priceType] = ($value['CAN_VIEW'] ? 'Y' : 'N');
5342 $initFields['~CATALOG_CAN_ACCESS_'.$priceType] = ($value['CAN_VIEW'] ? 'Y' : 'N');
5343 $initFields['CATALOG_CAN_BUY_'.$priceType] = ($value['CAN_BUY'] ? 'Y' : 'N');
5344 $initFields['~CATALOG_CAN_BUY_'.$priceType] = ($value['CAN_BUY'] ? 'Y' : 'N');
5345 $initFields['CATALOG_PRICE_ID_'.$priceType] = null;
5346 $initFields['~CATALOG_PRICE_ID_'.$priceType] = null;
5347 $initFields['CATALOG_PRICE_'.$priceType] = null;
5348 $initFields['~CATALOG_PRICE_'.$priceType] = null;
5349 $initFields['CATALOG_CURRENCY_'.$priceType] = null;
5350 $initFields['~CATALOG_CURRENCY_'.$priceType] = null;
5351 $initFields['CATALOG_QUANTITY_FROM_'.$priceType] = null;
5352 $initFields['~CATALOG_QUANTITY_FROM_'.$priceType] = null;
5353 $initFields['CATALOG_QUANTITY_TO_'.$priceType] = null;
5354 $initFields['~CATALOG_QUANTITY_TO_'.$priceType] = null;
5355 $initFields['CATALOG_EXTRA_ID_'.$priceType] = null;
5356 $initFields['~CATALOG_EXTRA_ID_'.$priceType] = null;
5357 unset($priceType);
5358 }
5359 unset($value);
5360 }
5361
5362 foreach (array_keys($items) as $index)
5363 $this->oldData[$items[$index]['ID']] = $initFields;
5364 unset($index, $initFields);
5365 }
5366
5376 {
5377 if (!isset($this->oldData[$id]) || empty($prices) || $this->arParams['USE_PRICE_COUNT'])
5378 return;
5379 foreach ($prices as $rawPrice)
5380 {
5381 $priceType = $rawPrice['CATALOG_GROUP_ID'];
5382 $this->oldData[$id]['CATALOG_PRICE_ID_'.$priceType] = $rawPrice['ID'];
5383 $this->oldData[$id]['~CATALOG_PRICE_ID_'.$priceType] = $rawPrice['ID'];
5384 $this->oldData[$id]['CATALOG_PRICE_'.$priceType] = $rawPrice['PRICE'];
5385 $this->oldData[$id]['~CATALOG_PRICE_'.$priceType] = $rawPrice['PRICE'];
5386 $this->oldData[$id]['CATALOG_CURRENCY_'.$priceType] = $rawPrice['CURRENCY'];
5387 $this->oldData[$id]['~CATALOG_CURRENCY_'.$priceType] = $rawPrice['CURRENCY'];
5388 $this->oldData[$id]['CATALOG_QUANTITY_FROM_'.$priceType] = $rawPrice['QUANTITY_FROM'];
5389 $this->oldData[$id]['~CATALOG_QUANTITY_FROM_'.$priceType] = $rawPrice['QUANTITY_FROM'];
5390 $this->oldData[$id]['CATALOG_QUANTITY_TO_'.$priceType] = $rawPrice['QUANTITY_TO'];
5391 $this->oldData[$id]['~CATALOG_QUANTITY_TO_'.$priceType] = $rawPrice['QUANTITY_TO'];
5392 $this->oldData[$id]['CATALOG_EXTRA_ID_'.$priceType] = $rawPrice['EXTRA_ID'];
5393 $this->oldData[$id]['~CATALOG_EXTRA_ID_'.$priceType] = $rawPrice['EXTRA_ID'];
5394 unset($priceType);
5395 }
5396 unset($rawPrice);
5397 }
5398
5407 protected function getCompatibleFieldValue($id, $field)
5408 {
5409 if (!isset($this->oldData[$id]))
5410 return null;
5411 return ($this->oldData[$id][$field] ?? null);
5412 }
5413
5421 protected function checkQuantityRange(array $row)
5422 {
5423 return (
5424 ($row['QUANTITY_FROM'] === null || $row['QUANTITY_FROM'] <= $this->arParams['SHOW_PRICE_COUNT'])
5425 && ($row['QUANTITY_TO'] === null || $row['QUANTITY_TO'] >= $this->arParams['SHOW_PRICE_COUNT'])
5426 );
5427 }
5428
5434 protected function getEmptyPriceMatrix(): array
5435 {
5436 return array(
5437 'ROWS' => array(),
5438 'COLS' => array(),
5439 'MATRIX' => array(),
5440 'CAN_BUY' => array(),
5441 'AVAILABLE' => 'N',
5442 'CURRENCY_LIST' => array()
5443 );
5444 }
5445
5453 private function resortOldPrices($id)
5454 {
5455 if (empty($this->oldData[$id]['PRICES']) || count($this->oldData[$id]['PRICES']) < 2)
5456 return;
5457 foreach (array_keys($this->oldData[$id]['PRICES']) as $priceCode)
5458 $this->oldData[$id]['PRICES'][$priceCode]['_SORT'] = $this->storage['PRICES'][$priceCode]['SORT'];
5459 unset($priceCode);
5461 $this->oldData[$id]['PRICES'],
5462 array('_SORT' => SORT_ASC, 'PRICE_ID' => SORT_ASC),
5463 '', null, true
5464 );
5465 foreach (array_keys($this->oldData[$id]['PRICES']) as $priceCode)
5466 unset($this->oldData[$id]['PRICES'][$priceCode]['_SORT']);
5467 unset($priceCode);
5468 }
5469
5475 protected function getCompatibleProductFields()
5476 {
5477 return [
5478 'TYPE' => 'CATALOG_TYPE',
5479 'AVAILABLE' => 'CATALOG_AVAILABLE',
5480 'BUNDLE' => 'CATALOG_BUNDLE',
5481 'QUANTITY' => 'CATALOG_QUANTITY',
5482 'QUANTITY_TRACE' => 'CATALOG_QUANTITY_TRACE',
5483 'CAN_BUY_ZERO' => 'CATALOG_CAN_BUY_ZERO',
5484 'MEASURE' => 'CATALOG_MEASURE',
5485 'SUBSCRIBE' => 'CATALOG_SUBSCRIBE',
5486 'VAT_ID' => 'CATALOG_VAT_ID',
5487 'VAT_INCLUDED' => 'CATALOG_VAT_INCLUDED',
5488 'WEIGHT' => 'CATALOG_WEIGHT',
5489 'WIDTH' => 'CATALOG_WIDTH',
5490 'LENGTH' => 'CATALOG_LENGTH',
5491 'HEIGHT' => 'CATALOG_HEIGHT',
5492 'PAYMENT_TYPE' => 'CATALOG_PRICE_TYPE',
5493 'RECUR_SCHEME_LENGTH' => 'CATALOG_RECUR_SCHEME_LENGTH',
5494 'RECUR_SCHEME_TYPE' => 'CATALOG_RECUR_SCHEME_TYPE',
5495 'QUANTITY_TRACE_RAW' => 'CATALOG_QUANTITY_TRACE_ORIG',
5496 'CAN_BUY_ZERO_RAW' => 'CATALOG_CAN_BUY_ZERO_ORIG',
5497 'SUBSCRIBE_RAW' => 'CATALOG_SUBSCRIBE_ORIG',
5498 'PURCHASING_PRICE' => 'CATALOG_PURCHASING_PRICE',
5499 'PURCHASING_CURRENCY' => 'CATALOG_PURCHASING_CURRENCY',
5500 'BARCODE_MULTI' => 'CATALOG_BARCODE_MULTI',
5501 'TRIAL_PRICE_ID' => 'CATALOG_TRIAL_PRICE_ID',
5502 'WITHOUT_ORDER' => 'CATALOG_WITHOUT_ORDER',
5503 '~TYPE' => '~CATALOG_TYPE',
5504 '~AVAILABLE' => '~CATALOG_AVAILABLE',
5505 '~BUNDLE' => '~CATALOG_BUNDLE',
5506 '~QUANTITY' => '~CATALOG_QUANTITY',
5507 '~QUANTITY_TRACE' => '~CATALOG_QUANTITY_TRACE',
5508 '~CAN_BUY_ZERO' => '~CATALOG_CAN_BUY_ZERO',
5509 '~MEASURE' => '~CATALOG_MEASURE',
5510 '~SUBSCRIBE' => '~CATALOG_SUBSCRIBE',
5511 '~VAT_ID' => '~CATALOG_VAT_ID',
5512 '~VAT_INCLUDED' => '~CATALOG_VAT_INCLUDED',
5513 '~WEIGHT' => '~CATALOG_WEIGHT',
5514 '~WIDTH' => '~CATALOG_WIDTH',
5515 '~LENGTH' => '~CATALOG_LENGTH',
5516 '~HEIGHT' => '~CATALOG_HEIGHT',
5517 '~PAYMENT_TYPE' => '~CATALOG_PRICE_TYPE',
5518 '~RECUR_SCHEME_LENGTH' => '~CATALOG_RECUR_SCHEME_LENGTH',
5519 '~RECUR_SCHEME_TYPE' => '~CATALOG_RECUR_SCHEME_TYPE',
5520 '~QUANTITY_TRACE_RAW' => '~CATALOG_QUANTITY_TRACE_ORIG',
5521 '~CAN_BUY_ZERO_RAW' => '~CATALOG_CAN_BUY_ZERO_ORIG',
5522 '~SUBSCRIBE_RAW' => '~CATALOG_SUBSCRIBE_ORIG',
5523 '~PURCHASING_PRICE' => '~CATALOG_PURCHASING_PRICE',
5524 '~PURCHASING_CURRENCY' => '~CATALOG_PURCHASING_CURRENCY',
5525 '~BARCODE_MULTI' => '~CATALOG_BARCODE_MULTI',
5526 '~TRIAL_PRICE_ID' => '~CATALOG_TRIAL_PRICE_ID',
5527 '~WITHOUT_ORDER' => '~CATALOG_WITHOUT_ORDER'
5528 ];
5529 }
5530
5531 /* compatibility tools end */
5532}
$arParams
Определения access_dialog.php:21
$path
Определения access_edit.php:21
$hash
Определения ajax_redirector.php:8
$type
Определения options.php:106
const CATALOG_VALUE_EPSILON
Определения include.php:108
global $APPLICATION
Определения include.php:80
SubscribeProduct($intProductID, $arRewriteFields=array(), $arProductParams=array())
Определения include.php:1539
if($strVal !='') $productFields
Определения options.php:1791
static preloadPriceData(array $productIds, array $catalogGroups)
Определения discountmanager.php:562
static preloadProductDataToExtendOrder(array $productIds, array $userGroups)
Определения discountmanager.php:626
static setProductPropertiesCache($productId, $props)
Определения discountmanager.php:517
static getParentSection($sectionId, $depth)
Определения viewed.php:24
const PRICE_MODE_QUANTITY
Определения product.php:92
const TYPE_SKU
Определения product.php:72
const PRICE_MODE_RATIO
Определения product.php:93
const PRICE_MODE_SIMPLE
Определения product.php:91
static isCurrencyExist($currency)
Определения currencymanager.php:263
$calculatePrices
Определения base.php:70
setAction($action)
Определения base.php:105
getEmptyPriceMatrix()
Определения base.php:5434
getRandomRecommendation($ids)
Определения base.php:1068
getProductInfo($productId)
Определения base.php:672
$isIblockCatalog
Определения base.php:61
parseConditionLevel($condition, $params)
Определения base.php:2153
searchItemSelectedQuantityRangeHash($id)
Определения base.php:3512
static checkEnlargedData(&$item, $propertyCode)
Определения base.php:5045
checkProductSection($productId, $sectionId=0, $sectionCode='')
Определения base.php:4369
clearItems()
Определения base.php:2541
getProductIdMap($productIds)
Определения base.php:1630
getMeasureIds(array $items)
Определения base.php:2657
getIblockElements($elementIterator)
applyTemplateModifications()
Определения base.php:4922
getAction()
Определения base.php:94
onPrepareComponentParams($params)
Определения base.php:268
parsePropertyCondition(array &$result, array $condition, $params)
Определения base.php:2265
getProductFields($iblockId)
Определения base.php:2009
searchItemSelectedRatioId($id)
Определения base.php:3460
__construct($component=null)
Определения base.php:83
getOffersIblockId($iblockId)
Определения base.php:5151
initVats()
Определения base.php:789
modifyElementPrices(&$element)
Определения base.php:3600
getEmptyRatio()
Определения base.php:2607
isNeedCheckQuantity(array $product)
Определения base.php:5268
const ERROR_404
Определения base.php:29
sliceItemsForSlider(&$items)
Определения base.php:4767
getFilter()
Определения base.php:1896
getFullQuantityRange()
Определения base.php:3501
getRewriteFields($action)
Определения base.php:4623
$compatibleMode
Определения base.php:65
processProducts()
Определения base.php:3695
$productIdMap
Определения base.php:49
$productIds
Определения base.php:47
getIblockOffers($iblockId)
Определения base.php:3767
const ACTION_ADD_TO_COMPARE
Определения base.php:25
editTemplateCatalogInfo(&$item)
Определения base.php:5102
const ACTION_DELETE_FROM_COMPARE
Определения base.php:26
processLinkAction()
Определения base.php:4287
getIblockSelectFields($iblockId)
Определения base.php:2079
calculateItemPrices(array &$items)
Определения base.php:2847
compactItemRatios($id)
Определения base.php:3485
const PARAM_TITLE_MASK
Определения base.php:31
getBigDataProductIds()
Определения base.php:928
getBestSellersProductIds()
Определения base.php:1525
getElementList($iblockId, $products)
Определения base.php:1734
getBestSellersRecommendation($ids)
Определения base.php:992
getUserGroups()
Определения base.php:5285
filterByParams($ids, $filterIds=array(), $useSectionFilter=true)
Определения base.php:1122
$sortFields
Определения base.php:44
getOffersSort()
Определения base.php:4090
initCatalogDiscountCache()
Определения base.php:557
getProductSelect($iblockId, array $selectFields)
Определения base.php:1985
isSeparateLoading()
Определения base.php:235
getSectionIdByCode($sectionCode='', int $iblockId=0)
Определения base.php:1183
convertOrder(array $order)
Определения base.php:2072
getTemplateEmptyPreview()
Определения base.php:4742
prepareData()
Определения base.php:4219
getUserGroupsCacheId()
Определения base.php:5303
const SORT_ORDER_MASK
Определения base.php:32
isExtendedMode()
Определения base.php:197
addProductToBasket($productId, $action)
Определения base.php:4436
getProductsSeparatedByIblock()
Определения base.php:1678
isMergeProductWhenAddedBasket()
Определения base.php:4618
setCompatibleMode($state)
Определения base.php:208
parseConditionName(array $condition)
Определения base.php:2183
$errorCollection
Определения base.php:38
const ACTION_SUBSCRIBE
Определения base.php:24
getSeparateList(array $params)
Определения base.php:1777
getCacheKeys()
Определения base.php:4147
makeOutputResult()
Определения base.php:4259
getBigDataServiceRequestParams($type='')
Определения base.php:1425
$useCatalog
Определения base.php:60
initResultCache()
Определения base.php:4123
loadMeasureRatios(array $itemIds)
Определения base.php:2557
getCompatibleFieldValue($id, $field)
Определения base.php:5407
processElement(array &$element)
Определения base.php:2346
filterIdBySection($elementIds, $iblockId, $sectionId, $limit, $depth=0)
Определения base.php:1274
isCacheDisabled()
Определения base.php:172
parseCondition($condition, $params)
Определения base.php:2093
checkProductIblock(array $product)
Определения base.php:4431
getTemplateDefaultParams()
Определения base.php:4964
getSelect()
Определения base.php:1862
$iblockProducts
Определения base.php:50
initialLoadAction()
Определения base.php:4697
$elementLinks
Определения base.php:52
parseConditionOperator($condition)
Определения base.php:2213
setCacheUsage($state)
Определения base.php:160
initUrlTemplates()
Определения base.php:3531
static getSettingsScript($componentPath, $settingsName)
Определения base.php:247
$productWithPrices
Определения base.php:55
parseConditionValue($condition, $name)
Определения base.php:2248
prepareAction()
Определения base.php:4859
$useDiscountCache
Определения base.php:62
getRecommendationLimit($ids)
Определения base.php:1398
getTemplatePropCell($code, $offer, &$matrixFields, $skuPropList)
Определения base.php:5118
loadOfferTreePropertyCodes($iblockId)
Определения base.php:5231
getSectionIdByElement($elementId, $elementCode='', int $iblockId=0)
Определения base.php:1229
initQueryFields()
Определения base.php:1849
prepareQueryFields(array $select, array $filter, array $order)
Определения base.php:1943
chooseOffer($offers, $iblockId)
getDeferredProductIds()
Определения base.php:1625
initCatalogInfo()
Определения base.php:650
const ACTION_ADD_TO_BASKET
Определения base.php:23
modifyElementCommonData(array &$element)
Определения base.php:2359
modifyOffers($offers)
Определения base.php:4102
getBestSellersFilter()
Определения base.php:1547
hasErrors()
Определения base.php:115
getQuantityRangeHash(array $range)
Определения base.php:3495
getOffersFilter($iblockId)
Определения base.php:4055
clearCatalogDiscountCache()
Определения base.php:582
setExtendedMode($state)
Определения base.php:185
getDefaultMeasure()
Определения base.php:1715
sortElementList()
Определения base.php:873
initIblockPropertyFeatures()
Определения base.php:827
prepareElementQueryFields()
Определения base.php:1921
convertFilter(array $filter)
Определения base.php:2059
loadBasketPropertyCodes($iblockId)
Определения base.php:5174
makeElementLinks()
Определения base.php:902
setSeparateLoading($state)
Определения base.php:227
prepareTemplateParams()
Определения base.php:4931
processOffers()
Определения base.php:3712
initCurrencyConvert()
Определения base.php:599
$filterFields
Определения base.php:43
setElementPanelButtons(&$element)
Определения base.php:2482
fillCompatibleRawPriceFields($id, array $prices)
Определения base.php:5375
filterPureOffers()
Определения base.php:4229
static getProductsMap(array $originalIds=array())
Определения base.php:1646
initPricesQuery()
Определения base.php:1973
const ACTION_BUY
Определения base.php:22
convertSelect(array $select)
Определения base.php:2046
initItemsMeasure(array &$items)
Определения base.php:2622
getCompatibleProductFields()
Определения base.php:5475
$recommendationIdToProduct
Определения base.php:77
checkQuantityRange(array $row)
Определения base.php:5421
$globalFilter
Определения base.php:57
getFullIterator(array $params)
Определения base.php:1833
checkTemplateTheme()
Определения base.php:4992
executeComponent()
Определения base.php:4896
initPrices()
Определения base.php:743
const ERROR_TEXT
Определения base.php:28
getPropertyList($iblock, $propertyCodes)
Определения base.php:2505
editTemplateData()
Определения base.php:5033
initCompatibleFields(array $items)
Определения base.php:5319
checkModules()
Определения base.php:540
$quantityRanges
Определения base.php:74
$selectFields
Определения base.php:42
loadMeasures(array $measureIds)
Определения base.php:2684
$separateLoading
Определения base.php:40
editTemplateProductSlider(&$item, $iblock, $limit=0, $addDetailToSlider=true, $default=array())
Определения base.php:5063
loadDisplayPropertyCodes($iblockId)
Определения base.php:5169
modifyDisplayProperties($iblock, &$iblockElements)
Определения base.php:2501
checkIblock()
Определения base.php:4185
deferredLoadAction()
Определения base.php:4649
isEnableCompatible()
Определения base.php:218
bigDataLoadAction()
Определения base.php:4666
$productWithOffers
Определения base.php:54
loadPrices(array $itemIds)
Определения base.php:2716
getTemplateCurrencies()
Определения base.php:4779
processResultData()
Определения base.php:4156
offerIblockExist($iblockId)
Определения base.php:632
initBigDataLastUsage()
Определения base.php:4684
editTemplateOfferSlider(&$item, $iblock, $limit=0, $addDetailToSlider=true, $default=array())
Определения base.php:5083
getProductIds()
Определения base.php:918
initElementList()
Определения base.php:837
getMostViewedRecommendation($ids)
Определения base.php:1025
transferItems(array &$items)
Определения base.php:2969
static sendJsonAnswer(array $result=[])
Определения base.php:4836
static getFieldImageData(array &$item, array $keys, $entity, $ipropertyKey='IPROPERTY_VALUES')
Определения tools.php:102
static process404($message="", $defineConstant=true, $setStatus=true, $showPage=false, $pageFile="")
Определения tools.php:37
const IPROPERTY_ENTITY_ELEMENT
Определения tools.php:17
static getProductIdsByOfferIds($offerIds)
Определения catalog.php:438
static getDocumentRoot()
Определения application.php:736
static getInstance()
Определения application.php:98
static get($moduleId, $name, $default="", $siteId=false)
Определения option.php:30
static set($moduleId, $name, $value="", $siteId="")
Определения option.php:261
Определения error.php:15
static getSystemParameters()
Определения httprequest.php:520
Определения loader.php:13
static includeModule($moduleName)
Определения loader.php:67
static getEntity()
Определения datamanager.php:65
static getList(array $parameters=array())
Определения datamanager.php:431
static encode($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения htmlfilter.php:12
static normalizeArrayValuesByInt(&$map, $sorted=true)
Определения collection.php:150
static sortByColumn(array &$array, $columns, $callbacks='', $defaultValueIfNotSetValue=null, $preserveKeys=false)
Определения collection.php:24
static encode($data, $options=null)
Определения json.php:22
Определения uri.php:17
static SetDiscountProductCache($arItem, $arParams=array())
Определения discount.php:3986
static SetProductSectionsCache($arItemIDs)
Определения discount.php:4147
static ClearDiscountCache($arTypes)
Определения discount.php:4224
static Disable()
Определения discount_save.php:38
static Enable()
Определения discount_save.php:33
static GetListArray()
Определения cataloggroup.php:291
static ConvertCurrency($valSum, $curFrom, $curTo, $valDate="")
Определения currency_rate.php:393
setSiteId($siteId)
Определения component.php:255
getSiteId()
Определения component.php:263
initComponentTemplate($templatePage="", $siteTemplate=false, $customTemplatePath="")
Определения component.php:757
getTemplate()
Определения component.php:343
startResultCache($cacheTime=false, $additionalCacheID=false, $cachePath=false)
Определения component.php:904
abortResultCache()
Определения component.php:1233
includeComponentTemplate($templatePage="", $customTemplatePath="")
Определения component.php:724
getParent()
Определения component.php:357
setResultCacheKeys($arResultCacheKeys)
Определения component.php:1338
static getList($arOrder=array(), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения measure_ratio.php:15
static clearCache()
Определения comp_formatprops.php:304
static GetCatalogPrices($IBLOCK_ID, $arPriceCode)
Определения comp_pricetools.php:27
static getMinPriceFromList($priceList)
Определения comp_pricetools.php:2672
static SetCatalogDiscountCache($arCatalogGroups, $arUserGroups, $siteId=false)
Определения comp_pricetools.php:124
static CheckProductProperties($iblockID, $elementID, $propertiesList, $propertiesValues, $enablePartialList=false)
Определения comp_pricetools.php:920
static getSliderForItem(&$item, $propertyCode, $addDetailToSlider, $encode=true)
Определения comp_pricetools.php:2475
static GetOfferProperties($offerID, $iblockID, $propertiesList, $skuTreeProps='')
Определения comp_pricetools.php:1189
static GetItemPrices( $IBLOCK_ID, $arCatalogPrices, $arItem, $bVATInclude=true, $arCurrencyParams=array(), $USER_ID=0, $LID=SITE_ID)
Определения comp_pricetools.php:274
static isEnabledCalculationDiscounts()
Определения comp_pricetools.php:2689
static GetAllowCatalogPrices($arPriceTypes)
Определения comp_pricetools.php:107
static Init($arExt=array(), $bReturn=false)
Определения jscore.php:66
static modifyFilterFromOrder(array $filter, array $order, array $options)
Определения querybuilder.php:256
static GetBestSellerList($by="AMOUNT", $arFilter=Array(), $arOrderFilter=Array(), $limit=0)
Определения product.php:88
if( $strWarning=="") if($strWarning=="") $componentPath
Определения component_props2.php:197
$templateId
Определения component_props2.php:51
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$iblockId
Определения iblock_catalog_edit.php:30
$catalog
Определения iblock_catalog_edit.php:135
if(! $catalogEdit->isSuccess()) $iblock
Определения iblock_catalog_edit.php:38
$getListParams
Определения iblock_catalog_list.php:210
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$iblockList
Определения iblock_catalog_list.php:271
$filterFields
Определения iblock_catalog_list.php:55
$selectFields
Определения iblock_catalog_list.php:160
$vatInclude
if(! $useStoreControl) $measureIterator
global $USER
Определения csv_new_run.php:40
$navParams
Определения csv_new_run.php:35
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
foreach(['Bitrix\\Main'=> '/lib', 'Psr\\Container'=> '/vendor/psr/container/src', 'Psr\\Log'=> '/vendor/psr/log/src', 'Psr\\Http\\Message'=> '/vendor/psr/http-message/src', 'Psr\\Http\\Client'=> '/vendor/psr/http-client/src', 'Http\\Promise'=> '/vendor/php-http/promise/src', 'PHPMailer\\PHPMailer'=> '/vendor/phpmailer/phpmailer/src', 'GeoIp2'=> '/vendor/geoip2/geoip2/src', 'MaxMind\\Db'=> '/vendor/maxmind-db/reader/src/MaxMind/Db', 'PhpParser'=> '/vendor/nikic/php-parser/lib/PhpParser', 'Recurr'=> '/vendor/simshaun/recurr/src/Recurr',] as $namespace=> $namespacePath) $documentRoot
Определения autoload.php:27
if(file_exists($_SERVER['DOCUMENT_ROOT'] . "/urlrewrite.php")) $uri
Определения urlrewrite.php:61
roundEx($value, $prec=0)
Определения tools.php:4635
ShowError($strError, $cls="errortext")
Определения tools.php:4499
AddToTimeStamp($arrAdd, $stmp=false)
Определения tools.php:687
LocalRedirect($url, $skip_security_check=false, $status="302 Found")
Определения tools.php:4005
$name
Определения menu_edit.php:35
$map
Определения config.php:5
Определения entity.php:2
Определения base.php:2
Определения ufield.php:9
Определения Image.php:9
Определения collection.php:2
Определения cookie.php:3
hasErrors()
Определения errorableimplementation.php:17
$order
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$errorMsg
Определения refund.php:16
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$currency
Определения template.php:266
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
if($params["BILLBY_ORDER_SUBJECT"]) if( $params["PAYMENT_DATE_PAY_BEFORE"]) if($params['BILLBY_PAYER_SHOW']=='Y') $currencyFormat
Определения template.php:393
$items
Определения template.php:224
$rows
Определения options.php:264
$error
Определения subscription_card_product.php:20
$action
Определения file_dialog.php:21
$iterator
Определения yandex_run.php:610