Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
base.php
1<?php
3
12
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
54 protected $productWithOffers = array();
55 protected $productWithPrices = array();
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();
77 protected $recommendationIdToProduct = 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 {
136 Tools::process404(
137 trim($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
268 public function onPrepareComponentParams($params)
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());
278 $uri->deleteParams(Main\HttpRequest::getSystemParameters());
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 return $params;
528 }
529
535 protected function checkModules()
536 {
537 $this->useCatalog = Loader::includeModule('catalog');
538 $this->storage['MODULES'] = array(
539 'iblock' => true,
540 'catalog' => $this->useCatalog,
541 'currency' => $this->useCatalog
542 );
543
544 return true;
545 }
546
552 protected function initCatalogDiscountCache()
553 {
554 if ($this->useCatalog && $this->useDiscountCache && !empty($this->elementLinks))
555 {
556 foreach ($this->iblockProducts as $iblock => $products)
557 {
558 if ($this->storage['USE_SALE_DISCOUNTS'])
559 {
560 Catalog\Discount\DiscountManager::preloadPriceData($products, $this->storage['PRICES_ALLOW']);
561 Catalog\Discount\DiscountManager::preloadProductDataToExtendOrder($products, $this->getUserGroups());
562 }
563 else
564 {
565 \CCatalogDiscount::SetProductSectionsCache($products);
566 \CCatalogDiscount::SetDiscountProductCache($products, array('IBLOCK_ID' => $iblock, 'GET_BY_ID' => 'Y'));
567 }
568 }
569 }
570 }
571
577 protected function clearCatalogDiscountCache()
578 {
579 if ($this->useCatalog && $this->useDiscountCache)
580 {
581 \CCatalogDiscount::ClearDiscountCache(array(
582 'PRODUCT' => true,
583 'SECTIONS' => true,
584 'PROPERTIES' => true
585 ));
586 }
587 }
588
594 protected function initCurrencyConvert()
595 {
596 $this->storage['CONVERT_CURRENCY'] = array();
597
598 if ($this->arParams['CONVERT_CURRENCY'] === 'Y')
599 {
600 $correct = false;
601 if (Loader::includeModule('currency'))
602 {
603 $this->storage['MODULES']['currency'] = true;
604 $correct = Currency\CurrencyManager::isCurrencyExist($this->arParams['CURRENCY_ID']);
605 }
606 if ($correct)
607 {
608 $this->storage['CONVERT_CURRENCY'] = array(
609 'CURRENCY_ID' => $this->arParams['CURRENCY_ID']
610 );
611 }
612 else
613 {
614 $this->arParams['CONVERT_CURRENCY'] = 'N';
615 $this->arParams['CURRENCY_ID'] = '';
616 }
617 unset($correct);
618 }
619 }
620
627 protected function offerIblockExist($iblockId)
628 {
629 if (empty($this->storage['CATALOGS'][$iblockId]))
630 return false;
631
632 $catalog = $this->storage['CATALOGS'][$iblockId];
633
634 if (empty($catalog['CATALOG_TYPE']))
635 return false;
636
637 return $catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_FULL || $catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_PRODUCT;
638 }
639
645 protected function initCatalogInfo()
646 {
647 $catalogs = array();
648
649 if ($this->useCatalog)
650 {
651 $this->storage['SHOW_CATALOG_WITH_OFFERS'] = Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') === 'Y';
652 $this->storage['USE_SALE_DISCOUNTS'] = Main\Config\Option::get('sale', 'use_sale_discount_only') === 'Y';
653 foreach (array_keys($this->iblockProducts) as $iblockId)
654 {
655 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
656 if (!empty($catalog) && is_array($catalog))
657 {
658 $this->isIblockCatalog = $this->isIblockCatalog || $catalog['CATALOG_TYPE'] != \CCatalogSku::TYPE_PRODUCT;
659 $catalogs[$iblockId] = $catalog;
660 }
661 }
662 }
663
664 $this->storage['CATALOGS'] = $catalogs;
665 }
666
667 protected function getProductInfo($productId)
668 {
669 if (!$this->useCatalog)
670 return null;
671
672 $productId = (int)$productId;
673 if ($productId <= 0)
674 return null;
675
676 $iblockId = (int)\CIBlockElement::GetIBlockByID($productId);
677 if ($iblockId <= 0)
678 return null;
679
680 $iterator = Catalog\ProductTable::getList([
681 'select' => ['ID', 'TYPE'],
682 'filter' => ['=ID' => $productId]
683 ]);
684 $row = $iterator->fetch();
685 unset($iterator);
686 if (empty($row))
687 return null;
688
689 $row['ID'] = (int)$row['ID'];
690 $row['TYPE'] = (int)$row['TYPE'];
691 if (
692 $row['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU
693 || $row['TYPE'] == Catalog\ProductTable::TYPE_FREE_OFFER
694 )
695 return null;
696
697 $row['ELEMENT_IBLOCK_ID'] = $iblockId;
698 $row['PRODUCT_IBLOCK_ID'] = 0;
699
700 if (isset($this->storage['CATALOGS'][$iblockId]))
701 {
702 if ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] == \CCatalogSku::TYPE_CATALOG)
703 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
704 else
705 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['PRODUCT_IBLOCK_ID'];
706 return $row;
707 }
708
709 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
710 if (empty($catalog) || !is_array($catalog))
711 return null;
712
713 if ($catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_PRODUCT)
714 return null;
715
716 if ($catalog['CATALOG_TYPE'] == \CCatalogSku::TYPE_OFFERS)
717 {
718 $iblockId = $catalog['PRODUCT_IBLOCK_ID'];
719 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
720 }
721 if (!isset($this->storage['CATALOGS']))
722 $this->storage['CATALOGS'] = [];
723 $this->storage['CATALOGS'][$iblockId] = $catalog;
724 unset($catalog);
725
726 if ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] == \CCatalogSku::TYPE_CATALOG)
727 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
728 else
729 $row['PRODUCT_IBLOCK_ID'] = $this->storage['CATALOGS'][$iblockId]['PRODUCT_IBLOCK_ID'];
730 return $row;
731 }
732
738 protected function initPrices()
739 {
740 // This function returns array with prices description and access rights
741 // in case catalog module n/a prices get values from element properties
742 $this->storage['PRICES'] = \CIBlockPriceTools::GetCatalogPrices(
743 isset($this->arParams['IBLOCK_ID']) && $this->arParams['IBLOCK_ID'] > 0 ? $this->arParams['IBLOCK_ID'] : false,
744 $this->arParams['PRICE_CODE']
745 );
746 $this->storage['PRICES_ALLOW'] = \CIBlockPriceTools::GetAllowCatalogPrices($this->storage['PRICES']);
747 $this->storage['PRICES_CAN_BUY'] = array();
748 $this->storage['PRICES_MAP'] = array();
749 foreach ($this->storage['PRICES'] as $priceType)
750 {
751 $this->storage['PRICES_MAP'][$priceType['ID']] = $priceType['CODE'];
752 if ($priceType['CAN_BUY'])
753 $this->storage['PRICES_CAN_BUY'][$priceType['ID']] = $priceType['ID'];
754 }
755
756 $this->storage['PRICE_TYPES'] = array();
757 if ($this->useCatalog)
758 $this->storage['PRICE_TYPES'] = \CCatalogGroup::GetListArray();
759
760 $this->useDiscountCache = false;
761 if ($this->useCatalog)
762 {
763 if (!empty($this->storage['CATALOGS']) && !empty($this->storage['PRICES_ALLOW']))
764 $this->useDiscountCache = true;
765 }
766
767 if ($this->useCatalog && $this->useDiscountCache)
768 {
769 $this->useDiscountCache = \CIBlockPriceTools::SetCatalogDiscountCache(
770 $this->storage['PRICES_ALLOW'],
771 $this->getUserGroups()
772 );
773 }
774
775 if ($this->useCatalog)
776 Catalog\Product\Price::loadRoundRules($this->storage['PRICES_ALLOW']);
777 }
778
784 protected function initVats()
785 {
786 $this->storage['VATS'] = [];
787 $this->storage['IBLOCKS_VAT'] = [];
788 if ($this->useCatalog)
789 {
790 $iterator = Catalog\VatTable::getList([
791 'select' => ['ID', 'RATE'],
792 'order' => ['ID' => 'ASC']
793 ]);
794 while ($row = $iterator->fetch())
795 $this->storage['VATS'][(int)$row['ID']] = (float)$row['RATE'];
796 unset($row, $iterator);
797
798 if (!empty($this->storage['CATALOGS']))
799 {
800 foreach ($this->storage['CATALOGS'] as $catalog)
801 {
802 $this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']] = 0;
803 if ($catalog['PRODUCT_IBLOCK_ID'] > 0)
804 $this->storage['IBLOCKS_VAT'][$catalog['PRODUCT_IBLOCK_ID']] = 0;
805 }
806 unset($catalog);
807
808 $iterator = Catalog\CatalogIblockTable::getList([
809 'select' => ['IBLOCK_ID', 'VAT_ID'],
810 'filter' => ['@IBLOCK_ID' => array_keys($this->storage['IBLOCKS_VAT'])]
811 ]);
812 while ($row = $iterator->fetch())
813 $this->storage['IBLOCKS_VAT'][(int)$row['IBLOCK_ID']] = (int)$row['VAT_ID'];
814 unset($row, $iterator);
815 }
816 }
817 }
818
822 protected function initIblockPropertyFeatures()
823 {
824
825 }
826
832 protected function initElementList()
833 {
834 $this->storage['CURRENCY_LIST'] = array();
835 $this->storage['DEFAULT_MEASURE'] = $this->getDefaultMeasure();
836
837 $this->initQueryFields();
838
839 foreach ($this->iblockProducts as $iblock => $products)
840 {
841 $elementIterator = $this->getElementList($iblock, $products);
842 $iblockElements = $this->getIblockElements($elementIterator);
843
844 if (!empty($iblockElements) && !$this->hasErrors())
845 {
846 $this->modifyDisplayProperties($iblock, $iblockElements);
847 $this->elements = array_merge($this->elements, array_values($iblockElements));
848 $this->iblockProducts[$iblock] = array_keys($iblockElements);
849 }
850
851 unset($elementIterator, $iblockElements, $element);
852 }
853 }
854
861 abstract protected function getIblockElements($elementIterator);
862
868 protected function sortElementList()
869 {
870 if (!empty($this->productIdMap) && is_array($this->productIdMap))
871 {
872 $sortedElements = array();
873
874 foreach (array_keys($this->productIdMap) as $productId)
875 {
876 $parentId = $this->productIdMap[$productId];
877
878 foreach ($this->elements as $element)
879 {
880 if ($element['ID'] == $parentId)
881 {
882 $sortedElements[$productId] = $element;
883 break;
884 }
885 }
886 }
887
888 $this->elements = array_values($sortedElements);
889 }
890 }
891
897 protected function makeElementLinks()
898 {
899 if (!empty($this->elements))
900 {
901 foreach ($this->elements as $index => $element)
902 {
903 $this->elementLinks[$element['ID']] =& $this->elements[$index];
904 }
905 }
906 }
907
913 protected function getProductIds()
914 {
915 return false;
916 }
917
923 protected function getBigDataProductIds()
924 {
925 $shownIds = $this->request->get('shownIds');
926 if (!empty($shownIds) && is_array($shownIds))
927 {
928 $this->arParams['FILTER_IDS'] += $shownIds;
929 }
930
931 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->request->get('count') ?: 20;
932 $this->arParams['FILTER'] ??= [];
933 $this->arParams['FILTER'] = $this->arParams['FILTER'] ?: ['PAYED'];
934 $this->arParams['BY'] ??= '';
935 $this->arParams['BY'] = $this->arParams['BY'] ?: 'AMOUNT';
936 $this->arParams['PERIOD'] ??= 0;
937 $this->arParams['PERIOD'] = (int)$this->arParams['PERIOD'] ?: 30;
938 $this->arParams['DEPTH'] ??= 0;
939 $this->arParams['DEPTH'] = (int)$this->arParams['DEPTH'] ?: 2;
940
941 // general filter
942 $this->filterFields = $this->getFilter();
943 $this->filterFields['IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
945
946 // try cloud
947 $ids = $this->request->get('items') ?: array();
948 if (!empty($ids))
949 {
950 $recommendationId = $this->request->get('rid');
951 $ids = $this->filterByParams($ids, $this->arParams['FILTER_IDS']);
952
953 foreach ($ids as $id)
954 {
955 $this->recommendationIdToProduct[$id] = $recommendationId;
956 }
957 }
958
959 // try bestsellers
960 if (Main\Loader::includeModule('sale') && count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
961 {
962 $ids = $this->getBestSellersRecommendation($ids);
963 }
964
965 // try most viewed
966 if ($this->useCatalog && count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
967 {
968 $ids = $this->getMostViewedRecommendation($ids);
969 }
970
971 // try random
972 if (count($ids) < $this->arParams['PAGE_ELEMENT_COUNT'])
973 {
974 $ids = $this->getRandomRecommendation($ids);
975 }
976
977 // limit
978 return array_slice($ids, 0, $this->arParams['PAGE_ELEMENT_COUNT']);
979 }
980
987 protected function getBestSellersRecommendation($ids)
988 {
989 // increase element count
990 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->arParams['PAGE_ELEMENT_COUNT'] * 10;
991 $bestsellers = $this->getBestSellersProductIds();
992 $this->arParams['PAGE_ELEMENT_COUNT'] = $this->arParams['PAGE_ELEMENT_COUNT'] / 10;
993
994 if (!empty($bestsellers))
995 {
996 $recommendationId = 'bestsellers';
997 $bestsellers = Main\Analytics\Catalog::getProductIdsByOfferIds($bestsellers);
998 $bestsellers = $this->filterByParams($bestsellers, $this->arParams['FILTER_IDS']);
999
1000 foreach ($bestsellers as $id)
1001 {
1002 if (!isset($this->recommendationIdToProduct[$id]))
1003 {
1004 $this->recommendationIdToProduct[$id] = $recommendationId;
1005 }
1006 }
1007
1008 $ids = array_unique(array_merge($ids, $bestsellers));
1009 }
1010
1011 return $ids;
1012 }
1013
1020 protected function getMostViewedRecommendation($ids)
1021 {
1022 $mostViewed = array();
1023 $recommendationId = 'mostviewed';
1024
1025 $result = Catalog\CatalogViewedProductTable::getList(array(
1026 'select' => array(
1027 'ELEMENT_ID',
1028 new Main\Entity\ExpressionField('SUM_HITS', 'SUM(%s)', 'VIEW_COUNT')
1029 ),
1030 'filter' => array(
1031 '=SITE_ID' => $this->getSiteId(),
1032 '>ELEMENT_ID' => 0,
1033 '>DATE_VISIT' => new Main\Type\DateTime(date('Y-m-d H:i:s', strtotime('-30 days')), 'Y-m-d H:i:s')
1034 ),
1035 'order' => array('SUM_HITS' => 'DESC'),
1036 'limit' => $this->arParams['PAGE_ELEMENT_COUNT'] * 10
1037 ));
1038 while ($row = $result->fetch())
1039 {
1040 $mostViewed[] = $row['ELEMENT_ID'];
1041 }
1042 unset($row, $result);
1043
1044 $mostViewed = $this->filterByParams($mostViewed, $this->arParams['FILTER_IDS']);
1045
1046 foreach ($mostViewed as $id)
1047 {
1048 if (!isset($this->recommendationIdToProduct[$id]))
1049 {
1050 $this->recommendationIdToProduct[$id] = $recommendationId;
1051 }
1052 }
1053
1054 return array_unique(array_merge($ids, $mostViewed));
1055 }
1056
1063 protected function getRandomRecommendation($ids)
1064 {
1065 $limit = $this->getRecommendationLimit($ids);
1066
1067 if ($limit <= 0)
1068 {
1069 return $ids;
1070 }
1071
1072 $randomIds = array();
1073 $recommendationId = 'random';
1074 $filter = $this->filterFields;
1075
1076 $filterIds = array_merge($ids, $this->arParams['FILTER_IDS']);
1077 if (!empty($filterIds))
1078 {
1079 $filter['!ID'] = $filterIds;
1080 }
1081
1082 if ($this->arParams['SHOW_FROM_SECTION'] === 'Y' && !empty($this->arParams['SECTION_ID']))
1083 {
1084 $filter['SECTION_ID'] = $this->arParams['SECTION_ID'];
1085 }
1086
1087 $elementIterator = \CIBlockElement::GetList(array('RAND' => 'ASC'), $filter, false, array('nTopCount' => $limit), array('ID'));
1088 while ($element = $elementIterator->Fetch())
1089 {
1090 $randomIds[] = $element['ID'];
1091 }
1092
1093 if (!empty($randomIds))
1094 {
1095 $this->setCacheUsage(false);
1096 }
1097
1098 foreach ($randomIds as $id)
1099 {
1100 if (!isset($this->recommendationIdToProduct[$id]))
1101 {
1102 $this->recommendationIdToProduct[$id] = $recommendationId;
1103 }
1104 }
1105
1106 return array_merge($ids, $randomIds);
1107 }
1108
1117 protected function filterByParams($ids, $filterIds = array(), $useSectionFilter = true)
1118 {
1119 if (empty($ids))
1120 {
1121 return array();
1122 }
1123
1124 $ids = array_values(array_unique($ids));
1125 // remove duplicates of already showed items
1126 if (!empty($filterIds))
1127 {
1128 $ids = array_diff($ids, $filterIds);
1129 }
1130
1131 if (!empty($ids))
1132 {
1133 $filter = $this->filterFields;
1134 $filter['ID'] = $ids;
1135
1136 $correctIds = array();
1137 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
1138 while ($element = $elementIterator->Fetch())
1139 {
1140 $correctIds[] = $element['ID'];
1141 }
1142
1143 if ($useSectionFilter && !empty($correctIds) && $this->arParams['SHOW_FROM_SECTION'] === 'Y')
1144 {
1145 $correctIds = $this->filterIdBySection(
1146 $correctIds,
1147 $this->arParams['IBLOCK_ID'],
1148 $this->arParams['SECTION_ID'],
1149 $this->arParams['PAGE_ELEMENT_COUNT'],
1150 $this->arParams['DEPTH']
1151 );
1152 }
1153
1154 $correctIds = array_flip($correctIds);
1155 // remove invalid items
1156 foreach ($ids as $key => $id)
1157 {
1158 if (!isset($correctIds[$id]))
1159 {
1160 unset($ids[$key]);
1161 }
1162 }
1163
1164 return array_values($ids);
1165 }
1166 else
1167 {
1168 return array();
1169 }
1170 }
1171
1178 protected function getSectionIdByCode($sectionCode = '', int $iblockId = 0)
1179 {
1180 $sectionId = 0;
1181 $sectionCode = (string)$sectionCode;
1182
1183 if ($sectionCode === '')
1184 {
1185 return $sectionId;
1186 }
1187
1188 $sectionFilter = [];
1189 if ($iblockId > 0)
1190 {
1191 $sectionFilter['=IBLOCK_ID'] = $iblockId;
1192 }
1193 elseif (!empty($this->arParams['IBLOCK_ID']))
1194 {
1195 $sectionFilter['@IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
1196 }
1197 if (empty($sectionFilter))
1198 {
1199 return $sectionId;
1200 }
1201
1202 $sectionFilter['=IBLOCK.ACTIVE'] = 'Y';
1203 $sectionFilter['=CODE'] = $sectionCode;
1204
1205 $section = Iblock\SectionTable::getList(array(
1206 'select' => array('ID'),
1207 'filter' => $sectionFilter
1208 ))->fetch();
1209 if (!empty($section))
1210 {
1211 $sectionId = (int)$section['ID'];
1212 }
1213
1214 return $sectionId;
1215 }
1216
1224 protected function getSectionIdByElement($elementId, $elementCode = '', int $iblockId = 0)
1225 {
1226 $sectionId = 0;
1227 $elementId = (int)$elementId;
1228 $elementCode = (string)$elementCode;
1229 $filter = [];
1230
1231 if ($iblockId > 0)
1232 {
1233 $filter['=IBLOCK_ID'] = $iblockId;
1234 }
1235 elseif (!empty($this->arParams['IBLOCK_ID']))
1236 {
1237 $filter['=IBLOCK_ID'] = $this->arParams['IBLOCK_ID'];
1238 }
1239 if (empty($filter))
1240 {
1241 return $sectionId;
1242 }
1243
1244 if ($elementId > 0)
1245 {
1246 $filter['=ID'] = $elementId;
1247 }
1248 elseif ($elementCode !== '')
1249 {
1250 $filter['=CODE'] = $elementCode;
1251 }
1252 else
1253 {
1254 return $sectionId;
1255 }
1256
1257 $itemIterator = Iblock\ElementTable::getList(array(
1258 'select' => array('ID', 'IBLOCK_SECTION_ID'),
1259 'filter' => $filter
1260 ));
1261 if ($item = $itemIterator->fetch())
1262 {
1263 $sectionId = (int)$item['IBLOCK_SECTION_ID'];
1264 }
1265
1266 return $sectionId;
1267 }
1268
1269 protected function filterIdBySection($elementIds, $iblockId, $sectionId, $limit, $depth = 0)
1270 {
1271 $map = array();
1272
1273 Main\Type\Collection::normalizeArrayValuesByInt($elementIds);
1274
1275 if (empty($elementIds))
1276 return $map;
1277
1278 $iblockId = (int)$iblockId;
1279 $sectionId = (int)$sectionId;
1280 $limit = (int)$limit;
1281 $depth = (int)$depth;
1282
1283 if ($iblockId <= 0 ||$depth < 0)
1284 return $map;
1285
1286 $subSections = array();
1287 if ($depth > 0)
1288 {
1289 $parentSectionId = Catalog\Product\Viewed::getParentSection($sectionId, $depth);
1290 if ($parentSectionId !== null)
1291 {
1292 $subSections[$parentSectionId] = $parentSectionId;
1293 }
1294 unset($parentSectionId);
1295 }
1296
1297 if (empty($subSections) && $sectionId <= 0)
1298 {
1299 $getListParams = array(
1300 'select' => array('ID'),
1301 'filter' => array(
1302 '@ID' => $elementIds,
1303 '=IBLOCK_ID' => $iblockId,
1304 '=WF_STATUS_ID' => 1,
1305 '=WF_PARENT_ELEMENT_ID' => null
1306 ),
1307 );
1308 if ($limit > 0)
1309 {
1310 $getListParams['limit'] = $limit;
1311 }
1312
1313 $iterator = Iblock\ElementTable::getList($getListParams);
1314 }
1315 else
1316 {
1317 if (empty($subSections))
1318 {
1319 $subSections[$sectionId] = $sectionId;
1320 }
1321
1322 $sectionQuery = new Main\Entity\Query(Iblock\SectionTable::getEntity());
1323 $sectionQuery->setTableAliasPostfix('_parent');
1324 $sectionQuery->setSelect(array('ID', 'LEFT_MARGIN', 'RIGHT_MARGIN'));
1325 $sectionQuery->setFilter(array('@ID' => $subSections));
1326
1327 $subSectionQuery = new Main\Entity\Query(Iblock\SectionTable::getEntity());
1328 $subSectionQuery->setTableAliasPostfix('_sub');
1329 $subSectionQuery->setSelect(array('ID'));
1330 $subSectionQuery->setFilter(array('=IBLOCK_ID' => $iblockId));
1331 $subSectionQuery->registerRuntimeField(
1332 '',
1333 new Main\Entity\ReferenceField(
1334 'BS',
1335 Main\Entity\Base::getInstanceByQuery($sectionQuery),
1336 array('>=this.LEFT_MARGIN' => 'ref.LEFT_MARGIN', '<=this.RIGHT_MARGIN' => 'ref.RIGHT_MARGIN'),
1337 array('join_type' => 'INNER')
1338 )
1339 );
1340
1341 $sectionElementQuery = new Main\Entity\Query(Iblock\SectionElementTable::getEntity());
1342 $sectionElementQuery->setSelect(array('IBLOCK_ELEMENT_ID'));
1343 $sectionElementQuery->setGroup(array('IBLOCK_ELEMENT_ID'));
1344 $sectionElementQuery->setFilter(array('=ADDITIONAL_PROPERTY_ID' => null));
1345 $sectionElementQuery->registerRuntimeField(
1346 '',
1347 new Main\Entity\ReferenceField(
1348 'BSUB',
1349 Main\Entity\Base::getInstanceByQuery($subSectionQuery),
1350 array('=this.IBLOCK_SECTION_ID' => 'ref.ID'),
1351 array('join_type' => 'INNER')
1352 )
1353 );
1354
1355 $elementQuery = new Main\Entity\Query(Iblock\ElementTable::getEntity());
1356 $elementQuery->setSelect(array('ID'));
1357 $elementQuery->setFilter(array('=IBLOCK_ID' => $iblockId, '=WF_STATUS_ID' => 1, '=WF_PARENT_ELEMENT_ID' => null));
1358 $elementQuery->registerRuntimeField(
1359 '',
1360 new Main\Entity\ReferenceField(
1361 'BSE',
1362 Main\Entity\Base::getInstanceByQuery($sectionElementQuery),
1363 array('=this.ID' => 'ref.IBLOCK_ELEMENT_ID'),
1364 array('join_type' => 'INNER')
1365 )
1366 );
1367 if ($limit > 0)
1368 {
1369 $elementQuery->setLimit($limit);
1370 }
1371
1372 $iterator = $elementQuery->exec();
1373
1374 unset($elementQuery, $sectionElementQuery, $subSectionQuery, $sectionQuery);
1375 }
1376
1377 while ($row = $iterator->fetch())
1378 {
1379 $map[] = $row['ID'];
1380 }
1381 unset($row, $iterator);
1382
1383 return $map;
1384 }
1385
1393 protected function getRecommendationLimit($ids)
1394 {
1395 $limit = 0;
1396 $idsCount = count($ids);
1397 $rowsRange = $this->request->get('rowsRange');
1398
1399 if (!empty($rowsRange))
1400 {
1401 foreach ($rowsRange as $range)
1402 {
1403 $range = (int)$range;
1404
1405 if ($range > $idsCount)
1406 {
1407 $limit = $range - $idsCount;
1408 break;
1409 }
1410 }
1411 }
1412 else
1413 {
1414 $limit = $this->arParams['PAGE_ELEMENT_COUNT'] - $idsCount;
1415 }
1416
1417 return $limit;
1418 }
1419
1420 protected function getBigDataServiceRequestParams($type = '')
1421 {
1422 $params = array(
1423 'uid' => ($_COOKIE['BX_USER_ID'] ?? ''),
1424 'aid' => Main\Analytics\Counter::getAccountId(),
1425 'count' => max($this->arParams['PAGE_ELEMENT_COUNT'] * 2, 30)
1426 );
1427
1428 // random choices
1429 if ($type === 'any_similar')
1430 {
1431 $possible = array('similar_sell', 'similar_view', 'similar');
1432 $type = $possible[array_rand($possible)];
1433 }
1434 elseif ($type === 'any_personal')
1435 {
1436 $possible = array('bestsell', 'personal');
1437 $type = $possible[array_rand($possible)];
1438 }
1439 elseif ($type === 'any')
1440 {
1441 $possible = array('similar_sell', 'similar_view', 'similar', 'bestsell', 'personal');
1442 $type = $possible[array_rand($possible)];
1443 }
1444
1445 // configure
1446 switch ($type)
1447 {
1448 case 'bestsell':
1449 $params['op'] = 'sim_domain_items';
1450 $params['type'] = 'order';
1451 $params['domain'] = Main\Context::getCurrent()->getServer()->getHttpHost();
1452 break;
1453 case 'personal':
1454 $params['op'] = 'recommend';
1455 break;
1456 case 'similar_sell':
1457 $params['op'] = 'simitems';
1458 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1459 $params['type'] = 'order';
1460 break;
1461 case 'similar_view':
1462 $params['op'] = 'simitems';
1463 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1464 $params['type'] = 'view';
1465 break;
1466 case 'similar':
1467 $params['op'] = 'simitems';
1468 $params['eid'] = $this->arParams['RCM_PROD_ID'];
1469 break;
1470 default:
1471 $params['op'] = 'recommend';
1472 }
1473
1474 $iblocks = array();
1475
1476 if (!empty($this->storage['IBLOCK_PARAMS']))
1477 {
1478 $iblocks = array_keys($this->storage['IBLOCK_PARAMS']);
1479 }
1480 else
1481 {
1482 $iblockList = array();
1483 /* catalog */
1484 $iblockIterator = Catalog\CatalogIblockTable::getList(array(
1485 'select' => array('IBLOCK_ID', 'PRODUCT_IBLOCK_ID')
1486 ));
1487 while ($iblock = $iblockIterator->fetch())
1488 {
1489 $iblock['IBLOCK_ID'] = (int)$iblock['IBLOCK_ID'];
1490 $iblock['PRODUCT_IBLOCK_ID'] = (int)$iblock['PRODUCT_IBLOCK_ID'];
1491 $iblockList[$iblock['IBLOCK_ID']] = $iblock['IBLOCK_ID'];
1492
1493 if ($iblock['PRODUCT_IBLOCK_ID'] > 0)
1494 {
1495 $iblockList[$iblock['PRODUCT_IBLOCK_ID']] = $iblock['PRODUCT_IBLOCK_ID'];
1496 }
1497 }
1498
1499 /* iblock */
1500 $iblockIterator = Iblock\IblockSiteTable::getList(array(
1501 'select' => array('IBLOCK_ID'),
1502 'filter' => array('@IBLOCK_ID' => $iblockList, '=SITE_ID' => $this->getSiteId())
1503 ));
1504 while ($iblock = $iblockIterator->fetch())
1505 {
1506 $iblocks[] = $iblock['IBLOCK_ID'];
1507 }
1508 }
1509
1510 $params['ib'] = join('.', $iblocks);
1511
1512 return $params;
1513 }
1514
1520 protected function getBestSellersProductIds()
1521 {
1522 $productIds = array();
1523 $filter = $this->getBestSellersFilter();
1524
1525 if (!empty($filter))
1526 {
1527 $productIterator = \CSaleProduct::GetBestSellerList(
1528 $this->arParams['BY'],
1529 array(),
1530 $filter,
1531 $this->arParams['PAGE_ELEMENT_COUNT']
1532 );
1533 while($product = $productIterator->fetch())
1534 {
1535 $productIds[] = $product['PRODUCT_ID'];
1536 }
1537 }
1538
1539 return $productIds;
1540 }
1541
1542 protected function getBestSellersFilter()
1543 {
1544 $filter = array();
1545
1546 if (!empty($this->arParams['FILTER']))
1547 {
1548 $filter = array('=LID' => $this->getSiteId());
1549 $subFilter = array('LOGIC' => 'OR');
1550
1551 $statuses = array(
1552 'CANCELED' => true,
1553 'ALLOW_DELIVERY' => true,
1554 'PAYED' => true,
1555 'DEDUCTED' => true
1556 );
1557
1558 if ($this->arParams['PERIOD'] > 0)
1559 {
1560 $date = ConvertTimeStamp(AddToTimeStamp(array('DD' => '-'.$this->arParams['PERIOD'])));
1561 if (!empty($date))
1562 {
1563 foreach ($this->arParams['FILTER'] as $field)
1564 {
1565 if (isset($statuses[$field]))
1566 {
1567 $subFilter[] = array(
1568 '>=DATE_'.$field => $date,
1569 '='.$field => 'Y'
1570 );
1571 }
1572 else
1573 {
1574 if (empty($this->storage['ORDER_STATUS']) || in_array($field, $this->storage['ORDER_STATUS']))
1575 {
1576 $subFilter[] = array(
1577 '=STATUS_ID' => $field,
1578 '>=DATE_UPDATE' => $date,
1579 );
1580 }
1581 }
1582 }
1583 unset($field);
1584 }
1585 }
1586 else
1587 {
1588 foreach ($this->arParams['FILTER'] as $field)
1589 {
1590 if (isset($statuses[$field]))
1591 {
1592 $subFilter[] = array(
1593 '='.$field => 'Y'
1594 );
1595 }
1596 else
1597 {
1598 if (empty($this->storage['ORDER_STATUS']) || in_array($field, $this->storage['ORDER_STATUS']))
1599 {
1600 $subFilter[] = array(
1601 '=STATUS_ID' => $field,
1602 );
1603 }
1604 }
1605 }
1606 unset($field);
1607 }
1608
1609 $filter[] = $subFilter;
1610 }
1611
1612 return $filter;
1613 }
1614
1620 protected function getDeferredProductIds()
1621 {
1622 return array();
1623 }
1624
1625 protected function getProductIdMap($productIds)
1626 {
1627 if ($productIds === false)
1628 {
1629 return false;
1630 }
1631
1632 return $this->useCatalog ? static::getProductsMap($productIds) : $productIds;
1633 }
1634
1641 public static function getProductsMap(array $originalIds = array())
1642 {
1643 if (empty($originalIds))
1644 {
1645 return array();
1646 }
1647
1648 $result = array();
1649 $productList = \CCatalogSku::getProductList($originalIds);
1650 if ($productList === false)
1651 {
1652 $productList = array();
1653 }
1654
1655 foreach ($originalIds as $id)
1656 {
1657 $result[$id] = isset($productList[$id]) ? $productList[$id]['ID'] : (int)$id;
1658 }
1659
1660 return $result;
1661 }
1662
1673 protected function getProductsSeparatedByIblock()
1674 {
1675 $iblockItems = array();
1676
1677 if (!empty($this->productIdMap) && is_array($this->productIdMap))
1678 {
1679 $itemsIterator = Iblock\ElementTable::getList(array(
1680 'select' => array('ID', 'IBLOCK_ID'),
1681 'filter' => array('@ID' => $this->productIdMap)
1682 ));
1683 while ($item = $itemsIterator->fetch())
1684 {
1685 $item['ID'] = (int)$item['ID'];
1686 $item['IBLOCK_ID'] = (int)$item['IBLOCK_ID'];
1687
1688 if (!isset($iblockItems[$item['IBLOCK_ID']]))
1689 {
1690 $iblockItems[$item['IBLOCK_ID']] = array();
1691 }
1692
1693 $iblockItems[$item['IBLOCK_ID']][] = $item['ID'];
1694 }
1695 unset($item, $itemsIterator);
1696 }
1697 elseif ($this->productIdMap === false)
1698 {
1699 $iblockItems[$this->arParams['IBLOCK_ID']] = $this->arParams['ELEMENT_ID'] ?? 0;
1700 }
1701
1702 return $iblockItems;
1703 }
1704
1710 protected function getDefaultMeasure()
1711 {
1712 $defaultMeasure = array();
1713
1714 if ($this->useCatalog)
1715 {
1716 $defaultMeasure = \CCatalogMeasure::getDefaultMeasure(true, true);
1717 }
1718
1719 return $defaultMeasure;
1720 }
1721
1729 protected function getElementList($iblockId, $products)
1730 {
1731 $selectFields = $this->getIblockSelectFields($iblockId);
1732
1733 $filterFields = $this->filterFields;
1734 if ($iblockId > 0)
1735 {
1736 $filterFields['IBLOCK_ID'] = $iblockId;
1737 }
1738 if (!empty($products))
1739 {
1740 $filterFields['ID'] = $products;
1741 }
1742
1743 $globalFilter = [];
1744 if (!empty($this->globalFilter))
1745 $globalFilter = $this->convertFilter($this->globalFilter);
1746
1747 $iteratorParams = [
1748 'select' => $selectFields,
1749 'filter' => array_merge($globalFilter, $filterFields),
1750 'order' => $this->sortFields,
1751 'navigation' => $this->navParams
1752 ];
1753 if ($this->isSeparateLoading() && $iblockId > 0)
1754 {
1755 $elementIterator = $this->getSeparateList($iteratorParams);
1756 }
1757 else
1758 {
1759 $elementIterator = $this->getFullIterator($iteratorParams);
1760 }
1761 unset($iteratorParams);
1762
1763 $elementIterator->SetUrlTemplates($this->arParams['DETAIL_URL']);
1764
1765 return $elementIterator;
1766 }
1767
1772 protected function getSeparateList(array $params)
1773 {
1774 $list = [];
1775
1776 $selectFields = ['ID', 'IBLOCK_ID'];
1777 if (!empty($params['order']))
1778 {
1779 $selectFields = array_unique(array_merge(
1781 array_keys($params['order'])
1782 ));
1783 }
1784
1785 $iterator = \CIBlockElement::GetList(
1786 $params['order'],
1787 $params['filter'],
1788 false,
1789 $params['navigation'],
1791 );
1792 while ($row = $iterator->Fetch())
1793 {
1794 $id = (int)$row['ID'];
1795 $list[$id] = [
1796 'ID' => $row['ID'],
1797 'IBLOCK_ID' => $row['IBLOCK_ID'],
1798 ];
1799 }
1800 unset($row);
1801
1802 if (!empty($list))
1803 {
1804 $fullIterator = \CIBlockElement::GetList(
1805 [],
1806 ['IBLOCK_ID' => $params['filter']['IBLOCK_ID'], 'ID' => array_keys($list), 'SITE_ID' => $this->getSiteId()],
1807 false,
1808 false,
1809 $params['select']
1810 );
1811 while ($row = $fullIterator->Fetch())
1812 {
1813 $id = (int)$row['ID'];
1814 $list[$id] = $list[$id] + $row;
1815 }
1816 unset($row, $fullIterator);
1817
1818 $iterator->InitFromArray(array_values($list));
1819 }
1820
1821 return $iterator;
1822 }
1823
1828 protected function getFullIterator(array $params)
1829 {
1830 return \CIBlockElement::GetList(
1831 $params['order'],
1832 $params['filter'],
1833 false,
1834 $params['navigation'],
1835 $params['select']
1836 );
1837 }
1838
1844 protected function initQueryFields()
1845 {
1846 $this->selectFields = $this->getSelect();
1847 $this->filterFields = $this->getFilter();
1848 $this->sortFields = $this->getSort();
1850 }
1851
1857 protected function getSelect()
1858 {
1859 $result = [
1860 'ID', 'IBLOCK_ID', 'CODE', 'XML_ID', 'NAME', 'ACTIVE', 'DATE_ACTIVE_FROM', 'DATE_ACTIVE_TO', 'SORT',
1861 'PREVIEW_TEXT', 'PREVIEW_TEXT_TYPE', 'DETAIL_TEXT', 'DETAIL_TEXT_TYPE', 'DATE_CREATE', 'CREATED_BY', 'TAGS',
1862 'TIMESTAMP_X', 'MODIFIED_BY', 'IBLOCK_SECTION_ID', 'DETAIL_PAGE_URL', 'DETAIL_PICTURE', 'PREVIEW_PICTURE'
1863 ];
1864
1865 $checkPriceProperties = (
1866 !$this->useCatalog
1867 || (
1868 isset($this->arParams['IBLOCK_ID'])
1869 && $this->arParams['IBLOCK_ID'] > 0
1870 && !isset($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
1871 )
1872 );
1873
1874 if ($checkPriceProperties && !empty($this->storage['PRICES']))
1875 {
1876 foreach ($this->storage['PRICES'] as $row)
1877 {
1878 if (!empty($row['SELECT']))
1879 $result[] = $row['SELECT'];
1880 }
1881 }
1882
1883 return $result;
1884 }
1885
1891 protected function getFilter()
1892 {
1893 return array(
1894 'IBLOCK_LID' => $this->getSiteId(),
1895 'ACTIVE_DATE' => 'Y',
1896 'CHECK_PERMISSIONS' => 'Y',
1897 'MIN_PERMISSION' => 'R'
1898 );
1899 }
1900
1906 protected function getSort()
1907 {
1908 return array();
1909 }
1910
1916 protected function prepareElementQueryFields()
1917 {
1918 $result = $this->prepareQueryFields($this->selectFields, $this->filterFields, $this->sortFields);
1919 $this->selectFields = $result['SELECT'];
1920 $this->filterFields = $result['FILTER'];
1921 $this->sortFields = $result['ORDER'];
1922 if (!empty($this->globalFilter))
1923 {
1924 $result = $this->prepareQueryFields([], $this->globalFilter, []);
1925 $this->globalFilter = $result['FILTER'];
1926 }
1927 unset($result);
1928 }
1929
1938 protected function prepareQueryFields(array $select, array $filter, array $order)
1939 {
1940 if ($this->useCatalog)
1941 {
1942 $select = $this->convertSelect($select);
1943 $order = $this->convertOrder($order);
1944 $filter = $this->convertFilter($filter);
1945 $filter = \CProductQueryBuilder::modifyFilterFromOrder(
1946 $filter,
1947 $order,
1948 ['QUANTITY' => $this->arParams['SHOW_PRICE_COUNT']]
1949 );
1950 }
1951
1952 if (!empty($select))
1953 {
1954 $select = array_unique($select);
1955 }
1956
1957 return [
1958 'SELECT' => $select,
1959 'FILTER' => $filter,
1960 'ORDER' => $order
1961 ];
1962 }
1963
1968 protected function initPricesQuery()
1969 {
1971 }
1972
1980 protected function getProductSelect($iblockId, array $selectFields)
1981 {
1982 if (!$this->useCatalog)
1983 return $selectFields;
1984
1985 $additionalFields = $this->getProductFields($iblockId);
1986 $result = $selectFields;
1987
1988 if (!empty($additionalFields))
1989 {
1990 $result = array_merge($result, $additionalFields);
1991 $result = array_unique($result);
1992 }
1993 unset($additionalFields);
1994
1995 return $result;
1996 }
1997
2004 protected function getProductFields($iblockId)
2005 {
2006 if (!$this->isIblockCatalog && !$this->offerIblockExist($iblockId))
2007 return [];
2008
2009 $result = [
2010 'TYPE', 'AVAILABLE', 'BUNDLE',
2011 'QUANTITY', 'QUANTITY_TRACE', 'CAN_BUY_ZERO', 'MEASURE',
2012 'SUBSCRIBE',
2013 'VAT_ID', 'VAT_INCLUDED',
2014 'WEIGHT', 'WIDTH', 'LENGTH', 'HEIGHT',
2015 'PAYMENT_TYPE', 'RECUR_SCHEME_LENGTH', 'RECUR_SCHEME_TYPE',
2016 'TRIAL_PRICE_ID'
2017 ];
2018
2019 if ($this->isEnableCompatible())
2020 {
2021 $result = array_merge(
2022 $result,
2023 [
2024 'QUANTITY_TRACE_RAW', 'CAN_BUY_ZERO_RAW', 'SUBSCRIBE_RAW',
2025 'PURCHASING_PRICE', 'PURCHASING_CURRENCY',
2026 'BARCODE_MULTI',
2027 'WITHOUT_ORDER'
2028 ]
2029 );
2030 }
2031
2032 return $result;
2033 }
2034
2041 protected function convertSelect(array $select)
2042 {
2043 if (!$this->useCatalog)
2044 return $select;
2045 return \CProductQueryBuilder::convertOldSelect($select);
2046 }
2047
2054 protected function convertFilter(array $filter)
2055 {
2056 if (!$this->useCatalog)
2057 return $filter;
2058 return \CProductQueryBuilder::convertOldFilter($filter);
2059 }
2060
2067 protected function convertOrder(array $order)
2068 {
2069 if (!$this->useCatalog)
2070 return $order;
2071 return \CProductQueryBuilder::convertOldOrder($order);
2072 }
2073
2074 protected function getIblockSelectFields($iblockId)
2075 {
2076 if (!$this->useCatalog)
2077 return $this->selectFields;
2078 return $this->getProductSelect($iblockId, $this->selectFields);
2079 }
2080
2088 protected function parseCondition($condition, $params)
2089 {
2090 $result = array();
2091
2092 if (!empty($condition) && is_array($condition))
2093 {
2094 if ($condition['CLASS_ID'] === 'CondGroup')
2095 {
2096 if (!empty($condition['CHILDREN']))
2097 {
2098 foreach ($condition['CHILDREN'] as $child)
2099 {
2100 $childResult = $this->parseCondition($child, $params);
2101
2102 // is group
2103 if ($child['CLASS_ID'] === 'CondGroup')
2104 {
2105 $result[] = $childResult;
2106 }
2107 // same property names not overrides each other
2108 elseif (isset($result[key($childResult)]))
2109 {
2110 $fieldName = key($childResult);
2111
2112 if (!isset($result['LOGIC']))
2113 {
2114 $result = array(
2115 'LOGIC' => $condition['DATA']['All'],
2116 array($fieldName => $result[$fieldName])
2117 );
2118 }
2119
2120 $result[][$fieldName] = $childResult[$fieldName];
2121 }
2122 else
2123 {
2124 $result += $childResult;
2125 }
2126 }
2127
2128 if (!empty($result))
2129 {
2130 $this->parsePropertyCondition($result, $condition, $params);
2131
2132 if (count($result) > 1)
2133 {
2134 $result['LOGIC'] = $condition['DATA']['All'];
2135 }
2136 }
2137 }
2138 }
2139 else
2140 {
2141 $result += $this->parseConditionLevel($condition, $params);
2142 }
2143 }
2144
2145 return $result;
2146 }
2147
2148 protected function parseConditionLevel($condition, $params)
2149 {
2150 $result = array();
2151
2152 if (!empty($condition) && is_array($condition))
2153 {
2154 $name = $this->parseConditionName($condition);
2155 if (!empty($name))
2156 {
2157 $operator = $this->parseConditionOperator($condition);
2158 $value = $this->parseConditionValue($condition, $name);
2159 $result[$operator.$name] = $value;
2160
2161 if ($name === 'SECTION_ID')
2162 {
2163 $result['INCLUDE_SUBSECTIONS'] = isset($params['INCLUDE_SUBSECTIONS']) && $params['INCLUDE_SUBSECTIONS'] === 'N' ? 'N' : 'Y';
2164
2165 if (isset($params['INCLUDE_SUBSECTIONS']) && $params['INCLUDE_SUBSECTIONS'] === 'A')
2166 {
2167 $result['SECTION_GLOBAL_ACTIVE'] = 'Y';
2168 }
2169
2170 $result = array($result);
2171 }
2172 }
2173 }
2174
2175 return $result;
2176 }
2177
2178 protected function parseConditionName(array $condition)
2179 {
2180 $name = '';
2181 $conditionNameMap = array(
2182 'CondIBXmlID' => 'XML_ID',
2183 'CondIBSection' => 'SECTION_ID',
2184 'CondIBDateActiveFrom' => 'DATE_ACTIVE_FROM',
2185 'CondIBDateActiveTo' => 'DATE_ACTIVE_TO',
2186 'CondIBSort' => 'SORT',
2187 'CondIBDateCreate' => 'DATE_CREATE',
2188 'CondIBCreatedBy' => 'CREATED_BY',
2189 'CondIBTimestampX' => 'TIMESTAMP_X',
2190 'CondIBModifiedBy' => 'MODIFIED_BY',
2191 'CondIBTags' => 'TAGS',
2192 'CondCatQuantity' => 'QUANTITY',
2193 'CondCatWeight' => 'WEIGHT'
2194 );
2195
2196 if (isset($conditionNameMap[$condition['CLASS_ID']]))
2197 {
2198 $name = $conditionNameMap[$condition['CLASS_ID']];
2199 }
2200 elseif (mb_strpos($condition['CLASS_ID'], 'CondIBProp') !== false)
2201 {
2202 $name = $condition['CLASS_ID'];
2203 }
2204
2205 return $name;
2206 }
2207
2208 protected function parseConditionOperator($condition)
2209 {
2210 $operator = '';
2211
2212 switch ($condition['DATA']['logic'])
2213 {
2214 case 'Equal':
2215 $operator = '';
2216 break;
2217 case 'Not':
2218 $operator = '!';
2219 break;
2220 case 'Contain':
2221 $operator = '%';
2222 break;
2223 case 'NotCont':
2224 $operator = '!%';
2225 break;
2226 case 'Great':
2227 $operator = '>';
2228 break;
2229 case 'Less':
2230 $operator = '<';
2231 break;
2232 case 'EqGr':
2233 $operator = '>=';
2234 break;
2235 case 'EqLs':
2236 $operator = '<=';
2237 break;
2238 }
2239
2240 return $operator;
2241 }
2242
2243 protected function parseConditionValue($condition, $name)
2244 {
2245 $value = $condition['DATA']['value'];
2246
2247 switch ($name)
2248 {
2249 case 'DATE_ACTIVE_FROM':
2250 case 'DATE_ACTIVE_TO':
2251 case 'DATE_CREATE':
2252 case 'TIMESTAMP_X':
2253 $value = ConvertTimeStamp($value, 'FULL');
2254 break;
2255 }
2256
2257 return $value;
2258 }
2259
2260 protected function parsePropertyCondition(array &$result, array $condition, $params)
2261 {
2262 if (!empty($result))
2263 {
2264 $subFilter = array();
2265
2266 foreach ($result as $name => $value)
2267 {
2268 if (!empty($result[$name]) && is_array($result[$name]))
2269 {
2270 $this->parsePropertyCondition($result[$name], $condition, $params);
2271 }
2272 else
2273 {
2274 if (($ind = mb_strpos($name, 'CondIBProp')) !== false)
2275 {
2276 [$prefix, $iblock, $propertyId] = explode(':', $name);
2277 $operator = $ind > 0? mb_substr($prefix, 0, $ind) : '';
2278
2279 $catalogInfo = \CCatalogSku::GetInfoByIBlock($iblock);
2280 if (!empty($catalogInfo))
2281 {
2282 if (
2283 $catalogInfo['CATALOG_TYPE'] != \CCatalogSku::TYPE_CATALOG
2284 && $catalogInfo['IBLOCK_ID'] == $iblock
2285 )
2286 {
2287 $subFilter[$operator.'PROPERTY_'.$propertyId] = $value;
2288 }
2289 else
2290 {
2291 $result[$operator.'PROPERTY_'.$propertyId] = $value;
2292 }
2293 }
2294
2295 unset($result[$name]);
2296 }
2297 }
2298 }
2299
2300 if (!empty($subFilter) && !empty($catalogInfo))
2301 {
2302 $offerPropFilter = array(
2303 'IBLOCK_ID' => $catalogInfo['IBLOCK_ID'],
2304 'ACTIVE_DATE' => 'Y',
2305 'ACTIVE' => 'Y'
2306 );
2307
2308 if ($params['HIDE_NOT_AVAILABLE_OFFERS'] === 'Y')
2309 {
2310 $offerPropFilter['HIDE_NOT_AVAILABLE'] = 'Y';
2311 }
2312 elseif ($params['HIDE_NOT_AVAILABLE_OFFERS'] === 'L')
2313 {
2314 $offerPropFilter[] = array(
2315 'LOGIC' => 'OR',
2316 'AVAILABLE' => 'Y',
2317 'SUBSCRIBE' => 'Y'
2318 );
2319 }
2320
2321 if (count($subFilter) > 1)
2322 {
2323 $subFilter['LOGIC'] = $condition['DATA']['All'];
2324 $subFilter = array($subFilter);
2325 }
2326
2327 $result['=ID'] = \CIBlockElement::SubQuery(
2328 'PROPERTY_'.$catalogInfo['SKU_PROPERTY_ID'],
2329 $offerPropFilter + $subFilter
2330 );
2331 }
2332 }
2333 }
2334
2341 protected function processElement(array &$element)
2342 {
2343 $this->modifyElementCommonData($element);
2344 $this->modifyElementPrices($element);
2345 $this->setElementPanelButtons($element);
2346 }
2347
2354 protected function modifyElementCommonData(array &$element)
2355 {
2356 $element['ID'] = (int)$element['ID'];
2357 $element['IBLOCK_ID'] = (int)$element['IBLOCK_ID'];
2358
2359 if ($this->arParams['HIDE_DETAIL_URL'])
2360 {
2361 $element['DETAIL_PAGE_URL'] = $element['~DETAIL_PAGE_URL'] = '';
2362 }
2363
2364 if ($this->isEnableCompatible())
2365 {
2366 $element['ACTIVE_FROM'] = ($element['DATE_ACTIVE_FROM'] ?? null);
2367 $element['ACTIVE_TO'] = ($element['DATE_ACTIVE_TO'] ?? null);
2368 }
2369
2370 $ipropValues = new Iblock\InheritedProperty\ElementValues($element['IBLOCK_ID'], $element['ID']);
2371 $element['IPROPERTY_VALUES'] = $ipropValues->getValues();
2372
2373 Iblock\Component\Tools::getFieldImageData(
2374 $element,
2375 array('PREVIEW_PICTURE', 'DETAIL_PICTURE'),
2376 Iblock\Component\Tools::IPROPERTY_ENTITY_ELEMENT,
2377 'IPROPERTY_VALUES'
2378 );
2379
2380 if (isset($element['~TYPE']))
2381 {
2382 $productFields = $this->getProductFields($element['IBLOCK_ID']);
2383 $translateFields = $this->getCompatibleProductFields();
2384
2385 $element['PRODUCT'] = array(
2386 'TYPE' => (int)$element['~TYPE'],
2387 'AVAILABLE' => $element['~AVAILABLE'],
2388 'BUNDLE' => $element['~BUNDLE'],
2389 'QUANTITY' => $element['~QUANTITY'],
2390 'QUANTITY_TRACE' => $element['~QUANTITY_TRACE'],
2391 'CAN_BUY_ZERO' => $element['~CAN_BUY_ZERO'],
2392 'MEASURE' => (int)$element['~MEASURE'],
2393 'SUBSCRIBE' => $element['~SUBSCRIBE'],
2394 'VAT_ID' => (int)$element['~VAT_ID'],
2395 'VAT_RATE' => 0,
2396 'VAT_INCLUDED' => $element['~VAT_INCLUDED'],
2397 'WEIGHT' => (float)$element['~WEIGHT'],
2398 'WIDTH' => (float)$element['~WIDTH'],
2399 'LENGTH' => (float)$element['~LENGTH'],
2400 'HEIGHT' => (float)$element['~HEIGHT'],
2401 'PAYMENT_TYPE' => $element['~PAYMENT_TYPE'],
2402 'RECUR_SCHEME_TYPE' => $element['~RECUR_SCHEME_TYPE'],
2403 'RECUR_SCHEME_LENGTH' => (int)$element['~RECUR_SCHEME_LENGTH'],
2404 'TRIAL_PRICE_ID' => (int)$element['~TRIAL_PRICE_ID']
2405 );
2406
2407 $vatId = 0;
2408 $vatRate = 0;
2409 if ($element['PRODUCT']['VAT_ID'] > 0)
2410 $vatId = $element['PRODUCT']['VAT_ID'];
2411 elseif ($this->storage['IBLOCKS_VAT'][$element['IBLOCK_ID']] > 0)
2412 $vatId = $this->storage['IBLOCKS_VAT'][$element['IBLOCK_ID']];
2413 if ($vatId > 0 && isset($this->storage['VATS'][$vatId]))
2414 $vatRate = $this->storage['VATS'][$vatId];
2415 $element['PRODUCT']['VAT_RATE'] = $vatRate;
2416 unset($vatRate, $vatId);
2417 $element['PRODUCT']['USE_OFFERS'] = $element['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_SKU;
2418
2419 if ($this->isEnableCompatible())
2420 {
2421 foreach ($translateFields as $currentKey => $oldKey)
2422 $element[$oldKey] = $element[$currentKey];
2423 unset($currentKey, $oldKey);
2424 $element['~CATALOG_VAT'] = $element['PRODUCT']['VAT_RATE'];
2425 $element['CATALOG_VAT'] = $element['PRODUCT']['VAT_RATE'];
2426 }
2427 else
2428 {
2429 // temporary (compatibility custom templates)
2430 $element['~CATALOG_TYPE'] = $element['PRODUCT']['TYPE'];
2431 $element['CATALOG_TYPE'] = $element['PRODUCT']['TYPE'];
2432 $element['~CATALOG_QUANTITY'] = $element['PRODUCT']['QUANTITY'];
2433 $element['CATALOG_QUANTITY'] = $element['PRODUCT']['QUANTITY'];
2434 $element['~CATALOG_QUANTITY_TRACE'] = $element['PRODUCT']['QUANTITY_TRACE'];
2435 $element['CATALOG_QUANTITY_TRACE'] = $element['PRODUCT']['QUANTITY_TRACE'];
2436 $element['~CATALOG_CAN_BUY_ZERO'] = $element['PRODUCT']['CAN_BUY_ZERO'];
2437 $element['CATALOG_CAN_BUY_ZERO'] = $element['PRODUCT']['CAN_BUY_ZERO'];
2438 $element['~CATALOG_SUBSCRIBE'] = $element['PRODUCT']['SUBSCRIBE'];
2439 $element['CATALOG_SUBSCRIBE'] = $element['PRODUCT']['SUBSCRIBE'];
2440 }
2441
2442 foreach ($productFields as $field)
2443 unset($element[$field], $element['~'.$field]);
2444 unset($field);
2445 }
2446 else
2447 {
2448 $element['PRODUCT'] = array(
2449 'TYPE' => null,
2450 'AVAILABLE' => null,
2451 'USE_OFFERS' => false
2452 );
2453 }
2454
2455 $element['PROPERTIES'] = array();
2456 $element['DISPLAY_PROPERTIES'] = array();
2457 $element['PRODUCT_PROPERTIES'] = array();
2458 $element['PRODUCT_PROPERTIES_FILL'] = array();
2459 $element['OFFERS'] = array();
2460 $element['OFFER_ID_SELECTED'] = 0;
2461
2462 if (!empty($this->storage['CATALOGS'][$element['IBLOCK_ID']]))
2463 $element['CHECK_QUANTITY'] = $this->isNeedCheckQuantity($element['PRODUCT']);
2464
2465 if ($this->getAction() === 'bigDataLoad')
2466 {
2467 $element['RCM_ID'] = $this->recommendationIdToProduct[$element['ID']];
2468 }
2469 }
2470
2477 protected function setElementPanelButtons(&$element)
2478 {
2479 $buttons = \CIBlock::GetPanelButtons(
2480 $element['IBLOCK_ID'],
2481 $element['ID'],
2482 $element['IBLOCK_SECTION_ID'],
2483 array('SECTION_BUTTONS' => false, 'SESSID' => false, 'CATALOG' => true)
2484 );
2485 $element['EDIT_LINK'] = ($buttons['edit']['edit_element']['ACTION_URL'] ?? null);
2486 $element['DELETE_LINK'] = ($buttons['edit']['delete_element']['ACTION_URL'] ?? null);
2487 }
2488
2496 protected function modifyDisplayProperties($iblock, &$iblockElements)
2497 {
2498 }
2499
2500 protected function getPropertyList($iblock, $propertyCodes)
2501 {
2502 $propertyList = array();
2503 if (empty($propertyCodes))
2504 return $propertyList;
2505
2506 $propertyCodes = array_fill_keys($propertyCodes, true);
2507
2508 $propertyIterator = Iblock\PropertyTable::getList(array(
2509 'select' => array('ID', 'CODE', 'SORT'),
2510 'filter' => array('=IBLOCK_ID' => $iblock, '=ACTIVE' => 'Y'),
2511 'order' => array('SORT' => 'ASC', 'ID' => 'ASC')
2512 ));
2513 while ($property = $propertyIterator->fetch())
2514 {
2515 $code = (string)$property['CODE'];
2516
2517 if ($code == '')
2518 {
2519 $code = $property['ID'];
2520 }
2521
2522 if (!isset($propertyCodes[$code]))
2523 continue;
2524
2525 $propertyList[] = $code;
2526 }
2527
2528 return $propertyList;
2529 }
2530
2536 protected function clearItems()
2537 {
2538 $this->prices = array();
2539 $this->measures = array();
2540 $this->ratios = array();
2541 $this->quantityRanges = array();
2542 $this->oldData = array();
2543 }
2544
2552 protected function loadMeasureRatios(array $itemIds)
2553 {
2554 if (empty($itemIds))
2555 return;
2556 Main\Type\Collection::normalizeArrayValuesByInt($itemIds, true);
2557 if (empty($itemIds))
2558 return;
2559 $emptyRatioIds = array_fill_keys($itemIds, true);
2560
2561 $iterator = Catalog\MeasureRatioTable::getList(array(
2562 'select' => array('ID', 'RATIO', 'IS_DEFAULT', 'PRODUCT_ID'),
2563 'filter' => array('@PRODUCT_ID' => $itemIds),
2564 'order' => array('PRODUCT_ID' => 'ASC')// not add 'RATIO' => 'ASC' - result will be resorted after load prices
2565 ));
2566 while ($row = $iterator->fetch())
2567 {
2568 $ratio = max((float)$row['RATIO'], (int)$row['RATIO']);
2569 if ($ratio > CATALOG_VALUE_EPSILON)
2570 {
2571 $row['RATIO'] = $ratio;
2572 $row['ID'] = (int)$row['ID'];
2573 $id = (int)$row['PRODUCT_ID'];
2574 if (!isset($this->ratios[$id]))
2575 $this->ratios[$id] = array();
2576 $this->ratios[$id][$row['ID']] = $row;
2577 unset($emptyRatioIds[$id]);
2578 unset($id);
2579 }
2580 unset($ratio);
2581 }
2582 unset($row, $iterator);
2583 if (!empty($emptyRatioIds))
2584 {
2585 $emptyRatio = $this->getEmptyRatio();
2586 foreach (array_keys($emptyRatioIds) as $id)
2587 {
2588 $this->ratios[$id] = array(
2589 $emptyRatio['ID'] => $emptyRatio
2590 );
2591 }
2592 unset($id, $emptyRatio);
2593 }
2594 unset($emptyRatioIds);
2595 }
2596
2602 protected function getEmptyRatio()
2603 {
2604 return array(
2605 'ID' => 0,
2606 'RATIO' => 1,
2607 'IS_DEFAULT' => 'Y'
2608 );
2609 }
2610
2617 protected function initItemsMeasure(array &$items)
2618 {
2619 if (empty($items))
2620 return;
2621
2622 foreach (array_keys($items) as $index)
2623 {
2624 if (!isset($items[$index]['PRODUCT']['MEASURE']))
2625 continue;
2626 if ($items[$index]['PRODUCT']['MEASURE'] > 0)
2627 {
2628 $items[$index]['ITEM_MEASURE'] = array(
2629 'ID' => $items[$index]['PRODUCT']['MEASURE'],
2630 'TITLE' => '',
2631 '~TITLE' => ''
2632 );
2633 }
2634 else
2635 {
2636 $items[$index]['ITEM_MEASURE'] = array(
2637 'ID' => null,
2638 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
2639 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS']
2640 );
2641 }
2642 }
2643 unset($index);
2644 }
2645
2652 protected function getMeasureIds(array $items)
2653 {
2654 $result = array();
2655
2656 if (!empty($items))
2657 {
2658 foreach (array_keys($items) as $itemId)
2659 {
2660 if (!isset($items[$itemId]['ITEM_MEASURE']))
2661 continue;
2662 $measureId = (int)$items[$itemId]['ITEM_MEASURE']['ID'];
2663 if ($measureId > 0)
2664 $result[$measureId] = $measureId;
2665 unset($measureId);
2666 }
2667 unset($itemId);
2668 }
2669
2670 return $result;
2671 }
2672
2679 protected function loadMeasures(array $measureIds)
2680 {
2681 if (empty($measureIds))
2682 return;
2683 Main\Type\Collection::normalizeArrayValuesByInt($measureIds, true);
2684 if (empty($measureIds))
2685 return;
2686
2687 $measureIterator = \CCatalogMeasure::getList(
2688 array(),
2689 array('@ID' => $measureIds),
2690 false,
2691 false,
2692 array('ID', 'SYMBOL_RUS')
2693 );
2694 while ($measure = $measureIterator->GetNext())
2695 {
2696 $measure['ID'] = (int)$measure['ID'];
2697 $measure['TITLE'] = $measure['SYMBOL_RUS'];
2698 $measure['~TITLE'] = $measure['~SYMBOL_RUS'];
2699 unset($measure['SYMBOL_RUS'], $measure['~SYMBOL_RUS']);
2700 $this->measures[$measure['ID']] = $measure;
2701 }
2702 unset($measure, $measureIterator);
2703 }
2704
2711 protected function loadPrices(array $itemIds)
2712 {
2713 if (empty($itemIds))
2714 return;
2715 Main\Type\Collection::normalizeArrayValuesByInt($itemIds, true);
2716 if (empty($itemIds))
2717 return;
2718
2719 $this->loadMeasureRatios($itemIds);
2720
2721 if (empty($this->storage['PRICES_ALLOW']))
2722 return;
2723
2724 $enableCompatible = $this->isEnableCompatible();
2725
2726 $quantityList = array_fill_keys($itemIds, array());
2727
2728 $select = array(
2729 'ID', 'PRODUCT_ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY',
2730 'QUANTITY_FROM', 'QUANTITY_TO', 'PRICE_SCALE'
2731 );
2732 if ($enableCompatible)
2733 $select[] = 'EXTRA_ID';
2734
2735 $pagedItemIds = array_chunk($itemIds, 500);
2736 foreach ($pagedItemIds as $pageIds)
2737 {
2738 if (empty($pageIds))
2739 continue;
2740
2741 $iterator = Catalog\PriceTable::getList(array(
2742 'select' => $select,
2743 'filter' => array('@PRODUCT_ID' => $pageIds, '@CATALOG_GROUP_ID' => $this->storage['PRICES_ALLOW']),
2744 'order' => array('PRODUCT_ID' => 'ASC', 'CATALOG_GROUP_ID' => 'ASC')
2745 ));
2746 while ($row = $iterator->fetch())
2747 {
2748 $id = (int)$row['PRODUCT_ID'];
2749 unset($row['PRODUCT_ID']);
2750 if (!isset($this->prices[$id]))
2751 {
2752 $this->prices[$id] = array(
2753 'RATIO' => array(),
2754 'QUANTITY' => array(),
2755 'SIMPLE' => array()
2756 );
2757 }
2758
2759 if ($row['QUANTITY_FROM'] !== null || $row['QUANTITY_TO'] !== null)
2760 {
2761 $hash = $this->getQuantityRangeHash($row);
2762 if (!isset($quantityList[$id][$hash]))
2763 {
2764 $quantityList[$id][$hash] = array(
2765 'HASH' => $hash,
2766 'QUANTITY_FROM' => $row['QUANTITY_FROM'],
2767 'QUANTITY_TO' => $row['QUANTITY_TO'],
2768 'SORT_FROM' => (int)$row['QUANTITY_FROM'],
2769 'SORT_TO' => ($row['QUANTITY_TO'] === null ? INF : (int)$row['QUANTITY_TO'])
2770 );
2771 }
2772 if (!isset($this->prices[$id]['QUANTITY'][$hash]))
2773 {
2774 $this->prices[$id]['QUANTITY'][$hash] = array();
2775 }
2776 $this->prices[$id]['QUANTITY'][$hash][$row['CATALOG_GROUP_ID']] = $row;
2777 unset($hash);
2778 }
2779 elseif (!isset($row['MEASURE_RATIO_ID']))
2780 {
2781 $this->prices[$id]['SIMPLE'][$row['CATALOG_GROUP_ID']] = $row;
2782 }
2783 $this->storage['CURRENCY_LIST'][$row['CURRENCY']] = $row['CURRENCY'];
2784
2785 unset($id);
2786 }
2787 unset($row, $iterator);
2788 }
2789 unset($pageIds, $pagedItemIds);
2790
2791 foreach ($itemIds as $id)
2792 {
2793 if (isset($this->prices[$id]))
2794 {
2795 foreach ($this->prices[$id] as $key => $data)
2796 {
2797 if (empty($data))
2798 unset($this->prices[$id][$key]);
2799 }
2800 unset($key, $data);
2801
2802 if (count($this->prices[$id]) !== 1)
2803 {
2804 unset($this->prices[$id]);
2805 }
2806 else
2807 {
2808 if (!empty($this->prices[$id]['QUANTITY']))
2809 {
2810 $productQuantity = $quantityList[$id];
2811 Main\Type\Collection::sortByColumn(
2812 $productQuantity,
2813 array('SORT_FROM' => SORT_ASC, 'SORT_TO' => SORT_ASC),
2814 '', null, true
2815 );
2816 $this->quantityRanges[$id] = $productQuantity;
2817 unset($productQuantity);
2818
2819 if (count($this->ratios[$id]) > 1)
2820 $this->compactItemRatios($id);
2821 }
2822 if (!empty($this->prices[$id]['SIMPLE']))
2823 {
2824 $range = $this->getFullQuantityRange();
2825 $this->quantityRanges[$id] = array(
2826 $range['HASH'] => $range
2827 );
2828 unset($range);
2829 if (count($this->ratios[$id]) > 1)
2830 $this->compactItemRatios($id);
2831 }
2832 }
2833 }
2834 }
2835 unset($id);
2836
2837 unset($quantityList);
2838
2839 unset($enableCompatible);
2840 }
2841
2842 protected function calculateItemPrices(array &$items)
2843 {
2844 if (empty($items))
2845 return;
2846
2847 $enableCompatible = $this->isEnableCompatible();
2848
2849 if ($enableCompatible)
2850 $this->initCompatibleFields($items);
2851
2852 foreach (array_keys($items) as $index)
2853 {
2854 $id = $items[$index]['ID'];
2855 if (!isset($this->calculatePrices[$id]))
2856 continue;
2857 if (empty($this->prices[$id]))
2858 continue;
2859 $productPrices = $this->prices[$id];
2860 $result = array(
2861 'ITEM_PRICE_MODE' => null,
2862 'ITEM_PRICES' => array(),
2863 'ITEM_PRICES_CAN_BUY' => false
2864 );
2865 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2866 $result['ITEM_ALL_PRICES'] = array();
2867 $priceBlockIndex = 0;
2868 if (!empty($productPrices['QUANTITY']))
2869 {
2870 $result['ITEM_PRICE_MODE'] = Catalog\ProductTable::PRICE_MODE_QUANTITY;
2871 $ratio = current($this->ratios[$id]);
2872 foreach ($this->quantityRanges[$id] as $range)
2873 {
2874 $priceBlock = $this->calculatePriceBlock(
2875 $items[$index],
2876 $productPrices['QUANTITY'][$range['HASH']],
2877 $ratio['RATIO'],
2878 $this->arParams['USE_PRICE_COUNT'] || $this->checkQuantityRange($range)
2879 );
2880 if (!empty($priceBlock))
2881 {
2882 $minimalPrice = ($this->arParams['FILL_ITEM_ALL_PRICES']
2883 ? $priceBlock['MINIMAL_PRICE']
2884 : $priceBlock
2885 );
2886 if ($minimalPrice['QUANTITY_FROM'] === null)
2887 {
2888 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'];
2889 }
2890 else
2891 {
2892 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'] * ((int)($minimalPrice['QUANTITY_FROM']/$ratio['RATIO']));
2893 if ($minimalPrice['MIN_QUANTITY'] < $minimalPrice['QUANTITY_FROM'])
2894 $minimalPrice['MIN_QUANTITY'] += $ratio['RATIO'];
2895 }
2896 $result['ITEM_PRICES'][$priceBlockIndex] = $minimalPrice;
2897 if (isset($this->storage['PRICES_CAN_BUY'][$minimalPrice['PRICE_TYPE_ID']]))
2898 $result['ITEM_PRICES_CAN_BUY'] = true;
2899 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2900 {
2901 $priceBlock['ALL_PRICES']['MIN_QUANTITY'] = $minimalPrice['MIN_QUANTITY'];
2902 $result['ITEM_ALL_PRICES'][$priceBlockIndex] = $priceBlock['ALL_PRICES'];
2903 }
2904 unset($minimalPrice);
2905 $priceBlockIndex++;
2906 }
2907 unset($priceBlock);
2908 }
2909 unset($range);
2910 unset($ratio);
2911 }
2912 if (!empty($productPrices['SIMPLE']))
2913 {
2914 $result['ITEM_PRICE_MODE'] = Catalog\ProductTable::PRICE_MODE_SIMPLE;
2915 $ratio = current($this->ratios[$id]);
2916 $priceBlock = $this->calculatePriceBlock(
2917 $items[$index],
2918 $productPrices['SIMPLE'],
2919 $ratio['RATIO'],
2920 true
2921 );
2922 if (!empty($priceBlock))
2923 {
2924 $minimalPrice = ($this->arParams['FILL_ITEM_ALL_PRICES']
2925 ? $priceBlock['MINIMAL_PRICE']
2926 : $priceBlock
2927 );
2928 $minimalPrice['MIN_QUANTITY'] = $ratio['RATIO'];
2929 $result['ITEM_PRICES'][$priceBlockIndex] = $minimalPrice;
2930 if (isset($this->storage['PRICES_CAN_BUY'][$minimalPrice['PRICE_TYPE_ID']]))
2931 $result['ITEM_PRICES_CAN_BUY'] = true;
2932 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
2933 {
2934 $priceBlock['ALL_PRICES']['MIN_QUANTITY'] = $minimalPrice['MIN_QUANTITY'];
2935 $result['ITEM_ALL_PRICES'][$priceBlockIndex] = $priceBlock['ALL_PRICES'];
2936 }
2937 unset($minimalPrice);
2938 $priceBlockIndex++;
2939 }
2940 unset($priceBlock);
2941 unset($ratio);
2942 }
2943 $this->prices[$id] = $result;
2944
2945 if (isset($items[$index]['ACTIVE']) && $items[$index]['ACTIVE'] === 'N')
2946 {
2947 $items[$index]['CAN_BUY'] = false;
2948 }
2949 else
2950 {
2951 $items[$index]['CAN_BUY'] = $result['ITEM_PRICES_CAN_BUY'] && $items[$index]['PRODUCT']['AVAILABLE'] === 'Y';
2952 }
2953
2954 unset($priceBlockIndex, $result);
2955 unset($productPrices);
2956
2957 if ($enableCompatible)
2958 $this->resortOldPrices($id);
2959 }
2960 unset($index);
2961 }
2962
2963 protected function transferItems(array &$items)
2964 {
2965 if (empty($items))
2966 return;
2967
2968 $enableCompatible = $this->isEnableCompatible();
2969 $urls = $this->storage['URLS'];
2970
2971 foreach (array_keys($items) as $index)
2972 {
2973 $itemId = $items[$index]['ID'];
2974 // measure
2975 if (!empty($items[$index]['ITEM_MEASURE']))
2976 {
2977 $id = (int)$items[$index]['ITEM_MEASURE']['ID'];
2978 if (isset($this->measures[$id]))
2979 {
2980 $items[$index]['ITEM_MEASURE']['TITLE'] = $this->measures[$id]['TITLE'];
2981 $items[$index]['ITEM_MEASURE']['~TITLE'] = $this->measures[$id]['~TITLE'];
2982 }
2983 unset($id);
2984 }
2985 // prices
2986 $items[$index]['ITEM_MEASURE_RATIOS'] = $this->ratios[$itemId] ?? [];
2987 $items[$index]['ITEM_MEASURE_RATIO_SELECTED'] = $this->searchItemSelectedRatioId($itemId);
2988 $items[$index]['ITEM_QUANTITY_RANGES'] = $this->quantityRanges[$itemId] ?? [];
2989 $items[$index]['ITEM_QUANTITY_RANGE_SELECTED'] = $this->searchItemSelectedQuantityRangeHash($itemId);
2990 if (!empty($this->prices[$itemId]))
2991 {
2992 $items[$index] = array_merge($items[$index], $this->prices[$itemId]);
2993 if (!empty($items[$index]['ITEM_PRICES']))
2994 {
2995 switch ($items[$index]['ITEM_PRICE_MODE'])
2996 {
2997 case Catalog\ProductTable::PRICE_MODE_SIMPLE:
2998 $items[$index]['ITEM_PRICE_SELECTED'] = 0;
2999 break;
3000 case Catalog\ProductTable::PRICE_MODE_QUANTITY:
3001 foreach (array_keys($items[$index]['ITEM_PRICES']) as $priceIndex)
3002 {
3003 if ($items[$index]['ITEM_PRICES'][$priceIndex]['QUANTITY_HASH'] == $items[$index]['ITEM_QUANTITY_RANGE_SELECTED'])
3004 {
3005 $items[$index]['ITEM_PRICE_SELECTED'] = $priceIndex;
3006 break;
3007 }
3008 }
3009 break;
3010 case Catalog\ProductTable::PRICE_MODE_RATIO:
3011 foreach (array_keys($items[$index]['ITEM_PRICES']) as $priceIndex)
3012 {
3013 if ($items[$index]['ITEM_PRICES'][$priceIndex]['MEASURE_RATIO_ID'] == $items[$index]['ITEM_MEASURE_RATIO_SELECTED'])
3014 {
3015 $items[$index]['ITEM_PRICE_SELECTED'] = $priceIndex;
3016 break;
3017 }
3018 }
3019 break;
3020 }
3021 }
3022 }
3023
3024 // compatibility
3025 if ($enableCompatible)
3026 {
3027 // old links to buy, add to basket, etc
3028 $id = $items[$index]['ID'];
3029 $items[$index]['~BUY_URL'] = str_replace('#ID#', $id, $urls['~BUY_URL_TEMPLATE']);
3030 $items[$index]['BUY_URL'] = str_replace('#ID#', $id, $urls['BUY_URL_TEMPLATE']);
3031 $items[$index]['~ADD_URL'] = str_replace('#ID#', $id, $urls['~ADD_URL_TEMPLATE']);
3032 $items[$index]['ADD_URL'] = str_replace('#ID#', $id, $urls['ADD_URL_TEMPLATE']);
3033 $items[$index]['~SUBSCRIBE_URL'] = str_replace('#ID#', $id, $urls['~SUBSCRIBE_URL_TEMPLATE']);
3034 $items[$index]['SUBSCRIBE_URL'] = str_replace('#ID#', $id, $urls['SUBSCRIBE_URL_TEMPLATE']);
3035 if ($this->arParams['DISPLAY_COMPARE'])
3036 {
3037 $items[$index]['~COMPARE_URL'] = str_replace('#ID#', $id, $urls['~COMPARE_URL_TEMPLATE']);
3038 $items[$index]['COMPARE_URL'] = str_replace('#ID#', $id, $urls['COMPARE_URL_TEMPLATE']);
3039 $items[$index]['~COMPARE_DELETE_URL'] = str_replace('#ID#', $id, $urls['~COMPARE_DELETE_URL_TEMPLATE']);
3040 $items[$index]['COMPARE_DELETE_URL'] = str_replace('#ID#', $id, $urls['COMPARE_DELETE_URL_TEMPLATE']);
3041 }
3042 unset($id);
3043
3044 // old measure
3045 $items[$index]['CATALOG_MEASURE_NAME'] = $items[$index]['ITEM_MEASURE']['TITLE'];
3046 $items[$index]['~CATALOG_MEASURE_NAME'] = $items[$index]['ITEM_MEASURE']['~TITLE'];
3047
3048 // old measure ratio
3049 $items[$index]['CATALOG_MEASURE_RATIO'] = $items[$index]['ITEM_MEASURE_RATIOS'][$items[$index]['ITEM_MEASURE_RATIO_SELECTED']]['RATIO'] ?? 1;
3050
3051 // old fields
3052 if (!empty($this->oldData[$itemId]))
3053 $items[$index] = array_merge($this->oldData[$itemId], $items[$index]);
3054 }
3055 unset($itemId);
3056 }
3057 unset($index);
3058 unset($urls, $enableCompatible);
3059 }
3060
3070 protected function calculatePriceBlock(array $product, array $priceBlock, $ratio, $defaultBlock = false)
3071 {
3072 if (empty($product) || empty($priceBlock))
3073 return null;
3074
3075 $enableCompatible = $defaultBlock && $this->isEnableCompatible();
3076
3077 if ($enableCompatible && !$this->arParams['USE_PRICE_COUNT'])
3078 $this->fillCompatibleRawPriceFields($product['ID'], $priceBlock);
3079
3080 $userGroups = $this->getUserGroups();
3081
3082 $baseCurrency = Currency\CurrencyManager::getBaseCurrency();
3084 $minimalPrice = null;
3086 $minimalBuyerPrice = null;
3087 $fullPrices = array();
3088
3089 $currencyConvert = $this->arParams['CONVERT_CURRENCY'] === 'Y';
3090 $resultCurrency = ($currencyConvert ? $this->storage['CONVERT_CURRENCY']['CURRENCY_ID'] : null);
3091
3092 $vatRate = (float)$product['PRODUCT']['VAT_RATE'];
3093 $percentVat = $vatRate * 0.01;
3094 $percentPriceWithVat = 1 + $percentVat;
3095 $vatInclude = $product['PRODUCT']['VAT_INCLUDED'] === 'Y';
3096
3097 $oldPrices = array();
3098 $oldMinPrice = false;
3099 $oldMatrix = false;
3100 if ($enableCompatible && $this->arParams['USE_PRICE_COUNT'])
3101 {
3102 $oldMatrix = $this->getCompatibleFieldValue($product['ID'], 'PRICE_MATRIX');
3103 if (empty($oldMatrix))
3104 {
3105 $oldMatrix = $this->getEmptyPriceMatrix();
3106 $oldMatrix['AVAILABLE'] = $product['PRODUCT']['AVAILABLE'];
3107 }
3108 }
3109
3110 foreach ($priceBlock as $rawPrice)
3111 {
3112 $priceType = (int)$rawPrice['CATALOG_GROUP_ID'];
3113 $price = (float)$rawPrice['PRICE'];
3114 if (!$vatInclude)
3115 $price *= $percentPriceWithVat;
3116 $currency = $rawPrice['CURRENCY'];
3117
3118 $changeCurrency = $currencyConvert && $currency !== $resultCurrency;
3119 if ($changeCurrency)
3120 {
3121 $price = \CCurrencyRates::ConvertCurrency($price, $currency, $resultCurrency);
3122 $currency = $resultCurrency;
3123 }
3124
3125 $discounts = array();
3126 if (\CIBlockPriceTools::isEnabledCalculationDiscounts())
3127 {
3128 \CCatalogDiscountSave::Disable();
3129 $discounts = \CCatalogDiscount::GetDiscount(
3130 $product['ID'],
3131 $product['IBLOCK_ID'],
3132 array($priceType),
3133 $userGroups,
3134 'N',
3135 $this->getSiteId(),
3136 array()
3137 );
3138 \CCatalogDiscountSave::Enable();
3139 }
3140 $discountPrice = \CCatalogProduct::CountPriceWithDiscount(
3141 $price,
3142 $currency,
3143 $discounts
3144 );
3145 if ($discountPrice !== false)
3146 {
3147 $priceWithVat = array(
3148 'UNROUND_BASE_PRICE' => $price,
3149 'UNROUND_PRICE' => $discountPrice,
3150 'BASE_PRICE' => Catalog\Product\Price::roundPrice(
3151 $priceType,
3152 $price,
3153 $currency
3154 ),
3155 'PRICE' => Catalog\Product\Price::roundPrice(
3156 $priceType,
3157 $discountPrice,
3158 $currency
3159 )
3160 );
3161
3162 $price /= $percentPriceWithVat;
3163 $discountPrice /= $percentPriceWithVat;
3164
3165 $priceWithoutVat = array(
3166 'UNROUND_BASE_PRICE' => $price,
3167 'UNROUND_PRICE' => $discountPrice,
3168 'BASE_PRICE' => Catalog\Product\Price::roundPrice(
3169 $priceType,
3170 $price,
3171 $currency
3172 ),
3173 'PRICE' => Catalog\Product\Price::roundPrice(
3174 $priceType,
3175 $discountPrice,
3176 $currency
3177 )
3178 );
3179
3180 if ($this->arParams['PRICE_VAT_INCLUDE'])
3181 $priceRow = $priceWithVat;
3182 else
3183 $priceRow = $priceWithoutVat;
3184 $priceRow['ID'] = $rawPrice['ID'];
3185 $priceRow['PRICE_TYPE_ID'] = $rawPrice['CATALOG_GROUP_ID'];
3186 $priceRow['CURRENCY'] = $currency;
3187
3188 if (
3189 empty($discounts)
3190 || ($priceRow['BASE_PRICE'] <= $priceRow['PRICE'])
3191 )
3192 {
3193 $priceRow['BASE_PRICE'] = $priceRow['PRICE'];
3194 $priceRow['DISCOUNT'] = 0;
3195 $priceRow['PERCENT'] = 0;
3196 }
3197 else
3198 {
3199 $priceRow['DISCOUNT'] = $priceRow['BASE_PRICE'] - $priceRow['PRICE'];
3200 $priceRow['PERCENT'] = roundEx(100*$priceRow['DISCOUNT']/$priceRow['BASE_PRICE'], 0);
3201 }
3202 if (isset($this->arParams['PRICE_VAT_SHOW_VALUE']) && $this->arParams['PRICE_VAT_SHOW_VALUE'])
3203 $priceRow['VAT'] = ($vatRate > 0 ? $priceWithVat['PRICE'] - $priceWithoutVat['PRICE'] : 0);
3204
3205 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
3206 $fullPrices[$priceType] = $priceRow;
3207
3208 $priceRow['QUANTITY_FROM'] = $rawPrice['QUANTITY_FROM'];
3209 $priceRow['QUANTITY_TO'] = $rawPrice['QUANTITY_TO'];
3210 $priceRow['QUANTITY_HASH'] = $this->getQuantityRangeHash($rawPrice);
3211 $priceRow['MEASURE_RATIO_ID'] = $rawPrice['MEASURE_RATIO_ID'] ?? null;
3212 $priceRow['PRICE_SCALE'] = \CCurrencyRates::ConvertCurrency(
3213 $priceRow['PRICE'],
3214 $priceRow['CURRENCY'],
3215 $baseCurrency
3216 );
3217 $priceRow['BASE_PRICE_SCALE'] = $rawPrice['PRICE_SCALE'];
3218
3219 if (
3220 $minimalPrice === null
3221 || $minimalPrice['PRICE_SCALE'] > $priceRow['PRICE_SCALE']
3222 )
3223 {
3224 $minimalPrice = $priceRow;
3225 }
3226 elseif (
3227 $minimalPrice['PRICE_SCALE'] == $priceRow['PRICE_SCALE']
3228 && $minimalPrice['BASE_PRICE_SCALE'] > $priceRow['BASE_PRICE_SCALE']
3229 )
3230 {
3231 $minimalPrice = $priceRow;
3232 }
3233 if (isset($this->storage['PRICES_CAN_BUY'][$priceRow['PRICE_TYPE_ID']]))
3234 {
3235 if (
3236 $minimalBuyerPrice === null
3237 || $minimalBuyerPrice['PRICE_SCALE'] > $priceRow['PRICE_SCALE']
3238 )
3239 {
3240 $minimalBuyerPrice = $priceRow;
3241 }
3242 elseif (
3243 $minimalBuyerPrice['PRICE_SCALE'] == $priceRow['PRICE_SCALE']
3244 && $minimalBuyerPrice['BASE_PRICE_SCALE'] > $priceRow['BASE_PRICE_SCALE']
3245 )
3246 {
3247 $minimalBuyerPrice = $priceRow;
3248 }
3249 }
3250
3251 if ($enableCompatible)
3252 {
3253 if ($this->arParams['USE_PRICE_COUNT'])
3254 {
3255 $rowIndex = $this->getQuantityRangeHash($rawPrice);
3256 $oldMatrix['ROWS'][$rowIndex] = array(
3257 'QUANTITY_FROM' => (float)$rawPrice['QUANTITY_FROM'],
3258 'QUANTITY_TO' => (float)$rawPrice['QUANTITY_TO']
3259 );
3260 if (!isset($oldMatrix['MATRIX'][$priceType]))
3261 {
3262 $oldMatrix['MATRIX'][$priceType] = array();
3263 $oldMatrix['COLS'][$priceType] = $this->storage['PRICE_TYPES'][$priceType];
3264 }
3265 $oldMatrix['MATRIX'][$priceType][$rowIndex] = array(
3266 'ID' => $priceRow['ID'],
3267 'PRICE' => $priceRow['BASE_PRICE'],
3268 'DISCOUNT_PRICE' => $priceRow['PRICE'],
3269 'UNROUND_DISCOUNT_PRICE' => $priceRow['UNROUND_PRICE'],
3270 'CURRENCY' => $priceRow['CURRENCY'],
3271 'VAT_RATE' => $percentVat
3272 );
3273 if ($changeCurrency)
3274 {
3275 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_CURRENCY'] = $rawPrice['CURRENCY'];
3276 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_PRICE'] = \CCurrencyRates::ConvertCurrency(
3277 $priceRow['BASE_PRICE'],
3278 $priceRow['CURRENCY'],
3279 $rawPrice['CURRENCY']
3280 );
3281 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_DISCOUNT_PRICE'] = \CCurrencyRates::ConvertCurrency(
3282 $priceRow['PRICE'],
3283 $priceRow['CURRENCY'],
3284 $rawPrice['CURRENCY']
3285 );
3286 $oldMatrix['MATRIX'][$priceType][$rowIndex]['ORIG_VAT_RATE'] = $percentVat; // crazy key, but above all the compatibility
3287 }
3288 }
3289 else
3290 {
3291 $priceCode = $this->storage['PRICES_MAP'][$priceType];
3292 $oldPriceRow = array(
3293 'PRICE_ID' => $priceRow['PRICE_TYPE_ID'],
3294 'ID' => $priceRow['ID'],
3295 'CAN_ACCESS' => ($this->storage['PRICES'][$priceCode]['CAN_VIEW'] ? 'Y' : 'N'),
3296 'CAN_BUY' => ($this->storage['PRICES'][$priceCode]['CAN_BUY'] ? 'Y' : 'N'),
3297 'MIN_PRICE' => 'N',
3298 'CURRENCY' => $priceRow['CURRENCY'],
3299 'VALUE_VAT' => $priceWithVat['UNROUND_BASE_PRICE'],
3300 'VALUE_NOVAT' => $priceWithoutVat['UNROUND_BASE_PRICE'],
3301 'DISCOUNT_VALUE_VAT' => $priceWithVat['UNROUND_PRICE'],
3302 'DISCOUNT_VALUE_NOVAT' => $priceWithoutVat['UNROUND_PRICE'],
3303 'ROUND_VALUE_VAT' => $priceWithVat['PRICE'],
3304 'ROUND_VALUE_NOVAT' => $priceWithoutVat['PRICE'],
3305 'VALUE' => $priceRow['BASE_PRICE'],
3306 'UNROUND_DISCOUNT_VALUE' => $priceRow['UNROUND_PRICE'],
3307 'DISCOUNT_VALUE' => $priceRow['PRICE'],
3308 'DISCOUNT_DIFF' => $priceRow['DISCOUNT'],
3309 'DISCOUNT_DIFF_PERCENT' => $priceRow['PERCENT']
3310 );
3311 $oldPriceRow['VATRATE_VALUE'] = $oldPriceRow['VALUE_VAT'] - $oldPriceRow['VALUE_NOVAT'];
3312 $oldPriceRow['DISCOUNT_VATRATE_VALUE'] = $oldPriceRow['DISCOUNT_VALUE_VAT'] - $oldPriceRow['DISCOUNT_VALUE_NOVAT'];
3313 $oldPriceRow['ROUND_VATRATE_VALUE'] = $oldPriceRow['ROUND_VALUE_VAT'] - $oldPriceRow['ROUND_VALUE_NOVAT'];
3314 if ($changeCurrency)
3315 $oldPriceRow['ORIG_CURRENCY'] = $rawPrice['CURRENCY'];
3316 $oldPrices[$priceCode] = $oldPriceRow;
3317 unset($oldPriceRow);
3318 }
3319 }
3320 }
3321 unset($discounts);
3322 unset($priceType);
3323 }
3324 unset($price);
3325
3326 $minimalPriceId = null;
3327 if (is_array($minimalBuyerPrice))
3328 $minimalPrice = $minimalBuyerPrice;
3329 if (is_array($minimalPrice))
3330 {
3331 unset($minimalPrice['PRICE_SCALE']);
3332 unset($minimalPrice['BASE_PRICE_SCALE']);
3333 $minimalPriceId = $minimalPrice['PRICE_TYPE_ID'];
3334 $prepareFields = array(
3335 'BASE_PRICE', 'PRICE', 'DISCOUNT'
3336 );
3337 if (isset($this->arParams['PRICE_VAT_SHOW_VALUE']) && $this->arParams['PRICE_VAT_SHOW_VALUE'])
3338 $prepareFields[] = 'VAT';
3339
3340 foreach ($prepareFields as $fieldName)
3341 {
3342 $minimalPrice['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3343 $minimalPrice[$fieldName],
3344 $minimalPrice['CURRENCY'],
3345 true
3346 );
3347 $minimalPrice['RATIO_'.$fieldName] = $minimalPrice[$fieldName]*$ratio;
3348 $minimalPrice['PRINT_RATIO_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3349 $minimalPrice['RATIO_'.$fieldName],
3350 $minimalPrice['CURRENCY'],
3351 true
3352 );
3353 }
3354 unset($fieldName);
3355
3356 if ($this->arParams['FILL_ITEM_ALL_PRICES'])
3357 {
3358 foreach (array_keys($fullPrices) as $priceType)
3359 {
3360 foreach ($prepareFields as $fieldName)
3361 {
3362 $fullPrices[$priceType]['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3363 $fullPrices[$priceType][$fieldName],
3364 $fullPrices[$priceType]['CURRENCY'],
3365 true
3366 );
3367 $fullPrices[$priceType]['RATIO_'.$fieldName] = $fullPrices[$priceType][$fieldName]*$ratio;
3368 $fullPrices[$priceType]['PRINT_RATIO_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3369 $minimalPrice['RATIO_'.$fieldName],
3370 $minimalPrice['CURRENCY'],
3371 true
3372 );
3373 }
3374 unset($fieldName);
3375 }
3376 unset($priceType);
3377 }
3378
3379 unset($prepareFields);
3380 }
3381
3382 if ($enableCompatible)
3383 {
3384 if ($this->arParams['USE_PRICE_COUNT'])
3385 {
3386 $oldMatrix['CAN_BUY'] = array_values($this->storage['PRICES_CAN_BUY']);
3387 $this->oldData[$product['ID']]['PRICE_MATRIX'] = $oldMatrix;
3388 }
3389 else
3390 {
3391 $convertFields = array(
3392 'VALUE_NOVAT', 'VALUE_VAT', 'VATRATE_VALUE',
3393 'DISCOUNT_VALUE_NOVAT', 'DISCOUNT_VALUE_VAT', 'DISCOUNT_VATRATE_VALUE'
3394 );
3395
3396 $prepareFields = array(
3397 'VALUE_NOVAT', 'VALUE_VAT', 'VATRATE_VALUE',
3398 'DISCOUNT_VALUE_NOVAT', 'DISCOUNT_VALUE_VAT', 'DISCOUNT_VATRATE_VALUE',
3399 'VALUE', 'DISCOUNT_VALUE', 'DISCOUNT_DIFF'
3400 );
3401
3402 if (!empty($oldPrices))
3403 {
3404 foreach (array_keys($oldPrices) as $index)
3405 {
3406 foreach ($prepareFields as $fieldName)
3407 $oldPrices[$index]['PRINT_'.$fieldName] = \CCurrencyLang::CurrencyFormat(
3408 $oldPrices[$index][$fieldName],
3409 $oldPrices[$index]['CURRENCY'],
3410 true
3411 );
3412 unset($fieldName);
3413 if (isset($oldPrices[$index]['ORIG_CURRENCY']))
3414 {
3415 foreach ($convertFields as $fieldName)
3416 $oldPrices[$index]['ORIG_' . $fieldName] = \CCurrencyRates::ConvertCurrency(
3417 $oldPrices[$index][$fieldName],
3418 $oldPrices[$index]['CURRENCY'],
3419 $oldPrices[$index]['ORIG_CURRENCY']
3420 );
3421 unset($fieldName);
3422 }
3423 if ($oldPrices[$index]['PRICE_ID'] === $minimalPriceId)
3424 {
3425 $oldPrices[$index]['MIN_PRICE'] = 'Y';
3426 $oldMinPrice = $oldPrices[$index];
3427 }
3428 }
3429 unset($index);
3430 }
3431 unset($prepareFields);
3432
3433 $this->oldData[$product['ID']]['PRICES'] = $oldPrices;
3434 $this->oldData[$product['ID']]['MIN_PRICE'] = $oldMinPrice;
3435 }
3436 }
3437 unset($oldMatrix, $oldMinPrice, $oldPrices);
3438
3439 if (!$this->arParams['FILL_ITEM_ALL_PRICES'])
3440 return $minimalPrice;
3441
3442 return array(
3443 'MINIMAL_PRICE' => $minimalPrice,
3444 'ALL_PRICES' => array(
3445 'QUANTITY_FROM' => $minimalPrice['QUANTITY_FROM'],
3446 'QUANTITY_TO' => $minimalPrice['QUANTITY_TO'],
3447 'QUANTITY_HASH' => $minimalPrice['QUANTITY_HASH'],
3448 'MEASURE_RATIO_ID' => $minimalPrice['MEASURE_RATIO_ID'],
3449 'PRICES' => $fullPrices
3450 )
3451 );
3452 }
3453
3454 protected function searchItemSelectedRatioId($id)
3455 {
3456 if (!isset($this->ratios[$id]))
3457 return null;
3458
3459 $minimal = null;
3460 $minimalRatio = null;
3461 $result = null;
3462 foreach ($this->ratios[$id] as $ratio)
3463 {
3464 if ($minimalRatio === null || $minimalRatio > $ratio['RATIO'])
3465 {
3466 $minimalRatio = $ratio['RATIO'];
3467 $minimal = $ratio['ID'];
3468 }
3469 if ($ratio['IS_DEFAULT'] === 'Y')
3470 {
3471 $result = $ratio['ID'];
3472 break;
3473 }
3474 }
3475 unset($ratio);
3476 return ($result === null ? $minimal : $result);
3477 }
3478
3479 protected function compactItemRatios($id)
3480 {
3481 $ratioId = $this->searchItemSelectedRatioId($id);
3482 if ($ratioId === null)
3483 return;
3484 $this->ratios[$id] = array(
3485 $ratioId => $this->ratios[$id][$ratioId]
3486 );
3487 }
3488
3489 protected function getQuantityRangeHash(array $range)
3490 {
3491 return ($range['QUANTITY_FROM'] === null ? 'ZERO' : $range['QUANTITY_FROM']).
3492 '-'.($range['QUANTITY_TO'] === null ? 'INF' : $range['QUANTITY_TO']);
3493 }
3494
3495 protected function getFullQuantityRange()
3496 {
3497 return array(
3498 'HASH' => $this->getQuantityRangeHash(array('QUANTITY_FROM' => null, 'QUANTITY_TO' => null)),
3499 'QUANTITY_FROM' => null,
3500 'QUANTITY_TO' => null,
3501 'SORT_FROM' => 0,
3502 'SORT_TO' => INF
3503 );
3504 }
3505
3507 {
3508 if (empty($this->quantityRanges[$id]))
3509 return null;
3510 foreach ($this->quantityRanges[$id] as $range)
3511 {
3512 if ($this->checkQuantityRange($range))
3513 return $range['HASH'];
3514 }
3515 reset($this->quantityRanges[$id]);
3516 $firsrRange = current($this->quantityRanges[$id]);
3517 return $firsrRange['HASH'];
3518 }
3519
3525 protected function initUrlTemplates()
3526 {
3527 $actionVar = $this->arParams['ACTION_VARIABLE'];
3528 $productIdVar = $this->arParams['PRODUCT_ID_VARIABLE'];
3529 $compareActionVar = $this->arParams['ACTION_COMPARE_VARIABLE'];
3530
3531 $clearParams = Main\HttpRequest::getSystemParameters();
3532 $clearParams[] = $actionVar;
3533 $clearParams[] = $productIdVar;
3534 $clearParams[] = $compareActionVar;
3535 $clearParams[] = '';
3536
3537 if (!empty($this->arParams['CUSTOM_CURRENT_PAGE']))
3538 {
3539 $pageUrl = $this->arParams['CUSTOM_CURRENT_PAGE'];
3540 }
3541 else
3542 {
3543 if ($this->request->isAjaxRequest())
3544 {
3545 $pageUrl = $this->arParams['CURRENT_BASE_PAGE'];
3546 }
3547 else
3548 {
3549 $pageUrl = Main\Application::getInstance()->getContext()->getRequest()->getDecodedUri();
3550
3551 }
3552 }
3553 $currentUri = new Main\Web\Uri($pageUrl);
3554
3555 if ($this->arParams['USE_COMPARE_LIST'] == 'N' && $this->arParams['COMPARE_PATH'] != '')
3556 {
3557 $compareUri = new Main\Web\Uri($this->arParams['COMPARE_PATH']);
3558 }
3559 else
3560 {
3561 $compareUri = $currentUri;
3562 }
3563
3564 $currentUri->deleteParams($clearParams);
3565 $compareUri->deleteParams($clearParams);
3566
3567 $urls = [];
3568 $urls['BUY_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_BUY, $productIdVar => '#ID#'])->getUri();
3569 $urls['ADD_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_ADD_TO_BASKET, $productIdVar => '#ID#'])->getUri();
3570 $urls['SUBSCRIBE_URL_TEMPLATE'] = $currentUri->addParams([$actionVar => self::ACTION_SUBSCRIBE, $productIdVar => '#ID#'])->getUri();
3571
3572 $urls['COMPARE_URL_TEMPLATE'] = $compareUri->addParams([$compareActionVar => self::ACTION_ADD_TO_COMPARE, $productIdVar => '#ID#'])->getUri();
3573 $urls['COMPARE_DELETE_URL_TEMPLATE'] = $compareUri->addParams([$compareActionVar => self::ACTION_DELETE_FROM_COMPARE, $productIdVar => '#ID#'])->getUri();
3574
3575 unset($compareUri, $currentUri, $clearParams);
3576
3577 foreach (array_keys($urls) as $index)
3578 {
3579 $value = str_replace('%23ID%23', '#ID#', $urls[$index]); // format compatibility
3580 $urls['~'.$index] = $value;
3581 $urls[$index] = Main\Text\HtmlFilter::encode($value, ENT_QUOTES);
3582 }
3583 unset($index);
3584
3585 $this->storage['URLS'] = $urls;
3586 }
3587
3594 protected function modifyElementPrices(&$element)
3595 {
3596 $enableCompatible = $this->isEnableCompatible();
3597 $id = $element['ID'];
3598 $iblockId = $element['IBLOCK_ID'];
3599 $catalog = !empty($this->storage['CATALOGS'][$element['IBLOCK_ID']])
3600 ? $this->storage['CATALOGS'][$element['IBLOCK_ID']]
3601 : array();
3602
3603 $element['ITEM_PRICE_MODE'] = null;
3604 $element['ITEM_PRICES'] = array();
3605 $element['ITEM_QUANTITY_RANGES'] = array();
3606 $element['ITEM_MEASURE_RATIOS'] = array();
3607 $element['ITEM_MEASURE'] = array();
3608 $element['ITEM_MEASURE_RATIO_SELECTED'] = null;
3609 $element['ITEM_QUANTITY_RANGE_SELECTED'] = null;
3610 $element['ITEM_PRICE_SELECTED'] = null;
3611
3612 if (!empty($catalog))
3613 {
3614 if (!isset($this->productWithOffers[$iblockId]))
3615 $this->productWithOffers[$iblockId] = array();
3616 if ($element['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_SKU)
3617 {
3618 $this->productWithOffers[$iblockId][$id] = $id;
3619 if ($this->storage['SHOW_CATALOG_WITH_OFFERS'] && $enableCompatible)
3620 {
3621 $this->productWithPrices[$id] = $id;
3622 $this->calculatePrices[$id] = $id;
3623 }
3624 }
3625
3626 if (in_array(
3627 $element['PRODUCT']['TYPE'],
3628 array(
3630 Catalog\ProductTable::TYPE_SET,
3633 )
3634 ))
3635 {
3636 $this->productWithPrices[$id] = $id;
3637 $this->calculatePrices[$id] = $id;
3638 }
3639
3640 if (isset($this->productWithPrices[$id]))
3641 {
3642 if ($element['PRODUCT']['MEASURE'] > 0)
3643 {
3644 $element['ITEM_MEASURE'] = array(
3645 'ID' => $element['PRODUCT']['MEASURE'],
3646 'TITLE' => '',
3647 '~TITLE' => ''
3648 );
3649 }
3650 else
3651 {
3652 $element['ITEM_MEASURE'] = array(
3653 'ID' => null,
3654 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
3655 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS']
3656 );
3657 }
3658 if ($enableCompatible)
3659 {
3660 $element['CATALOG_MEASURE'] = $element['ITEM_MEASURE']['ID'];
3661 $element['CATALOG_MEASURE_NAME'] = $element['ITEM_MEASURE']['TITLE'];
3662 $element['~CATALOG_MEASURE_NAME'] = $element['ITEM_MEASURE']['~TITLE'];
3663 }
3664 }
3665 }
3666 else
3667 {
3668 $element['PRICES'] = \CIBlockPriceTools::GetItemPrices(
3669 $element['IBLOCK_ID'],
3670 $this->storage['PRICES'],
3671 $element,
3672 $this->arParams['PRICE_VAT_INCLUDE'],
3673 $this->storage['CONVERT_CURRENCY']
3674 );
3675 if (!empty($element['PRICES']))
3676 {
3677 $element['MIN_PRICE'] = \CIBlockPriceTools::getMinPriceFromList($element['PRICES']);
3678 }
3679
3680 $element['CAN_BUY'] = !empty($element['PRICES']);
3681 }
3682 }
3683
3689 protected function processProducts()
3690 {
3691 $this->initItemsMeasure($this->elements);
3692 $this->loadMeasures($this->getMeasureIds($this->elements));
3693
3694 $this->loadPrices($this->productWithPrices);
3695 $this->calculateItemPrices($this->elements);
3696
3697 $this->transferItems($this->elements);
3698 }
3699
3706 protected function processOffers()
3707 {
3708 if ($this->useCatalog && !empty($this->iblockProducts))
3709 {
3710 $offers = array();
3711
3712 $paramStack = array();
3713 $enableCompatible = $this->isEnableCompatible();
3714 if ($enableCompatible)
3715 {
3716 $paramStack['USE_PRICE_COUNT'] = $this->arParams['USE_PRICE_COUNT'];
3717 $paramStack['SHOW_PRICE_COUNT'] = $this->arParams['SHOW_PRICE_COUNT'];
3718 $this->arParams['USE_PRICE_COUNT'] = false;
3719 $this->arParams['SHOW_PRICE_COUNT'] = 1;
3720 }
3721
3722 foreach (array_keys($this->iblockProducts) as $iblock)
3723 {
3724 if (!empty($this->productWithOffers[$iblock]))
3725 {
3726 $iblockOffers = $this->getIblockOffers($iblock);
3727 if (!empty($iblockOffers))
3728 {
3729 $offersId = array_keys($iblockOffers);
3730 $this->initItemsMeasure($iblockOffers);
3731 $this->loadMeasures($this->getMeasureIds($iblockOffers));
3732
3733 $this->loadPrices($offersId);
3734 $this->calculateItemPrices($iblockOffers);
3735
3736 $this->transferItems($iblockOffers);
3737
3738 $this->modifyOffers($iblockOffers);
3739 $this->chooseOffer($iblockOffers, $iblock);
3740
3741 $offers = array_merge($offers, $iblockOffers);
3742 }
3743 unset($iblockOffers);
3744 }
3745 }
3746 if ($enableCompatible)
3747 {
3748 $this->arParams['USE_PRICE_COUNT'] = $paramStack['USE_PRICE_COUNT'];
3749 $this->arParams['SHOW_PRICE_COUNT'] = $paramStack['SHOW_PRICE_COUNT'];
3750 }
3751 unset($enableCompatible, $paramStack);
3752 }
3753 }
3754
3761 protected function getIblockOffers($iblockId)
3762 {
3763 $offers = array();
3764 $iblockParams = $this->storage['IBLOCK_PARAMS'][$iblockId];
3765
3766 $enableCompatible = $this->isEnableCompatible();
3767
3768 if (
3769 $this->useCatalog
3770 && $this->offerIblockExist($iblockId)
3771 && !empty($this->productWithOffers[$iblockId])
3772 )
3773 {
3774 $catalog = $this->storage['CATALOGS'][$iblockId];
3775
3776 $productProperty = 'PROPERTY_'.$catalog['SKU_PROPERTY_ID'];
3777 $productPropertyValue = $productProperty.'_VALUE';
3778
3779 $offersFilter = $this->getOffersFilter($catalog['IBLOCK_ID']);
3780 $offersFilter[$productProperty] = $this->productWithOffers[$iblockId];
3781
3782 $offersSelect = array(
3783 'ID' => 1,
3784 'IBLOCK_ID' => 1,
3785 'CODE' => 1,
3786 $productProperty => 1,
3787 'PREVIEW_PICTURE' => 1,
3788 'DETAIL_PICTURE' => 1,
3789 );
3790
3791 if ($this->arParams['SHOW_SKU_DESCRIPTION'] === 'Y')
3792 {
3793 $offersSelect['PREVIEW_TEXT'] = 1;
3794 $offersSelect['DETAIL_TEXT'] = 1;
3795 $offersSelect['PREVIEW_TEXT_TYPE'] = 1;
3796 $offersSelect['DETAIL_TEXT_TYPE'] = 1;
3797 }
3798
3799 if (!empty($iblockParams['OFFERS_FIELD_CODE']))
3800 {
3801 foreach ($iblockParams['OFFERS_FIELD_CODE'] as $code)
3802 $offersSelect[$code] = 1;
3803 unset($code);
3804 }
3805
3806 $offersSelect = $this->getProductSelect($iblockId, array_keys($offersSelect));
3807
3808 $getListParams = $this->prepareQueryFields($offersSelect, $offersFilter, $this->getOffersSort());
3809 $offersSelect = $getListParams['SELECT'];
3810 $offersFilter = $getListParams['FILTER'];
3811 $offersOrder = $getListParams['ORDER'];
3812 unset($getListParams);
3813
3814 $checkFields = array();
3815 foreach (array_keys($offersOrder) as $code)
3816 {
3817 $code = mb_strtoupper($code);
3818 if ($code == 'ID' || $code == 'AVAILABLE')
3819 continue;
3820 $checkFields[] = $code;
3821 }
3822 unset($code);
3823
3824 $productFields = $this->getProductFields($iblockId);
3825 $translateFields = $this->getCompatibleProductFields();
3826
3827 $offersId = array();
3828 $offersCount = array();
3829 $iterator = \CIBlockElement::GetList(
3830 $offersOrder,
3831 $offersFilter,
3832 false,
3833 false,
3834 $offersSelect
3835 );
3836 while($row = $iterator->GetNext())
3837 {
3838 $row['ID'] = (int)$row['ID'];
3839 $row['IBLOCK_ID'] = (int)$row['IBLOCK_ID'];
3840 $productId = (int)$row[$productPropertyValue];
3841
3842 if ($productId <= 0)
3843 continue;
3844
3845 if ($enableCompatible && $this->arParams['OFFERS_LIMIT'] > 0)
3846 {
3847 $offersCount[$productId]++;
3848 if($offersCount[$productId] > $this->arParams['OFFERS_LIMIT'])
3849 continue;
3850 }
3851
3852 $row['SORT_HASH'] = 'ID';
3853 if (!empty($checkFields))
3854 {
3855 $checkValues = '';
3856 foreach ($checkFields as $code)
3857 $checkValues .= ($row[$code] ?? '').'|';
3858 unset($code);
3859 if ($checkValues != '')
3860 $row['SORT_HASH'] = md5($checkValues);
3861 unset($checkValues);
3862 }
3863 $row['LINK_ELEMENT_ID'] = $productId;
3864 $row['PROPERTIES'] = array();
3865 $row['DISPLAY_PROPERTIES'] = array();
3866
3867 $row['PRODUCT'] = array(
3868 'TYPE' => (int)$row['~TYPE'],
3869 'AVAILABLE' => $row['~AVAILABLE'],
3870 'BUNDLE' => $row['~BUNDLE'],
3871 'QUANTITY' => $row['~QUANTITY'],
3872 'QUANTITY_TRACE' => $row['~QUANTITY_TRACE'],
3873 'CAN_BUY_ZERO' => $row['~CAN_BUY_ZERO'],
3874 'MEASURE' => (int)$row['~MEASURE'],
3875 'SUBSCRIBE' => $row['~SUBSCRIBE'],
3876 'VAT_ID' => (int)$row['~VAT_ID'],
3877 'VAT_RATE' => 0,
3878 'VAT_INCLUDED' => $row['~VAT_INCLUDED'],
3879 'WEIGHT' => (float)$row['~WEIGHT'],
3880 'WIDTH' => (float)$row['~WIDTH'],
3881 'LENGTH' => (float)$row['~LENGTH'],
3882 'HEIGHT' => (float)$row['~HEIGHT'],
3883 'PAYMENT_TYPE' => $row['~PAYMENT_TYPE'],
3884 'RECUR_SCHEME_TYPE' => $row['~RECUR_SCHEME_TYPE'],
3885 'RECUR_SCHEME_LENGTH' => (int)$row['~RECUR_SCHEME_LENGTH'],
3886 'TRIAL_PRICE_ID' => (int)$row['~TRIAL_PRICE_ID']
3887 );
3888
3889 $vatId = 0;
3890 $vatRate = 0;
3891 if ($row['PRODUCT']['VAT_ID'] > 0)
3892 $vatId = $row['PRODUCT']['VAT_ID'];
3893 elseif ($this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']] > 0)
3894 $vatId = $this->storage['IBLOCKS_VAT'][$catalog['IBLOCK_ID']];
3895 if ($vatId > 0 && isset($this->storage['VATS'][$vatId]))
3896 $vatRate = $this->storage['VATS'][$vatId];
3897 $row['PRODUCT']['VAT_RATE'] = $vatRate;
3898 unset($vatRate, $vatId);
3899
3900 if ($enableCompatible)
3901 {
3902 foreach ($translateFields as $currentKey => $oldKey)
3903 $row[$oldKey] = $row[$currentKey];
3904 unset($currentKey, $oldKey);
3905 $row['~CATALOG_VAT'] = $row['PRODUCT']['VAT_RATE'];
3906 $row['CATALOG_VAT'] = $row['PRODUCT']['VAT_RATE'];
3907 }
3908 else
3909 {
3910 // temporary (compatibility custom templates)
3911 $row['~CATALOG_TYPE'] = $row['PRODUCT']['TYPE'];
3912 $row['CATALOG_TYPE'] = $row['PRODUCT']['TYPE'];
3913 $row['~CATALOG_QUANTITY'] = $row['PRODUCT']['QUANTITY'];
3914 $row['CATALOG_QUANTITY'] = $row['PRODUCT']['QUANTITY'];
3915 $row['~CATALOG_QUANTITY_TRACE'] = $row['PRODUCT']['QUANTITY_TRACE'];
3916 $row['CATALOG_QUANTITY_TRACE'] = $row['PRODUCT']['QUANTITY_TRACE'];
3917 $row['~CATALOG_CAN_BUY_ZERO'] = $row['PRODUCT']['CAN_BUY_ZERO'];
3918 $row['CATALOG_CAN_BUY_ZERO'] = $row['PRODUCT']['CAN_BUY_ZERO'];
3919 $row['~CATALOG_SUBSCRIBE'] = $row['PRODUCT']['SUBSCRIBE'];
3920 $row['CATALOG_SUBSCRIBE'] = $row['PRODUCT']['SUBSCRIBE'];
3921 }
3922
3923 foreach ($productFields as $field)
3924 unset($row[$field], $row['~'.$field]);
3925 unset($field);
3926
3927 if ($row['PRODUCT']['TYPE'] == Catalog\ProductTable::TYPE_OFFER)
3928 $this->calculatePrices[$row['ID']] = $row['ID'];
3929
3930 $row['ITEM_PRICE_MODE'] = null;
3931 $row['ITEM_PRICES'] = array();
3932 $row['ITEM_QUANTITY_RANGES'] = array();
3933 $row['ITEM_MEASURE_RATIOS'] = array();
3934 $row['ITEM_MEASURE'] = array();
3935 $row['ITEM_MEASURE_RATIO_SELECTED'] = null;
3936 $row['ITEM_QUANTITY_RANGE_SELECTED'] = null;
3937 $row['ITEM_PRICE_SELECTED'] = null;
3938 $row['CHECK_QUANTITY'] = $this->isNeedCheckQuantity($row['PRODUCT']);
3939
3940 if ($row['PRODUCT']['MEASURE'] > 0)
3941 {
3942 $row['ITEM_MEASURE'] = array(
3943 'ID' => $row['PRODUCT']['MEASURE'],
3944 'TITLE' => '',
3945 '~TITLE' => ''
3946 );
3947 }
3948 else
3949 {
3950 $row['ITEM_MEASURE'] = array(
3951 'ID' => null,
3952 'TITLE' => $this->storage['DEFAULT_MEASURE']['SYMBOL_RUS'],
3953 '~TITLE' => $this->storage['DEFAULT_MEASURE']['~SYMBOL_RUS']
3954 );
3955 }
3956 if ($enableCompatible)
3957 {
3958 $row['CATALOG_MEASURE'] = $row['ITEM_MEASURE']['ID'];
3959 $row['CATALOG_MEASURE_NAME'] = $row['ITEM_MEASURE']['TITLE'];
3960 $row['~CATALOG_MEASURE_NAME'] = $row['ITEM_MEASURE']['~TITLE'];
3961 }
3962
3963 $row['PROPERTIES'] = array();
3964 $row['DISPLAY_PROPERTIES'] = array();
3965
3966 Iblock\Component\Tools::getFieldImageData(
3967 $row,
3968 array('PREVIEW_PICTURE', 'DETAIL_PICTURE'),
3969 Iblock\Component\Tools::IPROPERTY_ENTITY_ELEMENT,
3970 ''
3971 );
3972
3973 $offersId[$row['ID']] = $row['ID'];
3974 $offers[$row['ID']] = $row;
3975 }
3976 unset($row, $iterator);
3977
3978 if (!empty($offersId))
3979 {
3980 $loadPropertyCodes = ($iblockParams['OFFERS_PROPERTY_CODE'] ?? []);
3981 if (Iblock\Model\PropertyFeature::isEnabledFeatures())
3982 {
3983 $loadPropertyCodes = array_merge($loadPropertyCodes, $iblockParams['OFFERS_TREE_PROPS'] ?? []);
3984 }
3985
3986 $propertyList = $this->getPropertyList($catalog['IBLOCK_ID'], $loadPropertyCodes);
3987 unset($loadPropertyCodes);
3988
3989 if (!empty($propertyList) || $this->useDiscountCache)
3990 {
3991 \CIBlockElement::GetPropertyValuesArray($offers, $catalog['IBLOCK_ID'], $offersFilter);
3992 foreach ($offers as &$row)
3993 {
3994 if ($this->useDiscountCache)
3995 {
3996 if ($this->storage['USE_SALE_DISCOUNTS'])
3997 Catalog\Discount\DiscountManager::setProductPropertiesCache($row['ID'], $row["PROPERTIES"]);
3998 else
3999 \CCatalogDiscount::SetProductPropertiesCache($row['ID'], $row["PROPERTIES"]);
4000 }
4001
4002 if (!empty($propertyList))
4003 {
4004 foreach ($propertyList as $pid)
4005 {
4006 if (!isset($row["PROPERTIES"][$pid]))
4007 continue;
4008 $prop = &$row["PROPERTIES"][$pid];
4009 $boolArr = is_array($prop["VALUE"]);
4010 if (
4011 ($boolArr && !empty($prop["VALUE"])) ||
4012 (!$boolArr && (string)$prop["VALUE"] !== '')
4013 )
4014 {
4015 $row["DISPLAY_PROPERTIES"][$pid] = \CIBlockFormatProperties::GetDisplayValue($row, $prop);
4016 }
4017 unset($boolArr, $prop);
4018 }
4019 unset($pid);
4020 }
4021 }
4022 unset($row);
4023 }
4024 if (!empty($propertyList))
4025 {
4026 \CIBlockFormatProperties::clearCache();
4027 }
4028
4029 if ($this->useDiscountCache)
4030 {
4031 if ($this->storage['USE_SALE_DISCOUNTS'])
4032 {
4033 Catalog\Discount\DiscountManager::preloadPriceData($offersId, $this->storage['PRICES_ALLOW']);
4034 Catalog\Discount\DiscountManager::preloadProductDataToExtendOrder($offersId, $this->getUserGroups());
4035 }
4036 else
4037 {
4038 \CCatalogDiscount::SetProductSectionsCache($offersId);
4039 \CCatalogDiscount::SetDiscountProductCache($offersId, array('IBLOCK_ID' => $catalog['IBLOCK_ID'], 'GET_BY_ID' => 'Y'));
4040 }
4041 }
4042 }
4043 unset($offersId);
4044 }
4045
4046 return $offers;
4047 }
4048
4049 protected function getOffersFilter($iblockId)
4050 {
4051 $offersFilter = array(
4052 'IBLOCK_ID' => $iblockId,
4053 'ACTIVE' => 'Y',
4054 'ACTIVE_DATE' => 'Y',
4055 'CHECK_PERMISSIONS' => 'N'
4056 );
4057
4058 if ($this->arParams['HIDE_NOT_AVAILABLE_OFFERS'] === 'Y')
4059 {
4060 $offersFilter['AVAILABLE'] = 'Y';
4061 }
4062 elseif ($this->arParams['HIDE_NOT_AVAILABLE_OFFERS'] === 'L')
4063 {
4064 $offersFilter['CUSTOM_FILTER'] = array(
4065 'LOGIC' => 'OR',
4066 'AVAILABLE' => 'Y',
4067 'SUBSCRIBE' => 'Y'
4068 );
4069 }
4070
4071 if (!$this->arParams['USE_PRICE_COUNT'])
4072 {
4073 $offersFilter['SHOW_PRICE_COUNT'] = $this->arParams['SHOW_PRICE_COUNT'];
4074 }
4075
4076 return $offersFilter;
4077 }
4078
4084 protected function getOffersSort()
4085 {
4086 $offersOrder = array(
4087 mb_strtoupper($this->arParams['OFFERS_SORT_FIELD']) => $this->arParams['OFFERS_SORT_ORDER'],
4088 mb_strtoupper($this->arParams['OFFERS_SORT_FIELD2']) => $this->arParams['OFFERS_SORT_ORDER2']
4089 );
4090 if (!isset($offersOrder['ID']))
4091 $offersOrder['ID'] = 'DESC';
4092
4093 return $offersOrder;
4094 }
4095
4096 protected function modifyOffers($offers)
4097 {
4098 //$urls = $this->storage['URLS'];
4099
4100 foreach ($offers as &$offer)
4101 {
4102 $elementId = $offer['LINK_ELEMENT_ID'];
4103
4104 if (!isset($this->elementLinks[$elementId]))
4105 continue;
4106
4107 $offer['CAN_BUY'] = $this->elementLinks[$elementId]['ACTIVE'] === 'Y' && $offer['CAN_BUY'];
4108
4109 $this->elementLinks[$elementId]['OFFERS'][] = $offer;
4110
4111 unset($elementId, $offer);
4112 }
4113 }
4114
4115 abstract protected function chooseOffer($offers, $iblockId);
4116
4117 protected function initResultCache()
4118 {
4119 if (
4120 $this->arParams['CONVERT_CURRENCY'] === 'Y'
4121 && !empty($this->storage['CURRENCY_LIST'])
4122 && defined('BX_COMP_MANAGED_CACHE')
4123 )
4124 {
4125 $this->storage['CURRENCY_LIST'][$this->storage['CONVERT_CURRENCY']['CURRENCY_ID']] = $this->storage['CONVERT_CURRENCY']['CURRENCY_ID'];
4126 $taggedCache = Main\Application::getInstance()->getTaggedCache();
4127 foreach ($this->storage['CURRENCY_LIST'] as $oneCurrency)
4128 {
4129 $taggedCache->registerTag('currency_id_'.$oneCurrency);
4130 }
4131
4132 unset($oneCurrency);
4133 unset($taggedCache);
4134 }
4135
4136 unset($this->storage['CURRENCY_LIST']);
4137
4138 $this->setResultCacheKeys($this->getCacheKeys());
4139 }
4140
4141 protected function getCacheKeys()
4142 {
4143 return array();
4144 }
4145
4150 protected function processResultData()
4151 {
4152 $this->iblockProducts = $this->getProductsSeparatedByIblock();
4153 $this->checkIblock();
4154
4155 if ($this->hasErrors())
4156 return;
4157
4158 $this->initCurrencyConvert();
4159 $this->initCatalogInfo();
4161 $this->initPrices();
4162 $this->initVats();
4163 $this->initUrlTemplates();
4164
4165 $this->initElementList();
4166 if (!$this->hasErrors())
4167 {
4168 $this->sortElementList();
4169 $this->makeElementLinks();
4170 $this->prepareData();
4171 $this->filterPureOffers();
4172 $this->makeOutputResult();
4173 }
4174 }
4175
4179 protected function checkIblock()
4180 {
4181 if (!empty($this->iblockProducts))
4182 {
4183 $iblocks = array();
4184 $iblockIterator = Iblock\IblockSiteTable::getList(array(
4185 'select' => array('IBLOCK_ID'),
4186 'filter' => array(
4187 '=IBLOCK_ID' => array_keys($this->iblockProducts),
4188 '=SITE_ID' => $this->getSiteId(),
4189 '=IBLOCK.ACTIVE' => 'Y'
4190 )
4191 ));
4192 while ($iblock = $iblockIterator->fetch())
4193 {
4194 $iblocks[$iblock['IBLOCK_ID']] = true;
4195 }
4196
4197 foreach ($this->iblockProducts as $iblock => $products)
4198 {
4199 if (!isset($iblocks[$iblock]))
4200 {
4201 unset($this->iblockProducts[$iblock]);
4202 }
4203 }
4204
4205 if (empty($this->iblockProducts))
4206 {
4207 $this->abortResultCache();
4208 $this->errorCollection->setError(new Error(Loc::getMessage('INVALID_IBLOCK'), self::ERROR_TEXT));
4209 }
4210 }
4211 }
4212
4213 protected function prepareData()
4214 {
4215 $this->clearItems();
4216 $this->initCatalogDiscountCache();
4217 $this->processProducts();
4218 $this->processOffers();
4219 $this->makeOutputResult();
4220 $this->clearItems();
4221 }
4222
4223 protected function filterPureOffers()
4224 {
4225 if (!empty($this->productIds) && is_array($this->productIds))
4226 {
4227 foreach ($this->productIds as $productId)
4228 {
4229 // check if it's element
4230 if ($this->productIdMap[$productId] == $productId)
4231 {
4232 continue;
4233 }
4234
4235 if (isset($this->elementLinks[$this->productIdMap[$productId]]) && !empty($this->elementLinks[$this->productIdMap[$productId]]['OFFERS']))
4236 {
4237 // clear all unwanted offers
4238 foreach ($this->elementLinks[$this->productIdMap[$productId]]['OFFERS'] as $key => $offer)
4239 {
4240 if ($offer['ID'] != $productId)
4241 {
4242 unset($this->elementLinks[$this->productIdMap[$productId]]['OFFERS'][$key]);
4243 }
4244 }
4245 }
4246 }
4247 }
4248 }
4249
4253 protected function makeOutputResult()
4254 {
4255 $this->arResult = array_merge($this->arResult, $this->storage['URLS']);
4256 $this->arResult['CONVERT_CURRENCY'] = $this->storage['CONVERT_CURRENCY'];
4257 $this->arResult['CATALOGS'] = $this->storage['CATALOGS'];
4258 $this->arResult['MODULES'] = $this->storage['MODULES'];
4259 $this->arResult['PRICES_ALLOW'] = $this->storage['PRICES_ALLOW'];
4260
4261 if ($this->isEnableCompatible())
4262 {
4263 if ($this->arParams['IBLOCK_ID'] > 0)
4264 {
4265 $this->arResult['CATALOG'] = false;
4266
4267 if (
4268 !empty($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
4269 && is_array($this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']])
4270 )
4271 {
4272 $this->arResult['CATALOG'] = $this->storage['CATALOGS'][$this->arParams['IBLOCK_ID']];
4273 }
4274 }
4275 }
4276 }
4277
4281 protected function processLinkAction()
4282 {
4283 global $APPLICATION;
4284
4285 if ($this->request->get($this->arParams['ACTION_VARIABLE'].self::ACTION_BUY) !== null)
4286 {
4287 $action = self::ACTION_BUY;
4288 }
4289 elseif ($this->request->get($this->arParams['ACTION_VARIABLE'].self::ACTION_ADD_TO_BASKET))
4290 {
4291 $action = self::ACTION_ADD_TO_BASKET;
4292 }
4293 else
4294 {
4295 $action = mb_strtoupper($this->request->get($this->arParams['ACTION_VARIABLE']));
4296 }
4297
4298 $productId = (int)$this->request->get($this->arParams['PRODUCT_ID_VARIABLE']);
4299
4300 if (
4301 ($action == self::ACTION_ADD_TO_BASKET || $action == self::ACTION_BUY || $action == self::ACTION_SUBSCRIBE)
4302 && Loader::includeModule('sale')
4303 && Loader::includeModule('catalog')
4304 )
4305 {
4306 $addByAjax = $this->request->get('ajax_basket') === 'Y';
4307 if ($addByAjax)
4308 {
4309 $this->request->set(Main\Text\Encoding::convertEncoding($this->request->toArray(), 'UTF-8', SITE_CHARSET));
4310 }
4311
4312 [$successfulAdd, $errorMsg] = $this->addProductToBasket($productId, $action);
4313
4314 if ($addByAjax)
4315 {
4316 if ($successfulAdd)
4317 {
4318 $addResult = array(
4319 'STATUS' => 'OK',
4320 'MESSAGE' => Loc::getMessage('CATALOG_SUCCESSFUL_ADD_TO_BASKET')
4321 );
4322 }
4323 else
4324 {
4325 $addResult = array(
4326 'STATUS' => 'ERROR',
4327 'MESSAGE' => $errorMsg
4328 );
4329 }
4330
4331 $APPLICATION->RestartBuffer();
4332 header('Content-Type: application/json');
4333 \CMain::FinalActions(Main\Web\Json::encode($addResult));
4334 }
4335 else
4336 {
4337 if ($successfulAdd)
4338 {
4339 $pathRedirect = $action == self::ACTION_BUY
4340 ? $this->arParams['BASKET_URL']
4341 : $APPLICATION->GetCurPageParam('', array(
4342 $this->arParams['PRODUCT_ID_VARIABLE'],
4343 $this->arParams['ACTION_VARIABLE'],
4344 $this->arParams['PRODUCT_QUANTITY_VARIABLE'],
4345 $this->arParams['PRODUCT_PROPS_VARIABLE']
4346 ));
4347
4348 LocalRedirect($pathRedirect);
4349 }
4350 else
4351 {
4352 $this->errorCollection->setError(new Error($errorMsg, self::ERROR_TEXT));
4353 }
4354 }
4355 }
4356 }
4357
4358 protected function checkProductSection($productId, $sectionId = 0, $sectionCode = '')
4359 {
4360 $successfulAdd = true;
4361 $errorMsg = '';
4362
4363 if (!empty($productId) && ($sectionId > 0 || !empty($sectionCode)))
4364 {
4365 $productsMap = $this->getProductIdMap([$productId]);
4366
4367 if (!empty($productsMap[$productId]))
4368 {
4369 $sectionId = (int)$sectionId;
4370 $sectionCode = (string)$sectionCode;
4371
4372 $filter = ['ID' => $productsMap[$productId]];
4373
4374 $element = false;
4375 if ($sectionId > 0)
4376 {
4377 $filter['SECTION_ID'] = $sectionId;
4378 $filter['INCLUDE_SUBSECTIONS'] = 'Y';
4379 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
4380 $element = $elementIterator->Fetch();
4381 unset($elementIterator);
4382 }
4383 elseif ($sectionCode != '')
4384 {
4385 $iblockId = (int)\CIBlockElement::GetIBlockByID($productsMap[$productId]);
4386 if ($iblockId > 0)
4387 {
4388 $sectionIterator = \CIBlockSection::GetList(
4389 [],
4390 ['IBLOCK_ID' => $iblockId, '=CODE' => $sectionCode],
4391 false,
4392 ['ID', 'IBLOCK_ID']
4393 );
4394 $section = $sectionIterator->Fetch();
4395 unset($sectionIterator);
4396 if (!empty($section))
4397 {
4398 $filter['SECTION_ID'] = (int)$section['ID'];
4399 $filter['INCLUDE_SUBSECTIONS'] = 'Y';
4400 $elementIterator = \CIBlockElement::GetList(array(), $filter, false, false, array('ID'));
4401 $element = $elementIterator->Fetch();
4402 unset($elementIterator);
4403 }
4404 unset($section);
4405 }
4406 unset($iblockId);
4407 }
4408
4409 if (empty($element))
4410 {
4411 $successfulAdd = false;
4412 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4413 }
4414 }
4415 }
4416
4417 return [$successfulAdd, $errorMsg];
4418 }
4419
4420 protected function checkProductIblock(array $product): bool
4421 {
4422 return true;
4423 }
4424
4425 protected function addProductToBasket($productId, $action)
4426 {
4428 global $APPLICATION;
4429
4430 $successfulAdd = true;
4431 $errorMsg = '';
4432
4433 $quantity = 0;
4434 $productProperties = array();
4435
4436 $productId = (int)$productId;
4437 if ($productId <= 0)
4438 {
4439 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_ID_IS_ABSENT');
4440 $successfulAdd = false;
4441 }
4442 $product = [];
4443 if ($successfulAdd)
4444 {
4445 $product = $this->getProductInfo($productId);
4446 if (empty($product))
4447 {
4448 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4449 $successfulAdd = false;
4450 }
4451 }
4452 if ($successfulAdd)
4453 {
4454 if ($this->arParams['CHECK_LANDING_PRODUCT_SECTION'])
4455 {
4456 [$successfulAdd, $errorMsg] = $this->checkProductSection(
4457 $productId, $this->arParams['SECTION_ID'], $this->arParams['SECTION_CODE']
4458 );
4459 }
4460 }
4461 if ($successfulAdd)
4462 {
4463 if (!$this->checkProductIblock($product))
4464 {
4465 $errorMsg = Loc::getMessage('CATALOG_PRODUCT_NOT_FOUND');
4466 $successfulAdd = false;
4467 }
4468 }
4469 if ($successfulAdd)
4470 {
4471 if ($this->arParams['ADD_PROPERTIES_TO_BASKET'] === 'Y')
4472 {
4474 $iblockParams = $this->storage['IBLOCK_PARAMS'][$product['PRODUCT_IBLOCK_ID']];
4475 if ($product['TYPE'] == Catalog\ProductTable::TYPE_OFFER)
4476 {
4477 $skuAddProps = $this->request->get('basket_props') ?: '';
4478 if (!empty($iblockParams['OFFERS_CART_PROPERTIES']) || !empty($skuAddProps))
4479 {
4480 $productProperties = \CIBlockPriceTools::GetOfferProperties(
4481 $productId,
4482 $product['PRODUCT_IBLOCK_ID'],
4483 $iblockParams['OFFERS_CART_PROPERTIES'],
4484 $skuAddProps
4485 );
4486 }
4487 unset($skuAddProps);
4488 }
4489 else
4490 {
4491 if (!empty($iblockParams['CART_PROPERTIES']))
4492 {
4493 $productPropsVar = $this->request->get($this->arParams['PRODUCT_PROPS_VARIABLE']);
4494 if (is_array($productPropsVar))
4495 {
4496 $productProperties = \CIBlockPriceTools::CheckProductProperties(
4497 $product['PRODUCT_IBLOCK_ID'],
4498 $productId,
4499 $iblockParams['CART_PROPERTIES'],
4500 $productPropsVar,
4501 $this->arParams['PARTIAL_PRODUCT_PROPERTIES'] === 'Y'
4502 );
4503 if (!is_array($productProperties))
4504 {
4505 $errorMsg = Loc::getMessage('CATALOG_PARTIAL_BASKET_PROPERTIES_ERROR');
4506 $successfulAdd = false;
4507 }
4508 }
4509 else
4510 {
4511 if ($this->arParams['PARTIAL_PRODUCT_PROPERTIES'] !== 'Y')
4512 {
4513 $errorMsg = Loc::getMessage('CATALOG_EMPTY_BASKET_PROPERTIES_ERROR');
4514 $successfulAdd = false;
4515 }
4516 }
4517 unset($productPropsVar);
4518 }
4519 }
4520 unset($iblockParams);
4521 }
4522 }
4523
4524 if ($successfulAdd)
4525 {
4526 if ($this->arParams['USE_PRODUCT_QUANTITY'])
4527 {
4528 $quantity = (float)$this->request->get($this->arParams['PRODUCT_QUANTITY_VARIABLE']);
4529 }
4530
4531 if ($quantity <= 0)
4532 {
4533 $ratioIterator = \CCatalogMeasureRatio::getList(
4534 array(),
4535 array('PRODUCT_ID' => $productId),
4536 false,
4537 false,
4538 array('PRODUCT_ID', 'RATIO')
4539 );
4540 if ($ratio = $ratioIterator->Fetch())
4541 {
4542 $intRatio = (int)$ratio['RATIO'];
4543 $floatRatio = (float)$ratio['RATIO'];
4544 $quantity = $floatRatio > $intRatio ? $floatRatio : $intRatio;
4545 }
4546 }
4547
4548 if ($quantity <= 0)
4549 {
4550 $quantity = 1;
4551 }
4552 }
4553
4554 if ($successfulAdd)
4555 {
4556 $rewriteFields = $this->getRewriteFields($action);
4557 if (isset($rewriteFields['SUBSCRIBE']) && $rewriteFields['SUBSCRIBE'] == 'Y')
4558 {
4559 if (!SubscribeProduct($productId, $rewriteFields, $productProperties))
4560 {
4561 if ($ex = $APPLICATION->GetException())
4562 {
4563 $errorMsg = $ex->GetString();
4564 }
4565 else
4566 {
4567 $errorMsg = Loc::getMessage('CATALOG_ERROR2BASKET');
4568 }
4569
4570 $successfulAdd = false;
4571 }
4572 }
4573 else
4574 {
4575 $product = [
4576 'PRODUCT_ID' => $productId,
4577 'QUANTITY' => $quantity
4578 ];
4579 if (!empty($productProperties))
4580 {
4581 $product['PROPS'] = $productProperties;
4582 }
4583
4584 $basketResult = Catalog\Product\Basket::addProduct($product, $rewriteFields, [
4585 'USE_MERGE' => $this->isMergeProductWhenAddedBasket() ? 'Y' : 'N',
4586 ]);
4587 if (!$basketResult->isSuccess())
4588 {
4589 $errorMsg = implode('; ', $basketResult->getErrorMessages());
4590 $successfulAdd = false;
4591 }
4592 unset($basketResult);
4593 }
4594 }
4595
4596 return array($successfulAdd, $errorMsg);
4597 }
4598
4607 {
4608 return ($this->arParams['USE_MERGE_WHEN_ADD_PRODUCT_TO_BASKET'] ?? 'Y') !== 'N';
4609 }
4610
4611 protected function getRewriteFields($action)
4612 {
4613 $rewriteFields = [];
4614
4615 if ($action === self::ACTION_ADD_TO_BASKET || $action === self::ACTION_BUY)
4616 {
4617 $rewriteFields['DELAY'] = 'N';
4618 }
4619
4620 if ($action == self::ACTION_SUBSCRIBE)
4621 {
4622 $notify = unserialize(Main\Config\Option::get('sale', 'subscribe_prod', ''), ['allowed_classes' => false]);
4623 if (!empty($notify[$this->getSiteId()]) && $notify[$this->getSiteId()]['use'] === 'Y')
4624 {
4625 $rewriteFields['SUBSCRIBE'] = 'Y';
4626 $rewriteFields['CAN_BUY'] = 'N';
4627 }
4628 }
4629
4630 return $rewriteFields;
4631 }
4632
4637 protected function deferredLoadAction()
4638 {
4639 $this->productIds = $this->getDeferredProductIds();
4640
4641 // if no products - show empty response
4642 if (empty($this->productIds))
4643 {
4644 static::sendJsonAnswer();
4645 }
4646
4647 $this->productIdMap = $this->getProductIdMap($this->productIds);
4648 $this->loadData();
4649 }
4650
4654 protected function bigDataLoadAction()
4655 {
4656 $this->initBigDataLastUsage();
4657 $this->productIds = $this->getBigDataProductIds();
4658
4659 // if no products - show empty response
4660 if (empty($this->productIds))
4661 {
4662 static::sendJsonAnswer();
4663 }
4664
4665 $this->productIdMap = $this->getProductIdMap($this->productIds);
4666 $this->loadData();
4667 }
4668
4672 protected function initBigDataLastUsage()
4673 {
4674 $lastUsage = Main\Config\Option::get('main', 'rcm_component_usage', 0);
4675
4676 if ($lastUsage == 0 || (time() - $lastUsage) > 3600)
4677 {
4678 Main\Config\Option::set('main', 'rcm_component_usage', time());
4679 }
4680 }
4681
4685 protected function initialLoadAction()
4686 {
4687 $this->productIds = $this->getProductIds();
4688 $this->productIdMap = $this->getProductIdMap($this->productIds);
4689 $this->loadData();
4690 }
4691
4696 protected function loadData()
4697 {
4698 if ($this->isCacheDisabled() || $this->startResultCache(false, $this->getAdditionalCacheId(), $this->getComponentCachePath()))
4699 {
4700 $this->processResultData();
4701 if (!$this->hasErrors())
4702 {
4703 if ($this->isExtendedMode())
4704 {
4705 $this->initComponentTemplate();
4707 }
4708
4709 $this->initResultCache();
4710 $this->includeComponentTemplate();
4712 }
4713 }
4714 }
4715
4721 abstract protected function getAdditionalCacheId();
4722
4728 abstract protected function getComponentCachePath();
4729
4730 public function getTemplateEmptyPreview()
4731 {
4732 $emptyPreview = false;
4733 $documentRoot = Main\Application::getDocumentRoot();
4734 $emptyPreviewPath = $this->getTemplate()->GetFolder().'/images/no_photo.png';
4735
4736 $file = new Main\IO\File($documentRoot.$emptyPreviewPath);
4737 if ($file->isExists())
4738 {
4739 $size = getimagesize($documentRoot.$emptyPreviewPath);
4740 if (!empty($size))
4741 {
4742 $emptyPreview = array(
4743 'ID' => 0,
4744 'SRC' => $emptyPreviewPath,
4745 'FILE_NAME' => 'no_photo.png',
4746 'WIDTH' => (int)$size[0],
4747 'HEIGHT' => (int)$size[1]
4748 );
4749 }
4750 }
4751
4752 return $emptyPreview;
4753 }
4754
4755 protected function sliceItemsForSlider(&$items)
4756 {
4757 $rows = array();
4758
4759 while (!empty($items))
4760 {
4761 $rows[] = array_splice($items, 0, $this->arParams['LINE_ELEMENT_COUNT']);
4762 }
4763
4764 $items = $rows;
4765 }
4766
4767 protected function getTemplateCurrencies()
4768 {
4769 $currencies = array();
4770
4771 if ($this->arResult['MODULES']['currency'])
4772 {
4773 if (isset($this->arResult['CONVERT_CURRENCY']['CURRENCY_ID']))
4774 {
4775 $currencyFormat = \CCurrencyLang::GetFormatDescription($this->arResult['CONVERT_CURRENCY']['CURRENCY_ID']);
4776 $currencies = array(
4777 array(
4778 'CURRENCY' => $this->arResult['CONVERT_CURRENCY']['CURRENCY_ID'],
4779 'FORMAT' => array(
4780 'FORMAT_STRING' => $currencyFormat['FORMAT_STRING'],
4781 'DEC_POINT' => $currencyFormat['DEC_POINT'],
4782 'THOUSANDS_SEP' => $currencyFormat['THOUSANDS_SEP'],
4783 'DECIMALS' => $currencyFormat['DECIMALS'],
4784 'THOUSANDS_VARIANT' => $currencyFormat['THOUSANDS_VARIANT'],
4785 'HIDE_ZERO' => $currencyFormat['HIDE_ZERO']
4786 )
4787 )
4788 );
4789 unset($currencyFormat);
4790 }
4791 else
4792 {
4793 $currencyIterator = Currency\CurrencyTable::getList(array(
4794 'select' => array('CURRENCY')
4795 ));
4796 while ($currency = $currencyIterator->fetch())
4797 {
4798 $currencyFormat = \CCurrencyLang::GetFormatDescription($currency['CURRENCY']);
4799 $currencies[] = array(
4800 'CURRENCY' => $currency['CURRENCY'],
4801 'FORMAT' => array(
4802 'FORMAT_STRING' => $currencyFormat['FORMAT_STRING'],
4803 'DEC_POINT' => $currencyFormat['DEC_POINT'],
4804 'THOUSANDS_SEP' => $currencyFormat['THOUSANDS_SEP'],
4805 'DECIMALS' => $currencyFormat['DECIMALS'],
4806 'THOUSANDS_VARIANT' => $currencyFormat['THOUSANDS_VARIANT'],
4807 'HIDE_ZERO' => $currencyFormat['HIDE_ZERO']
4808 )
4809 );
4810 }
4811 unset($currencyFormat, $currency, $currencyIterator);
4812 }
4813 }
4814
4815 return $currencies;
4816 }
4817
4823 public static function sendJsonAnswer(array $result = array())
4824 {
4825 global $APPLICATION;
4826
4827 if (!empty($result))
4828 {
4829 $result['JS'] = Main\Page\Asset::getInstance()->getJs();
4830 }
4831
4832 $APPLICATION->RestartBuffer();
4833
4834 /* don't change this block, because delayed \CFile::ResizeImageGet is not started in cloud */
4835 echo Main\Web\Json::encode($result);
4836 \CMain::FinalActions();
4837 /* block end */
4838 }
4839
4846 protected function prepareAction()
4847 {
4848 if (
4849 $this->request->get($this->arParams['ACTION_VARIABLE']) !== null
4850 && $this->request->get($this->arParams['PRODUCT_ID_VARIABLE']) !== null
4851 )
4852 {
4853 $action = 'processLink';
4854 }
4855 elseif ($this->request->isAjaxRequest() && $this->request->get('action') === 'deferredLoad')
4856 {
4857 $action = $this->request->get('bigData') === 'Y' ? 'bigDataLoad' : 'deferredLoad';
4858 }
4859 else
4860 {
4861 $action = 'initialLoad';
4862 }
4863
4864 return $action;
4865 }
4866
4870 protected function doAction()
4871 {
4872 $action = $this->getAction();
4873
4874 if (is_callable(array($this, $action.'Action')))
4875 {
4876 call_user_func(array($this, $action.'Action'));
4877 }
4878 }
4879
4883 public function executeComponent()
4884 {
4885 $this->checkModules();
4886
4887 if ($this->hasErrors())
4888 {
4889 return $this->processErrors();
4890 }
4891
4892 $action = $this->prepareAction();
4893 $this->setAction($action);
4894 $this->doAction();
4895
4896 if ($this->hasErrors())
4897 {
4898 return $this->processErrors();
4899 }
4900
4901 return $this->arResult['ID'] ?? false;
4902 }
4903
4905 {
4906 $this->prepareTemplateParams();
4907 $this->checkTemplateTheme();
4908 $this->editTemplateData();
4909
4910 return $this->arParams;
4911 }
4912
4913 protected function prepareTemplateParams()
4914 {
4915 $params =& $this->arParams;
4916 $defaultParams = $this->getTemplateDefaultParams();
4917 $params = array_merge($defaultParams, $params);
4918
4919 $params['SHOW_OLD_PRICE'] = $params['SHOW_OLD_PRICE'] === 'Y' ? 'Y' : 'N';
4920 $params['SHOW_CLOSE_POPUP'] = $params['SHOW_CLOSE_POPUP'] === 'Y' ? 'Y' : 'N';
4921 $params['SHOW_DISCOUNT_PERCENT'] = $params['SHOW_DISCOUNT_PERCENT'] === 'Y' ? 'Y' : 'N';
4922 $params['DISCOUNT_PERCENT_POSITION'] = trim($params['DISCOUNT_PERCENT_POSITION']) ?: 'bottom-right';
4923 $params['LABEL_PROP_POSITION'] = trim($params['LABEL_PROP_POSITION']) ?: 'top-left';
4924 $params['PRODUCT_SUBSCRIPTION'] = $params['PRODUCT_SUBSCRIPTION'] === 'N' ? 'N' : 'Y';
4925 $params['MESS_BTN_BUY'] = trim($params['MESS_BTN_BUY']);
4926 $params['MESS_BTN_ADD_TO_BASKET'] = trim($params['MESS_BTN_ADD_TO_BASKET']);
4927 $params['MESS_BTN_SUBSCRIBE'] = trim($params['MESS_BTN_SUBSCRIBE']);
4928 $params['MESS_BTN_DETAIL'] = trim($params['MESS_BTN_DETAIL']);
4929 $params['MESS_NOT_AVAILABLE'] = trim($params['MESS_NOT_AVAILABLE']);
4930 $params['MESS_BTN_COMPARE'] = trim($params['MESS_BTN_COMPARE']);
4931 $params['SHOW_SLIDER'] = $params['SHOW_SLIDER'] === 'N' ? 'N' : 'Y';
4932 $params['SLIDER_INTERVAL'] = (int)$params['SLIDER_INTERVAL'] ?: 5000;
4933 $params['SLIDER_PROGRESS'] = $params['SLIDER_PROGRESS'] === 'Y' ? 'Y' : 'N';
4934 $params['USE_ENHANCED_ECOMMERCE'] = $params['USE_ENHANCED_ECOMMERCE'] === 'Y' ? 'Y' : 'N';
4935 $params['DATA_LAYER_NAME'] = trim($params['DATA_LAYER_NAME']);
4936 $params['BRAND_PROPERTY'] = $params['BRAND_PROPERTY'] !== '-' ? trim($params['BRAND_PROPERTY']) : '';
4937
4938 if (!isset($params['SHOW_MAX_QUANTITY']) || !in_array($params['SHOW_MAX_QUANTITY'], array('Y', 'M', 'N')))
4939 {
4940 $params['SHOW_MAX_QUANTITY'] = 'N';
4941 }
4942
4943 $params['RELATIVE_QUANTITY_FACTOR'] = (int)($params['RELATIVE_QUANTITY_FACTOR'] ?? 0) > 0 ? (int)$params['RELATIVE_QUANTITY_FACTOR'] : 5;
4944 }
4945
4946 protected function getTemplateDefaultParams()
4947 {
4948 return array(
4949 'TEMPLATE_THEME' => 'blue',
4950 'SHOW_MAX_QUANTITY' => 'N',
4951 'SHOW_OLD_PRICE' => 'N',
4952 'SHOW_CLOSE_POPUP' => 'N',
4953 'SHOW_DISCOUNT_PERCENT' => 'N',
4954 'DISCOUNT_PERCENT_POSITION' => 'bottom-right',
4955 'LABEL_PROP' => array(),
4956 'LABEL_PROP_MOBILE' => array(),
4957 'LABEL_PROP_POSITION' => 'top-left',
4958 'PRODUCT_SUBSCRIPTION' => 'Y',
4959 'MESS_BTN_BUY' => '',
4960 'MESS_BTN_ADD_TO_BASKET' => '',
4961 'MESS_BTN_SUBSCRIBE' => '',
4962 'MESS_BTN_DETAIL' => '',
4963 'MESS_NOT_AVAILABLE' => '',
4964 'MESS_BTN_COMPARE' => '',
4965 'SHOW_SLIDER' => 'N',
4966 'SLIDER_INTERVAL' => 5000,
4967 'SLIDER_PROGRESS' => 'N',
4968 'USE_ENHANCED_ECOMMERCE' => 'N',
4969 'DATA_LAYER_NAME' => 'dataLayer',
4970 'BRAND_PROPERTY' => ''
4971 );
4972 }
4973
4974 protected function checkTemplateTheme()
4975 {
4976 $theme =& $this->arParams['TEMPLATE_THEME'];
4977 $theme = (string)$theme;
4978
4979 if ($theme != '')
4980 {
4981 $theme = preg_replace('/[^a-zA-Z0-9_\-\‍(\‍)\!]/', '', $theme);
4982 if ($theme === 'site')
4983 {
4984 $siteId = $this->getSiteId();
4985 $templateId = Main\Config\Option::get('main', 'wizard_template_id', 'eshop_bootstrap', $siteId);
4986 $templateId = preg_match('/^eshop_adapt/', $templateId) ? 'eshop_adapt' : $templateId;
4987 $theme = Main\Config\Option::get('main', 'wizard_'.$templateId.'_theme_id', 'blue', $siteId);
4988 }
4989
4990 if ($theme != '')
4991 {
4992 $documentRoot = Main\Application::getDocumentRoot();
4993 $templateFolder = $this->getTemplate()->GetFolder();
4994
4995 $themesFolder = new Main\IO\Directory($documentRoot.$templateFolder.'/themes/');
4996
4997 if ($themesFolder->isExists())
4998 {
4999 $file = new Main\IO\File($documentRoot.$templateFolder.'/themes/'.$theme.'/style.css');
5000
5001 if (!$file->isExists())
5002 {
5003 $theme = '';
5004 }
5005 }
5006 }
5007 }
5008
5009 if ($theme == '')
5010 {
5011 $theme = 'blue';
5012 }
5013 }
5014
5015 protected function editTemplateData()
5016 {
5017 //
5018 }
5019
5020 public static function checkEnlargedData(&$item, $propertyCode)
5021 {
5022 if (!empty($item) && is_array($item))
5023 {
5024 $item['ENLARGED'] = 'N';
5025 $propertyCode = (string)$propertyCode;
5026
5027 if ($propertyCode !== '' && isset($item['PROPERTIES'][$propertyCode]))
5028 {
5029 $prop = $item['PROPERTIES'][$propertyCode];
5030 if (!empty($prop['VALUE']))
5031 {
5032 $item['ENLARGED'] = 'Y';
5033 }
5034 }
5035 }
5036 }
5037
5038 protected function editTemplateProductSlider(&$item, $iblock, $limit = 0, $addDetailToSlider = true, $default = array())
5039 {
5040 $propCode = $this->storage['IBLOCK_PARAMS'][$iblock]['ADD_PICT_PROP'];
5041
5042 $slider = \CIBlockPriceTools::getSliderForItem($item, $propCode, $addDetailToSlider);
5043 if (empty($slider))
5044 {
5045 $slider = $default;
5046 }
5047
5048 if ($limit > 0)
5049 {
5050 $slider = array_slice($slider, 0, $limit);
5051 }
5052
5053 $item['SHOW_SLIDER'] = true;
5054 $item['MORE_PHOTO'] = $slider;
5055 $item['MORE_PHOTO_COUNT'] = count($slider);
5056 }
5057
5058 protected function editTemplateOfferSlider(&$item, $iblock, $limit = 0, $addDetailToSlider = true, $default = array())
5059 {
5060 $propCode = $this->storage['IBLOCK_PARAMS'][$iblock]['OFFERS_ADD_PICT_PROP'];
5061
5062 $slider = \CIBlockPriceTools::getSliderForItem($item, $propCode, $addDetailToSlider);
5063 if (empty($slider))
5064 {
5065 $slider = $default;
5066 }
5067
5068 if ($limit > 0)
5069 {
5070 $slider = array_slice($slider, 0, $limit);
5071 }
5072
5073 $item['MORE_PHOTO'] = $slider;
5074 $item['MORE_PHOTO_COUNT'] = count($slider);
5075 }
5076
5077 protected function editTemplateCatalogInfo(&$item)
5078 {
5079 if ($this->arResult['MODULES']['catalog'])
5080 {
5081 $item['CATALOG'] = true;
5082 if ($this->isEnableCompatible())
5083 $item['CATALOG_TYPE'] = $item['PRODUCT']['TYPE'];
5084 }
5085 else
5086 {
5087 if ($this->isEnableCompatible())
5088 $item['CATALOG_TYPE'] = 0;
5089 $item['OFFERS'] = array();
5090 }
5091 }
5092
5093 protected function getTemplatePropCell($code, $offer, &$matrixFields, $skuPropList)
5094 {
5095 $cell = array(
5096 'VALUE' => 0,
5097 'SORT' => PHP_INT_MAX,
5098 'NA' => true
5099 );
5100
5101 if (isset($offer['DISPLAY_PROPERTIES'][$code]))
5102 {
5103 $matrixFields[$code] = true;
5104 $cell['NA'] = false;
5105
5106 if ($skuPropList[$code]['USER_TYPE'] === 'directory')
5107 {
5108 $intValue = $skuPropList[$code]['XML_MAP'][$offer['DISPLAY_PROPERTIES'][$code]['VALUE']];
5109 $cell['VALUE'] = $intValue;
5110 }
5111 elseif ($skuPropList[$code]['PROPERTY_TYPE'] === 'L')
5112 {
5113 $cell['VALUE'] = (int)$offer['DISPLAY_PROPERTIES'][$code]['VALUE_ENUM_ID'];
5114 }
5115 elseif ($skuPropList[$code]['PROPERTY_TYPE'] === 'E')
5116 {
5117 $cell['VALUE'] = (int)$offer['DISPLAY_PROPERTIES'][$code]['VALUE'];
5118 }
5119
5120 $cell['SORT'] = $skuPropList[$code]['VALUES'][$cell['VALUE']]['SORT'];
5121 }
5122
5123 return $cell;
5124 }
5125
5126 protected function getOffersIblockId($iblockId)
5127 {
5128 if (!$this->useCatalog)
5129 return null;
5130 if (!isset($this->storage['CATALOGS'][$iblockId]))
5131 return null;
5132 if (
5133 $this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] != \CCatalogSku::TYPE_PRODUCT
5134 && $this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'] != \CCatalogSku::TYPE_FULL
5135 )
5136 return null;
5137 return $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'];
5138 }
5139
5144 protected function loadDisplayPropertyCodes($iblockId)
5145 {
5146
5147 }
5148
5149 protected function loadBasketPropertyCodes($iblockId)
5150 {
5151 if (!$this->useCatalog)
5152 return;
5153 if (!isset($this->storage['CATALOGS'][$iblockId]))
5154 return;
5155
5156 switch ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'])
5157 {
5158 case \CCatalogSku::TYPE_CATALOG:
5159 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5160 $iblockId,
5161 ['CODE' => 'Y']
5162 );
5163 if ($list === null)
5164 $list = [];
5165 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = $list;
5166 unset($list);
5167 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = [];
5168 break;
5169 case \CCatalogSku::TYPE_PRODUCT:
5170 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = [];
5171 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5172 $this->getOffersIblockId($iblockId),
5173 ['CODE' => 'Y']
5174 );
5175 if ($list === null)
5176 $list = [];
5177 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = $list;
5178 unset($list);
5179 break;
5180 case \CCatalogSku::TYPE_FULL:
5181 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5182 $iblockId,
5183 ['CODE' => 'Y']
5184 );
5185 if ($list === null)
5186 $list = [];
5187 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = $list;
5188 $list = Catalog\Product\PropertyCatalogFeature::getBasketPropertyCodes(
5189 $this->getOffersIblockId($iblockId),
5190 ['CODE' => 'Y']
5191 );
5192 if ($list === null)
5193 $list = [];
5194 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = $list;
5195 unset($list);
5196 break;
5197 case \CCatalogSku::TYPE_OFFERS:
5198 $this->storage['IBLOCK_PARAMS'][$iblockId]['CART_PROPERTIES'] = [];
5199 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_CART_PROPERTIES'] = [];
5200 break;
5201 default:
5202 break;
5203 }
5204 }
5205
5206 protected function loadOfferTreePropertyCodes($iblockId)
5207 {
5208 if (!$this->useCatalog)
5209 return;
5210 if (!isset($this->storage['CATALOGS'][$iblockId]))
5211 return;
5212
5213 switch ($this->storage['CATALOGS'][$iblockId]['CATALOG_TYPE'])
5214 {
5215 case \CCatalogSku::TYPE_CATALOG:
5216 case \CCatalogSku::TYPE_OFFERS:
5217 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_TREE_PROPS'] = [];
5218 break;
5219 case \CCatalogSku::TYPE_PRODUCT:
5220 case \CCatalogSku::TYPE_FULL:
5221 $list = Catalog\Product\PropertyCatalogFeature::getOfferTreePropertyCodes(
5222 $this->storage['CATALOGS'][$iblockId]['IBLOCK_ID'],
5223 ['CODE' => 'Y']
5224 );
5225 if ($list === null)
5226 $list = [];
5227 $this->storage['IBLOCK_PARAMS'][$iblockId]['OFFERS_TREE_PROPS'] = $list;
5228 unset($list);
5229 break;
5230 default:
5231 break;
5232 }
5233 }
5234
5235 /* product tools */
5236
5243 protected function isNeedCheckQuantity(array $product)
5244 {
5245 return (
5246 $product['QUANTITY_TRACE'] === Catalog\ProductTable::STATUS_YES
5247 && $product['CAN_BUY_ZERO'] === Catalog\ProductTable::STATUS_NO
5248 );
5249 }
5250
5251 /* product tools end */
5252
5253 /* user tools */
5254
5260 protected function getUserGroups()
5261 {
5263 global $USER;
5264 $result = array(2);
5265 if (isset($USER) && $USER instanceof \CUser)
5266 {
5267 $result = $USER->GetUserGroupArray();
5268 Main\Type\Collection::normalizeArrayValuesByInt($result, true);
5269 }
5270 return $result;
5271 }
5272
5278 protected function getUserGroupsCacheId()
5279 {
5280 return implode(',', $this->getUserGroups());
5281 }
5282
5283 /* user tools end */
5284
5285 /* compatibility tools */
5286
5294 protected function initCompatibleFields(array $items)
5295 {
5296 if (empty($items))
5297 return;
5298
5299 $initFields = array(
5300 'PRICES' => array(),
5301 'PRICE_MATRIX' => false,
5302 'MIN_PRICE' => false
5303 );
5304 if (!$this->arParams['USE_PRICE_COUNT'] && !empty($this->storage['PRICES']))
5305 {
5306 foreach ($this->storage['PRICES'] as $value)
5307 {
5308 if (!$value['CAN_VIEW'] && !$value['CAN_BUY'])
5309 continue;
5310
5311 $priceType = $value['ID'];
5312 $initFields['CATALOG_GROUP_ID_'.$priceType] = $priceType;
5313 $initFields['~CATALOG_GROUP_ID_'.$priceType] = $priceType;
5314 $initFields['CATALOG_GROUP_NAME_'.$priceType] = $value['TITLE'];
5315 $initFields['~CATALOG_GROUP_NAME_'.$priceType] = $value['~TITLE'];
5316 $initFields['CATALOG_CAN_ACCESS_'.$priceType] = ($value['CAN_VIEW'] ? 'Y' : 'N');
5317 $initFields['~CATALOG_CAN_ACCESS_'.$priceType] = ($value['CAN_VIEW'] ? 'Y' : 'N');
5318 $initFields['CATALOG_CAN_BUY_'.$priceType] = ($value['CAN_BUY'] ? 'Y' : 'N');
5319 $initFields['~CATALOG_CAN_BUY_'.$priceType] = ($value['CAN_BUY'] ? 'Y' : 'N');
5320 $initFields['CATALOG_PRICE_ID_'.$priceType] = null;
5321 $initFields['~CATALOG_PRICE_ID_'.$priceType] = null;
5322 $initFields['CATALOG_PRICE_'.$priceType] = null;
5323 $initFields['~CATALOG_PRICE_'.$priceType] = null;
5324 $initFields['CATALOG_CURRENCY_'.$priceType] = null;
5325 $initFields['~CATALOG_CURRENCY_'.$priceType] = null;
5326 $initFields['CATALOG_QUANTITY_FROM_'.$priceType] = null;
5327 $initFields['~CATALOG_QUANTITY_FROM_'.$priceType] = null;
5328 $initFields['CATALOG_QUANTITY_TO_'.$priceType] = null;
5329 $initFields['~CATALOG_QUANTITY_TO_'.$priceType] = null;
5330 $initFields['CATALOG_EXTRA_ID_'.$priceType] = null;
5331 $initFields['~CATALOG_EXTRA_ID_'.$priceType] = null;
5332 unset($priceType);
5333 }
5334 unset($value);
5335 }
5336
5337 foreach (array_keys($items) as $index)
5338 $this->oldData[$items[$index]['ID']] = $initFields;
5339 unset($index, $initFields);
5340 }
5341
5350 protected function fillCompatibleRawPriceFields($id, array $prices)
5351 {
5352 if (!isset($this->oldData[$id]) || empty($prices) || $this->arParams['USE_PRICE_COUNT'])
5353 return;
5354 foreach ($prices as $rawPrice)
5355 {
5356 $priceType = $rawPrice['CATALOG_GROUP_ID'];
5357 $this->oldData[$id]['CATALOG_PRICE_ID_'.$priceType] = $rawPrice['ID'];
5358 $this->oldData[$id]['~CATALOG_PRICE_ID_'.$priceType] = $rawPrice['ID'];
5359 $this->oldData[$id]['CATALOG_PRICE_'.$priceType] = $rawPrice['PRICE'];
5360 $this->oldData[$id]['~CATALOG_PRICE_'.$priceType] = $rawPrice['PRICE'];
5361 $this->oldData[$id]['CATALOG_CURRENCY_'.$priceType] = $rawPrice['CURRENCY'];
5362 $this->oldData[$id]['~CATALOG_CURRENCY_'.$priceType] = $rawPrice['CURRENCY'];
5363 $this->oldData[$id]['CATALOG_QUANTITY_FROM_'.$priceType] = $rawPrice['QUANTITY_FROM'];
5364 $this->oldData[$id]['~CATALOG_QUANTITY_FROM_'.$priceType] = $rawPrice['QUANTITY_FROM'];
5365 $this->oldData[$id]['CATALOG_QUANTITY_TO_'.$priceType] = $rawPrice['QUANTITY_TO'];
5366 $this->oldData[$id]['~CATALOG_QUANTITY_TO_'.$priceType] = $rawPrice['QUANTITY_TO'];
5367 $this->oldData[$id]['CATALOG_EXTRA_ID_'.$priceType] = $rawPrice['EXTRA_ID'];
5368 $this->oldData[$id]['~CATALOG_EXTRA_ID_'.$priceType] = $rawPrice['EXTRA_ID'];
5369 unset($priceType);
5370 }
5371 unset($rawPrice);
5372 }
5373
5382 protected function getCompatibleFieldValue($id, $field)
5383 {
5384 if (!isset($this->oldData[$id]))
5385 return null;
5386 return ($this->oldData[$id][$field] ?? null);
5387 }
5388
5396 protected function checkQuantityRange(array $row)
5397 {
5398 return (
5399 ($row['QUANTITY_FROM'] === null || $row['QUANTITY_FROM'] <= $this->arParams['SHOW_PRICE_COUNT'])
5400 && ($row['QUANTITY_TO'] === null || $row['QUANTITY_TO'] >= $this->arParams['SHOW_PRICE_COUNT'])
5401 );
5402 }
5403
5409 protected function getEmptyPriceMatrix(): array
5410 {
5411 return array(
5412 'ROWS' => array(),
5413 'COLS' => array(),
5414 'MATRIX' => array(),
5415 'CAN_BUY' => array(),
5416 'AVAILABLE' => 'N',
5417 'CURRENCY_LIST' => array()
5418 );
5419 }
5420
5428 private function resortOldPrices($id)
5429 {
5430 if (empty($this->oldData[$id]['PRICES']) || count($this->oldData[$id]['PRICES']) < 2)
5431 return;
5432 foreach (array_keys($this->oldData[$id]['PRICES']) as $priceCode)
5433 $this->oldData[$id]['PRICES'][$priceCode]['_SORT'] = $this->storage['PRICES'][$priceCode]['SORT'];
5434 unset($priceCode);
5435 Main\Type\Collection::sortByColumn(
5436 $this->oldData[$id]['PRICES'],
5437 array('_SORT' => SORT_ASC, 'PRICE_ID' => SORT_ASC),
5438 '', null, true
5439 );
5440 foreach (array_keys($this->oldData[$id]['PRICES']) as $priceCode)
5441 unset($this->oldData[$id]['PRICES'][$priceCode]['_SORT']);
5442 unset($priceCode);
5443 }
5444
5450 protected function getCompatibleProductFields()
5451 {
5452 return [
5453 'TYPE' => 'CATALOG_TYPE',
5454 'AVAILABLE' => 'CATALOG_AVAILABLE',
5455 'BUNDLE' => 'CATALOG_BUNDLE',
5456 'QUANTITY' => 'CATALOG_QUANTITY',
5457 'QUANTITY_TRACE' => 'CATALOG_QUANTITY_TRACE',
5458 'CAN_BUY_ZERO' => 'CATALOG_CAN_BUY_ZERO',
5459 'MEASURE' => 'CATALOG_MEASURE',
5460 'SUBSCRIBE' => 'CATALOG_SUBSCRIBE',
5461 'VAT_ID' => 'CATALOG_VAT_ID',
5462 'VAT_INCLUDED' => 'CATALOG_VAT_INCLUDED',
5463 'WEIGHT' => 'CATALOG_WEIGHT',
5464 'WIDTH' => 'CATALOG_WIDTH',
5465 'LENGTH' => 'CATALOG_LENGTH',
5466 'HEIGHT' => 'CATALOG_HEIGHT',
5467 'PAYMENT_TYPE' => 'CATALOG_PRICE_TYPE',
5468 'RECUR_SCHEME_LENGTH' => 'CATALOG_RECUR_SCHEME_LENGTH',
5469 'RECUR_SCHEME_TYPE' => 'CATALOG_RECUR_SCHEME_TYPE',
5470 'QUANTITY_TRACE_RAW' => 'CATALOG_QUANTITY_TRACE_ORIG',
5471 'CAN_BUY_ZERO_RAW' => 'CATALOG_CAN_BUY_ZERO_ORIG',
5472 'SUBSCRIBE_RAW' => 'CATALOG_SUBSCRIBE_ORIG',
5473 'PURCHASING_PRICE' => 'CATALOG_PURCHASING_PRICE',
5474 'PURCHASING_CURRENCY' => 'CATALOG_PURCHASING_CURRENCY',
5475 'BARCODE_MULTI' => 'CATALOG_BARCODE_MULTI',
5476 'TRIAL_PRICE_ID' => 'CATALOG_TRIAL_PRICE_ID',
5477 'WITHOUT_ORDER' => 'CATALOG_WITHOUT_ORDER',
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 ];
5504 }
5505
5506 /* compatibility tools end */
5507}
getProductInfo($productId)
Definition base.php:667
parseConditionLevel($condition, $params)
Definition base.php:2148
static checkEnlargedData(&$item, $propertyCode)
Definition base.php:5020
checkProductSection($productId, $sectionId=0, $sectionCode='')
Definition base.php:4358
getProductIdMap($productIds)
Definition base.php:1625
getMeasureIds(array $items)
Definition base.php:2652
getIblockElements($elementIterator)
onPrepareComponentParams($params)
Definition base.php:268
parsePropertyCondition(array &$result, array $condition, $params)
Definition base.php:2260
__construct($component=null)
Definition base.php:83
modifyElementPrices(&$element)
Definition base.php:3594
isNeedCheckQuantity(array $product)
Definition base.php:5243
getIblockSelectFields($iblockId)
Definition base.php:2074
calculateItemPrices(array &$items)
Definition base.php:2842
getElementList($iblockId, $products)
Definition base.php:1729
filterByParams($ids, $filterIds=array(), $useSectionFilter=true)
Definition base.php:1117
getProductSelect($iblockId, array $selectFields)
Definition base.php:1980
getSectionIdByCode($sectionCode='', int $iblockId=0)
Definition base.php:1178
convertOrder(array $order)
Definition base.php:2067
addProductToBasket($productId, $action)
Definition base.php:4425
parseConditionName(array $condition)
Definition base.php:2178
getSeparateList(array $params)
Definition base.php:1772
getBigDataServiceRequestParams($type='')
Definition base.php:1420
loadMeasureRatios(array $itemIds)
Definition base.php:2552
getCompatibleFieldValue($id, $field)
Definition base.php:5382
processElement(array &$element)
Definition base.php:2341
filterIdBySection($elementIds, $iblockId, $sectionId, $limit, $depth=0)
Definition base.php:1269
parseCondition($condition, $params)
Definition base.php:2088
checkProductIblock(array $product)
Definition base.php:4420
parseConditionOperator($condition)
Definition base.php:2208
static getSettingsScript($componentPath, $settingsName)
Definition base.php:247
parseConditionValue($condition, $name)
Definition base.php:2243
getTemplatePropCell($code, $offer, &$matrixFields, $skuPropList)
Definition base.php:5093
loadOfferTreePropertyCodes($iblockId)
Definition base.php:5206
getSectionIdByElement($elementId, $elementCode='', int $iblockId=0)
Definition base.php:1224
prepareQueryFields(array $select, array $filter, array $order)
Definition base.php:1938
chooseOffer($offers, $iblockId)
modifyElementCommonData(array &$element)
Definition base.php:2354
getQuantityRangeHash(array $range)
Definition base.php:3489
convertFilter(array $filter)
Definition base.php:2054
loadBasketPropertyCodes($iblockId)
Definition base.php:5149
setElementPanelButtons(&$element)
Definition base.php:2477
fillCompatibleRawPriceFields($id, array $prices)
Definition base.php:5350
static getProductsMap(array $originalIds=array())
Definition base.php:1641
convertSelect(array $select)
Definition base.php:2041
initItemsMeasure(array &$items)
Definition base.php:2617
checkQuantityRange(array $row)
Definition base.php:5396
static sendJsonAnswer(array $result=array())
Definition base.php:4823
getFullIterator(array $params)
Definition base.php:1828
getPropertyList($iblock, $propertyCodes)
Definition base.php:2500
initCompatibleFields(array $items)
Definition base.php:5294
loadMeasures(array $measureIds)
Definition base.php:2679
editTemplateProductSlider(&$item, $iblock, $limit=0, $addDetailToSlider=true, $default=array())
Definition base.php:5038
loadDisplayPropertyCodes($iblockId)
Definition base.php:5144
modifyDisplayProperties($iblock, &$iblockElements)
Definition base.php:2496
loadPrices(array $itemIds)
Definition base.php:2711
offerIblockExist($iblockId)
Definition base.php:627
editTemplateOfferSlider(&$item, $iblock, $limit=0, $addDetailToSlider=true, $default=array())
Definition base.php:5058
transferItems(array &$items)
Definition base.php:2963
static loadMessages($file)
Definition loc.php:64
static getMessage($code, $replace=null, $language=null)
Definition loc.php:29