12 private const STORAGE_NAME =
'SALE_USER_BASKET';
13 private const SECTION_QUANTITY =
'SALE_USER_BASKET_QUANTITY';
14 private const SECTION_PRICE =
'SALE_USER_BASKET_PRICE';
16 private static array $currentState;
31 $fuserId = self::prepareFuserId($fuserId);
33 return self::getCurrentBasketQuantity($fuserId,
$siteId) !==
null;
46 $fuserId = self::prepareFuserId($fuserId);
48 return self::getCurrentBasketPrice($fuserId,
$siteId) !==
null;
60 $fuserId = self::prepareFuserId($fuserId);
62 $quantity = self::getCurrentBasketQuantity($fuserId,
$siteId);
63 if ($quantity ===
null)
65 static::updateFUserBasketQuantity($fuserId,
$siteId);
66 $quantity = self::getCurrentBasketQuantity($fuserId,
$siteId);
81 $fuserId = self::prepareFuserId($fuserId);
83 $price = self::getCurrentBasketPrice($fuserId,
$siteId);
86 static::updateFUserBasketPrice($fuserId,
$siteId);
87 $price = self::getCurrentBasketPrice($fuserId,
$siteId);
102 $fuserId = self::prepareFuserId($fuserId);
103 $quantity = self::prepareValue($quantity);
105 self::setCurrentBasketQuantity($fuserId,
$siteId, $quantity);
115 $fuserId = self::prepareFuserId($fuserId);
117 self::clearCurrentBasketQuantity($fuserId,
$siteId);
129 $fuserId = self::prepareFuserId($fuserId);
130 $price = self::prepareValue($price);
132 self::setCurrentBasketPrice($fuserId,
$siteId, $price);
142 $fuserId = self::prepareFuserId($fuserId);
144 self::clearCurrentBasketPrice($fuserId,
$siteId);
157 $fuserId = self::prepareFuserId($fuserId);
161 if ($basketList ===
null)
163 $basketList = static::getFUserBasketList($fuserId,
$siteId);
166 if (!empty($basketList) && is_array($basketList))
168 $orderData = static::calculatePrice($fuserId, $basketList);
169 $price = $orderData[
'ORDER_PRICE'];
172 static::setFUserBasketPrice($fuserId, $price,
$siteId);
185 $fuserId = self::prepareFuserId($fuserId);
189 if ($basketList ===
null)
191 $basketList = static::getFUserBasketList($fuserId,
$siteId);
194 if (!empty($basketList) && is_array($basketList))
196 $quantity =
count($basketList);
199 static::setFUserBasketQuantity($fuserId, $quantity,
$siteId);
211 $fuserId = self::prepareFuserId($fuserId);
213 $basketList = static::getFUserBasketList($fuserId,
$siteId);
215 static::updateFUserBasketPrice($fuserId,
$siteId, $basketList);
216 static::updateFUserBasketQuantity($fuserId,
$siteId, $basketList);
225 protected static function getFUserBasketList($fuserId,
$siteId =
null)
228 $fuserId = self::prepareFuserId($fuserId);
232 $basketClassName = $registry->getBasketClassName();
235 $res = $basketClassName::getList([
237 '=FUSER_ID' => $fuserId,
249 if (!isset(
$data[
'BASE_PRICE']) || (
float)
$data[
'BASE_PRICE'] <= 0)
254 $basketList[] =
$data;
266 protected static function calculatePrice($fuserId,
array $basketList)
271 foreach ($basketList as $basketData)
273 $totalPrice += $basketData[
"PRICE"] * $basketData[
"QUANTITY"];
274 $totalWeight += $basketData[
"WEIGHT"] * $basketData[
"QUANTITY"];
279 'ORDER_PRICE' => $totalPrice,
280 'ORDER_WEIGHT' => $totalWeight,
281 'BASKET_ITEMS' => $basketList
286 $basketClassName = $registry->getBasketClassName();
289 $basket = $basketClassName::create(
SITE_ID);
290 $basket->setFUserId($fuserId);
291 foreach ($basketList as $oldItem)
293 $item = $basket->createItem($oldItem[
'MODULE'], $oldItem[
'PRODUCT_ID']);
294 unset($oldItem[
'MODULE'], $oldItem[
'PRODUCT_ID']);
295 $item->initFields($oldItem);
297 $orderData[
'ORDER_PRICE'] = self::calculateBasketCost($basket);
309 public static function onSaleBasketItemEntitySaved(Main\Event
$event): Main\EventResult
312 $basketItem =
$event->getParameter(
'ENTITY');
314 if (!($basketItem instanceof BasketItem))
316 return new Main\EventResult( Main\EventResult::SUCCESS,
null,
'sale');
318 if ($basketItem->isBundleChild())
320 return new Main\EventResult( Main\EventResult::SUCCESS,
null,
'sale');
324 if (!($basket = $basketItem->getCollection())
325 || ($basketItem->getFUserId() != $fuserId)
328 return new Main\EventResult( Main\EventResult::SUCCESS,
null,
'sale');
331 if ($basketItem->isChanged())
333 $originalValues =
$event->getParameter(
'VALUES');
335 $updateSessionData =
false;
337 if (!$basket->getOrder())
339 if (!$updateSessionData && array_key_exists(
'QUANTITY', $originalValues))
341 $updateSessionData =
true;
346 $updateSessionData =
true;
351 $updateSessionData =
true;
355 if (!$updateSessionData && (array_key_exists(
'ORDER_ID', $originalValues) && (intval($originalValues[
'ORDER_ID'])) == 0 && intval($basketItem->getField(
'ORDER_ID') > 0)))
357 $updateSessionData =
true;
360 if (!$updateSessionData
361 && (array_key_exists(
'CAN_BUY', $originalValues) && ($originalValues[
'CAN_BUY'] !== $basketItem->getField(
'CAN_BUY'))))
363 $updateSessionData =
true;
366 if (!$updateSessionData
367 && (array_key_exists(
'DELAY', $originalValues) && ($originalValues[
'DELAY'] !== $basketItem->getField(
'DELAY'))))
369 $updateSessionData =
true;
372 if ($updateSessionData)
374 $siteId = $basket->getSiteId();
375 static::clearFUserBasketPrice($fuserId,
$siteId);
376 static::clearFUserBasketQuantity($fuserId,
$siteId);
380 return new Main\EventResult( Main\EventResult::SUCCESS,
null,
'sale');
391 $originalValues =
$event->getParameter(
'VALUES');
392 if ($originalValues[
'FUSER_ID'] != $fuserId)
397 static::clearFUserBasketPrice($fuserId,
SITE_ID);
398 static::clearFUserBasketQuantity($fuserId,
SITE_ID);
410 public static function checkQuantityRatio(
Basket $basket,
BasketItem $item =
null)
414 $basketItemRatioList =
array();
415 $ratioList =
array();
416 $ratioResult = static::getRatio($basket, $item);
418 if ($ratioResult->isSuccess())
420 $ratioData = $ratioResult->getData();
422 if (!empty($ratioData[
'RATIO_LIST']) && is_array($ratioData[
'RATIO_LIST']))
424 $ratioList = $ratioData[
'RATIO_LIST'];
429 foreach ($basket as $basketItem)
431 $basketItemCode = $basketItem->getBasketCode();
433 if ($item ===
null || $item->getBasketCode() === $basketItemCode)
435 $basketItemRatioList[$basketItemCode] =
false;
437 if (isset($ratioList[$basketItemCode]))
439 $basketItemQuantity = $basketItem->getQuantity();
440 $basketItemRatio = (float)$ratioList[$basketItemCode];
442 $mod =
roundEx(($basketItemQuantity / $basketItemRatio - round($basketItemQuantity / $basketItemRatio)), 6);
446 $basketItemRatioList[$basketItemCode] =
true;
452 if (!empty($basketItemRatioList))
454 $result->addData(
array(
'CHECK_RATIO_LIST' => $basketItemRatioList));
469 public static function correctQuantityRatio(Basket $basket, BasketItem $item =
null)
472 $changedItems =
array();
474 $checkRatioList =
array();
475 $checkRatioResult = static::checkQuantityRatio($basket, $item);
477 if ($checkRatioResult->isSuccess())
479 $checkRatioData = $checkRatioResult->getData();
481 if (!empty($checkRatioData[
'CHECK_RATIO_LIST']) && is_array($checkRatioData[
'CHECK_RATIO_LIST']))
483 $checkRatioList = $checkRatioData[
'CHECK_RATIO_LIST'];
487 $basketItemRatioList =
array();
491 foreach ($basket as $basketItem)
493 $basketItemCode = $basketItem->getBasketCode();
495 if ($item ===
null || $item->getBasketCode() === $basketItemCode)
497 $basketItemRatioList[$basketItemCode] =
false;
499 if (isset($checkRatioList[$basketItemCode]) && $checkRatioList[$basketItemCode] ===
false)
501 if ($ratioList ===
null)
503 $ratioList =
array();
504 $ratioResult = static::getRatio($basket, $item);
506 if ($ratioResult->isSuccess())
508 $ratioData = $ratioResult->getData();
510 if (!empty($ratioData[
'RATIO_LIST']) && is_array($ratioData[
'RATIO_LIST']))
512 $ratioList = $ratioData[
'RATIO_LIST'];
517 if (!isset($ratioList[$basketItemCode]))
519 $result->addError(
new ResultError(Main\Localization\Loc::getMessage(
'SALE_BASKET_COMPONENT_HELPER_PRODUCT_RATIO_NOT_FOUND',
array(
520 '#PRODUCT_NAME#' => $basketItem->getField(
'NAME')
521 )),
'SALE_BASKET_COMPONENT_HELPER_PRODUCT_RATIO_NOT_FOUND'));
525 $basketItemQuantity = $basketItem->getQuantity();
526 $basketItemRatio = (float)$ratioList[$basketItemCode];
528 $mod =
roundEx(($basketItemQuantity / $basketItemRatio - round($basketItemQuantity / $basketItemRatio)), 6);
532 $changedItems[] = $basketItemCode;
534 $closestQuantity = round($basketItemQuantity / $basketItemRatio) * $basketItemRatio;
535 if ($closestQuantity < $basketItemRatio)
537 $closestQuantity = $basketItemRatio;
540 $r = $basketItem->setField(
'QUANTITY', $closestQuantity);
541 if (!$r->isSuccess())
543 $floorQuantity = floor(ceil($basketItemQuantity) / $basketItemRatio) * $basketItemRatio;
544 if ($floorQuantity < $basketItemRatio)
546 $floorQuantity = $basketItemRatio;
549 if ($floorQuantity != $closestQuantity)
551 $r = $basketItem->setField(
'QUANTITY', $floorQuantity);
555 if (!$r->isSuccess())
557 $result->addErrors($r->getErrors());
559 $r = $basketItem->setField(
'CAN_BUY',
'N');
560 if (!$r->isSuccess())
562 $result->addErrors($r->getErrors());
570 $result->addData(
array(
'CHANGED_BASKET_ITEMS' => $changedItems));
582 public static function getRatio(Basket $basket, BasketItem $item =
null)
585 $ratioList =
array();
586 if (Main\Loader::includeModule(
'catalog'))
589 $elementList =
array();
592 foreach ($basket as $basketItem)
594 $code = $basketItem->getBasketCode();
595 if ($item !==
null && $item->getBasketCode() !=
$code)
600 $hash = md5((strval($basketItem->getField(
"PRODUCT_PROVIDER_CLASS")) !=
'' ? $basketItem->getField(
"PRODUCT_PROVIDER_CLASS"):
"").
"|".(strval($basketItem->getField(
"MODULE")) !=
'' ? $basketItem->getField(
"MODULE"):
"").
"|".$basketItem->getField(
"PRODUCT_ID"));
602 if (array_key_exists(
$hash, static::$cacheRatio))
604 $ratioList[
$code] = static::$cacheRatio[
$hash];
608 $elementList[$basketItem->getField(
"PRODUCT_ID")] = $basketItem->getField(
"PRODUCT_ID");
611 if (!isset(
$map[$basketItem->getField(
"PRODUCT_ID")]))
613 $map[$basketItem->getField(
"PRODUCT_ID")] =
array();
616 $map[$basketItem->getField(
"PRODUCT_ID")][] =
$code;
619 if (!empty($elementList))
621 $res = Catalog\MeasureRatioTable::getList(
array(
622 'select' =>
array(
'*'),
623 'filter' =>
array(
'@PRODUCT_ID' => $elementList,
'=IS_DEFAULT' =>
'Y')
625 while ($ratioData =
$res->fetch())
627 if (empty(
$map[$ratioData[
"PRODUCT_ID"]]))
630 foreach (
$map[$ratioData[
"PRODUCT_ID"]] as
$key)
632 $ratioList[
$key] = $ratioData[
"RATIO"];
634 if (!$basketItem = $basket->getItemByBasketCode(
$key))
637 $hash = md5((strval($basketItem->getField(
"PRODUCT_PROVIDER_CLASS")) !=
'' ? $basketItem->getField(
"PRODUCT_PROVIDER_CLASS"):
"").
"|".(strval($basketItem->getField(
"MODULE")) !=
'' ? $basketItem->getField(
"MODULE"):
"").
"|".$basketItem->getField(
"PRODUCT_ID"));
639 static::$cacheRatio[
$hash] = $ratioData[
"RATIO"];
640 static::$cacheRatioData[
$hash] = $ratioData;
644 unset($ratioData, $dbRatio);
646 unset($elementList,
$map);
649 if (!empty($ratioList))
660 protected static function calculateBasketCost(Basket $basket)
662 if ($basket->count() == 0)
665 $oldApiStatus = Compatible\DiscountCompatibility::isUsed();
667 Compatible\DiscountCompatibility::stopUsageCompatible();
669 $basket->refreshData(
array(
'PRICE',
'COUPONS'));
670 $discounts = Discount::buildFromBasket($basket,
new Discount\Context\Fuser($basket->getFUserId(
true)));
671 $discounts->calculate();
672 $discountResult = $discounts->getApplyResult();
675 Compatible\DiscountCompatibility::revertUsageCompatible();
677 if (empty($discountResult[
'PRICES'][
'BASKET']))
681 $discountResult = $discountResult[
'PRICES'][
'BASKET'];
683 foreach ($basket as $basketItem)
685 if (!$basketItem->canBuy())
687 $code = $basketItem->getBasketCode();
688 if (!empty($discountResult[
$code]))
689 $result += $discountResult[
$code][
'PRICE'] * $basketItem->getQuantity();
693 unset($discountResult);
704 return static::$cacheRatio;
713 return static::$cacheRatioData;
718 return Application::getInstance()->getLocalSession(self::STORAGE_NAME);
721 private static function loadStateFromStorage(): void
723 if (isset(self::$currentState))
728 self::$currentState = self::verifyState(
$storage->getData());
731 private static function saveStateToStorage(): void
733 if (!isset(self::$currentState))
738 $storage->setData(self::$currentState);
741 private static function getEmptyState():
array
744 self::SECTION_PRICE => [],
745 self::SECTION_QUANTITY => [],
749 private static function verifyState(
array $state):
array
751 $emptyState = self::getEmptyState();
752 $state = array_intersect_key($state, $emptyState);
755 foreach ($state as $sectionId =>
$sites)
764 return array_merge($emptyState,
$result);
767 private static function verifySection(
array $section):
array
770 foreach ($section as
$siteId => $users)
772 if (!is_string(
$siteId) || !is_array($users))
777 foreach ($users as
$userId => $value)
783 $newUsers[
$userId] = (float)$value;
792 private static function getCurrentValue(
string $sectionId, ?
int $fuserId,
string $siteId): null|int|float
794 if ($fuserId ===
null)
798 self::loadStateFromStorage();
800 return (self::$currentState[$sectionId][
$siteId][$fuserId] ??
null);
803 private static function setCurrentValue(
string $sectionId, ?
int $fuserId,
string $siteId,
int|
float $value): void
805 if ($fuserId ===
null)
809 if (!isset(self::$currentState[$sectionId]))
815 self::saveStateToStorage();
818 private static function clearCurrentValue(
string $sectionId, ?
int $fuserId,
string $siteId): void
820 if ($fuserId ===
null)
824 self::loadStateFromStorage();
825 if (!isset(self::$currentState[$sectionId]))
829 unset(self::$currentState[$sectionId][
$siteId][$fuserId]);
830 self::saveStateToStorage();
833 private static function getCurrentBasketPrice(?
int $fuserId,
string $siteId): null|int|float
835 return self::getCurrentValue(self::SECTION_PRICE, $fuserId,
$siteId);
838 private static function setCurrentBasketPrice(?
int $fuserId,
string $siteId,
int|
float $price): void
840 self::setCurrentValue(self::SECTION_PRICE, $fuserId,
$siteId, $price);
843 private static function clearCurrentBasketPrice(?
int $fuserId,
string $siteId): void
845 self::clearCurrentValue(self::SECTION_PRICE, $fuserId,
$siteId);
848 private static function getCurrentBasketQuantity(?
int $fuserId,
string $siteId): null|int|float
850 return self::getCurrentValue(self::SECTION_QUANTITY, $fuserId,
$siteId);
853 private static function setCurrentBasketQuantity(?
int $fuserId,
string $siteId,
int|
float $quantity): void
855 self::setCurrentValue(self::SECTION_QUANTITY, $fuserId,
$siteId, $quantity);
858 private static function clearCurrentBasketQuantity(?
int $fuserId,
string $siteId): void
860 self::clearCurrentValue(self::SECTION_QUANTITY, $fuserId,
$siteId);
863 private static function prepareSiteId(mixed
$siteId): string
881 private static function prepareFuserId(mixed $fuserId): ?int
883 if ($fuserId !==
null)
885 $fuserId = (int)$fuserId;
895 private static function prepareValue(mixed $value): int|float
897 if (is_int($value) || is_float($value))