Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
manager.php
1<?php
2
4
5
15use CCatalogSKU;
16use CSaleDiscountActionApply;
17use SplObjectStorage;
18
19final class Manager
20{
28 private static $instance;
30 private $userId;
31
32 private function __construct()
33 {
34 $this->errorCollection = new ErrorCollection;
35 $this->basketCloneCache = new SplObjectStorage;
36 $this->basketAddedProduct = new SplObjectStorage;
37 }
38
44 private function getBasketCopy(Basket $basket)
45 {
46 if(!$this->basketCloneCache->contains($basket))
47 {
48 $this->basketCloneCache[$basket] = $basket->copy();
49 }
50
51 if($this->basketAddedProduct->contains($this->basketCloneCache[$basket]))
52 {
53 foreach($this->basketAddedProduct[$this->basketCloneCache[$basket]] as $product)
54 {
55 $this->deleteProductFromBasket($this->basketCloneCache[$basket], $product);
56 }
57 $this->basketAddedProduct->detach($this->basketCloneCache[$basket]);
58 }
59
60 return $this->basketCloneCache[$basket];
61 }
62
66 public function getUserId()
67 {
68 return $this->userId;
69 }
70
75 public function setUserId($userId)
76 {
77 $this->userId = $userId;
78
79 return $this;
80 }
81
82 private function __clone()
83 {}
84
89 public static function getInstance()
90 {
91 if (!isset(self::$instance))
92 {
93 self::$instance = new self;
94 }
95
96 return self::$instance;
97 }
98
108 public function getCollectionsByBasket(Basket $basket, array $discounts = null, array $appliedDiscounts = null)
109 {
110 $this->errorCollection->clear();
111
112 if(!$this->existsDiscountsWithGift())
113 {
114 return array();
115 }
116
117 if($discounts === null || $appliedDiscounts === null)
118 {
119 list($discounts, $appliedDiscounts) = $this->getDiscounts($basket);
120 }
121
122 $appliedList = array();
123 foreach($appliedDiscounts as $discount)
124 {
125 $appliedList[$discount['ID']] = $discount;
126 }
127 unset($discount, $appliedDiscounts);
128
129 if(!$discounts)
130 {
131 return array();
132 }
133
134 $potentialGiftData = $this->getPotentialGiftData($discounts, $appliedList);
135
136 $collections = array();
137 foreach($potentialGiftData as $giftData)
138 {
139 $giftData['GiftValue'] = is_array($giftData['GiftValue'])? $giftData['GiftValue'] : array($giftData['GiftValue']);
140
141 $giftCollection = new Collection(array(), $giftData['Type']);
142 foreach($giftData['GiftValue'] as $value)
143 {
144 $giftCollection[] = new Gift($value);
145 }
146 unset($value);
147
148 $collections[] = $giftCollection;
149 }
150 unset($giftData);
151
152 return $collections;
153 }
154
155 private function getGiftedProductIdsByAppliedDiscount(array $discount)
156 {
157 if(empty($discount['RESULT']['BASKET']))
158 {
159 return array();
160 }
161 $giftedProducts = array();
162 foreach($discount['RESULT']['BASKET'] as $item)
163 {
164 if(empty($item['VALUE_PERCENT']) || $item['VALUE_PERCENT'] != 100)
165 {
166 continue;
167 }
168 //todo today we work only with catalog items. In future we will move the method to gifter and there
169 //will return gifted products.
170 if(empty($item['MODULE']) || $item['MODULE'] !== 'catalog')
171 {
172 continue;
173 }
174 $giftedProducts[] = $item['PRODUCT_ID'];
175 }
176 unset($item);
177
178 return $giftedProducts;
179 }
180
181 private function deleteGiftedProducts(array $gifts, array $giftedProductIds)
182 {
183 foreach($gifts as $i => &$giftItem)
184 {
185 if($giftItem['Type'] === CSaleDiscountActionApply::GIFT_SELECT_TYPE_ONE)
186 {
187 if(array_intersect($giftedProductIds, (array)$giftItem['GiftValue']))
188 {
189 unset($gifts[$i]);
190 continue;
191 }
192 }
193 elseif($giftItem['Type'] === CSaleDiscountActionApply::GIFT_SELECT_TYPE_ALL)
194 {
195 $giftItem['GiftValue'] = array_diff((array)$giftItem['GiftValue'], $giftedProductIds);
196 if(!$giftItem['GiftValue'])
197 {
198 unset($gifts[$i]);
199 continue;
200 }
201 }
202 }
203 unset($giftItem);
204
205 return $gifts;
206 }
207
208 private function getPotentialGiftData(array $discounts, array $appliedDiscounts = array())
209 {
210 if(!$discounts)
211 {
212 return array();
213 }
214
215 $potentialGiftData = array();
216 foreach($appliedDiscounts as $discount)
217 {
218 $giftedProductIds = $this->getGiftedProductIdsByAppliedDiscount($discount);
219 $potentialGiftData = array_merge(
220 $potentialGiftData,
221 $this->deleteGiftedProducts(
222 \CSaleActionGiftCtrlGroup::ProvideGiftProductData($discount),
223 \CSaleActionGiftCtrlGroup::ExtendProductIds($giftedProductIds)
224 )
225 );
226 }
227 unset($discount);
228
229 foreach($discounts as $discount)
230 {
231 if(isset($appliedDiscounts[$discount['ID']]))
232 {
233 continue;
234 }
235 //todo Does the list use LAST_DISCOUNT configuration?
236 $data = \CSaleActionGiftCtrlGroup::ProvideGiftProductData($discount);
237 if(!$data)
238 {
239 continue;
240 }
241 $potentialGiftData = array_merge($potentialGiftData, $data);
242 }
243 unset($discount);
244
245 return $potentialGiftData;
246 }
247
248 private function isValidProduct(array $product)
249 {
250 if(empty($product['ID']))
251 {
252 $this->errorCollection[] = new Error('Product array has to have ID');
253 }
254 if(empty($product['MODULE']))
255 {
256 $this->errorCollection[] = new Error('Product array has to have MODULE');
257 }
258 if(empty($product['PRODUCT_PROVIDER_CLASS']))
259 {
260 $this->errorCollection[] = new Error('Product array has to have PRODUCT_PROVIDER_CLASS');
261 }
262 if(empty($product['QUANTITY']))
263 {
264 $this->errorCollection[] = new Error('Product array has to have QUANTITY');
265 }
266 if($this->errorCollection->count())
267 {
268 return false;
269 }
270
271 return true;
272 }
273
284 public function getCollectionsByProduct(Basket $basket, array $product)
285 {
286 $this->errorCollection->clear();
287
288 if(!$this->existsDiscountsWithGift())
289 {
290 return array();
291 }
292
293 if(!$this->isValidProduct($product))
294 {
295 return null;
296 }
297
298 $pseudoBasket = $this->getBasketCopy($basket);
299 $checkProductInBasket = $this->checkProductInBasket($product, $pseudoBasket);
300 if($checkProductInBasket)
301 {
302 $this->deleteProductFromBasket($pseudoBasket, $product, false);
303 }
304 else
305 {
306 $this->addProductToBasket($pseudoBasket, $product);
307 }
308
309 $collectionsByPseudoBasket = $this->getCollectionsByBasket($pseudoBasket);
310 $collectionsByBasket = $this->getCollectionsByBasket($basket);
311
312 if(!$this->hasDifference($collectionsByBasket, $collectionsByPseudoBasket))
313 {
314 return array();
315 }
316
317 return $checkProductInBasket? $collectionsByBasket : $collectionsByPseudoBasket;
318 }
319
320 private function hasDifference(array $collectionsA, array $collectionsB)
321 {
322 foreach($collectionsA as $i => $collectionA)
323 {
324 $found = false;
325 foreach($collectionsB as $j => $collectionB)
326 {
327 if($this->isEqual($collectionA, $collectionB))
328 {
329 $found = true;
330 unset($collectionsA[$i]);
331 unset($collectionsB[$j]);
332
333 break;
334 }
335 }
336 unset($collectionB);
337
338 if(!$found)
339 {
340 return true;
341 }
342 }
343 unset($collectionA);
344
345 return (bool)$collectionsB;
346 }
347
348 private function isEqual(Collection $collectionA, Collection $collectionB)
349 {
350 $productIdsFromCollectionA = $this->getProductIdsFromCollection($collectionA);
351 $productIdsFromCollectionB = $this->getProductIdsFromCollection($collectionB);
352
353 return
354 !array_diff($productIdsFromCollectionA, $productIdsFromCollectionB) &&
355 !array_diff($productIdsFromCollectionB, $productIdsFromCollectionA)
356 ;
357 }
358
359 private function getProductIdsFromCollection(Collection $collection)
360 {
361 $idsFrom = array();
362 foreach($collection as $gift)
363 {
365 $idsFrom[$gift->getProductId()] = $gift->getProductId();
366 }
367 unset($gift);
368
369 return $idsFrom;
370 }
371
381 private function getAffectedReformattedBasketItemsInDiscount(Basket $basket, array $discountData, array $calcResults)
382 {
383 $items = array();
384 foreach($calcResults['PRICES']['BASKET'] as $basketCode => $priceData)
385 {
386 if(empty($priceData['DISCOUNT']))
387 {
388 continue;
389 }
390 if(!empty($priceData['PRICE']))
391 {
392 continue;
393 }
394 if(empty($calcResults['RESULT']['BASKET'][$basketCode]))
395 {
396 continue;
397 }
398 //we have gift and PRICE equals 0.
399 $found = false;
400 foreach($calcResults['RESULT']['BASKET'][$basketCode] as $data)
401 {
402 if($data['DISCOUNT_ID'] == $discountData['ID'])
403 {
404 $found = true;
405 }
406 }
407 unset($data);
408
409 if(!$found)
410 {
411 continue;
412 }
413
414 $basketItem = $basket->getItemByBasketCode($basketCode);
415 if(!$basketItem || $basketItem->getField('MODULE') != 'catalog')
416 {
417 continue;
418 }
419
420 $items[] = array(
421 'PRODUCT_ID' => $basketItem->getProductId(),
422 'VALUE_PERCENT' => '100',
423 'MODULE' => 'catalog',
424 );
425 }
426 unset($priceData);
427
428 return $items;
429 }
430
431 private function getDiscounts(Basket $basket)
432 {
433 if($basket->getOrder())
434 {
435 throw new SystemException('Could not get discounts by basket which has order.');
436 }
437
440 $orderClass = $registry->getOrderClassName();
441
442 $order = $orderClass::create($basket->getSiteId(), $this->userId);
443 if(!$order->setBasket($basket)->isSuccess())
444 {
445 return null;
446 }
447 $discount = $order->getDiscount();
448 $discount->calculate();
449 $calcResults = $discount->getApplyResult(true);
450 unset($discount);
451
452 $appliedDiscounts = array();
453 foreach($calcResults['DISCOUNT_LIST'] as $discountData)
454 {
455 if(isset($calcResults['FULL_DISCOUNT_LIST'][$discountData['REAL_DISCOUNT_ID']]))
456 {
457 $appliedDiscounts[$discountData['REAL_DISCOUNT_ID']] = $calcResults['FULL_DISCOUNT_LIST'][$discountData['REAL_DISCOUNT_ID']];
458 if(empty($appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET']))
459 {
460 $appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'] = array();
461 }
462
463 $appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'] = array_merge(
464 $appliedDiscounts[$discountData['REAL_DISCOUNT_ID']]['RESULT']['BASKET'],
465 $this->getAffectedReformattedBasketItemsInDiscount($basket, $discountData, $calcResults)
466 );
467 }
468 }
469 unset($discountData);
470
471 return array(
472 $calcResults['FULL_DISCOUNT_LIST'],
473 $appliedDiscounts,
474 );
475 }
476
477 private function checkProductInBasket(array $product, Basket $basket)
478 {
479 return (bool)$this->getItemFromBasket($product, $basket);
480 }
481
482 private function getItemFromBasket(array $product, Basket $basket)
483 {
484 foreach($basket as $item)
485 {
487 if(
488 $item->getProductId() == $product['ID'] &&
489 $item->getField('MODULE') === $product['MODULE']
490 )
491 {
492 return $item;
493 }
494 }
495
496 return null;
497 }
498
499 private function addProductToBasket(Basket $basket, array $product)
500 {
501 $basketItem = $basket->createItem($product['MODULE'], $product['ID']);
502 unset($product['MODULE'], $product['ID']);
503
504 $result = $basketItem->setFields($product);
505 if(!$result->isSuccess())
506 {
507 return;
508 }
509
510 if(!$this->basketAddedProduct->contains($basket))
511 {
512 $this->basketAddedProduct[$basket] = array($product);
513 }
514 else
515 {
516 $this->basketAddedProduct[$basket][] = $product;
517 }
518 }
519
520 private function deleteProductFromBasket(Basket $basket, array $product, bool $checkQuantity = true)
521 {
522 $item = $this->getItemFromBasket($product, $basket);
523 if($item && (!$checkQuantity || $item->getQuantity() == $product['QUANTITY']))
524 {
525 $item->delete();
526 }
527 }
528
529 private function existProductInAppliedDiscounts(array $product, array $appliedDiscounts)
530 {
531 foreach($appliedDiscounts as $discount)
532 {
533 if(array_search($product['ID'], $this->getGiftedProductIdsByAppliedDiscount($discount)) !== false)
534 {
535 return true;
536 }
537 }
538
539 return false;
540 }
541
549 public function isGift(Basket $basket, array $product)
550 {
551 $this->errorCollection->clear();
552
553 if(!$this->existsDiscountsWithGift())
554 {
555 return false;
556 }
557
558 if(!$this->isValidProduct($product))
559 {
560 return null;
561 }
562
563 if(!$this->checkProductInBasket($product, $basket))
564 {
565 $basket = $this->getBasketCopy($basket);
566 $this->addProductToBasket($basket, $product);
567 }
568 list(, $appliedDiscounts) = $this->getDiscounts($basket);
569
570 return $this->existProductInAppliedDiscounts($product, $appliedDiscounts);
571 }
572
579 public function isContainGiftAction(array $discount)
580 {
581 return Analyzer::getInstance()->isContainGiftAction($discount);
582 }
583
589 public function existsDiscountsWithGift()
590 {
591 return Option::get('sale', 'exists_discounts_with_gift', 'N') === 'Y';
592 }
593
599 {
600 Option::set('sale', 'exists_discounts_with_gift', 'N');
601 }
602
608 {
609 Option::set('sale', 'exists_discounts_with_gift', 'Y');
610 }
611}
getCollectionsByProduct(Basket $basket, array $product)
Definition manager.php:284
isContainGiftAction(array $discount)
Definition manager.php:579
getCollectionsByBasket(Basket $basket, array $discounts=null, array $appliedDiscounts=null)
Definition manager.php:108
isGift(Basket $basket, array $product)
Definition manager.php:549
static getInstance($type)
Definition registry.php:183