%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/sale/lib/discount/prediction/ |
| Current File : //home/bitrix/www/bitrix/modules/sale/lib/discount/prediction/manager.php |
<?php
namespace Bitrix\Sale\Discount\Prediction;
use Bitrix\Iblock\ElementTable;
use Bitrix\Iblock\SectionTable;
use Bitrix\Main\Error;
use Bitrix\Main\ErrorCollection;
use Bitrix\Main\Loader;
use Bitrix\Main\SystemException;
use Bitrix\Sale\Basket;
use Bitrix\Sale\BasketItem;
use Bitrix\Sale\Discount;
use Bitrix\Sale\Order;
use Bitrix\Sale\PriceMaths;
use Sale\Handlers\DiscountPreset\ConnectedProduct;
use Sale\Handlers\DiscountPreset\OrderAmount;
final class Manager
{
/** @var ErrorCollection */
protected $errorCollection;
/** @var Manager */
private static $instance;
/** @var int */
private $userId;
private function __construct()
{
$this->errorCollection = new ErrorCollection;
}
/**
* @return int
*/
public function getUserId()
{
return $this->userId;
}
/**
* @param int $userId
* @return $this
*/
public function setUserId($userId)
{
$this->userId = $userId;
return $this;
}
private function __clone()
{}
/**
* Returns Singleton of Manager
* @return Manager
*/
public static function getInstance()
{
if (!isset(self::$instance))
{
self::$instance = new self;
}
return self::$instance;
}
/**
* Returns first prediction which has user by current basket.
* @param Basket $basket Basket.
* @param array $product Target product.
* @return mixed|null
* @throws SystemException
*/
public function getFirstPredictionTextByProduct(Basket $basket, array $product)
{
$this->errorCollection->clear();
if(!$this->isValidProduct($product))
{
return null;
}
$basketCopied = $basket->copy();
$discounts = $this->getDiscounts($basketCopied);
Discount\Preset\Manager::getInstance()->registerAutoLoader();
$predictionDiscount = $this->findFirstPredictionDiscount($discounts, OrderAmount::className());
if($predictionDiscount)
{
$text = $this->buildTextByPredictionDiscount($basketCopied, $predictionDiscount);
if($text)
{
return $text;
}
}
return $this->tryToFindPredictionConnectedProducts($basket->copy(), $product);
}
private function tryToFindPredictionConnectedProducts(Basket $basket, array $product)
{
if(!$this->checkProductInBasket($product, $basket))
{
$this->addProductToBasket($basket, $product);
}
$discounts = $this->getDiscounts($basket);
$predictionDiscount = $this->findFirstPredictionDiscount($discounts, ConnectedProduct::className());
$manager = Discount\Preset\Manager::getInstance();
$preset = $manager->getPresetById($predictionDiscount['PRESET_ID']);
if(!$preset instanceof ConnectedProduct)
{
return null;
}
$currentProductIds = $this->extendProductIds(array($product['ID']));
$currentSectionIds = $this->getSectionIdsByProduct($product);
$state = $preset->generateState($predictionDiscount);
list($typeConditionProduct, $dataCondition) = $preset->getDescribedDataProductCondition($state);
list($typeActionProduct, $dataAction) = $preset->getDescribedDataProductAction($state);
$isAct = $isCond = false;
$actProductIds = $condProductIds = array();
if($typeConditionProduct === $preset::TYPE_PRODUCT)
{
$condProductIds = $this->extendProductIds($dataCondition);
if(array_intersect($condProductIds, $currentProductIds))
{
$isCond = true;
}
}
elseif($typeConditionProduct === $preset::TYPE_SECTION)
{
if(array_intersect($dataCondition, $currentSectionIds))
{
$isCond = true;
}
}
if($typeActionProduct === $preset::TYPE_PRODUCT)
{
$actProductIds = $this->extendProductIds($dataAction);
if(array_intersect($actProductIds, $currentProductIds))
{
$isAct = true;
}
}
elseif($typeActionProduct === $preset::TYPE_SECTION)
{
if(array_intersect($dataAction, $currentSectionIds))
{
$isAct = true;
}
}
if(!$isAct && !$isCond)
{
return null;
}
$predictionText = $preset->getPredictionText(
$state,
$isAct? $preset::PREDICTION_TEXT_TYPE_ACTION : $preset::PREDICTION_TEXT_TYPE_CONDITION
);
$currencyFormat = '# ' . $predictionDiscount['CURRENCY'];
if(Loader::includeModule('currency'))
{
$currencyFormat = \CCurrencyLang::getCurrencyFormat($predictionDiscount['CURRENCY']);
$currencyFormat = $currencyFormat['FORMAT_STRING'];
}
$discountValue = str_replace('#', $state['discount_value'], $currencyFormat);
if($state['discount_type'] === 'Perc')
{
$discountValue = $state['discount_value'] . ' %';
}
$placeholders = array(
'#DISCOUNT_VALUE#' => $discountValue,
);
if($isCond)
{
if($typeActionProduct === $preset::TYPE_SECTION)
{
$placeholders['#NAME#'] = $this->getSectionName($dataAction);
$placeholders['#LINK#'] = $this->getSectionUrl($dataAction);
}
if($typeActionProduct === $preset::TYPE_PRODUCT)
{
$placeholders['#NAME#'] = $this->getProductName($dataAction);
$placeholders['#LINK#'] = $this->getProductUrl($dataAction);
}
}
elseif($isAct)
{
if($typeConditionProduct === $preset::TYPE_SECTION)
{
$placeholders['#NAME#'] = $this->getSectionName($dataCondition);
$placeholders['#LINK#'] = $this->getSectionUrl($dataCondition);
}
if($typeConditionProduct === $preset::TYPE_PRODUCT)
{
$placeholders['#NAME#'] = $this->getProductName($dataCondition);
$placeholders['#LINK#'] = $this->getProductUrl($dataCondition);
}
}
if (empty($placeholders['#NAME#']) || empty($placeholders['#LINK#']))
{
return null;
}
return str_replace(
array_keys($placeholders),
array_values($placeholders),
$predictionText
);
}
private function getProductUrl($productId)
{
if(is_array($productId))
{
$productId = array_pop($productId);
}
$arElement = \CIBlockElement::GetList(array(), array("=ID" => $productId), false, array("nTopCount" => 1))->fetch();
$url = \CIBlock::ReplaceDetailUrl($arElement['DETAIL_PAGE_URL'], $arElement, false, "E");
return $url;
}
private function getSectionUrl($sectionId)
{
if(is_array($sectionId))
{
$sectionId = array_pop($sectionId);
}
$arSection = \CIBlockSection::GetList(array(), array("=ID" => $sectionId), false, array("nTopCount" => 1))->fetch();
$url = \CIBlock::ReplaceDetailUrl($arSection['SECTION_PAGE_URL'], $arSection, false, "S");
return $url;
}
private function getProductName($productId)
{
if(is_array($productId))
{
$productId = array_pop($productId);
}
$product = ElementTable::getList(array(
'select' => array('NAME'),
'filter' => array('ID' => $productId)
))->fetch();
return $product['NAME'];
}
private function getSectionName($sectionId)
{
if(is_array($sectionId))
{
$sectionId = array_pop($sectionId);
}
$section = SectionTable::getList(array(
'select' => array('NAME'),
'filter' => array('ID' => $sectionId)
))->fetch();
return $section['NAME'];
}
private function getSectionIdsByProduct(array $product)
{
$sectionIds = array();
foreach($this->extendProductIds(array($product['ID'])) as $productId)
{
$sectionIds = array_merge($sectionIds, $this->getSectionIdsByElement($productId));
}
return $this->extendSectionIds(array_unique($sectionIds));
}
private function getSectionIdsByElement($elementId)
{
$sectionIds = array();
$query = \CIBlockElement::getElementGroups($elementId, true, array(
"ID",
"IBLOCK_SECTION_ID",
"IBLOCK_ELEMENT_ID",
));
while($section = $query->fetch())
{
$sectionIds[] = $section['ID'];
}
return $sectionIds;
}
private function extendSectionIds(array $sectionIds)
{
if(empty($sectionIds))
{
return array();
}
$extendedSectionIds = array();
$query = \CIBlockSection::GetList(array(), array(
'ID' => $sectionIds
), false, array('IBLOCK_ID', 'ID'));
while($row = $query->fetch())
{
$rsParents = \CIBlockSection::getNavChain($row['IBLOCK_ID'], $row['ID'], array('ID'));
while($arParent = $rsParents->fetch())
{
$extendedSectionIds[] = $arParent['ID'];
}
}
return $extendedSectionIds;
}
/**
* Extends to sku ids.
* @param array $productIds
* @return array
*/
private function extendProductIds(array $productIds)
{
//todo catalog!!!
$products = \CCatalogSku::getProductList($productIds);
if (empty($products))
{
return $productIds;
}
foreach($products as $product)
{
$productIds[] = $product['ID'];
}
return $productIds;
}
private function checkProductInBasket(array $product, Basket $basket)
{
foreach($basket as $item)
{
/** @var BasketItem $item */
if(
$item->getProductId() == $product['ID'] &&
$item->getField('MODULE') == $product['MODULE']
)
{
return true;
}
}
return false;
}
private function buildTextByPredictionDiscount(Basket $basket, array $discount)
{
$manager = Discount\Preset\Manager::getInstance();
$preset = $manager->getPresetById($discount['PRESET_ID']);
$state = $preset->generateState($discount);
$currencyFormat = '# ' . $discount['CURRENCY'];
if(Loader::includeModule('currency'))
{
$currencyFormat = \CCurrencyLang::getCurrencyFormat($discount['CURRENCY']);
$currencyFormat = $currencyFormat['FORMAT_STRING'];
}
$placeholders = array();
if($preset instanceof OrderAmount)
{
$discountValue = str_replace('#', $state['discount_value'], $currencyFormat);
if($state['discount_type'] === 'Perc')
{
$discountValue = $state['discount_value'] . ' %';
}
$shortage = $state['discount_order_amount'] - $basket->getPrice();
if($shortage <= 0)
{
return '';
}
$shortage = PriceMaths::roundByFormatCurrency($shortage, $discount['CURRENCY']);
$placeholders = array(
'#SHORTAGE#' => str_replace('#', $shortage, $currencyFormat),
'#DISCOUNT_VALUE#' => $discountValue,
);
}
return str_replace(
array_keys($placeholders),
array_values($placeholders),
(string)$discount['PREDICTION_TEXT']
);
}
private function findFirstPredictionDiscount(array $discounts, $typePrediction)
{
if(empty($discounts))
{
return null;
}
$manager = Discount\Preset\Manager::getInstance();
foreach($discounts as $discount)
{
if(empty($discount['PRESET_ID']) || empty($discount['PREDICTION_TEXT']))
{
continue;
}
$preset = $manager->getPresetById($discount['PRESET_ID']);
if($preset instanceof $typePrediction)
{
return $discount;
}
}
return null;
}
private function addProductToBasket(Basket $basket, array $product)
{
$basketItem = $basket->createItem($product['MODULE'], $product['ID']);
unset($product['MODULE'], $product['ID']);
$basketItem->setFields($product);
}
private function getDiscounts(Basket $basket)
{
if($basket->getOrder())
{
throw new SystemException('Could not get discounts by basket which has order.');
}
/** @var Order $order */
$order = Order::create($basket->getSiteId(), $this->userId);
$discount = $order->getDiscount();
$discount->enableCheckingPrediction();
if(!$order->setBasket($basket)->isSuccess())
{
return array();
}
$calcResults = $discount->getApplyResult(true);
return $calcResults['FULL_DISCOUNT_LIST']?: array();
}
private function isValidProduct(array $product)
{
if(empty($product['ID']))
{
$this->errorCollection[] = new Error('Product array has to have ID');
}
if(empty($product['MODULE']))
{
$this->errorCollection[] = new Error('Product array has to have MODULE');
}
if(empty($product['PRODUCT_PROVIDER_CLASS']))
{
$this->errorCollection[] = new Error('Product array has to have PRODUCT_PROVIDER_CLASS');
}
if(empty($product['QUANTITY']))
{
$this->errorCollection[] = new Error('Product array has to have QUANTITY');
}
if($this->errorCollection->count())
{
return false;
}
return true;
}
}