26 private static $instance;
30 private function __construct()
49 $this->userId = $userId;
54 private function __clone()
63 if (!isset(self::$instance))
65 self::$instance =
new self;
68 return self::$instance;
81 $this->errorCollection->clear();
83 if(!$this->isValidProduct($product))
88 $basketCopied = $basket->copy();
89 $discounts = $this->getDiscounts($basketCopied);
93 if($predictionDiscount)
95 $text = $this->buildTextByPredictionDiscount($basketCopied, $predictionDiscount);
102 $templates = (!empty(
$options[
'PAGE_TEMPLATES']) && is_array(
$options[
'PAGE_TEMPLATES']) ?
$options[
'PAGE_TEMPLATES'] : []);
104 return $this->tryToFindPredictionConnectedProducts($basket->copy(), $product, $templates);
107 private function tryToFindPredictionConnectedProducts(
Basket $basket,
array $product,
array $templates = []): ?string
109 if(!$this->checkProductInBasket($product, $basket))
111 $this->addProductToBasket($basket, $product);
114 $discounts = $this->getDiscounts($basket);
115 $predictionDiscount = $this->findFirstPredictionDiscount($discounts, ConnectedProduct::className());
116 if ($predictionDiscount ===
null)
122 $preset =
$manager->getPresetById($predictionDiscount[
'PRESET_ID']);
123 if(!$preset instanceof ConnectedProduct)
128 $currentProductIds = $this->extendProductIds(
array($product[
'ID']));
129 $currentSectionIds = $this->getSectionIdsByProduct($product);
131 $state = $preset->generateState($predictionDiscount);
132 list($typeConditionProduct, $dataCondition) = $preset->getDescribedDataProductCondition($state);
133 list($typeActionProduct, $dataAction) = $preset->getDescribedDataProductAction($state);
135 $isAct = $isCond =
false;
137 if($typeConditionProduct === $preset::TYPE_PRODUCT)
139 $condProductIds = $this->extendProductIds($dataCondition);
140 if(array_intersect($condProductIds, $currentProductIds))
145 elseif($typeConditionProduct === $preset::TYPE_SECTION)
147 if(array_intersect($dataCondition, $currentSectionIds))
153 if($typeActionProduct === $preset::TYPE_PRODUCT)
155 $actProductIds = $this->extendProductIds($dataAction);
156 if(array_intersect($actProductIds, $currentProductIds))
161 elseif($typeActionProduct === $preset::TYPE_SECTION)
163 if(array_intersect($dataAction, $currentSectionIds))
169 if(!$isAct && !$isCond)
174 $predictionText = $preset->getPredictionText(
176 $isAct? $preset::PREDICTION_TEXT_TYPE_ACTION : $preset::PREDICTION_TEXT_TYPE_CONDITION
180 if(Loader::includeModule(
'currency'))
182 $currencyFormat = \CCurrencyLang::getCurrencyFormat($predictionDiscount[
'CURRENCY']);
186 $discountValue = str_replace(
'#', $state[
'discount_value'],
$currencyFormat);
187 if($state[
'discount_type'] ===
'Perc')
189 $discountValue = $state[
'discount_value'] .
' %';
192 $placeholders =
array(
193 '#DISCOUNT_VALUE#' => $discountValue,
198 if ($typeActionProduct === $preset::TYPE_SECTION)
200 $itemData = $this->getSectionData($dataAction, $templates);
201 if (!empty($itemData))
203 $placeholders = $placeholders + $itemData;
207 if ($typeActionProduct === $preset::TYPE_PRODUCT)
209 $itemData = $this->getProductData($dataAction, $templates);
210 if (!empty($itemData))
212 $placeholders = $placeholders + $itemData;
219 if ($typeConditionProduct === $preset::TYPE_SECTION)
221 $itemData = $this->getSectionData($dataCondition, $templates);
222 if (!empty($itemData))
224 $placeholders = $placeholders + $itemData;
228 if($typeConditionProduct === $preset::TYPE_PRODUCT)
230 $itemData = $this->getProductData($dataCondition, $templates);
231 if (!empty($itemData))
233 $placeholders = $placeholders + $itemData;
239 if (empty($placeholders[
'#NAME#']) || empty($placeholders[
'#LINK#']))
245 array_keys($placeholders),
246 array_values($placeholders),
251 private function getProductData($productId,
array $templates = []): ?
array
253 if (is_array($productId))
265 [
'=ID' => $productId,
'CHECK_PERMISSIONS' =>
'N'],
268 [
'ID',
'IBLOCK_ID',
'NAME',
'DETAIL_PAGE_URL',
'CODE',
'IBLOCK_SECTION_ID']
270 if (!empty($templates[
'PRODUCT_URL']))
272 $iterator->SetUrlTemplates($templates[
'PRODUCT_URL']);
281 '#NAME#' => $row[
'~NAME'],
282 '#LINK#' => $row[
'~DETAIL_PAGE_URL']
286 private function getSectionData($sectionId,
array $templates = []): ?
array
288 if (is_array($sectionId))
300 [
'=ID' => $sectionId,
'CHECK_PERMISSIONS' =>
'N'],
303 [
'ID',
'IBLOCK_ID',
'NAME',
'SECTION_PAGE_URL',
'CODE',
'IBLOCK_SECTION_ID']
305 if (!empty($templates[
'SECTION_URL']))
307 $iterator->SetUrlTemplates(
'', $templates[
'SECTION_URL']);
316 '#NAME#' => $row[
'~NAME'],
317 '#LINK#' => $row[
'~SECTION_PAGE_URL']
321 private function getSectionIdsByProduct(
array $product)
323 $sectionIds =
array();
324 foreach($this->extendProductIds(
array($product[
'ID'])) as $productId)
326 $sectionIds = array_merge($sectionIds, $this->getSectionIdsByElement($productId));
329 return $this->extendSectionIds(array_unique($sectionIds));
332 private function getSectionIdsByElement($elementId)
334 $sectionIds =
array();
335 $query = \CIBlockElement::getElementGroups($elementId,
true,
array(
340 while($section =
$query->fetch())
342 $sectionIds[] = $section[
'ID'];
348 private function extendSectionIds(
array $sectionIds)
350 if(empty($sectionIds))
355 $extendedSectionIds =
array();
359 ),
false,
array(
'IBLOCK_ID',
'ID'));
361 while($row =
$query->fetch())
363 $rsParents = \CIBlockSection::getNavChain($row[
'IBLOCK_ID'], $row[
'ID'],
array(
'ID'));
364 while($arParent = $rsParents->fetch())
366 $extendedSectionIds[] = $arParent[
'ID'];
371 return $extendedSectionIds;
379 private function extendProductIds(
array $productIds)
382 $products = \CCatalogSku::getProductList($productIds);
383 if (empty($products))
388 foreach($products as $product)
390 $productIds[] = $product[
'ID'];
396 private function checkProductInBasket(
array $product, Basket $basket)
398 foreach($basket as $item)
402 $item->getProductId() == $product[
'ID'] &&
403 $item->getField(
'MODULE') == $product[
'MODULE']
413 private function buildTextByPredictionDiscount(Basket $basket,
array $discount): ?string
419 $manager = Discount\Preset\Manager::getInstance();
421 $state = $preset->generateState(
$discount);
424 if(Loader::includeModule(
'currency'))
430 $placeholders =
array();
431 if($preset instanceof OrderAmount)
433 $discountValue = str_replace(
'#', $state[
'discount_value'],
$currencyFormat);
434 if($state[
'discount_type'] ===
'Perc')
436 $discountValue = $state[
'discount_value'] .
' %';
439 $shortage = $state[
'discount_order_amount'] - $basket->getPrice();
447 $placeholders =
array(
449 '#DISCOUNT_VALUE#' => $discountValue,
454 array_keys($placeholders),
455 array_values($placeholders),
460 private function findFirstPredictionDiscount(
array $discounts, $typePrediction)
462 if(empty($discounts))
467 $manager = Discount\Preset\Manager::getInstance();
476 if($preset instanceof $typePrediction)
485 private function addProductToBasket(Basket $basket,
array $product)
487 $basketItem = $basket->createItem($product[
'MODULE'], $product[
'ID']);
488 unset($product[
'MODULE'], $product[
'ID']);
490 $basketItem->setFields($product);
493 private function getDiscounts(Basket $basket)
495 if($basket->getOrder())
497 throw new SystemException(
'Could not get discounts by basket which has order.');
503 $orderClass = $registry->getOrderClassName();
506 $order = $orderClass::create($basket->getSiteId(), $this->userId);
509 if(!
$order->setBasket($basket)->isSuccess())
513 $calcResults =
$discount->getApplyResult(
true);
515 return $calcResults[
'FULL_DISCOUNT_LIST']?:
array();
518 private function isValidProduct(
array $product)
520 if(empty($product[
'ID']))
522 $this->errorCollection[] =
new Error(
'Product array has to have ID');
524 if(empty($product[
'MODULE']))
526 $this->errorCollection[] =
new Error(
'Product array has to have MODULE');
528 if(empty($product[
'PRODUCT_PROVIDER_CLASS']))
530 $this->errorCollection[] =
new Error(
'Product array has to have PRODUCT_PROVIDER_CLASS');
532 if(empty($product[
'QUANTITY']))
534 $this->errorCollection[] =
new Error(
'Product array has to have QUANTITY');
536 if($this->errorCollection->count())
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)