Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
manager.php
1<?php
2
4
5
18use Sale\Handlers\DiscountPreset\ConnectedProduct;
19use Sale\Handlers\DiscountPreset\OrderAmount;
20
21final class Manager
22{
26 private static $instance;
28 private $userId;
29
30 private function __construct()
31 {
32 $this->errorCollection = new ErrorCollection;
33 }
34
38 public function getUserId()
39 {
40 return $this->userId;
41 }
42
47 public function setUserId($userId)
48 {
49 $this->userId = $userId;
50
51 return $this;
52 }
53
54 private function __clone()
55 {}
56
61 public static function getInstance()
62 {
63 if (!isset(self::$instance))
64 {
65 self::$instance = new self;
66 }
67
68 return self::$instance;
69 }
70
79 public function getFirstPredictionTextByProduct(Basket $basket, array $product, array $options = []): ?string
80 {
81 $this->errorCollection->clear();
82
83 if(!$this->isValidProduct($product))
84 {
85 return null;
86 }
87
88 $basketCopied = $basket->copy();
89 $discounts = $this->getDiscounts($basketCopied);
90 Discount\Preset\Manager::getInstance()->registerAutoLoader();
91 $predictionDiscount = $this->findFirstPredictionDiscount($discounts, OrderAmount::className());
92
93 if($predictionDiscount)
94 {
95 $text = $this->buildTextByPredictionDiscount($basketCopied, $predictionDiscount);
96 if ($text)
97 {
98 return $text;
99 }
100 }
101
102 $templates = (!empty($options['PAGE_TEMPLATES']) && is_array($options['PAGE_TEMPLATES']) ? $options['PAGE_TEMPLATES'] : []);
103
104 return $this->tryToFindPredictionConnectedProducts($basket->copy(), $product, $templates);
105 }
106
107 private function tryToFindPredictionConnectedProducts(Basket $basket, array $product, array $templates = []): ?string
108 {
109 if(!$this->checkProductInBasket($product, $basket))
110 {
111 $this->addProductToBasket($basket, $product);
112 }
113
114 $discounts = $this->getDiscounts($basket);
115 $predictionDiscount = $this->findFirstPredictionDiscount($discounts, ConnectedProduct::className());
116 if ($predictionDiscount === null)
117 {
118 return null;
119 }
120
121 $manager = Discount\Preset\Manager::getInstance();
122 $preset = $manager->getPresetById($predictionDiscount['PRESET_ID']);
123 if(!$preset instanceof ConnectedProduct)
124 {
125 return null;
126 }
127
128 $currentProductIds = $this->extendProductIds(array($product['ID']));
129 $currentSectionIds = $this->getSectionIdsByProduct($product);
130
131 $state = $preset->generateState($predictionDiscount);
132 list($typeConditionProduct, $dataCondition) = $preset->getDescribedDataProductCondition($state);
133 list($typeActionProduct, $dataAction) = $preset->getDescribedDataProductAction($state);
134
135 $isAct = $isCond = false;
136
137 if($typeConditionProduct === $preset::TYPE_PRODUCT)
138 {
139 $condProductIds = $this->extendProductIds($dataCondition);
140 if(array_intersect($condProductIds, $currentProductIds))
141 {
142 $isCond = true;
143 }
144 }
145 elseif($typeConditionProduct === $preset::TYPE_SECTION)
146 {
147 if(array_intersect($dataCondition, $currentSectionIds))
148 {
149 $isCond = true;
150 }
151 }
152
153 if($typeActionProduct === $preset::TYPE_PRODUCT)
154 {
155 $actProductIds = $this->extendProductIds($dataAction);
156 if(array_intersect($actProductIds, $currentProductIds))
157 {
158 $isAct = true;
159 }
160 }
161 elseif($typeActionProduct === $preset::TYPE_SECTION)
162 {
163 if(array_intersect($dataAction, $currentSectionIds))
164 {
165 $isAct = true;
166 }
167 }
168
169 if(!$isAct && !$isCond)
170 {
171 return null;
172 }
173
174 $predictionText = $preset->getPredictionText(
175 $state,
176 $isAct? $preset::PREDICTION_TEXT_TYPE_ACTION : $preset::PREDICTION_TEXT_TYPE_CONDITION
177 );
178
179 $currencyFormat = '# ' . $predictionDiscount['CURRENCY'];
180 if(Loader::includeModule('currency'))
181 {
182 $currencyFormat = \CCurrencyLang::getCurrencyFormat($predictionDiscount['CURRENCY']);
183 $currencyFormat = $currencyFormat['FORMAT_STRING'];
184 }
185
186 $discountValue = str_replace('#', $state['discount_value'], $currencyFormat);
187 if($state['discount_type'] === 'Perc')
188 {
189 $discountValue = $state['discount_value'] . ' %';
190 }
191
192 $placeholders = array(
193 '#DISCOUNT_VALUE#' => $discountValue,
194 );
195
196 if ($isCond)
197 {
198 if ($typeActionProduct === $preset::TYPE_SECTION)
199 {
200 $itemData = $this->getSectionData($dataAction, $templates);
201 if (!empty($itemData))
202 {
203 $placeholders = $placeholders + $itemData;
204 }
205 unset($itemData);
206 }
207 if ($typeActionProduct === $preset::TYPE_PRODUCT)
208 {
209 $itemData = $this->getProductData($dataAction, $templates);
210 if (!empty($itemData))
211 {
212 $placeholders = $placeholders + $itemData;
213 }
214 unset($itemData);
215 }
216 }
217 elseif ($isAct)
218 {
219 if ($typeConditionProduct === $preset::TYPE_SECTION)
220 {
221 $itemData = $this->getSectionData($dataCondition, $templates);
222 if (!empty($itemData))
223 {
224 $placeholders = $placeholders + $itemData;
225 }
226 unset($itemData);
227 }
228 if($typeConditionProduct === $preset::TYPE_PRODUCT)
229 {
230 $itemData = $this->getProductData($dataCondition, $templates);
231 if (!empty($itemData))
232 {
233 $placeholders = $placeholders + $itemData;
234 }
235 unset($itemData);
236 }
237 }
238
239 if (empty($placeholders['#NAME#']) || empty($placeholders['#LINK#']))
240 {
241 return null;
242 }
243
244 return str_replace(
245 array_keys($placeholders),
246 array_values($placeholders),
247 $predictionText
248 );
249 }
250
251 private function getProductData($productId, array $templates = []): ?array
252 {
253 if (is_array($productId))
254 {
255 $productId = array_pop($productId);
256 }
257 $productId = (int)$productId;
258 if ($productId <= 0)
259 {
260 return null;
261 }
262
263 $iterator = \CIBlockElement::GetList(
264 [],
265 ['=ID' => $productId, 'CHECK_PERMISSIONS' => 'N'],
266 false,
267 false,
268 ['ID', 'IBLOCK_ID', 'NAME', 'DETAIL_PAGE_URL', 'CODE', 'IBLOCK_SECTION_ID']
269 );
270 if (!empty($templates['PRODUCT_URL']))
271 {
272 $iterator->SetUrlTemplates($templates['PRODUCT_URL']);
273 }
274 $row = $iterator->GetNext();
275 unset($iterator);
276 if (empty($row))
277 {
278 return null;
279 }
280 return [
281 '#NAME#' => $row['~NAME'],
282 '#LINK#' => $row['~DETAIL_PAGE_URL']
283 ];
284 }
285
286 private function getSectionData($sectionId, array $templates = []): ?array
287 {
288 if (is_array($sectionId))
289 {
290 $sectionId = array_pop($sectionId);
291 }
292 $sectionId = (int)$sectionId;
293 if ($sectionId <= 0)
294 {
295 return null;
296 }
297
298 $iterator = \CIBlockSection::GetList(
299 [],
300 ['=ID' => $sectionId, 'CHECK_PERMISSIONS' => 'N'],
301 false,
302 false,
303 ['ID', 'IBLOCK_ID', 'NAME', 'SECTION_PAGE_URL', 'CODE', 'IBLOCK_SECTION_ID']
304 );
305 if (!empty($templates['SECTION_URL']))
306 {
307 $iterator->SetUrlTemplates('', $templates['SECTION_URL']);
308 }
309 $row = $iterator->GetNext();
310 unset($iterator);
311 if (empty($row))
312 {
313 return null;
314 }
315 return [
316 '#NAME#' => $row['~NAME'],
317 '#LINK#' => $row['~SECTION_PAGE_URL']
318 ];
319 }
320
321 private function getSectionIdsByProduct(array $product)
322 {
323 $sectionIds = array();
324 foreach($this->extendProductIds(array($product['ID'])) as $productId)
325 {
326 $sectionIds = array_merge($sectionIds, $this->getSectionIdsByElement($productId));
327 }
328
329 return $this->extendSectionIds(array_unique($sectionIds));
330 }
331
332 private function getSectionIdsByElement($elementId)
333 {
334 $sectionIds = array();
335 $query = \CIBlockElement::getElementGroups($elementId, true, array(
336 "ID",
337 "IBLOCK_SECTION_ID",
338 "IBLOCK_ELEMENT_ID",
339 ));
340 while($section = $query->fetch())
341 {
342 $sectionIds[] = $section['ID'];
343 }
344
345 return $sectionIds;
346 }
347
348 private function extendSectionIds(array $sectionIds)
349 {
350 if(empty($sectionIds))
351 {
352 return array();
353 }
354
355 $extendedSectionIds = array();
356
357 $query = \CIBlockSection::GetList(array(), array(
358 'ID' => $sectionIds
359 ), false, array('IBLOCK_ID', 'ID'));
360
361 while($row = $query->fetch())
362 {
363 $rsParents = \CIBlockSection::getNavChain($row['IBLOCK_ID'], $row['ID'], array('ID'));
364 while($arParent = $rsParents->fetch())
365 {
366 $extendedSectionIds[] = $arParent['ID'];
367 }
368 }
369
370
371 return $extendedSectionIds;
372 }
373
379 private function extendProductIds(array $productIds)
380 {
381 //todo catalog!!!
382 $products = \CCatalogSku::getProductList($productIds);
383 if (empty($products))
384 {
385 return $productIds;
386 }
387
388 foreach($products as $product)
389 {
390 $productIds[] = $product['ID'];
391 }
392
393 return $productIds;
394 }
395
396 private function checkProductInBasket(array $product, Basket $basket)
397 {
398 foreach($basket as $item)
399 {
401 if(
402 $item->getProductId() == $product['ID'] &&
403 $item->getField('MODULE') == $product['MODULE']
404 )
405 {
406 return true;
407 }
408 }
409
410 return false;
411 }
412
413 private function buildTextByPredictionDiscount(Basket $basket, array $discount): ?string
414 {
415 if (empty($discount['PREDICTION_TEXT']))
416 {
417 return null;
418 }
419 $manager = Discount\Preset\Manager::getInstance();
420 $preset = $manager->getPresetById($discount['PRESET_ID']);
421 $state = $preset->generateState($discount);
422
423 $currencyFormat = '# ' . $discount['CURRENCY'];
424 if(Loader::includeModule('currency'))
425 {
426 $currencyFormat = \CCurrencyLang::getCurrencyFormat($discount['CURRENCY']);
427 $currencyFormat = $currencyFormat['FORMAT_STRING'];
428 }
429
430 $placeholders = array();
431 if($preset instanceof OrderAmount)
432 {
433 $discountValue = str_replace('#', $state['discount_value'], $currencyFormat);
434 if($state['discount_type'] === 'Perc')
435 {
436 $discountValue = $state['discount_value'] . ' %';
437 }
438
439 $shortage = $state['discount_order_amount'] - $basket->getPrice();
440 if($shortage <= 0)
441 {
442 return null;
443 }
444
445 $shortage = PriceMaths::roundPrecision($shortage);
446
447 $placeholders = array(
448 '#SHORTAGE#' => str_replace('#', $shortage, $currencyFormat),
449 '#DISCOUNT_VALUE#' => $discountValue,
450 );
451 }
452
453 return str_replace(
454 array_keys($placeholders),
455 array_values($placeholders),
456 (string)$discount['PREDICTION_TEXT']
457 );
458 }
459
460 private function findFirstPredictionDiscount(array $discounts, $typePrediction)
461 {
462 if(empty($discounts))
463 {
464 return null;
465 }
466
467 $manager = Discount\Preset\Manager::getInstance();
468 foreach($discounts as $discount)
469 {
470 if(empty($discount['PRESET_ID']) || empty($discount['PREDICTION_TEXT']))
471 {
472 continue;
473 }
474
475 $preset = $manager->getPresetById($discount['PRESET_ID']);
476 if($preset instanceof $typePrediction)
477 {
478 return $discount;
479 }
480 }
481
482 return null;
483 }
484
485 private function addProductToBasket(Basket $basket, array $product)
486 {
487 $basketItem = $basket->createItem($product['MODULE'], $product['ID']);
488 unset($product['MODULE'], $product['ID']);
489
490 $basketItem->setFields($product);
491 }
492
493 private function getDiscounts(Basket $basket)
494 {
495 if($basket->getOrder())
496 {
497 throw new SystemException('Could not get discounts by basket which has order.');
498 }
499
501
503 $orderClass = $registry->getOrderClassName();
504
506 $order = $orderClass::create($basket->getSiteId(), $this->userId);
507 $discount = $order->getDiscount();
508 $discount->enableCheckingPrediction();
509 if(!$order->setBasket($basket)->isSuccess())
510 {
511 return array();
512 }
513 $calcResults = $discount->getApplyResult(true);
514
515 return $calcResults['FULL_DISCOUNT_LIST']?: array();
516 }
517
518 private function isValidProduct(array $product)
519 {
520 if(empty($product['ID']))
521 {
522 $this->errorCollection[] = new Error('Product array has to have ID');
523 }
524 if(empty($product['MODULE']))
525 {
526 $this->errorCollection[] = new Error('Product array has to have MODULE');
527 }
528 if(empty($product['PRODUCT_PROVIDER_CLASS']))
529 {
530 $this->errorCollection[] = new Error('Product array has to have PRODUCT_PROVIDER_CLASS');
531 }
532 if(empty($product['QUANTITY']))
533 {
534 $this->errorCollection[] = new Error('Product array has to have QUANTITY');
535 }
536 if($this->errorCollection->count())
537 {
538 return false;
539 }
540
541 return true;
542 }
543}
getFirstPredictionTextByProduct(Basket $basket, array $product, array $options=[])
Definition manager.php:79
static roundPrecision($value)
static getInstance($type)
Definition registry.php:183