Current Path : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/sale/lib/ |
Current File : /home/bitrix/ext_www/klimatlend.ua/bitrix/modules/sale/lib/discount.php |
<?php /** * Bitrix Framework * @package bitrix * @subpackage sale * @copyright 2001-2012 Bitrix */ namespace Bitrix\Sale; use Bitrix\Main, Bitrix\Main\Localization\Loc, Bitrix\Sale\Compatible, Bitrix\Sale\Internals, Bitrix\Sale\Discount\Context, Bitrix\Sale\Discount\RuntimeCache; Loc::loadMessages(__FILE__); class Discount { const EVENT_EXTEND_ORDER_DATA = 'onExtendOrderData'; const APPLY_MODE_ADD = 0x0001; const APPLY_MODE_DISABLE = 0x0002; const APPLY_MODE_LAST = 0x0004; const APPLY_MODE_FULL_DISABLE = 0x0008; const APPLY_MODE_FULL_LAST = 0x0010; const USE_MODE_FULL = 0x00001; const USE_MODE_APPLY = 0x0002; const USE_MODE_MIXED = 0x0004; const USE_MODE_COUPONS = 0x0008; const ROUND_MODE_BASKET_DISCOUNT = 0x0001; const ROUND_MODE_SALE_DISCOUNT = 0x0002; const ROUND_MODE_FINAL_PRICE = 0x0004; const ERROR_ID = 'BX_SALE_DISCOUNT'; /* Instances */ /** @var array of Discount */ private static $instances = array(); /* Sale objects */ /** @var Order|null */ protected $order = null; /** @var Basket|null */ protected $basket = null; /** @var null|Shipment $shipment */ protected $shipment = null; /** @var array */ protected $shipmentIds = array(); /** @var Context\BaseContext */ protected $context; /* Calculate data */ /** @var array|null */ protected $orderData = null; /* Calculate options */ /** @var bool */ protected $newOrder = null; /** @var bool */ protected $convertedOrder = null; /** @var int */ protected $useMode = null; /** @var bool */ protected $orderRefresh = false; /** @var array */ protected $saleOptions = array(); /** @var array */ protected $executeModuleFilter = array('all', 'sale', 'catalog'); /* Product discounts for basket items */ /** @var array */ protected $basketDiscountList = array(); /* Various basket items data */ /** @var array */ protected $basketItemsData = array(); /* Sale discount cache on hit */ /** @var array|null */ protected $discountIds = null; /** @var array */ protected $discountByUserCache = array(); /** @var array */ protected $saleDiscountCache = array(); /** @var string */ protected $saleDiscountCacheKey = ''; /** @var array */ protected $cacheDiscountModules = array(); /* Order discounts and coupons, converted to unified format */ /** @var array */ protected $discountsCache = array(); /** @var array */ protected $couponsCache = array(); /* Calculation results and applyed flags */ /** @var array */ protected $discountResult = array(); /** @var int */ protected $discountResultCounter = 0; /** @var array */ protected $applyResult = array(); /* Contains additional data used to calculate discounts for an existing order */ protected $discountStoredActionData = array(); /** @var array */ protected $loadedModules = array(); /** @var array */ protected $entityList = array(); /** @var array */ protected $entityResultCache = array(); /** @var array */ protected $currentStep = array(); /** @var array */ protected $forwardBasketTable = array(); /** @var array */ protected $reverseBasketTable = array(); /* Round mode and data */ protected $roundApplyMode = self::ROUND_MODE_FINAL_PRICE; protected $roundApplyConfig = array(); /** * Contains list of discounts which pass condition (@see method checkDiscountConditions()). * * @var array */ protected $fullDiscountList = array(); /** @var bool */ protected $isClone = false; /** @var bool */ protected $enableCheckingPrediction = false; protected function __construct() { } /** * Enables prediction checking instead real condition. * @return void */ public function enableCheckingPrediction() { $this->enableCheckingPrediction = true; $this->saleOptions = array( 'APPLY_MODE' => $this::APPLY_MODE_ADD, ); } /** * Disables prediction checking instead real condition. * @return void */ public function disableCheckingPrediction() { $this->enableCheckingPrediction = false; } /** * Get discount by fuser and site. * * @param string|int $fuser Fuser id. * @param string $site Site id. * @return null|Discount */ public static function loadByFuser($fuser, $site) { $instanceIndex = static::getInstanceIndexByFuser((int)$fuser, (string)$site); if (isset(self::$instances[$instanceIndex])) return self::$instances[$instanceIndex]; return null; } /** * Get discount by basket. * * @deprecated deprecated sinse sale 17.0.11 * @see Discount::buildFromBasket * * @param BasketBase $basket Basket object. * @return null|Discount */ public static function loadByBasket(BasketBase $basket) { $order = $basket->getOrder(); if ($order instanceof Order) { return self::buildFromOrder($order); } return self::buildFromBasket($basket, new Context\Fuser($basket->getFUserId(true))); } /** * Get discount by order. * * @deprecated deprecated sinse sale 17.0.11 * @see Discount::buildFromOrder * * @param Order $order Order object. * @return Discount */ public static function load(Order $order) { return self::buildFromOrder($order); } /** * @return Discount */ protected static function createDiscountObject() { $registry = Registry::getInstance(Registry::REGISTRY_TYPE_ORDER); $discountClassName = $registry->getDiscountClassName(); return new $discountClassName(); } /** * Builds discounts from order. * * @param Order $order Order object. * @return Discount */ public static function buildFromOrder(Order $order) { $instanceIndex = static::getInstanceIndexByOrder($order); if (!isset(self::$instances[$instanceIndex])) { $discount = static::createDiscountObject(); $discount->order = $order; $discount->context = new Context\User($order->getUserId()); $discount->initInstanceData(); self::$instances[$instanceIndex] = $discount; unset($discount); } return self::$instances[$instanceIndex]; } /** * Builds discounts from basket. Basket doesn't have to have a order. * Context describes user and user groups which use in * * @param BasketBase $basket Basket. * @param Context\BaseContext $context Context. * * @return mixed|null * @throws Main\InvalidOperationException */ public static function buildFromBasket(BasketBase $basket, Context\BaseContext $context) { if ($basket->getOrder()) { throw new Main\InvalidOperationException( 'Could not build discounts from basket which has the order. You have to use buildFromOrder.' ); } if ($basket->count() == 0) { return null; } //todo be careful $context may be important in getInstanceIndexByBasket() $instanceIndex = static::getInstanceIndexByBasket($basket, $context); if (!isset(self::$instances[$instanceIndex])) { $discount = static::createDiscountObject(); } else { $discount = self::$instances[$instanceIndex]; } /** @var Discount $discount */ $discount->basket = $basket; $discount->context = $context; $discount->initInstanceData(); self::$instances[$instanceIndex] = $discount; return self::$instances[$instanceIndex]; } /** * Get discount by order basket. * * @param Basket $basket Basket. * @return Discount */ public static function setOrder(Basket $basket) { $instanceIndex = static::getInstanceIndexByBasket($basket); if (!isset(self::$instances[$instanceIndex])) return static::loadByBasket($basket); $order = $basket->getOrder(); if (!($order instanceof Order)) return self::$instances[$instanceIndex]; $newInstanceIndex = static::getInstanceIndexByOrder($order); if (!isset(self::$instances[$newInstanceIndex])) { /** @var Discount $discount */ $discount = self::$instances[$instanceIndex]; unset(self::$instances[$instanceIndex]); $discount->basket = null; $discount->order = $order; $discount->setNewOrder(); $discount->loadShipment(); $discount->fillShipmentData(); self::$instances[$newInstanceIndex] = $discount; unset($discount); return self::$instances[$newInstanceIndex]; } else { unset(self::$instances[$instanceIndex]); unset(self::$instances[$newInstanceIndex]); return static::load($order); } } /** * Return order. * * @return Order|null */ public function getOrder() { return $this->order; } /** * Return flag is order exists. * * @return bool */ public function isOrderExists() { return ($this->order instanceof Order); } /** * Clone entity. * * @internal * @param \SplObjectStorage $cloneEntity Clone repository. * * @return Discount */ public function createClone(\SplObjectStorage $cloneEntity) { if ($this->isClone() && $cloneEntity->contains($this)) return $cloneEntity[$this]; $discountClone = clone $this; $discountClone->isClone = true; if (!$cloneEntity->contains($this)) $cloneEntity[$this] = $discountClone; if ($this->isOrderExists()) { if ($cloneEntity->contains($this->order)) $discountClone->order = $cloneEntity[$this->order]; } elseif ($this->isBasketExist()) { if ($cloneEntity->contains($this->basket)) $discountClone->basket = $cloneEntity[$this->basket]; } if ($this->isShipmentExists()) { if ($cloneEntity->contains($this->shipment)) $discountClone->shipment = $cloneEntity[$this->shipment]; } return $discountClone; } /** * Returns true if discount entity is cloned. * * @return bool */ public function isClone() { return $this->isClone; } /** * Return apply mode list. * * @param bool $extendedMode Get mode list with names. * @return array */ public static function getApplyModeList($extendedMode = false) { $extendedMode = ($extendedMode === true); if ($extendedMode) { return array( self::APPLY_MODE_ADD => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_ADD_EXT'), self::APPLY_MODE_LAST => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_LAST_EXT'), self::APPLY_MODE_DISABLE => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_DISABLE_EXT'), self::APPLY_MODE_FULL_LAST => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_FULL_LAST'), self::APPLY_MODE_FULL_DISABLE => Loc::getMessage('BX_SALE_DISCOUNT_APPLY_MODE_FULL_DISABLE') ); } return array( self::APPLY_MODE_ADD, self::APPLY_MODE_LAST, self::APPLY_MODE_DISABLE, self::APPLY_MODE_FULL_LAST, self::APPLY_MODE_FULL_DISABLE ); } /** * Returns current sale discount apply mode. * * @return int * @throws Main\ArgumentNullException */ public static function getApplyMode() { $applyMode = self::APPLY_MODE_ADD; if ((string)Main\Config\Option::get('sale', 'use_sale_discount_only') != 'Y') { $applyMode = (int)Main\Config\Option::get('sale', 'discount_apply_mode'); if (!in_array($applyMode, self::getApplyModeList(false))) $applyMode = self::APPLY_MODE_ADD; } return $applyMode; } /** * Set calculate mode. * * @param int $useMode Calculate mode. * @return void */ public function setUseMode($useMode) { $useMode = (int)$useMode; if ($useMode <= 0) return; $this->useMode = $useMode; } /** * Return calculate mode. * * @return int */ public function getUseMode() { return $this->useMode; } /** * Set full refresh status from edit order form. * * @param bool $state Refresh or not order. * @return void */ public function setOrderRefresh($state) { if ($state !== true && $state !== false) return; $this->orderRefresh = $state; } /** * Returns full refresh status value. * * @return bool */ public function isOrderRefresh() { return $this->orderRefresh; } /** * Returns new order flag value. * * @return bool */ public function isOrderNew() { return $this->newOrder; } /** * Sets list of execute module which will be used to filter discount. * * @internal * @param array $moduleList Allowed execute module list. * @return void */ public function setExecuteModuleFilter(array $moduleList) { $this->executeModuleFilter = $moduleList; } /** * Set calculate shipments. * * @param Shipment $shipment Current shipment. * @return void */ public function setCalculateShipments(Shipment $shipment = null) { $this->shipment = $shipment; } /** * Return shipment id list for existing order. * * @return array */ public function getShipmentsIds() { return $this->shipmentIds; } /** * Calculate discounts. * * @return Result * @throws Main\ArgumentNullException */ public function calculate() { /** @var Result $result */ $result = new Result; $process = true; if ($this->stopCalculate()) return $result; $this->discountsCache = array(); $this->couponsCache = array(); if (Compatible\DiscountCompatibility::isUsed()) return $result; $this->initUseMode(); if ($this->isOrderExists() && !$this->isOrderNew()) { if ($this->isOrderRefresh()) { $this->setApplyResult(array()); DiscountCouponsManager::useSavedCouponsForApply(true); } } $this->orderData = null; $orderResult = $this->loadOrderData(); if (!$orderResult->isSuccess()) { $process = false; $result->addErrors($orderResult->getErrors()); } unset($orderResult); if ($this->convertedOrder) return $result; if ($process) { DiscountCouponsManager::setUseOnlySaleDiscounts($this->useOnlySaleDiscounts()); $this->resetBasketPrices(); switch ($this->getUseMode()) { case self::USE_MODE_APPLY: $calculateResult = $this->calculateApply(); break; case self::USE_MODE_MIXED: $calculateResult = $this->calculateMixed(); break; case self::USE_MODE_FULL: $calculateResult = $this->calculateFull(); break; default: $calculateResult = new Result; $calculateResult->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_USE_MODE'), self::ERROR_ID )); break; } if (!$calculateResult->isSuccess()) $result->addErrors($calculateResult->getErrors()); else $result->setData($this->fillDiscountResult()); unset($calculateResult); } return $result; } /* apply result methods */ /** * Change applied discount list. * * @param array $applyResult Change apply result. * @return void */ public function setApplyResult($applyResult) { if (is_array($applyResult)) $this->applyResult = $applyResult; if (!empty($this->applyResult['DISCOUNT_LIST'])) { if (!empty($this->applyResult['BASKET']) && is_array($this->applyResult['BASKET'])) { foreach ($this->applyResult['BASKET'] as $discountList) { if (empty($discountList) || !is_array($discountList)) continue; foreach ($discountList as $orderDiscountId => $apply) { if ($apply == 'Y') $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] = 'Y'; } unset($apply, $orderDiscountId); } unset($discountList); } if (!empty($this->applyResult['DELIVERY']) && is_array($this->applyResult['DELIVERY'])) { foreach ($this->applyResult['DELIVERY'] as $orderDiscountId => $apply) { if ($apply == 'Y') $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] = 'Y'; } unset($apply, $orderDiscountId); } } } /** * Return discount list description. * * @param bool $extMode Extended mode. * @return array */ public function getApplyResult($extMode = false) { if (Compatible\DiscountCompatibility::isUsed()) return Compatible\DiscountCompatibility::getApplyResult($extMode); $extMode = ($extMode === true); if (!$this->isOrderNew() && empty($this->orderData)) { $this->initUseMode(); $this->loadOrderData(); } $this->getApplyDiscounts(); $this->getApplyPrices(); $this->getApplyDeliveryList(); if ($extMode) $this->remakingDiscountResult(); $result = $this->discountResult; $result['CONVERTED_ORDER'] = ($this->convertedOrder ? 'Y' : 'N'); $result['FULL_DISCOUNT_LIST'] = $this->fullDiscountList; if ($extMode) { unset($result['APPLY_BLOCKS']); } else { /* for compatibility only */ if (isset($this->discountResult['APPLY_BLOCKS'][0]['BASKET'])) $result['BASKET'] = $this->discountResult['APPLY_BLOCKS'][0]['BASKET']; if (isset($this->discountResult['APPLY_BLOCKS'][0]['ORDER'])) $result['ORDER'] = $this->discountResult['APPLY_BLOCKS'][0]['ORDER']; } return $result; } /* apply result methods finish */ /** * Save discount result. * * @return Result */ public function save() { $process = true; $result = new Result; if (!$this->isOrderExists() || !$this->isBasketNotEmpty()) return $result; $orderId = (int)$this->getOrder()->getId(); if ($this->isUsedDiscountCompatibility()) { if (Compatible\DiscountCompatibility::isRepeatSave()) return $result; $compatibleResult = Compatible\DiscountCompatibility::getResult(); if ($compatibleResult === false) return $result; if (empty($compatibleResult)) return $result; $this->setUseMode($compatibleResult['CALCULATE']['USE_MODE']); $this->newOrder = $compatibleResult['CALCULATE']['NEW_ORDER']; $this->discountsCache = $compatibleResult['DISCOUNT_LIST']; $this->couponsCache = $compatibleResult['COUPONS_LIST']; $this->discountResult = $compatibleResult['DISCOUNT_RESULT']; $this->forwardBasketTable = $compatibleResult['FORWARD_BASKET_TABLE']; $this->reverseBasketTable = $compatibleResult['REVERSE_BASKET_TABLE']; if ($this->isOrderNew()) { $shipmentResult = $this->loadShipment(); if (!$shipmentResult->isSuccess()) { } unset($shipmentResult); $this->fillShipmentData(); } unset($compatibleResult); } if ($this->getUseMode() === null) return $result; if ($process) { switch ($this->getUseMode()) { case self::USE_MODE_FULL: $saveResult = $this->saveFull(); break; case self::USE_MODE_APPLY: $saveResult = $this->saveApply(); break; case self::USE_MODE_MIXED: $saveResult = $this->saveMixed(); break; default: $saveResult = new Result; $saveResult->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_BAD_USE_MODE'), self::ERROR_ID )); } if (!$saveResult->isSuccess()) { $result->addErrors($saveResult->getErrors()); } else { if ($orderId > 0) OrderHistory::addLog('DISCOUNT', $orderId, 'DISCOUNT_SAVED', null, null, array(), OrderHistory::SALE_ORDER_HISTORY_LOG_LEVEL_1); } unset($saveResult); } if ($orderId > 0) OrderHistory::collectEntityFields('DISCOUNT', $orderId); return $result; } /** * Initial instance data. * * @return void */ protected function initInstanceData() { $this->orderData = null; $this->isClone = false; $this->entityResultCache = array(); $this->setNewOrder(); $this->fillEmptyDiscountResult(); if ($this->isOrderExists()) { $orderDiscountConfig = array( 'SITE_ID' => $this->getOrder()->getSiteId(), 'CURRENCY' => $this->getOrder()->getCurrency() ); } else { /** @var BasketItem $basketItem */ $basketItem = $this->getBasket()->rewind(); $orderDiscountConfig = array( 'SITE_ID' => $basketItem->getField('LID'), 'CURRENCY' => $basketItem->getCurrency() ); unset($basketItem); } OrderDiscountManager::init(); OrderDiscountManager::setManagerConfig($orderDiscountConfig); unset($orderDiscountConfig); } /** * Return is allow discount calculate. * * @return bool */ protected function stopCalculate() { if (!$this->isBasketNotEmpty()) return true; if ($this->isOrderExists() && $this->getOrder()->isExternal()) return true; if ($this->isOrderExists() && !$this->isShipmentExists()) return false; return false; } /** * Set new order flag. * * @return void */ protected function setNewOrder() { if ($this->newOrder !== null) return; $this->newOrder = true; if ($this->isOrderExists()) $this->newOrder = ((int)$this->getOrder()->getId() <= 0); } /** * Return true, if only sale discounts is allowed. For new order or refreshed order use sale option, otherwise use order option. * * @return bool */ protected function useOnlySaleDiscounts() { if (!$this->isOrderExists() || $this->isOrderNew() || $this->isOrderRefresh()) return (string)Main\Config\Option::get('sale', 'use_sale_discount_only') == 'Y'; else return (isset($this->saleOptions['SALE_DISCOUNT_ONLY']) && $this->saleOptions['SALE_DISCOUNT_ONLY'] == 'Y'); } /** * Return basket item currency. * * @param string|int $basketCode Basket item code. * @return string */ protected function getBasketCurrency($basketCode) { if ($this->isOrderExists()) return $this->getOrder()->getCurrency(); $currency = ''; /** @var BasketItem $basketItem */ if ($this->isBasketNotEmpty()) { $basket = $this->getBasket(); foreach ($basket as $basketItem) { if ($basketItem->getBasketCode() == $basketCode) { $currency = $basketItem->getCurrency(); break; } } unset($basket, $basketItem); } if ($currency == '') $currency = (string)Main\Config\Option::get('sale', 'default_currency'); return $currency; } /** * Return current basket. * * @return Basket */ protected function getBasket() { if ($this->isOrderExists()) return $this->getOrder()->getBasket(); else return $this->basket; } /** * Return exists basket. * * @return bool */ protected function isBasketExist() { if ($this->isOrderExists()) return ($this->getOrder()->getBasket() instanceof Basket); else return ($this->basket instanceof Basket); } /** * Returns the existence of a non-empty basket. * * @return bool */ protected function isBasketNotEmpty() { if ($this->isOrderExists()) { $basket = $this->getOrder()->getBasket(); $result = ($basket instanceof Basket && $basket->count() > 0); unset($basket); } else { $result = ($this->basket instanceof Basket && $this->basket->count() > 0); } return $result; } /** * Initialization of the discount calculation mode. * * @return void */ protected function initUseMode() { $this->setUseMode(self::USE_MODE_FULL); if ($this->isOrderExists() && !$this->isOrderNew()) { if ($this->isOrderRefresh()) $this->setUseMode(self::USE_MODE_FULL); elseif ($this->isMixedBasket()) $this->setUseMode(self::USE_MODE_MIXED); elseif ($this->getOrder()->getCalculateType() == Order::SALE_ORDER_CALC_TYPE_REFRESH) $this->setUseMode(self::USE_MODE_FULL); else $this->setUseMode(self::USE_MODE_APPLY); } } /** * Load order information. * * @return Result */ protected function loadOrderData() { $result = new Result; $orderId = 0; if ($this->isOrderExists()) $orderId = $this->getOrder()->getId(); if (empty($this->orderData)) $this->fillEmptyOrderData(); $this->shipmentIds = array(); $basketResult = $this->loadBasket(); if (!$basketResult->isSuccess()) { $result->addErrors($basketResult->getErrors()); return $result; } unset($basketResult); if ($this->isOrderExists() && $orderId > 0) { $basketResult = $this->getBasketTables(); if (!$basketResult->isSuccess()) { $result->addErrors($basketResult->getErrors()); return $result; } unset($basketResult); } $this->loadOrderConfig(); $discountResult = $this->loadOrderDiscounts(); if (!$discountResult->isSuccess()) $result->addErrors($discountResult->getErrors()); unset($discountResult); $dataResult = $this->loadBasketStoredData(); if (!$dataResult->isSuccess()) $result->addErrors($dataResult->getErrors()); unset($dataResult); if (!$this->isShipmentExists()) { $shipmentResult = $this->loadShipment(); if (!$shipmentResult->isSuccess()) { $result->addErrors($shipmentResult->getErrors()); return $result; } unset($shipmentResult); } $this->fillShipmentData(); return $result; } /** * Fill empty order data. * * @return void */ protected function fillEmptyOrderData() { /** @var Basket $basket*/ $basket = $this->getBasket(); if ($this->isOrderExists()) { $order = $this->getOrder(); $this->orderData = array( 'ID' => $order->getId(), 'USER_ID' => $order->getUserId(), 'SITE_ID' => $order->getSiteId(), 'LID' => $order->getSiteId(), // compatibility only 'ORDER_PRICE' => $order->getPrice(), 'ORDER_WEIGHT' => $basket->getWeight(), 'CURRENCY' => $order->getCurrency(), 'PERSON_TYPE_ID' => $order->getPersonTypeId(), 'RECURRING_ID' => $order->getField('RECURRING_ID'), 'BASKET_ITEMS' => array(), 'PRICE_DELIVERY' => 0, 'PRICE_DELIVERY_DIFF' => 0, 'DELIVERY_ID' => 0, 'CUSTOM_PRICE_DELIVERY' => 'N', 'SHIPMENT_CODE' => 0, 'SHIPMENT_ID' => 0, 'ORDER_PROP' => array() ); $paymentCollection = $order->getPaymentCollection(); /** @var Payment $payment */ foreach ($paymentCollection as $payment) { if ($payment->isInner()) continue; if (!isset($this->orderData['PAY_SYSTEM_ID'])) { $this->orderData['PAY_SYSTEM_ID'] = (int)$payment->getPaymentSystemId(); break; } } unset($payment, $paymentCollection); if (!isset($this->orderData['PAY_SYSTEM_ID'])) $this->orderData['PAY_SYSTEM_ID'] = 0; /** @var \Bitrix\Sale\PropertyValueCollection $shipmentCollection */ $propertyCollection = $order->getPropertyCollection(); /** @var \Bitrix\Sale\PropertyValue $orderProperty */ foreach ($propertyCollection as $orderProperty) $this->orderData['ORDER_PROP'][$orderProperty->getPropertyId()] = $orderProperty->getValue(); unset($orderProperty); foreach (static::getOrderPropertyCodes() as $propertyCode => $attribute) { $this->orderData[$propertyCode] = ''; $orderProperty = $propertyCollection->getAttribute($attribute); if ($orderProperty instanceof PropertyValue) $this->orderData[$propertyCode] = $orderProperty->getValue(); unset($orderProperty); } unset($propertyCode, $attribute); unset($propertyCollection); } else { if ($this->isBasketNotEmpty()) { /** @var BasketItem $basketItem */ $basketItem = $basket->rewind(); $currency = $basketItem->getCurrency(); unset($basketItem); } else { $currency = Internals\SiteCurrencyTable::getCurrency($basket->getSiteId()); } $this->orderData = array( 'ID' => 0, 'USER_ID' => $this->context->getUserId(), 'SITE_ID' => $basket->getSiteId(), 'ORDER_PRICE' => $basket->getPrice(), 'ORDER_WEIGHT' => $basket->getWeight(), 'CURRENCY' => $currency, 'PERSON_TYPE_ID' => 0, 'BASKET_ITEMS' => array(), 'PRICE_DELIVERY' => 0, 'PRICE_DELIVERY_DIFF' => 0, 'DELIVERY_ID' => 0, 'CUSTOM_PRICE_DELIVERY' => 'N', 'SHIPMENT_CODE' => 0, 'SHIPMENT_ID' => 0, 'PAY_SYSTEM_ID' => 0 ); unset($currency); } unset($basket); } /** * Get basket data. * * @return Result * @throws Main\ObjectNotFoundException */ protected function loadBasket() { $result = new Result; $process = true; if (!$this->isBasketExist()) throw new Main\ObjectNotFoundException('Entity "Basket" not found'); elseif (!$this->isBasketNotEmpty()) return $result; /** @var Basket $basket */ $basket = $this->getBasket(); if ($process) { /** @var BasketItem $basketItem */ foreach ($basket as $basketItem) { if (!$basketItem->canBuy()) continue; $code = $basketItem->getBasketCode(); $this->orderData['BASKET_ITEMS'][$code] = $basketItem->getFieldValues(); unset($this->orderData['BASKET_ITEMS'][$code]['DATE_INSERT']); unset($this->orderData['BASKET_ITEMS'][$code]['DATE_UPDATE']); $this->orderData['BASKET_ITEMS'][$code]['PROPERTIES'] = $basketItem->getPropertyCollection()->getPropertyValues(); if (!isset($this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE'])) $this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE'] = 0; $this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] = $basketItem->getField('BASE_PRICE'); if ($this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] === null) { $this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$code]['PRICE'] + $this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE']; } if (empty($this->orderData['BASKET_ITEMS'][$code]['PRICE_TYPE_ID'])) $this->orderData['BASKET_ITEMS'][$code]['PRICE_TYPE_ID'] = $this->getBasketItemValue($code, 'PRICE_TYPE_ID'); if ($basketItem->isBundleParent()) { $bundle = $basketItem->getBundleCollection(); if (empty($bundle)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_BASKET_BUNDLE_EMPTY'), self::ERROR_ID )); break; } /** @var BasketItem $bundleItem */ foreach ($bundle as $bundleItem) { $code = $bundleItem->getBasketCode(); $this->orderData['BASKET_ITEMS'][$code] = $bundleItem->getFieldValues(); $this->orderData['BASKET_ITEMS'][$code]['PROPERTIES'] = $bundleItem->getPropertyCollection()->getPropertyValues(); $this->orderData['BASKET_ITEMS'][$code]['IN_SET'] = 'Y'; if (!isset($this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE'])) $this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE'] = 0; $this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] = $bundleItem->getField('BASE_PRICE'); if ($this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] === null) { $this->orderData['BASKET_ITEMS'][$code]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$code]['PRICE'] + $this->orderData['BASKET_ITEMS'][$code]['DISCOUNT_PRICE']; } } unset($bundle, $bundleItem); } } unset($code, $basketItem); } unset($basket, $process); return $result; } /** * Return is exists discount shipment. * * @return bool */ protected function isShipmentExists() { return ($this->shipment instanceof Shipment); } /** * Load shipment. * * @return Result */ protected function loadShipment() { $result = new Result; if (!$this->isOrderExists()) return $result; if (!$this->isShipmentExists()) { $loadDelivery = false; $order = $this->getOrder(); /** @var ShipmentCollection $orderShipmentList */ $orderShipmentList = $order->getShipmentCollection(); /** @var Shipment $shipment */ if ($this->isOrderNew()) { foreach ($orderShipmentList as $shipment) { if ($shipment->isSystem()) continue; if (!$loadDelivery) { $this->shipment = $shipment; $loadDelivery = true; break; } } } else { $shipmentId = false; foreach ($orderShipmentList as $shipment) { if ($shipment->isSystem()) continue; $currentShipmentId = (int)$shipment->getId(); if ($shipmentId === false || $shipmentId > $currentShipmentId) $shipmentId = $currentShipmentId; } unset($currentShipmentId, $shipment); if (!empty($shipmentId)) { $this->shipment = $orderShipmentList->getItemById($shipmentId); $loadDelivery = true; } unset($shipmentId); } unset($loadDelivery); } return $result; } /** * Fill data from shipment. * * @return void */ protected function fillShipmentData() { if (!$this->isShipmentExists()) return; $this->orderData['DELIVERY_ID'] = $this->shipment->getDeliveryId(); $this->orderData['CUSTOM_PRICE_DELIVERY'] = ($this->shipment->isCustomPrice() ? 'Y' : 'N'); $this->orderData['BASE_PRICE_DELIVERY'] = $this->shipment->getField('BASE_PRICE_DELIVERY'); $this->orderData['PRICE_DELIVERY'] = $this->orderData['BASE_PRICE_DELIVERY']; $this->orderData['SHIPMENT_CODE'] = $this->shipment->getShipmentCode(); $this->orderData['SHIPMENT_ID'] = (int)$this->shipment->getId(); } /** * Load order config for exists order. * * @return void * @throws Main\ArgumentException * @throws Main\ArgumentNullException */ protected function loadOrderConfig() { $this->convertedOrder = false; $this->shipment = null; $this->saleOptions = array( 'USE_BASE_PRICE' => Main\Config\Option::get('sale', 'get_discount_percent_from_base_price'), 'SALE_DISCOUNT_ONLY' => Main\Config\Option::get('sale', 'use_sale_discount_only'), 'APPLY_MODE' => Main\Config\Option::get('sale', 'discount_apply_mode') ); $this->fillCompatibleOrderFields(); if (!$this->isOrderExists()) return; $this->convertedOrder = ($this->isOrderNew() === false); if ($this->getUseMode() == self::USE_MODE_FULL) $this->convertedOrder = false; $order = $this->getOrder(); $orderId = $order->getId(); if ($this->isOrderNew() || $this->getUseMode() == self::USE_MODE_FULL) return; $data = Internals\OrderDiscountDataTable::getList(array( 'select' => array('*'), 'filter' => array( '=ORDER_ID' => $orderId, '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ORDER, '=ENTITY_ID' => $orderId ) ))->fetch(); if (empty($data)) return; $entityData = &$data['ENTITY_DATA']; if (!empty($entityData['DELIVERY'])) { $this->orderData['DELIVERY_ID'] = $entityData['DELIVERY']['DELIVERY_ID']; if (isset($entityData['DELIVERY']['CUSTOM_PRICE_DELIVERY'])) $this->orderData['CUSTOM_PRICE_DELIVERY'] = $entityData['DELIVERY']['CUSTOM_PRICE_DELIVERY']; if (isset($entityData['DELIVERY']['SHIPMENT_ID'])) { $entityData['DELIVERY']['SHIPMENT_ID'] = (int)$entityData['DELIVERY']['SHIPMENT_ID']; if ($entityData['DELIVERY']['SHIPMENT_ID'] > 0) { $this->shipmentIds[] = $entityData['DELIVERY']['SHIPMENT_ID']; /** @var ShipmentCollection $orderShipmentList */ $orderShipmentList = $order->getShipmentCollection(); $this->shipment = $orderShipmentList->getItemById($entityData['DELIVERY']['SHIPMENT_ID']); if (empty($this->shipment)) { $this->shipment = null; $this->shipmentIds[] = array(); } } } } if (!empty($entityData['OPTIONS']) && is_array($entityData['OPTIONS'])) { foreach (array_keys($this->saleOptions) as $key) { if (isset($entityData['OPTIONS'][$key])) $this->saleOptions[$key] = $entityData['OPTIONS'][$key]; } unset($key); $this->fillCompatibleOrderFields(); } if (!isset($entityData['OLD_ORDER'])) $this->convertedOrder = false; unset($entityData); unset($data, $orderId, $order); $this->loadRoundConfig(); } /** * Load discounrs for exists order. * * @return Result */ protected function loadOrderDiscounts() { $result = new Result; $this->discountsCache = array(); $this->couponsCache = array(); if (!$this->isOrderExists()) return $result; $order = $this->getOrder(); if ($this->isOrderNew() || ($this->getUseMode() == self::USE_MODE_FULL && !$this->isMixedBasket())) return $result; $applyResult = OrderDiscountManager::loadResultFromDatabase( $order->getId(), true, $this->reverseBasketTable, $this->orderData['BASKET_ITEMS'] ); if (!$applyResult->isSuccess()) $result->addErrors($applyResult->getErrors()); $applyResultData = $applyResult->getData(); if (!empty($applyResultData['DISCOUNT_LIST'])) { foreach ($applyResultData['DISCOUNT_LIST'] as $orderDiscountId => $discountData) { $discountData['ACTIONS_DESCR_DATA'] = false; if (!empty($discountData['ACTIONS_DESCR']) && is_array($discountData['ACTIONS_DESCR'])) { $discountData['ACTIONS_DESCR_DATA'] = $discountData['ACTIONS_DESCR']; $discountData['ACTIONS_DESCR'] = self::formatDescription($discountData['ACTIONS_DESCR']); } else { $discountData['ACTIONS_DESCR'] = false; } if (empty($discountData['ACTIONS_DESCR'])) { $discountData['ACTIONS_DESCR'] = false; $discountData['ACTIONS_DESCR_DATA'] = false; } $this->discountsCache[$orderDiscountId] = $discountData; if (isset($applyResultData['DISCOUNT_MODULES'][$orderDiscountId])) $this->cacheDiscountModules[$orderDiscountId] = $applyResultData['DISCOUNT_MODULES'][$orderDiscountId]; } unset($orderDiscountId, $discountData); } if (!empty($applyResultData['COUPON_LIST'])) $this->couponsCache = $applyResultData['COUPON_LIST']; $this->discountResultCounter = 0; $this->discountResult['APPLY_BLOCKS'] = $applyResultData['APPLY_BLOCKS']; if (!empty($this->discountResult['APPLY_BLOCKS'])) { foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if (!empty($applyBlock['BASKET'])) { foreach ($applyBlock['BASKET'] as $discountList) { foreach ($discountList as $discount) { if ($discount['COUPON_ID'] == '') continue; DiscountCouponsManager::setApplyByProduct($discount, array($discount['COUPON_ID'])); } } unset($discountList); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if ($discount['COUPON_ID'] != '') DiscountCouponsManager::setApply($discount['COUPON_ID'], $discount['RESULT']); } unset($discount); } $this->discountResultCounter = $counter + 1; } unset($counter, $applyBlock); } if (!empty($applyResultData['DATA']['STORED_ACTION_DATA']) && is_array($applyResultData['DATA']['STORED_ACTION_DATA'])) $this->discountStoredActionData = $applyResultData['DATA']['STORED_ACTION_DATA']; unset($applyResultData, $applyResult); return $result; } /** * Load basket stored data for order. * * @return Result */ protected function loadBasketStoredData() { $result = new Result; $this->basketItemsData = array(); if (!$this->isOrderExists()) return $result; $order = $this->getOrder(); if ($this->isOrderNew() || ($this->getUseMode() == self::USE_MODE_FULL && !$this->isMixedBasket())) return $result; $basketData = []; $iterator = Internals\OrderDiscountDataTable::getList([ 'select' => ['*'], 'filter' => [ '=ORDER_ID' => $order->getId(), '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_BASKET_ITEM ] ]); while ($row = $iterator->fetch()) { if (empty($row['ENTITY_DATA']) || !is_array($row['ENTITY_DATA'])) continue; $basketData[$row['ENTITY_ID']] = $row['ENTITY_DATA']; } unset($row, $iterator); if (empty($basketData)) return $result; foreach ($this->reverseBasketTable as $basketId => $basketCode) { if (!isset($basketData[$basketId]) || !is_array($basketData[$basketId])) continue; $this->basketItemsData[$basketCode] = $basketData[$basketId]; } unset($basketId, $basketCode); unset($basketData); unset($order); return $result; } /** * Return basket item data value from provider. * @internal * * @param int|string $code Basket code. * @param string $field Field name. * @return null|mixed */ protected function getBasketItemValue($code, $field) { if (!isset($this->basketItemsData[$code])) return null; return (isset($this->basketItemsData[$code][$field]) ? $this->basketItemsData[$code][$field] : null); } /** * Return basket item data from provider. * @internal * * @param int|string $code Basket code. * @param array $fields Field names. * @return array|null */ protected function getBasketItemValueList($code, array $fields) { if (!isset($this->basketItemsData[$code]) || empty($fields)) return null; $result = array(); foreach ($fields as $fieldName) { $result[$fieldName] = ( isset($this->basketItemsData[$code][$fieldName]) ? $this->basketItemsData[$code][$fieldName] : null ); } unset($fieldName); return $result; } /** * Calculate discount by new order. * * @return Result */ protected function calculateFull() { $result = new Result; if (!$this->isBasketNotEmpty()) return $result; $this->discountIds = array(); Discount\Actions::setUseMode( Discount\Actions::MODE_CALCULATE, array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'SITE_ID' => $this->orderData['SITE_ID'], 'CURRENCY' => $this->orderData['CURRENCY'] ) ); $this->fillEmptyDiscountResult(); $this->getRoundForBasePrices(); $basket = $this->getBasket(); /** @var BasketItem $basketItem */ foreach ($basket as $basketItem) { $code = $basketItem->getBasketCode(); if ($this->isCustomPriceByCode($code)) { if (array_key_exists($code, $this->basketDiscountList)) unset($this->basketDiscountList[$code]); } else { if (!isset($this->basketDiscountList[$code])) { $this->basketDiscountList[$code] = $basketItem->getField('DISCOUNT_LIST'); if ($this->basketDiscountList[$code] === null) unset($this->basketDiscountList[$code]); } } } unset($code, $basketItem, $basket); DiscountCouponsManager::clearApply(); $basketDiscountResult = $this->calculateFullBasketDiscount(); if (!$basketDiscountResult->isSuccess()) { $result->addErrors($basketDiscountResult->getErrors()); unset($basketDiscountResult); return $result; } unset($basketDiscountResult); if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT)) $this->roundFullBasketPrices(); if (!$this->isBasketLastDiscount()) { $this->loadDiscountByUserGroups(); $this->loadDiscountList(); $executeResult = $this->executeDiscountList(); if (!$executeResult->isSuccess()) { $result->addErrors($executeResult->getErrors()); unset($executeResult); return $result; } unset($executeResult); } if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE)) $this->roundFullBasketPrices(); return $result; } /** * Calculate discount by exist order. * * @return Result */ protected function calculateApply() { $result = new Result; if (!$this->isOrderExists()) return $result; if ($this->convertedOrder) return $result; if (!empty($this->discountResult['APPLY_BLOCKS'])) { Discount\Actions::setUseMode( Discount\Actions::MODE_MANUAL, array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'SITE_ID' => $this->orderData['SITE_ID'], 'CURRENCY' => $this->orderData['CURRENCY'] ) ); $currentCounter = $this->discountResultCounter; foreach (array_keys($this->discountResult['APPLY_BLOCKS']) as $counter) { $this->discountResultCounter = $counter; $blockResult = $this->calculateApplyDiscountBlock(); if (!$blockResult->isSuccess()) { $result->addErrors($blockResult->getErrors()); unset($blockResult); return $result; } unset($blockResult); } $this->discountResultCounter = $currentCounter; unset($currentCounter); } if ($result->isSuccess()) { Discount\Actions::setUseMode( Discount\Actions::MODE_CALCULATE, array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'SITE_ID' => $this->orderData['SITE_ID'], 'CURRENCY' => $this->orderData['CURRENCY'] ) ); $this->clearCurrentApplyBlock(); $couponsResult = $this->calculateApplyAdditionalCoupons(); if (!$couponsResult->isSuccess()) { $result->addErrors($couponsResult->getErrors()); unset($couponsResult); return $result; } unset($couponsResult); if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE)) $this->roundChangedBasketPrices(); } return $result; } /** * Calculate discount by exist order with new items. * * @return Result */ protected function calculateMixed() { $result = new Result; if (!$this->isOrderExists()) return $result; if ($this->convertedOrder) return $result; if (!empty($this->discountResult['APPLY_BLOCKS'])) { Discount\Actions::setUseMode( Discount\Actions::MODE_MANUAL, array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'SITE_ID' => $this->orderData['SITE_ID'], 'CURRENCY' => $this->orderData['CURRENCY'] ) ); $currentCounter = $this->discountResultCounter; foreach (array_keys($this->discountResult['APPLY_BLOCKS']) as $counter) { $this->discountResultCounter = $counter; $blockResult = $this->calculateApplyDiscountBlock(); if (!$blockResult->isSuccess()) { $result->addErrors($blockResult->getErrors()); unset($blockResult); return $result; } unset($blockResult); } $this->discountResultCounter = $currentCounter; unset($currentCounter); } if ($result->isSuccess()) { Discount\Actions::setUseMode( Discount\Actions::MODE_CALCULATE, array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'SITE_ID' => $this->orderData['SITE_ID'], 'CURRENCY' => $this->orderData['CURRENCY'] ) ); $this->clearCurrentApplyBlock(); $this->getRoundForBasePrices(); $basketCodeList = $this->getBasketCodes(true); if (!empty($basketCodeList)) { /** @var \Bitrix\Sale\Basket $basket */ $basket = $this->getBasket(); foreach ($basketCodeList as $basketCode) { $basketItem = $basket->getItemByBasketCode($basketCode); if ($basketItem instanceof BasketItemBase) { if (!isset($this->basketDiscountList[$basketCode])) { $this->basketDiscountList[$basketCode] = $basketItem->getField('DISCOUNT_LIST'); if ($this->basketDiscountList[$basketCode] === null) unset($this->basketDiscountList[$basketCode]); } } } unset($basketCode); unset($basket); } unset($basketCodeList); $basketDiscountResult = $this->calculateFullBasketDiscount(); if (!$basketDiscountResult->isSuccess()) { $result->addErrors($basketDiscountResult->getErrors()); unset($basketDiscountResult); return $result; } unset($basketDiscountResult); if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT)) $this->roundFullBasketPrices(); $couponsResult = $this->calculateApplyAdditionalCoupons(); if (!$couponsResult->isSuccess()) { $result->addErrors($couponsResult->getErrors()); unset($couponsResult); return $result; } unset($couponsResult); if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE)) $this->roundChangedBasketPrices(); } return $result; } /** * Save discount result for new order. * * @return Result */ protected function saveFull() { $result = new Result; $process = true; $order = $this->getOrder(); $orderId = $order->getId(); $orderCurrency = $order->getCurrency(); unset($order); if (!Compatible\DiscountCompatibility::isUsed() || !Compatible\DiscountCompatibility::isInited()) { $basketResult = $this->getBasketTables(); if (!$basketResult->isSuccess()) { $process = false; $result->addErrors($basketResult->getErrors()); } } if ($process) { DiscountCouponsManager::finalApply(); DiscountCouponsManager::saveApplied(); $couponsResult = $this->saveCoupons(); if (!$couponsResult->isSuccess()) { $process = false; $result->addErrors($couponsResult->getErrors()); } } if ($process) { Internals\OrderRulesTable::clearByOrder($orderId); Internals\OrderDiscountDataTable::clearByOrder($orderId); $lastApplyBlockResult = $this->saveLastApplyBlock(); if (!$lastApplyBlockResult->isSuccess()) { $process = false; $result->addErrors($lastApplyBlockResult->getErrors()); } unset($lastApplyBlockResult); } if ($process) { $fields = array( 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ORDER, 'ENTITY_ID' => $orderId, 'ENTITY_VALUE' => $orderId, 'ENTITY_DATA' => array( 'OPTIONS' => array( 'USE_BASE_PRICE' => Main\Config\Option::get('sale', 'get_discount_percent_from_base_price'), 'SALE_DISCOUNT_ONLY' => Main\Config\Option::get('sale', 'use_sale_discount_only'), 'APPLY_MODE' => Main\Config\Option::get('sale', 'discount_apply_mode') ), 'DELIVERY' => array( 'DELIVERY_ID' => $this->orderData['DELIVERY_ID'], 'CUSTOM_PRICE_DELIVERY' => $this->orderData['CUSTOM_PRICE_DELIVERY'], 'SHIPMENT_ID' => 0 ) ) ); if ($this->shipment instanceof Shipment) $fields['ENTITY_DATA']['DELIVERY']['SHIPMENT_ID'] = $this->shipment->getId(); $dataResult = Internals\OrderDiscountDataTable::add($fields); if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult, $fields); unset($orderCurrency); $fields = array( 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ROUND, 'ENTITY_ID' => $orderId, 'ENTITY_VALUE' => $orderId, 'ENTITY_DATA' => array( 'MODE' => $this->roundApplyMode, 'CONFIG' => $this->roundApplyConfig ) ); $dataResult = Internals\OrderDiscountDataTable::add($fields); if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult, $fields); if (!empty($this->discountStoredActionData)) { $fields = array( 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_DISCOUNT_STORED_DATA, 'ENTITY_ID' => $orderId, 'ENTITY_VALUE' => $orderId, 'ENTITY_DATA' => $this->discountStoredActionData ); $dataResult = Internals\OrderDiscountDataTable::add($fields); if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult, $fields); } $dataResult = $this->saveBasketStoredData($this->getBasketCodes(true)); if (!$dataResult->isSuccess()) { $result->addErrors($dataResult->getErrors()); } unset($dataResult); } if ($process) { if (DiscountCouponsManager::usedByManager()) DiscountCouponsManager::clear(true); } return $result; } /** * Save basket items stored data. * * @param array $basketCodeList Code list. * @return Result */ protected function saveBasketStoredData(array $basketCodeList) { $result = new Result(); if (empty($basketCodeList)) return $result; $useMode = $this->getUseMode(); if ($useMode != self::USE_MODE_FULL && $useMode != self::USE_MODE_MIXED) return $result; $itemsData = []; foreach ($basketCodeList as $basketCode) { if (!isset($this->basketItemsData[$basketCode])) continue; $data = $this->prepareBasketItemStoredData($basketCode); if ($data === null) continue; $basketId = $this->forwardBasketTable[$basketCode]; $itemsData[$basketId] = [ 'ENTITY_ID' => $basketId, 'ENTITY_VALUE' => $basketId, 'ENTITY_DATA' => $data ]; } unset($data, $basketCode); $orderId = $this->getOrder()->getId(); $collection = []; $deleteList = []; $iterator = Internals\OrderDiscountDataTable::getList([ 'select' => ['*'], 'filter' => [ '=ORDER_ID' => $orderId, '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_BASKET_ITEM, ] ]); while ($row = $iterator->fetch()) { $index = $row['ENTITY_ID']; $collection[$index] = $row; $deleteList[$index] = $row['ID']; } unset($index, $row, $iterator); if (!empty($itemsData)) { foreach ($itemsData as $index => $row) { if (isset($deleteList[$index])) unset($deleteList[$index]); if (empty($collection[$index])) { $row['ORDER_ID'] = $orderId; $row['ENTITY_TYPE'] = Internals\OrderDiscountDataTable::ENTITY_TYPE_BASKET_ITEM; $resultInternal = Internals\OrderDiscountDataTable::add($row); } else { $resultInternal = Internals\OrderDiscountDataTable::update( $collection[$index]['ID'], ['ENTITY_DATA' => $row['ENTITY_DATA']] ); } if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $index, $row); } unset($itemsData); if (!empty($deleteList)) { $conn = Main\Application::getConnection(); $helper = $conn->getSqlHelper(); $query = 'delete from '.$helper->quote(Internals\OrderDiscountDataTable::getTableName()).' where '.$helper->quote('ID'); foreach (array_chunk($deleteList, 500) as $row) { $conn->queryExecute($query.' in ('.implode(', ', $row).')'); } unset($row, $query); } return $result; } /** * Save discount result for exist order. * * @return Result */ protected function saveApply() { $result = new Result; $process = true; $orderId = $this->getOrder()->getId(); if ($this->convertedOrder) return $result; $basketResult = $this->getBasketTables(); if (!$basketResult->isSuccess()) { $process = false; $result->addErrors($basketResult->getErrors()); } $deleteList = array(); $rulesIterator = Internals\OrderRulesTable::getList(array( 'select' => array('ID'), 'filter' => array('=ORDER_ID' => $orderId) )); while ($rule = $rulesIterator->fetch()) { $rule['ID'] = (int)$rule['ID']; $deleteList[$rule['ID']] = $rule['ID']; } unset($rule, $rulesIterator); $deleteRoundList = array(); $iterator = Internals\OrderRoundTable::getList(array( 'select' => array('ID'), 'filter' => array('=ORDER_ID' => $orderId) )); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $deleteRoundList[$row['ID']] = $row['ID']; } unset($row, $iterator); $rulesList = array(); foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if ($counter == $this->discountResultCounter) continue; if (!empty($applyBlock['BASKET'])) { foreach ($applyBlock['BASKET'] as $basketCode => $discountList) { foreach ($discountList as $discount) { if (!isset($discount['RULE_ID']) || (int)$discount['RULE_ID'] < 0) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_EXT_DISCOUNT'), self::ERROR_ID )); continue; } $orderDiscountId = $discount['DISCOUNT_ID']; $ruleData = array( 'RULE_ID' => $discount['RULE_ID'], 'APPLY' => $discount['RESULT']['APPLY'], 'DESCR_ID' => (isset($discount['RULE_DESCR_ID']) ? (int)$discount['RULE_DESCR_ID'] : 0), 'DESCR' => $discount['RESULT']['DESCR_DATA']['BASKET'], ); if ($ruleData['DESCR_ID'] <= 0) { $ruleData['DESCR_ID'] = 0; $ruleData['MODULE_ID'] = $this->discountsCache[$orderDiscountId]['MODULE_ID']; $ruleData['ORDER_DISCOUNT_ID'] = $orderDiscountId; $ruleData['ORDER_ID'] = $orderId; } $rulesList[] = $ruleData; unset($ruleData); } unset($discount); } unset($basketCode, $discountList); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { $orderDiscountId = $discount['DISCOUNT_ID']; if (!empty($discount['RESULT']['BASKET'])) { foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyData) { if (!isset($applyData['RULE_ID']) || (int)$applyData['RULE_ID'] < 0) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_SALE_DISCOUNT'), self::ERROR_ID )); continue; } $ruleData = array( 'RULE_ID' => $applyData['RULE_ID'], 'APPLY' => $applyData['APPLY'], 'DESCR_ID' => (isset($applyData['RULE_DESCR_ID']) ? (int)$applyData['RULE_DESCR_ID'] : 0), 'DESCR' => $applyData['DESCR_DATA'], ); if ($ruleData['DESCR_ID'] <= 0) { $ruleData['DESCR_ID'] = 0; $ruleData['MODULE_ID'] = $this->discountsCache[$orderDiscountId]['MODULE_ID']; $ruleData['ORDER_DISCOUNT_ID'] = $orderDiscountId; $ruleData['ORDER_ID'] = $orderId; } if (!$discount['ACTION_BLOCK_LIST']) $ruleData['ACTION_BLOCK_LIST'] = $applyData['ACTION_BLOCK_LIST']; $rulesList[] = $ruleData; unset($ruleData); } unset($basketCode, $applyData); } if (!empty($discount['RESULT']['DELIVERY'])) { if (!isset($discount['RESULT']['DELIVERY']['RULE_ID']) || (int)$discount['RESULT']['DELIVERY']['RULE_ID'] < 0) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_EMPTY_RULE_ID_SALE_DISCOUNT'), self::ERROR_ID )); continue; } $ruleData = array( 'RULE_ID' => $discount['RESULT']['DELIVERY']['RULE_ID'], 'APPLY' => $discount['RESULT']['DELIVERY']['APPLY'], 'DESCR_ID' => (isset($discount['RESULT']['DELIVERY']['RULE_DESCR_ID']) ? (int)$discount['RESULT']['DELIVERY']['RULE_DESCR_ID'] : 0), 'DESCR' => $discount['RESULT']['DELIVERY']['DESCR_DATA'] ); if ($ruleData['DESCR_ID'] <= 0) { $ruleData['DESCR_ID'] = 0; $ruleData['MODULE_ID'] = $this->discountsCache[$orderDiscountId]['MODULE_ID']; $ruleData['ORDER_DISCOUNT_ID'] = $orderDiscountId; $ruleData['ORDER_ID'] = $orderId; } $rulesList[] = $ruleData; unset($ruleData); } } unset($discount); } if (!empty($applyBlock['BASKET_ROUND'])) { foreach ($applyBlock['BASKET_ROUND'] as $row) { if (isset($deleteRoundList[$row['RULE_ID']])) unset($deleteRoundList[$row['RULE_ID']]); } unset($row); } } if ($process && !empty($rulesList)) { foreach ($rulesList as $ruleRow) { $rowUpdate = array('APPLY' => $ruleRow['APPLY']); if (isset($ruleRow['ACTION_BLOCK_LIST'])) $rowUpdate['ACTION_BLOCK_LIST'] = $ruleRow['ACTION_BLOCK_LIST']; $ruleResult = Internals\OrderRulesTable::update($ruleRow['RULE_ID'], $rowUpdate); unset($rowUpdate); if ($ruleResult->isSuccess()) { if (isset($deleteList[$ruleRow['RULE_ID']])) unset($deleteList[$ruleRow['RULE_ID']]); if ($ruleRow['DESCR_ID'] > 0) { $descrResult = Internals\OrderRulesDescrTable::update($ruleRow['DESCR_ID'], array('DESCR' => $ruleRow['DESCR'])); } else { $descrData = array( 'DESCR' => $ruleRow['DESCR'], 'MODULE_ID' => $ruleRow['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $ruleRow['ORDER_DISCOUNT_ID'], 'ORDER_ID' => $ruleRow['ORDER_ID'] ); $descrResult = Internals\OrderRulesDescrTable::add($descrData); } if (!$descrResult->isSuccess(true)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); unset($descrResult); break; } unset($descrResult); } else { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); unset($ruleResult); break; } unset($ruleResult); } unset($ruleRow); } if ($process && (!empty($deleteList) || !empty($deleteRoundList))) { $conn = Main\Application::getConnection(); $helper = $conn->getSqlHelper(); if (!empty($deleteList)) { $mainQuery = 'delete from '.$helper->quote(Internals\OrderRulesTable::getTableName()).' where '.$helper->quote('ID'); $descrQuery = 'delete from '.$helper->quote(Internals\OrderRulesDescrTable::getTableName()).' where '.$helper->quote('RULE_ID'); foreach (array_chunk($deleteList, 500) as $row) { $conn->queryExecute($mainQuery.' in ('.implode(', ', $row).')'); $conn->queryExecute($descrQuery.' in ('.implode(', ', $row).')'); } unset($row, $descrQuery, $mainQuery); } if (!empty($deleteRoundList)) { $query = 'delete from '.$helper->quote(Internals\OrderRoundTable::getTableName()).' where '.$helper->quote('ID'); foreach (array_chunk($deleteRoundList, 500) as $row) { $conn->queryExecute($query.' in ('.implode(', ', $row).')'); } unset($row, $query); } unset($helper, $conn); } unset($deleteRoundList, $deleteList); if ($process) { if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter])) { DiscountCouponsManager::finalApply(); DiscountCouponsManager::saveApplied(); $couponsResult = $this->saveCoupons(); if (!$couponsResult->isSuccess()) { $process = false; $result->addErrors($couponsResult->getErrors()); } if ($process) { $lastApplyBlockResult = $this->saveLastApplyBlock(); if (!$lastApplyBlockResult->isSuccess()) { $process = false; $result->addErrors($lastApplyBlockResult->getErrors()); } unset($lastApplyBlockResult); } } } if ($process) { $configId = 0; $roundData = Internals\OrderDiscountDataTable::getList(array( 'select' => array('ID'), 'filter' => array( '=ORDER_ID' => $orderId, '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ROUND, '=ENTITY_ID' => $orderId ) ))->fetch(); if (!empty($roundData)) $configId = (int)$roundData['ID']; if ($configId > 0) { $fields = array( 'ENTITY_DATA' => array( 'MODE' => $this->roundApplyMode, 'CONFIG' => $this->roundApplyConfig ) ); $dataResult = Internals\OrderDiscountDataTable::update($configId, $fields); if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult, $fields); } else { $fields = array( 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ROUND, 'ENTITY_ID' => $orderId, 'ENTITY_VALUE' => $orderId, 'ENTITY_DATA' => array( 'MODE' => $this->roundApplyMode, 'CONFIG' => $this->roundApplyConfig ) ); $dataResult = Internals\OrderDiscountDataTable::add($fields); if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult, $fields); } if (!empty($this->discountStoredActionData)) { $storedDataId = 0; $iterator = Internals\OrderDiscountDataTable::getList(array( 'select' => array('ID'), 'filter' => array( '=ORDER_ID' => $orderId, '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_DISCOUNT_STORED_DATA, '=ENTITY_ID' => $orderId ) )); $row = $iterator->fetch(); if (!empty($row)) $storedDataId = (int)$row['ID']; if ($storedDataId > 0) { $dataResult = Internals\OrderDiscountDataTable::update( $storedDataId, array( 'ENTITY_DATA' => $this->discountStoredActionData ) ); } else { $dataResult = Internals\OrderDiscountDataTable::add(array( 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_DISCOUNT_STORED_DATA, 'ENTITY_ID' => $orderId, 'ENTITY_VALUE' => $orderId, 'ENTITY_DATA' => $this->discountStoredActionData )); } if (!$dataResult->isSuccess()) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); } unset($dataResult); } } if ($process) { if (DiscountCouponsManager::usedByManager()) DiscountCouponsManager::clear(true); } return $result; } /** * Save discount result for mixed order. * * @return Result */ protected function saveMixed() { $result = $this->saveApply(); if ($result->isSuccess()) { $basketCodeList = array_merge( $this->getBasketCodes(false), $this->getBasketCodes(true) ); $dataResult = $this->saveBasketStoredData($basketCodeList); if (!$dataResult->isSuccess()) { $result->addErrors($dataResult->getErrors()); } unset($dataResult, $basketCodeList); } return $result; } /** * Save coupons for order. * * @return Result */ protected function saveCoupons() { $result = new Result; if (!$this->isOrderExists()) return $result; if (!empty($this->couponsCache)) { $orderId = $this->getOrder()->getId(); foreach ($this->couponsCache as $orderCouponId => $couponData) { if ($couponData['ID'] > 0) continue; $fields = $couponData; $fields['ORDER_ID'] = $orderId; $couponResult = OrderDiscountManager::saveCoupon($fields); if (!$couponResult->isSuccess()) { $result->addErrors($couponResult->getErrors()); unset($couponResult); continue; } $this->couponsCache[$orderCouponId]['ID'] = $couponResult->getId(); unset($couponResult); } unset($orderId); } return $result; } public function saveExternalLastApplyblock(BasketItem $basketItem, $orderDiscountId) { $basket = $basketItem->getCollection(); $this->order = $basket->getOrder(); $this->loadOrderData(); $listItems[$basketItem->getBasketCode()] = array('APPLY'=>'Y', 'ACTION_BLOCK_LIST'=>array(), 'DESCR_DATA'=>array(), 'DESCR'=>array()); $this->discountsCache[$orderDiscountId]['MODULE_ID'] = 'sale'; $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]; $applyBlock['ORDER'][] = array( 'DISCOUNT_ID'=>$orderDiscountId, 'COUPON_ID'=>0, 'RESULT'=>array('BASKET'=>$listItems), ); $this->saveLastApplyBlock(); } /** * Save result last apply block discount. * * @return Result * @throws \Exception */ protected function saveLastApplyBlock() { $result = new Result; $process = true; $orderId = $this->getOrder()->getId(); $rulesList = array(); $ruleDescr = array(); $ruleIndex = 0; $roundList = array(); $roundIndex = 0; $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]; if (!empty($applyBlock['BASKET'])) { foreach ($this->forwardBasketTable as $basketCode => $basketId) { if (empty($applyBlock['BASKET'][$basketCode])) continue; $discountList = $applyBlock['BASKET'][$basketCode]; foreach ($discountList as $discount) { $orderDiscountId = $discount['DISCOUNT_ID']; $rulesList[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderRulesTable::ENTITY_TYPE_BASKET, 'ENTITY_ID' => $basketId, 'ENTITY_VALUE' => $basketId, 'COUPON_ID' => ($discount['COUPON_ID'] != '' ? $this->couponsCache[$discount['COUPON_ID']]['ID'] : 0), 'APPLY' => $discount['RESULT']['APPLY'], 'APPLY_BLOCK_COUNTER' => $this->discountResultCounter ); $ruleDescr[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'DESCR' => $discount['RESULT']['DESCR_DATA'] ); $ruleIndex++; } unset($discount, $discountList); } unset($basketCode, $basketId); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { $orderDiscountId = $discount['DISCOUNT_ID']; if (!empty($discount['RESULT']['BASKET'])) { foreach ($this->forwardBasketTable as $basketCode => $basketId) { if (empty($discount['RESULT']['BASKET'][$basketCode])) continue; $applyData = $discount['RESULT']['BASKET'][$basketCode]; $rulesList[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderRulesTable::ENTITY_TYPE_BASKET, 'ENTITY_ID' => $basketId, 'ENTITY_VALUE' => $basketId, 'COUPON_ID' => ($discount['COUPON_ID'] != '' ? $this->couponsCache[$discount['COUPON_ID']]['ID'] : 0), 'APPLY' => $applyData['APPLY'], 'APPLY_BLOCK_COUNTER' => $this->discountResultCounter, 'ACTION_BLOCK_LIST' => $applyData['ACTION_BLOCK_LIST'] ); $ruleDescr[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'DESCR' => $applyData['DESCR_DATA'] ); $ruleIndex++; unset($applyData); } unset($basketCode, $basketId); } if (!empty($discount['RESULT']['DELIVERY'])) { $rulesList[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'ENTITY_TYPE' => Internals\OrderRulesTable::ENTITY_TYPE_DELIVERY, 'ENTITY_ID' => (int)$discount['RESULT']['DELIVERY']['DELIVERY_ID'], 'ENTITY_VALUE' => (string)$discount['RESULT']['DELIVERY']['DELIVERY_ID'], 'COUPON_ID' => ($discount['COUPON_ID'] != '' ? $this->couponsCache[$discount['COUPON_ID']]['ID'] : 0), 'APPLY' => $discount['RESULT']['DELIVERY']['APPLY'], 'APPLY_BLOCK_COUNTER' => $this->discountResultCounter ); $ruleDescr[$ruleIndex] = array( 'MODULE_ID' => $this->discountsCache[$orderDiscountId]['MODULE_ID'], 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'ORDER_ID' => $orderId, 'DESCR' => $discount['RESULT']['DELIVERY']['DESCR_DATA'] ); $ruleIndex++; } } unset($discount); } if (!empty($applyBlock['BASKET_ROUND'])) { foreach ($applyBlock['BASKET_ROUND'] as $basketCode => $roundData) { $basketId = $this->forwardBasketTable[$basketCode]; $roundList[$roundIndex] = array( 'ORDER_ID' => $orderId, 'APPLY_BLOCK_COUNTER' => $this->discountResultCounter, 'ORDER_ROUND' => 'N', 'ENTITY_TYPE' => Internals\OrderRoundTable::ENTITY_TYPE_BASKET, 'ENTITY_ID' => $basketId, 'ENTITY_VALUE' => $basketId, 'APPLY' => $roundData['APPLY'], 'ROUND_RULE' => $roundData['ROUND_RULE'] ); $roundIndex++; unset($basketId); } unset($roundData, $basketCode); } unset($applyBlock); if (!empty($rulesList)) { foreach ($rulesList as $index => $ruleRow) { $ruleResult = Internals\OrderRulesTable::add($ruleRow); if ($ruleResult->isSuccess()) { $ruleDescr[$index]['RULE_ID'] = $ruleResult->getId(); $descrResult = Internals\OrderRulesDescrTable::add($ruleDescr[$index]); if (!$descrResult->isSuccess()) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); unset($descrResult); break; } unset($descrResult); } else { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); unset($ruleResult); break; } unset($ruleResult); } unset($ruleRow); } if (!empty($roundList)) { foreach ($roundList as $roundRow) { $roundResult = Internals\OrderRoundTable::add($roundRow); if (!$roundResult->isSuccess()) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SAVE_APPLY_RULES'), self::ERROR_ID )); unset($roundResult); break; } unset($roundResult); } unset($roundRow); } unset($process); return $result; } /** * Check duscount conditions. * * @return bool */ protected function checkDiscountConditions() { if ( !isset($this->currentStep['cacheIndex']) || !isset($this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']]) ) return false; $key = 'UNPACK'; $executeKey = $key.'_EXECUTE'; if ($this->enableCheckingPrediction && !empty($this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']]['PREDICTIONS_APP'])) { $key = 'PREDICTIONS_APP'; $executeKey = $key.'_EXECUTE'; } if (empty($this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']][$key])) return false; $discountLink = &$this->saleDiscountCache[$this->saleDiscountCacheKey][$this->currentStep['cacheIndex']]; if (!array_key_exists($executeKey, $discountLink)) { $checkOrder = null; $evalCode = '$checkOrder='.$discountLink[$key].';'; if (version_compare(PHP_VERSION, '7.0.0', '>=')) { try { eval($evalCode); } catch (\ParseError $e) { $this->showAdminError(); } } else { eval($evalCode); } unset($evalCode); if (!is_callable($checkOrder)) return false; $result = $checkOrder($this->orderData); unset($checkOrder); } else { if (!is_callable($discountLink[$executeKey])) return false; $result = $discountLink[$executeKey]($this->orderData); } unset($discountLink); return $result; } /** * Apply discount rules. * * @return Result */ protected function applySaleDiscount() { $result = new Result; Discount\Actions::clearApplyCounter(); $discount = ( isset($this->currentStep['discountIndex']) ? $this->discountsCache[$this->currentStep['discountId']] : $this->currentStep['discount'] ); if (isset($this->currentStep['discountIndex'])) { if (!empty($discount['APPLICATION']) && !$this->loadDiscountModules($this->currentStep['discountId'])) { $discount['APPLICATION'] = null; $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_SALE_DISCOUNT_MODULES_ABSENT'), self::ERROR_ID )); } } if (!empty($discount['APPLICATION'])) { if (!array_key_exists('APPLICATION_EXECUTE', $discount)) { $discount['APPLICATION_EXECUTE'] = null; $evalCode = '$discount["APPLICATION_EXECUTE"]='.$discount['APPLICATION'].';'; if (version_compare(PHP_VERSION, '7.0.0', '>=')) { try { eval($evalCode); } catch (\ParseError $e) { $this->showAdminError(); } } else { eval($evalCode); } unset($evalCode); } if (is_callable($discount['APPLICATION_EXECUTE'])) { $currentUseMode = $this->getUseMode(); $this->currentStep['oldData'] = $this->orderData; if ( $currentUseMode == self::USE_MODE_APPLY || $currentUseMode == self::USE_MODE_MIXED ) { $discountStoredActionData = $this->getDiscountStoredActionData($this->currentStep['discountId']); if (!empty($discountStoredActionData) && is_array($discountStoredActionData)) Discount\Actions::setStoredData($discountStoredActionData); unset($discountStoredActionData); } $discount['APPLICATION_EXECUTE']($this->orderData); switch ($currentUseMode) { case self::USE_MODE_COUPONS: case self::USE_MODE_FULL: $actionsResult = $this->calculateFullSaleDiscountResult(); break; case self::USE_MODE_APPLY: case self::USE_MODE_MIXED: $actionsResult = $this->calculateApplySaleDiscountResult(); break; default: $actionsResult = new Result; } if (!$actionsResult->isSuccess()) $result->addErrors($actionsResult->getErrors()); unset($actionsResult); unset($currentUseMode); } } unset($discount); Discount\Actions::clearAction(); return $result; } /** * Apply basket discount in new order. * * @return Result */ protected function calculateFullBasketDiscount() { $result = new Result; if ((string)Main\Config\Option::get('sale', 'use_sale_discount_only') == 'Y') return $result; if (empty($this->basketDiscountList)) return $result; $applyExist = $this->isBasketApplyResultExist(); $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET']; foreach ($this->getBasketCodes(true) as $basketCode) { if ($this->isOrderNew() && array_key_exists($basketCode, $applyBlock)) unset($applyBlock[$basketCode]); if (empty($this->basketDiscountList[$basketCode])) continue; $itemData = array( 'MODULE_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['MODULE'], 'PRODUCT_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'], 'BASKET_ID' => $basketCode ); foreach ($this->basketDiscountList[$basketCode] as $index => $discount) { $discountResult = $this->convertDiscount($discount); if (!$discountResult->isSuccess()) { $result->addErrors($discountResult->getErrors()); unset($discountResult); return $result; } $orderDiscountId = $discountResult->getId(); $discountData = $discountResult->getData(); $orderCouponId = ''; $this->basketDiscountList[$basketCode][$index]['ORDER_DISCOUNT_ID'] = $orderDiscountId; if ($discountData['USE_COUPONS'] == 'Y') { if (empty($discount['COUPON'])) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'), self::ERROR_ID )); return $result; } $couponResult = $this->convertCoupon($discount['COUPON'], $orderDiscountId); if (!$couponResult->isSuccess()) { $result->addErrors($couponResult->getErrors()); unset($couponResult); return $result; } $orderCouponId = $couponResult->getId(); DiscountCouponsManager::setApplyByProduct($itemData, array($orderCouponId)); unset($couponResult); } unset($discountData, $discountResult); if (!isset($applyBlock[$basketCode])) $applyBlock[$basketCode] = array(); $applyBlock[$basketCode][$index] = array( 'DISCOUNT_ID' => $orderDiscountId, 'COUPON_ID' => $orderCouponId, 'RESULT' => array( 'APPLY' => 'Y', 'DESCR' => false, 'DESCR_DATA' => false ) ); $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode]; $orderApplication = ( !empty($this->discountsCache[$orderDiscountId]['APPLICATION']) ? $this->discountsCache[$orderDiscountId]['APPLICATION'] : null ); if (!empty($orderApplication)) { $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = ( !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']) ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'] : false ); $applyProduct = null; eval('$applyProduct='.$orderApplication.';'); if (is_callable($applyProduct)) $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]); unset($applyProduct); if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'])) { $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']['BASKET']; $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = self::formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($orderApplication); if ($applyExist && !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId)) { $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct; $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N'; } unset($disable, $currentProduct); } unset($discount, $index); } unset($basketCode); unset($applyBlock); return $result; } /** * Apply basket discount in exist order. * * @return Result */ protected function calculateApplyBasketDiscount() { $result = new Result; if ($this->useOnlySaleDiscounts()) return $result; if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET'])) return $result; $applyExist = $this->isBasketApplyResultExist(); $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET']; foreach ($this->getBasketCodes(false) as $basketCode) { if ($this->isCustomPriceByCode($basketCode)) { if (isset($applyBlock[$basketCode])) unset($applyBlock[$basketCode]); continue; } if (empty($applyBlock[$basketCode])) continue; foreach ($applyBlock[$basketCode] as $index => $discount) { $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode]; $orderDiscountId = $discount['DISCOUNT_ID']; $orderCouponId = $discount['COUPON_ID']; $orderApplication = ( !empty($this->discountsCache[$orderDiscountId]['APPLICATION']) ? $this->discountsCache[$orderDiscountId]['APPLICATION'] : null ); if (!empty($orderApplication) && !$this->loadDiscountModules($orderDiscountId)) $orderApplication = null; if (!empty($orderApplication)) { $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = ( !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']) ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'] : false ); $applyProduct = null; eval('$applyProduct='.$orderApplication.';'); if (is_callable($applyProduct)) $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]); unset($applyProduct); if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'])) { $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']; $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = self::formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($orderApplication); $disable = ($applyBlock[$basketCode][$index]['RESULT']['APPLY'] == 'N'); if ($applyExist) { $applyDisable = !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId); if ($applyDisable != $disable) $disable = $applyDisable; unset($applyDisable); } if ($disable) { $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct; $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N'; } else { $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'Y'; } unset($disable, $currentProduct); } unset($index, $discount); } unset($basketCode); unset($applyBlock); return $result; } /** * Calculate discount block for existing order. * * @return Result */ protected function calculateApplyDiscountBlock() { $result = new Result; $basketDiscountResult = $this->calculateApplyBasketDiscount(); if (!$basketDiscountResult->isSuccess()) { $result->addErrors($basketDiscountResult->getErrors()); unset($basketDiscountResult); return $result; } unset($basketDiscountResult); $roundApply = false; if ($this->isRoundMode(self::ROUND_MODE_BASKET_DISCOUNT)) { $roundApply = true; $this->roundApplyBasketPrices(); } if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply) { $this->roundApplyBasketPricesByIndex(array( 'DISCOUNT_INDEX' => -1, 'DISCOUNT_ID' => 0 )); } if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'])) { $index = -1; foreach ($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'] as $indexDiscount => $discount) { $index++; $orderDiscountId = $discount['DISCOUNT_ID']; if (!isset($this->discountsCache[$orderDiscountId])) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_APPLY_WITHOUT_SALE_DISCOUNT'), self::ERROR_ID )); return $result; } Discount\Actions::clearAction(); if (!empty($discount['RESULT']['BASKET'])) { if ($discount['ACTION_BLOCK_LIST']) { $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_COUNTER; $blockList = array(); foreach ($discount['RESULT']['BASKET'] as $basketCode => $basketItem) $blockList[$basketCode] = $basketItem['ACTION_BLOCK_LIST']; unset($basketCode, $basketItem); } else { if ($this->discountsCache[$orderDiscountId]['SIMPLE_ACTION']) { $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_SIMPLE; $blockList = array_fill_keys(array_keys($discount['RESULT']['BASKET']), true); } else { $applyResultMode = Discount\Actions::APPLY_RESULT_MODE_DESCR; $blockList = array(); foreach ($discount['RESULT']['BASKET'] as $basketCode => $basketItem) $blockList[$basketCode] = $basketItem['DESCR_DATA']; unset($basketCode, $basketItem); } } Discount\Actions::setApplyResultMode($applyResultMode); Discount\Actions::setApplyResult(array('BASKET' => $blockList)); unset($blockList, $applyResultMode); } if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply) { $this->roundApplyBasketPricesByIndex(array( 'DISCOUNT_INDEX' => $index, 'DISCOUNT_ID' => $orderDiscountId )); } $this->fillCurrentStep(array( 'discountIndex' => $indexDiscount, 'discountId' => $orderDiscountId, )); $actionsResult = $this->applySaleDiscount(); if (!$actionsResult->isSuccess()) { $result->addErrors($actionsResult->getErrors()); unset($actionsResult); return $result; } unset($orderDiscountId); } if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT) && !$roundApply) { $index++; $this->roundApplyBasketPricesByIndex(array( 'DISCOUNT_INDEX' => $index, 'DISCOUNT_ID' => 0 )); $roundConfig = $this->getRoundIndex('BASKET_ROUND'); if (is_array($roundConfig)) { if ($roundConfig['DISCOUNT_INDEX'] > $index && $roundConfig['DISCOUNT_ID'] == 0) { $this->roundApplyBasketPricesByIndex(array( 'DISCOUNT_INDEX' => $roundConfig['DISCOUNT_INDEX'], 'DISCOUNT_ID' => 0 )); } } unset($roundConfig); } unset($discount, $indexDiscount, $currentList); } if ($this->isRoundMode(self::ROUND_MODE_FINAL_PRICE)) $this->roundApplyBasketPrices(); $this->fillEmptyCurrentStep(); return $result; } /** * Applyed additional coupons. * * @return Result */ protected function calculateApplyAdditionalCoupons() { $result = new Result; $useMode = $this->getUseMode(); if ($useMode != self::USE_MODE_APPLY && $useMode != self::USE_MODE_MIXED) return $result; if (!$this->useOnlySaleDiscounts()) { $couponList = $this->getAdditionalCoupons(array('!MODULE_ID' => 'sale')); if (!empty($couponList)) { $params = array( 'USE_BASE_PRICE' => $this->saleOptions['USE_BASE_PRICE'], 'USER_ID' => $this->orderData['USER_ID'], 'SITE_ID' => $this->orderData['SITE_ID'] ); $couponsByModule = array(); foreach ($couponList as &$coupon) { if (!isset($couponsByModule[$coupon['MODULE_ID']])) $couponsByModule[$coupon['MODULE_ID']] = array(); $couponsByModule[$coupon['MODULE_ID']][] = array( 'DISCOUNT_ID' => $coupon['DISCOUNT_ID'], 'COUPON' => $coupon['COUPON'] ); } unset($coupon); if (!empty($couponsByModule)) { foreach ($couponsByModule as $moduleId => $moduleCoupons) { if ($useMode == self::USE_MODE_APPLY) { $currentBasket = $this->orderData['BASKET_ITEMS']; } else { $currentBasket = array(); $basketCodeList = $this->getBasketCodes(false); foreach ($basketCodeList as $basketCode) $currentBasket[$basketCode] = $this->orderData['BASKET_ITEMS'][$basketCode]; unset($basketCode, $basketCodeList); } if (empty($currentBasket)) continue; $couponsApply = OrderDiscountManager::calculateApplyCoupons($moduleId, $moduleCoupons, $currentBasket, $params); unset($currentBasket); if (!empty($couponsApply)) { $couponsApplyResult = $this->calculateApplyBasketAdditionalCoupons($couponsApply); if (!$couponsApplyResult->isSuccess()) $result->addErrors($couponsApplyResult->getErrors()); unset($couponsApplyResult); } unset($couponsApply); } unset($moduleId, $moduleCoupons); } unset($couponsByModule, $params); } unset($couponList); } $couponList = $this->getAdditionalCoupons(array('MODULE_ID' => 'sale')); if (!empty($couponList)) { $couponsApplyResult = $this->calculateApplySaleAdditionalCoupons($couponList); if (!$couponsApplyResult->isSuccess()) $result->addErrors($couponsApplyResult->getErrors()); unset($couponsApplyResult); } unset($couponList); return $result; } /** * Calculate step discount result by new order. * * @return Result */ protected function calculateFullSaleDiscountResult() { $result = new Result; $this->orderData['DISCOUNT_RESULT'] = Discount\Actions::getActionResult(); $this->orderData['DISCOUNT_DESCR'] = Discount\Actions::getActionDescription(); if (!empty($this->orderData['DISCOUNT_RESULT']) && is_array($this->orderData['DISCOUNT_RESULT'])) { $stepResult = self::getStepResult($this->orderData); } else { $stepResult = self::getStepResultOld($this->orderData, $this->currentStep['oldData']); if (!empty($stepResult)) { if (empty($this->orderData['DISCOUNT_DESCR']) || !is_array($this->orderData['DISCOUNT_DESCR'])) $this->orderData['DISCOUNT_DESCR'] = $this->getSimpleActionDescription($stepResult); } } Discount\Actions::fillCompatibleFields($this->orderData); $applied = !empty($stepResult); $orderDiscountId = 0; $orderCouponId = ''; if ($applied) { $this->correctStepResult($stepResult, $this->currentStep['discount']); $this->currentStep['discount']['ACTIONS_DESCR'] = $this->orderData['DISCOUNT_DESCR']; $discountResult = $this->convertDiscount($this->currentStep['discount']); if (!$discountResult->isSuccess()) { $result->addErrors($discountResult->getErrors()); return $result; } $orderDiscountId = $discountResult->getId(); $discountData = $discountResult->getData(); $this->currentStep['discount']['ORDER_DISCOUNT_ID'] = $orderDiscountId; if ($discountData['USE_COUPONS'] == 'Y') { if (empty($this->currentStep['discount']['COUPON'])) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'), self::ERROR_ID )); return $result; } $couponResult = $this->convertCoupon($this->currentStep['discount']['COUPON']['COUPON'], $orderDiscountId); if (!$couponResult->isSuccess()) { $result->addErrors($couponResult->getErrors()); unset($couponResult); return $result; } $orderCouponId = $couponResult->getId(); DiscountCouponsManager::setApply($orderCouponId, $stepResult); unset($couponResult); } $this->setDiscountStoredActionData($orderDiscountId, Discount\Actions::getStoredData()); } unset($this->orderData['DISCOUNT_DESCR'], $this->orderData['DISCOUNT_RESULT']); if ($applied) { if ( ( !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId]) && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N' ) || ( $orderCouponId != '' && !empty($this->applyResult['COUPON_LIST'][$orderCouponId]) && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N' ) ) { $this->orderData = $this->currentStep['oldData']; if (!empty($stepResult['BASKET'])) { foreach ($stepResult['BASKET'] as &$basketItem) $basketItem['APPLY'] = 'N'; unset($basketItem); } if (!empty($stepResult['DELIVERY'])) $stepResult['DELIVERY']['APPLY'] = 'N'; } else { if (!empty($this->applyResult['BASKET']) && is_array($this->applyResult['BASKET'])) { foreach ($this->applyResult['BASKET'] as $basketCode => $discountList) { if ( is_array($discountList) && !empty($discountList[$orderDiscountId]) && $discountList[$orderDiscountId] == 'N' ) { if (empty($stepResult['BASKET'][$basketCode])) continue; $stepResult['BASKET'][$basketCode]['APPLY'] = 'N'; $this->orderData['BASKET_ITEMS'][$basketCode] = $this->currentStep['oldData']['BASKET_ITEMS'][$basketCode]; } } unset($basketCode, $discountList); } if (!empty($this->applyResult['DELIVERY'])) { if ( is_array($this->applyResult['DELIVERY']) && !empty($this->applyResult['DELIVERY'][$orderDiscountId]) && $this->applyResult['DELIVERY'][$orderDiscountId] == 'N' ) { $this->orderData['PRICE_DELIVERY'] = $this->currentStep['oldData']['PRICE_DELIVERY']; $this->orderData['PRICE_DELIVERY_DIFF'] = $this->currentStep['oldData']['PRICE_DELIVERY_DIFF']; $stepResult['DELIVERY']['APPLY'] = 'N'; } } } } if ($applied && $orderCouponId != '') { $couponApply = DiscountCouponsManager::setApply($this->couponsCache[$orderCouponId]['COUPON'], $stepResult); unset($couponApply); } if ($applied) { $this->tryToRevertApplyStatusInBlocks($stepResult); $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][] = array( 'DISCOUNT_ID' => $orderDiscountId, 'COUPON_ID' => $orderCouponId, 'RESULT' => $stepResult ); if ($this->currentStep['discount']['LAST_DISCOUNT'] == 'Y') $this->currentStep['stop'] = true; if ($this->currentStep['discount']['LAST_LEVEL_DISCOUNT'] == 'Y') $this->currentStep['stopLevel'] = true; } return $result; } /** * Tries to revert apply status of discounts. * It depends on current $stepResult. If it has REVERT_APPLY like true, that we have to cancel discounts on basket * items which were affected. * @param array $stepResult * @return void */ protected function tryToRevertApplyStatusInBlocks(array $stepResult) { if (empty($stepResult['BASKET'])) { return; } foreach ($stepResult['BASKET'] as $basketItemId => $item) { if ($item['APPLY'] !== 'Y') { continue; } if (empty($item['DESCR_DATA'])) { continue; } foreach ($item['DESCR_DATA'] as $rowDescription) { if ( !empty($rowDescription['REVERT_APPLY']) && $rowDescription['VALUE_ACTION'] === OrderDiscountManager::DESCR_VALUE_ACTION_CUMULATIVE ) { $this->revertApplyBlockForBasketItem($basketItemId); } } } } /** * Reverts apply flag in blocks for basket items which has for example cumulative discount * which cancels previous discounts on item. * @param int $basketItemId * @return void */ protected function revertApplyBlockForBasketItem($basketItemId) { if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter])) { return; } $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]; foreach ($applyBlock['ORDER'] as &$orderBlock) { foreach ($orderBlock['RESULT']['BASKET'] as $bid => &$basketItem) { if ($bid != $basketItemId) { continue; } $basketItem['APPLY'] = 'N'; } } } /** * Calculate step discount result by exist order. * * @return Result */ protected function calculateApplySaleDiscountResult() { $result = new Result; $this->orderData['DISCOUNT_RESULT'] = Discount\Actions::getActionResult(); if (!empty($this->orderData['DISCOUNT_RESULT']) && is_array($this->orderData['DISCOUNT_RESULT'])) $stepResult = self::getStepResult($this->orderData); else $stepResult = self::getStepResultOld($this->orderData, $this->currentStep['oldData']); $applied = !empty($stepResult); $orderDiscountId = 0; $orderCouponId = ''; if ($applied) { $this->correctStepResult($stepResult, $this->discountsCache[$this->currentStep['discountId']]); $orderDiscountId = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['DISCOUNT_ID']; $orderCouponId = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['COUPON_ID']; } unset($this->orderData['DISCOUNT_RESULT']); if ($applied) { if ( ( !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId]) && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N' ) || ( $orderCouponId != '' && !empty($this->applyResult['COUPON_LIST'][$orderCouponId]) && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N' ) ) { $this->orderData = $this->currentStep['oldData']; if (!empty($stepResult['BASKET'])) { foreach ($stepResult['BASKET'] as &$basketItem) $basketItem['APPLY'] = 'N'; unset($basketItem); } if (!empty($stepResult['DELIVERY'])) $stepResult['DELIVERY']['APPLY'] = 'N'; } else { if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT'])) { $existDiscountResult = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT']; if (!empty($existDiscountResult['BASKET'])) { $basketCodeList = $this->getBasketCodes(false); if (!empty($basketCodeList)) { foreach ($basketCodeList as &$basketCode) { if ($this->isCustomPriceByCode($basketCode)) continue; if (isset($existDiscountResult['BASKET'][$basketCode])) { $disable = ($existDiscountResult['BASKET'][$basketCode]['APPLY'] == 'N'); if (isset($this->applyResult['BASKET'][$basketCode][$orderDiscountId])) { $applyDisable = ($this->applyResult['BASKET'][$basketCode][$orderDiscountId] == 'N'); if ($disable != $applyDisable) $disable = $applyDisable; unset($applyDisable); } if ($disable) { $stepResult['BASKET'][$basketCode]['APPLY'] = 'N'; $this->orderData['BASKET_ITEMS'][$basketCode] = $this->currentStep['oldData']['BASKET_ITEMS'][$basketCode]; } else { $stepResult['BASKET'][$basketCode]['APPLY'] = 'Y'; } } } unset($disable, $basketCode); } } if (!empty($existDiscountResult['DELIVERY'])) { $disable = ($existDiscountResult['DELIVERY']['APPLY'] == 'N'); if (!empty($this->applyResult['DELIVERY'][$orderDiscountId])) { $applyDisable = ($this->applyResult['DELIVERY'][$orderDiscountId] == 'N'); if ($disable != $applyDisable) $disable = $applyDisable; unset($applyDisable); } if ($disable) { $this->orderData['PRICE_DELIVERY'] = $this->currentStep['oldData']['PRICE_DELIVERY']; $this->orderData['PRICE_DELIVERY_DIFF'] = $this->currentStep['oldData']['PRICE_DELIVERY_DIFF']; $stepResult['DELIVERY']['APPLY'] = 'N'; } else { $stepResult['DELIVERY']['APPLY'] = 'Y'; } unset($disable); } } } } if ($applied && $orderCouponId != '') { $couponApply = DiscountCouponsManager::setApply($this->couponsCache[$orderCouponId]['COUPON'], $stepResult); unset($couponApply); } if ($applied) { $this->mergeDiscountActionResult($this->currentStep['discountIndex'], $stepResult); } else { if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']])) $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$this->currentStep['discountIndex']]['RESULT'] = array(); } return $result; } /* rounding tools */ /** * Return order round apply mode. * @internal * * @return int */ protected function getRoundMode() { return $this->roundApplyMode; } /** * Return true, if selected check round apply mode. * @internal * * @param int $mode Checked mode. * @return bool */ protected function isRoundMode($mode) { return $this->roundApplyMode == $mode; } /** * Load round apply config for exist order. * @internal * * @return void */ protected function loadRoundConfig() { $defaultApplyMode = self::ROUND_MODE_FINAL_PRICE; $this->roundApplyMode = $defaultApplyMode; $this->roundApplyConfig = array(); if ($this->isOrderExists() && !$this->isOrderNew() && $this->getUseMode() != self::USE_MODE_FULL) { $orderId = $this->getOrder()->getId(); $data = Internals\OrderDiscountDataTable::getList(array( 'select' => array('*'), 'filter' => array( '=ORDER_ID' => $orderId, '=ENTITY_TYPE' => Internals\OrderDiscountDataTable::ENTITY_TYPE_ROUND, '=ENTITY_ID' => $orderId ) ))->fetch(); if (empty($data)) return; $entityData = $data['ENTITY_DATA']; unset($data, $orderId); if ( is_array($entityData) && isset($entityData['MODE']) ) { $this->roundApplyMode = (int)$entityData['MODE']; if ($this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) $this->roundApplyConfig = (isset($entityData['CONFIG']) ? $entityData['CONFIG'] : array()); } } if ( $this->roundApplyMode != self::ROUND_MODE_BASKET_DISCOUNT && $this->roundApplyMode != self::ROUND_MODE_SALE_DISCOUNT && $this->roundApplyMode != self::ROUND_MODE_FINAL_PRICE ) $this->roundApplyMode = null; if (!is_array($this->roundApplyConfig)) $this->roundApplyConfig = array(); unset($defaultApplyMode); } /** * Set discount index for use round. Only for sale discount mode. * @internal * * @param string $entity Entity id. * @param array $index Index data. * @return void */ protected function setRoundIndex($entity, array $index) { if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) return; if (!isset($index['DISCOUNT_INDEX']) || !isset($index['DISCOUNT_ID'])) return; if (!isset($this->roundApplyConfig[$this->discountResultCounter])) $this->roundApplyConfig[$this->discountResultCounter] = array(); $this->roundApplyConfig[$this->discountResultCounter][$entity] = $index; } /** * Return index data for use round. * @internal * * @param string $entity Entity id. * @param null|int $applyCounter Apply block counter. * @return null|array */ protected function getRoundIndex($entity, $applyCounter = null) { if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) return null; if ($applyCounter === null) $applyCounter = $this->discountResultCounter; return (isset($this->roundApplyConfig[$applyCounter][$entity]) ? $this->roundApplyConfig[$applyCounter][$entity] : null); } /** * Round prices. * * @return void */ protected function roundFullBasketPrices() { $basketCodeList = $this->getBasketCodes(true); if (!empty($basketCodeList)) { $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND']; $orderData = $this->orderData; unset($orderData['BASKET_ITEMS']); $basket = array_intersect_key( $this->orderData['BASKET_ITEMS'], array_fill_keys($basketCodeList, true) ); $result = OrderDiscountManager::roundBasket( $basket, array(), $orderData ); foreach ($result as $basketCode => $roundResult) { if (empty($roundResult) || !is_array($roundResult)) continue; if (!isset($this->orderData['BASKET_ITEMS'][$basketCode])) continue; $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE']; $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE']; $roundBlock[$basketCode] = array( 'APPLY' => 'Y', 'ROUND_RULE' => $roundResult['ROUND_RULE'] ); } unset($basketCode, $roundResult, $result); unset($basket, $orderData); unset($roundBlock); } unset($basketCodeList); } /** * Round prices. * * @return void */ protected function roundApplyBasketPrices() { if (empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND'])) return; $basketCodeList = $this->getBasketCodes(false); if (!empty($basketCodeList)) { $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND']; $basket = array(); $roundData = array(); foreach ($basketCodeList as $basketCode) { if (empty($roundBlock[$basketCode])) continue; if ($roundBlock[$basketCode]['APPLY'] != 'Y') continue; $basket[$basketCode] = $this->orderData['BASKET_ITEMS'][$basketCode]; $roundData[$basketCode] = $roundBlock[$basketCode]['ROUND_RULE']; } unset($basketCode); if (!empty($basket)) { $orderData = $this->orderData; unset($orderData['BASKET_ITEMS']); $result = OrderDiscountManager::roundBasket( $basket, $roundData, $orderData ); foreach ($result as $basketCode => $roundResult) { if (empty($roundResult) || !is_array($roundResult)) continue; if (!isset($this->orderData['BASKET_ITEMS'][$basketCode])) continue; $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE']; $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE']; } unset($basketCode, $roundResult, $result); unset($orderData); } unset($roundData, $basket); unset($roundBlock); } unset($basketCodeList); } /** * Round only changed prices. * * @return void */ protected function roundChangedBasketPrices() { $basketCodeList = array(); $applyBlock = $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]; switch ($this->getUseMode()) { case self::USE_MODE_APPLY: if (!empty($applyBlock['BASKET'])) { foreach (array_keys($applyBlock['BASKET']) as $basketCode) { $basketCodeList[$basketCode] = $basketCode; } unset($basketCode); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if (empty($discount['RESULT']['BASKET'])) continue; foreach (array_keys($discount['RESULT']['BASKET']) as $basketCode) { $basketCodeList[$basketCode] = $basketCode; } } unset($basketCode, $discount); } break; case self::USE_MODE_MIXED: if (!empty($applyBlock['BASKET'])) { foreach (array_keys($applyBlock['BASKET']) as $basketCode) { $basketCodeList[$basketCode] = $basketCode; } unset($basketCode); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if (empty($discount['RESULT']['BASKET'])) continue; foreach (array_keys($discount['RESULT']['BASKET']) as $basketCode) { $basketCodeList[$basketCode] = $basketCode; } } unset($basketCode, $discount); } foreach ($this->getBasketCodes(true) as $basketCode) $basketCodeList[$basketCode] = $basketCode; break; } if (!empty($basketCodeList)) { $roundBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND']; $orderData = $this->orderData; unset($orderData['BASKET_ITEMS']); $basket = array_intersect_key( $this->orderData['BASKET_ITEMS'], array_fill_keys($basketCodeList, true) ); $result = OrderDiscountManager::roundBasket( $basket, array(), $orderData ); foreach ($result as $basketCode => $roundResult) { if (empty($roundResult) || !is_array($roundResult)) continue; if (!isset($this->orderData['BASKET_ITEMS'][$basketCode])) continue; $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE'] = $roundResult['PRICE']; $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE'] = $roundResult['DISCOUNT_PRICE']; $roundBlock[$basketCode] = array( 'APPLY' => 'Y', 'ROUND_RULE' => $roundResult['ROUND_RULE'] ); } unset($basketCode, $roundResult, $result); unset($storageClassName); unset($basket, $orderData); unset($roundBlock); } unset($basketCodeList); } /** * Round prices in sale discount mode for new order. * @internal * * @param array $index Index data. * @return void */ protected function roundFullBasketPriceByIndex(array $index) { if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) return; if ($this->getUseMode() != self::USE_MODE_FULL) return; $this->roundFullBasketPrices(); if (!empty($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET_ROUND'])) $this->setRoundIndex('BASKET_ROUND', $index); } /** * Round prices in sale discount mode for exist order. * @internal * * @param array $index Index data. * @return void */ protected function roundApplyBasketPricesByIndex(array $index) { if (!isset($index['DISCOUNT_INDEX'])) return; if (!$this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) return; if ($this->getUseMode() != self::USE_MODE_APPLY && $this->getUseMode() != self::USE_MODE_MIXED) return; $roundConfig = $this->getRoundIndex('BASKET_ROUND'); if ($roundConfig === null) return; if ($roundConfig['DISCOUNT_INDEX'] != $index['DISCOUNT_INDEX']) return; $this->roundApplyBasketPrices(); } /* rounding tools finish */ /** * Convert discount for saving in order. * * @param array $discount Raw discount data. * @return Result */ protected function convertDiscount($discount) { $result = new Result; $discountResult = OrderDiscountManager::saveDiscount($discount, false); if (!$discountResult->isSuccess()) { $result->addErrors($discountResult->getErrors()); unset($discountResult); return $result; } $orderDiscountId = $discountResult->getId(); $discountData = $discountResult->getData(); $resultData = array( 'ORDER_DISCOUNT_ID' => $orderDiscountId, 'USE_COUPONS' => $discountData['USE_COUPONS'], 'MODULE_ID' => $discountData['MODULE_ID'], 'DISCOUNT_ID' => $discountData['DISCOUNT_ID'] ); if (!isset($this->discountsCache[$orderDiscountId])) { $discountData['ACTIONS_DESCR_DATA'] = false; if (!empty($discountData['ACTIONS_DESCR']) && is_array($discountData['ACTIONS_DESCR'])) { $discountData['ACTIONS_DESCR_DATA'] = $discountData['ACTIONS_DESCR']; $discountData['ACTIONS_DESCR'] = self::formatDescription($discountData['ACTIONS_DESCR']); } else { $discountData['ACTIONS_DESCR'] = false; } if (empty($discountData['ACTIONS_DESCR'])) { $discountData['ACTIONS_DESCR'] = false; $discountData['ACTIONS_DESCR_DATA'] = false; } $this->discountsCache[$orderDiscountId] = $discountData; } $result->setId($orderDiscountId); $result->setData($resultData); unset($discountData, $resultData, $orderDiscountId); return $result; } /** * Convert coupon for saving in order. * * @param string|array $coupon Coupon. * @param int $discount Order discount id. * @return Result */ protected function convertCoupon($coupon, $discount) { $result = new Result; if (!is_array($coupon)) { $couponData = DiscountCouponsManager::getEnteredCoupon($coupon, true); if (empty($couponData)) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_COUPON_NOT_FOUND'), self::ERROR_ID )); return $result; } $coupon = array( 'COUPON' => $couponData['COUPON'], 'TYPE' => $couponData['TYPE'], 'COUPON_ID' => $couponData['ID'], 'DATA' => $couponData ); unset($couponData); } $coupon['ORDER_DISCOUNT_ID'] = $discount; $coupon['ID'] = 0; $orderCouponId = $coupon['COUPON']; if (!isset($this->couponsCache[$orderCouponId])) $this->couponsCache[$orderCouponId] = $coupon; $result->setId($orderCouponId); $result->setData($coupon); unset($coupon, $orderCouponId); return $result; } /** * Returns result after one discount. * * @param array $order Order current data. * @return array */ protected static function getStepResult($order) { $result = array(); $stepResult = &$order['DISCOUNT_RESULT']; if (!empty($stepResult['DELIVERY']) && is_array($stepResult['DELIVERY'])) { $result['DELIVERY'] = array( 'APPLY' => 'Y', 'DELIVERY_ID' => (isset($order['DELIVERY_ID']) ? $order['DELIVERY_ID'] : false), 'SHIPMENT_CODE' => (isset($order['SHIPMENT_CODE']) ? $order['SHIPMENT_CODE'] : false), 'DESCR' => OrderDiscountManager::formatArrayDescription($stepResult['DELIVERY']), 'DESCR_DATA' => $stepResult['DELIVERY'], 'ACTION_BLOCK_LIST' => array_keys($stepResult['DELIVERY']) ); if (is_array($result['DELIVERY']['DESCR'])) $result['DELIVERY']['DESCR'] = implode(', ', $result['DELIVERY']['DESCR']); } if (!empty($stepResult['BASKET']) && is_array($stepResult['BASKET'])) { if (!isset($result['BASKET'])) $result['BASKET'] = array(); foreach ($stepResult['BASKET'] as $basketCode => $basketResult) { $result['BASKET'][$basketCode] = array( 'APPLY' => 'Y', 'DESCR' => OrderDiscountManager::formatArrayDescription($basketResult), 'DESCR_DATA' => $basketResult, 'MODULE' => $order['BASKET_ITEMS'][$basketCode]['MODULE'], 'PRODUCT_ID' => $order['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'], 'BASKET_ID' => (isset($order['BASKET_ITEMS'][$basketCode]['ID']) ? $order['BASKET_ITEMS'][$basketCode]['ID'] : $basketCode), 'ACTION_BLOCK_LIST' => array_keys($basketResult) ); if (is_array($result['BASKET'][$basketCode]['DESCR'])) $result['BASKET'][$basketCode]['DESCR'] = implode(', ', $result['BASKET'][$basketCode]['DESCR']); } unset($basketCode, $basketResult); } unset($stepResult); return $result; } /** * Returns result after one discount in old format. * * @param array $currentOrder Current order data. * @param array $oldOrder Old order data. * @return array */ protected static function getStepResultOld($currentOrder, $oldOrder) { $result = array(); if (isset($oldOrder['PRICE_DELIVERY']) && isset($currentOrder['PRICE_DELIVERY'])) { if ($oldOrder['PRICE_DELIVERY'] != $currentOrder['PRICE_DELIVERY']) { $descr = OrderDiscountManager::createSimpleDescription($currentOrder['PRICE_DELIVERY'], $oldOrder['PRICE_DELIVERY'], $oldOrder['CURRENCY']); $result['DELIVERY'] = array( 'APPLY' => 'Y', 'DELIVERY_ID' => (isset($currentOrder['DELIVERY_ID']) ? $currentOrder['DELIVERY_ID'] : false), 'SHIPMENT_CODE' => (isset($currentOrder['SHIPMENT_CODE']) ? $currentOrder['SHIPMENT_CODE'] : false), 'DESCR' => OrderDiscountManager::formatArrayDescription($descr), 'DESCR_DATA' => $descr ); unset($descr); if (is_array($result['DELIVERY']['DESCR'])) $result['DELIVERY']['DESCR'] = implode(', ', $result['DELIVERY']['DESCR']); } } if (!empty($oldOrder['BASKET_ITEMS']) && !empty($currentOrder['BASKET_ITEMS'])) { foreach ($oldOrder['BASKET_ITEMS'] as $basketCode => $item) { if (!isset($currentOrder['BASKET_ITEMS'][$basketCode])) continue; if ($item['PRICE'] != $currentOrder['BASKET_ITEMS'][$basketCode]['PRICE']) { if (!isset($result['BASKET'])) $result['BASKET'] = array(); $descr = OrderDiscountManager::createSimpleDescription($currentOrder['BASKET_ITEMS'][$basketCode]['PRICE'], $item['PRICE'], $oldOrder['CURRENCY']); $result['BASKET'][$basketCode] = array( 'APPLY' => 'Y', 'DESCR' => OrderDiscountManager::formatArrayDescription($descr), 'DESCR_DATA' => $descr, 'MODULE' => $currentOrder['BASKET_ITEMS'][$basketCode]['MODULE'], 'PRODUCT_ID' => $currentOrder['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'], 'BASKET_ID' => (isset($currentOrder['BASKET_ITEMS'][$basketCode]['ID']) ? $currentOrder['BASKET_ITEMS'][$basketCode]['ID'] : $basketCode) ); unset($descr); if (is_array($result['BASKET'][$basketCode]['DESCR'])) $result['BASKET'][$basketCode]['DESCR'] = implode(', ', $result['BASKET'][$basketCode]['DESCR']); } } } return $result; } /** * Correct data for exotic coupon. * * @param array &$stepResult Currenct discount result. * @param array $discount Discount data. * @return void */ protected function correctStepResult(&$stepResult, $discount) { if ($discount['USE_COUPONS'] == 'Y' && !empty($discount['COUPON'])) { if ( $discount['COUPON']['TYPE'] == Internals\DiscountCouponTable::TYPE_BASKET_ROW && (!empty($stepResult['BASKET']) && count($stepResult['BASKET']) > 1) ) { $maxPrice = 0; $maxKey = -1; $basketKeys = array(); foreach ($stepResult['BASKET'] as $key => $row) { $basketKeys[$key] = $key; if ($maxPrice < $this->currentStep['oldData']['BASKET_ITEMS'][$key]['PRICE']) { $maxPrice = $this->currentStep['oldData']['BASKET_ITEMS'][$key]['PRICE']; $maxKey = $key; } } unset($basketKeys[$maxKey]); foreach ($basketKeys as $key => $row) { unset($stepResult['BASKET'][$key]); $this->orderData['BASKET_ITEMS'][$row] = $this->currentStep['oldData']['BASKET_ITEMS'][$row]; } unset($row, $key); } } } /* discount action reference tools */ /** * Fill additional discount data. * * @param int $orderDiscountId Converted discount id. * @param array $data Discount data. * * @return void */ protected function setDiscountStoredActionData($orderDiscountId, array $data) { $orderDiscountId = (int)$orderDiscountId; if (!isset($this->discountsCache[$orderDiscountId])) return; if (empty($data)) return; if (!isset($this->discountStoredActionData[$this->discountResultCounter])) $this->discountStoredActionData[$this->discountResultCounter] = array(); $this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId] = $data; } /** * Returns stored action data for discount. * * @param int $orderDiscountId Converted discount id. * @return array|null */ protected function getDiscountStoredActionData($orderDiscountId) { $orderDiscountId = (int)$orderDiscountId; if (isset($this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId])) return $this->discountStoredActionData[$this->discountResultCounter][$orderDiscountId]; return null; } /* discount action reference tools finish */ /** * Return true, if exist apply result from form for basket. * * @return bool */ protected function isBasketApplyResultExist() { return ( !empty($this->applyResult['DISCOUNT_LIST']) || !empty($this->applyResult['COUPON_LIST']) || !empty($this->applyResult['BASKET']) ); } /** * Returns discount and coupon list. * * @return void */ protected function getApplyDiscounts() { $discountApply = array(); $couponApply = array(); if (!empty($this->discountsCache)) { foreach ($this->discountsCache as $id => $discount) { $this->discountResult['DISCOUNT_LIST'][$id] = array( 'ID' => $id, 'NAME' => $discount['NAME'], 'MODULE_ID' => $discount['MODULE_ID'], 'DISCOUNT_ID' => $discount['ID'], 'REAL_DISCOUNT_ID' => $discount['DISCOUNT_ID'], 'USE_COUPONS' => $discount['USE_COUPONS'], 'ACTIONS_DESCR' => $discount['ACTIONS_DESCR'], 'ACTIONS_DESCR_DATA' => $discount['ACTIONS_DESCR_DATA'], 'APPLY' => 'N', 'EDIT_PAGE_URL' => $discount['EDIT_PAGE_URL'] ); $discountApply[$id] = &$this->discountResult['DISCOUNT_LIST'][$id]; } unset($id, $discount); } if (!empty($this->couponsCache)) { foreach ($this->couponsCache as $id => $coupon) { $this->discountResult['COUPON_LIST'][$id] = $coupon; $this->discountResult['COUPON_LIST'][$id]['APPLY'] = 'N'; $couponApply[$id] = &$this->discountResult['COUPON_LIST'][$id]; } unset($id, $coupon); } if (empty($this->discountResult['APPLY_BLOCKS'])) { unset($discountApply, $couponApply); return; } foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if (!empty($applyBlock['BASKET'])) { foreach ($applyBlock['BASKET'] as $basketCode => $discountList) { foreach ($discountList as $discount) { if ($discount['RESULT']['APPLY'] == 'Y') { if (isset($discountApply[$discount['DISCOUNT_ID']])) $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y'; if (isset($couponApply[$discount['COUPON_ID']])) $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y'; } } unset($discount); } unset($basketCode, $discountList); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if (!empty($discount['RESULT']['BASKET'])) { foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyList) { if ($applyList['APPLY'] == 'Y') { if (isset($discountApply[$discount['DISCOUNT_ID']])) $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y'; if (isset($couponApply[$discount['COUPON_ID']])) $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y'; } } unset($basketCode, $applyList); } if (!empty($discount['RESULT']['DELIVERY']) && $discount['RESULT']['DELIVERY']['APPLY'] == 'Y') { if (isset($discountApply[$discount['DISCOUNT_ID']])) $discountApply[$discount['DISCOUNT_ID']]['APPLY'] = 'Y'; if (isset($couponApply[$discount['COUPON_ID']])) $couponApply[$discount['COUPON_ID']]['APPLY'] = 'Y'; } } unset($discount); } } unset($counter, $applyBlock); unset($discountApply, $couponApply); } /** * Fill prices in apply results. * * @return void */ protected function getApplyPrices() { $this->normalizeDiscountResult(); $basket = array(); if (!empty($this->orderData['BASKET_ITEMS'])) { foreach ($this->orderData['BASKET_ITEMS'] as $basketCode => $basketItem) { $basket[$basketCode] = array( 'BASE_PRICE' => $basketItem['BASE_PRICE'], 'PRICE' => $basketItem['PRICE'], 'DISCOUNT' => $basketItem['DISCOUNT_PRICE'] ); } unset($basketCode, $basketItem); } $this->discountResult['PRICES'] = array( 'BASKET' => $basket, 'DELIVERY' => $this->getApplyDeliveryPrice() ); unset($basket); } /** * Returns delivery price data. * * @return array */ protected function getApplyDeliveryPrice() { return array( 'BASE_PRICE' => $this->orderData['BASE_PRICE_DELIVERY'], 'PRICE' => $this->orderData['PRICE_DELIVERY'], 'DISCOUNT' => $this->orderData['PRICE_DELIVERY_DIFF'] ); } /** * Get discount delivery list. * * @return void */ protected function getApplyDeliveryList() { $delivery = array(); $shipment = array(); if (!empty($this->discountResult['APPLY_BLOCKS'])) { foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as &$discount) { if (empty($discount['RESULT']['DELIVERY'])) continue; $delivery[$discount['RESULT']['DELIVERY']['DELIVERY_ID']] = $discount['RESULT']['DELIVERY']['DELIVERY_ID']; } unset($discount); } } unset($counter, $applyBlock); } if ($this->shipment instanceof Shipment) $shipment[] = $this->shipment->getShipmentCode(); $this->discountResult['DELIVERY_LIST'] = ( empty($delivery) ? array() : array_values($delivery) ); $this->discountResult['SHIPMENT_LIST'] = $shipment; } /** * Change result format. * * @return void */ protected function remakingDiscountResult() { $basket = array(); $delivery = array(); if (!empty($this->discountResult['APPLY_BLOCKS'])) { foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if (!empty($applyBlock['BASKET'])) { foreach ($applyBlock['BASKET'] as $basketCode => $discountList) { if (!isset($basket[$basketCode])) $basket[$basketCode] = array(); foreach ($discountList as $discount) { $basket[$basketCode][] = array( 'DISCOUNT_ID' => $discount['DISCOUNT_ID'], 'COUPON_ID' => $discount['COUPON_ID'], 'APPLY' => $discount['RESULT']['APPLY'], 'DESCR' => $discount['RESULT']['DESCR'] ); } unset($discount); } unset($basketCode, $discountList); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if (!empty($discount['RESULT']['BASKET'])) { foreach ($discount['RESULT']['BASKET'] as $basketCode => $applyList) { if (!isset($basket[$basketCode])) $basket[$basketCode] = array(); $basket[$basketCode][] = array( 'DISCOUNT_ID' => $discount['DISCOUNT_ID'], 'COUPON_ID' => $discount['COUPON_ID'], 'APPLY' => $applyList['APPLY'], 'DESCR' => $applyList['DESCR'] ); } unset($basketCode, $applyList); } if (!empty($discount['RESULT']['DELIVERY'])) { $delivery[] = array( 'DISCOUNT_ID' => $discount['DISCOUNT_ID'], 'COUPON_ID' => $discount['COUPON_ID'], 'DELIVERY_ID' => $discount['RESULT']['DELIVERY']['DELIVERY_ID'], 'APPLY' => $discount['RESULT']['DELIVERY']['APPLY'], 'DESCR' => $discount['RESULT']['DELIVERY']['DESCR'] ); } } unset($discount); } } unset($counter, $applyBlock); } $this->discountResult['RESULT'] = array( 'BASKET' => $basket, 'DELIVERY' => $delivery ); } /** * Create correspondence between basket ids and basket codes. * * @return Result */ protected function getBasketTables() { $result = new Result; $this->forwardBasketTable = array(); $this->reverseBasketTable = array(); if (!$this->isBasketNotEmpty()) return $result; $basket = $this->getBasket(); /** @var BasketItem $basketItem */ foreach ($basket as $basketItem) { $code = $basketItem->getBasketCode(); $id = $basketItem->getField('ID'); $this->forwardBasketTable[$code] = $id; $this->reverseBasketTable[$id] = $code; unset($id, $code); if ($basketItem->isBundleParent()) { $bundle = $basketItem->getBundleCollection(); if (empty($bundle)) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_BASKET_BUNDLE_EMPTY'), self::ERROR_ID )); break; } /** @var BasketItem $bundleItem */ foreach ($bundle as $bundleItem) { $code = $bundleItem->getBasketCode(); $id = $bundleItem->getField('ID'); $this->forwardBasketTable[$code] = $id; $this->reverseBasketTable[$id] = $code; unset($id, $code); } unset($bundle, $bundleItem); } } unset($basketItem, $basket); return $result; } /** * Returns exist custom price for basket item code. * * @param int $code Basket code. * @return bool */ protected function isCustomPriceByCode($code) { if (!empty($this->orderData['BASKET_ITEMS'][$code]['CUSTOM_PRICE']) && $this->orderData['BASKET_ITEMS'][$code]['CUSTOM_PRICE'] == 'Y') return true; return false; } /** * Returns exist custom price for basket item. * * @param array $item Basket item. * @return bool */ protected static function isCustomPrice($item) { if (!empty($item['CUSTOM_PRICE']) && $item['CUSTOM_PRICE'] == 'Y') return true; return false; } /** * Returns check item in set for basket item code. * * @param int $code Basket code. * @return bool */ protected function isInSetByCode($code) { if (!empty($this->orderData['BASKET_ITEMS'][$code]['IN_SET']) && $this->orderData['BASKET_ITEMS'][$code]['IN_SET'] == 'Y') return true; return false; } /** * Returns check item in set for basket item. * * @param array $item Basket item. * @return bool */ protected static function isInSet($item) { if (!empty($item['IN_SET']) && $item['IN_SET'] == 'Y') return true; return false; } /** * Returns exist new item in basket. * * @return bool */ protected function isMixedBasket() { $result = false; if (empty($this->orderData['BASKET_ITEMS'])) return $result; foreach ($this->orderData['BASKET_ITEMS'] as $basketItem) { if (!isset($basketItem['ID']) || (int)$basketItem['ID'] <= 0) { $result = true; break; } } unset($basketItem); if (!$result) { if ($this->isOrderedBasketChanged()) $result = true; } return $result; } /** * Returns check new basket item for basket item code. * * @param int|string $code Basket code. * @return bool */ protected function isNewBasketItemByCode($code) { return ( $this->getUseMode() == self::USE_MODE_FULL || !isset($this->orderData['BASKET_ITEMS'][$code]['ID']) || $this->orderData['BASKET_ITEMS'][$code]['ID'] <= 0 ); } /** * Returns check new basket item for basket item. * * @param array $item Basket item. * @return bool */ protected static function isNewBasketItem($item) { return ( !isset($item['ID']) || $item['ID'] <= 0 ); } /** * Return true if basket saved order changed (change PRODUCT_ID). * * @return bool */ protected function isOrderedBasketChanged() { $result = false; if ($this->isOrderExists() && !$this->isOrderNew() && $this->isBasketNotEmpty()) { $basket = $this->getBasket(); /** @var BasketItem $basketItem */ foreach ($basket as $basketItem) { if (!$basketItem->isChanged()) continue; /** @noinspection PhpInternalEntityUsedInspection */ if (in_array('PRODUCT_ID', $basketItem->getFields()->getChangedKeys())) { $result = true; break; } } unset($basketItem, $basket); } return $result; } /** * Return true if ordered basket item changed (change PRODUCT_ID). * * @param int $code Basket code. * @return bool */ protected function isBasketItemChanged($code) { $result = false; if ($this->isOrderExists() && !$this->isOrderNew() && $this->isBasketNotEmpty()) { $basketItem = $this->getBasket()->getItemByBasketCode($code); if ($basketItem instanceof BasketItem) { /** @noinspection PhpInternalEntityUsedInspection */ if (in_array('PRODUCT_ID', $basketItem->getFields()->getChangedKeys())) $result = true; } } return $result; } /** * Returns basket codes for calculate. * * @param bool $full Full or apply mode. * @return array */ protected function getBasketCodes($full = true) { $result = array(); if (empty($this->orderData['BASKET_ITEMS'])) return $result; switch ($this->getUseMode()) { case self::USE_MODE_FULL: case self::USE_MODE_COUPONS: foreach ($this->orderData['BASKET_ITEMS'] as $code => $item) { if ($this->isCustomPrice($item) || $this->isInSet($item)) continue; $result[] = $code; } unset($code, $item); break; case self::USE_MODE_APPLY: foreach ($this->orderData['BASKET_ITEMS'] as $code => $item) { if ( $this->isCustomPrice($item) || $this->isNewBasketItem($item) || $this->isBasketItemChanged($code) || $this->isInSet($item) ) continue; $result[] = $code; } unset($code, $item); break; case self::USE_MODE_MIXED: $full = ($full === true); if ($full) { foreach ($this->orderData['BASKET_ITEMS'] as $code => $item) { if ( !$this->isCustomPrice($item) && !$this->isInSet($item) && ($this->isNewBasketItem($item) || $this->isBasketItemChanged($code)) ) $result[] = $code; } unset($code, $item); } else { foreach ($this->orderData['BASKET_ITEMS'] as $code => $item) { if ( $this->isCustomPrice($item) || $this->isNewBasketItem($item) || $this->isBasketItemChanged($code) || $this->isInSet($item) ) continue; $result[] = $code; } unset($code, $item); } break; } return $result; } /** * Merge discount actions result with old data. * * @param int $index Discount index. * @param array $stepResult New result. * @return void */ protected function mergeDiscountActionResult($index, $stepResult) { if (!isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index])) return; if (empty($stepResult) || !is_array($stepResult)) return; $basketKeys = array_keys($this->orderData['BASKET_ITEMS']); foreach ($basketKeys as &$basketCode) { if (!$this->isCustomPriceByCode($basketCode)) continue; if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['BASKET'][$basketCode])) unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['BASKET'][$basketCode]); } unset($basketCode); if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR_DATA'])) unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR_DATA']); if (isset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR'])) unset($this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']['DESCR']); self::recursiveMerge($stepResult, $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT']); $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'][$index]['RESULT'] = $stepResult; } /** * Fill empty discount result list. * * @return void */ protected function fillEmptyDiscountResult() { $this->discountResultCounter = 0; $this->discountResult = array( 'APPLY_BLOCKS' => array(), 'DISCOUNT_LIST' => array(), 'COUPON_LIST' => array(), 'DELIVERY_LIST' => array(), 'SHIPMENT_LIST' => array() ); $this->clearCurrentApplyBlock(); $this->discountStoredActionData = array(); } /** * Filtered result order data. * * @return array */ protected function fillDiscountResult() { $this->normalizeDiscountResult(); $orderKeys = array('PRICE_DELIVERY', 'PRICE_DELIVERY_DIFF', 'CURRENCY'); $basketKeys = array('PRICE', 'DISCOUNT_PRICE', 'VAT_RATE', 'VAT_VALUE', 'CURRENCY'); $result = array(); foreach ($orderKeys as $key) { if (isset($this->orderData[$key])) $result[$key] = $this->orderData[$key]; } unset($key); $result['DISCOUNT_PRICE'] = $result['PRICE_DELIVERY_DIFF']; unset($result['PRICE_DELIVERY_DIFF']); $result['BASKET_ITEMS'] = array(); foreach ($this->orderData['BASKET_ITEMS'] as $index => $basketItem) { $result['BASKET_ITEMS'][$index] = array(); foreach ($basketKeys as $key) { if (isset($basketItem[$key])) $result['BASKET_ITEMS'][$index][$key] = $basketItem[$key]; } unset($key); } unset($index, $basketItem); $result['SHIPMENT'] = null; if ($this->shipment instanceof Shipment) $result['SHIPMENT'] = $this->shipment->getShipmentCode(); return $result; } /** * Internal. Fill current apply block empty data. * * @return void */ protected function clearCurrentApplyBlock() { $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter] = static::getEmptyApplyBlock(); } /** * Internal. Clear current step data. * * @return void */ protected function fillEmptyCurrentStep() { $this->currentStep = array( 'oldData' => array(), 'discount' => array(), 'discountIndex' => null, 'discountId' => 0, 'result' => array(), 'stop' => false, ); } /** * Internal. Fill current step data. * * @param array $data Only not empty keys. * @return void */ protected function fillCurrentStep($data) { $this->fillEmptyCurrentStep(); if (!empty($data) && is_array($data)) { foreach ($data as $key => $value) $this->currentStep[$key] = $value; unset($value, $key); } } /** * Load from database need modules list for discounts. * * @return void */ protected function getDiscountModules() { if (empty($this->discountIds)) return; $loadList = $this->discountIds; if (!empty($this->cacheDiscountModules)) { $loadList = array(); foreach ($this->discountIds as $discount) { if (!isset($this->cacheDiscountModules['sale'.$discount])) $loadList[] = $discount; } unset($discount); } if (empty($loadList)) return; foreach ($loadList as &$discount) $this->cacheDiscountModules['sale'.$discount] = array(); unset($discount); $moduleList = RuntimeCache\DiscountCache::getInstance()->getDiscountModules($loadList); if (!empty($moduleList)) { foreach ($moduleList as $discount => $discountModule) $this->cacheDiscountModules['sale'.$discount] = $discountModule; unset($discount, $discountModule, $moduleList); } unset($moduleList); } /** * Extend order data for discounts. * * @return void */ protected function extendOrderData() { if (empty($this->discountIds)) return; $entityCacheKey = md5(serialize($this->discountIds)); if (!isset($this->entityResultCache[$entityCacheKey])) { $this->entityResultCache[$entityCacheKey] = array(); $this->entityList = RuntimeCache\DiscountCache::getInstance()->getDiscountEntities($this->discountIds); if (empty($this->entityList)) return; $event = new Main\Event( 'sale', self::EVENT_EXTEND_ORDER_DATA, array( 'ORDER' => $this->orderData, 'ENTITY' => $this->entityList ) ); $event->send(); $this->entityResultCache[$entityCacheKey] = $event->getResults(); } $resultList = $this->entityResultCache[$entityCacheKey]; if (empty($resultList) || !is_array($resultList)) return; /** @var Main\EventResult $eventResult */ foreach ($resultList as &$eventResult) { if ($eventResult->getType() != Main\EventResult::SUCCESS) continue; $newData = $eventResult->getParameters(); if (empty($newData) || !is_array($newData)) continue; $this->modifyOrderData($newData); } unset($newData, $eventResult, $resultList); } /** * Modify order data from handlers. * * @param array &$newData New order data from handler. * @return void */ protected function modifyOrderData(&$newData) { if (!empty($newData) && is_array($newData)) self::recursiveMerge($this->orderData, $newData); } /** * Return formatted discount description. * * @param array $descr Description. * @return array */ protected static function formatDescription($descr) { $result = array(); if (empty($descr) || !is_array($descr)) return $result; if (isset($descr['DELIVERY'])) { $result['DELIVERY'] = array(); foreach ($descr['DELIVERY'] as $index => $value) { $result['DELIVERY'][$index] = OrderDiscountManager::formatDescription($value); if ($result['DELIVERY'][$index] == false) unset($result['DELIVERY'][$index]); } unset($value, $index); if (!empty($result['DELIVERY'])) $result['DELIVERY'] = implode(', ', $result['DELIVERY']); } if (isset($descr['BASKET'])) { $result['BASKET'] = array(); foreach ($descr['BASKET'] as $index => $value) { $result['BASKET'][$index] = OrderDiscountManager::formatDescription($value); if ($result['BASKET'][$index] == false) unset($result['BASKET'][$index]); } unset($value, $index); if (!empty($result['BASKET'])) $result['BASKET'] = implode(', ', $result['BASKET']); } return $result; } /** * Added keys from source array to destination array. * * @param array &$dest Destination array. * @param array $src Source array. * @return void */ protected static function recursiveMerge(&$dest, $src) { if (!is_array($dest) || !is_array($src)) return; if (empty($dest)) { $dest = $src; return; } foreach ($src as $key => $value) { if (!isset($dest[$key])) { $dest[$key] = $value; continue; } if (is_array($dest[$key])) self::recursiveMerge($dest[$key], $value); } unset($value, $key); } /** * Fill basket prices from base prices. * * @return void */ protected function resetBasketPrices() { foreach ($this->orderData['BASKET_ITEMS'] as &$basketItem) { if (self::isCustomPrice($basketItem) || self::isInSet($basketItem)) continue; $basketItem = self::resetPrice($basketItem); } unset($basketItem); } /** * Load from database discount id for user groups. * * @param array $filter Additional filter. * @return void * @throws Main\ArgumentException */ protected function loadDiscountByUserGroups(array $filter = array()) { if (!array_key_exists('USER_ID', $this->orderData)) return; $userGroups = $this->context->getUserGroups(); $filter['@GROUP_ID'] = $userGroups; $filter['=ACTIVE'] = 'Y'; $cacheKey = md5('U'.implode('_', $userGroups).'-F'.serialize($filter)); if (!isset($this->discountByUserCache[$cacheKey])) { //RuntimeCache works only with basic filter. if(!array_diff_assoc($filter, array( '@GROUP_ID' => $userGroups, '=ACTIVE' => 'Y', ))) { $this->discountByUserCache[$cacheKey] = RuntimeCache\DiscountCache::getInstance()->getDiscountIds($userGroups); } else { $discountCache = array(); $groupDiscountIterator = Internals\DiscountGroupTable::getList(array( 'select' => array('DISCOUNT_ID'), 'filter' => $filter, 'order' => array('DISCOUNT_ID' => 'ASC') )); while ($groupDiscount = $groupDiscountIterator->fetch()) { $groupDiscount['DISCOUNT_ID'] = (int)$groupDiscount['DISCOUNT_ID']; if ($groupDiscount['DISCOUNT_ID'] > 0) $discountCache[$groupDiscount['DISCOUNT_ID']] = $groupDiscount['DISCOUNT_ID']; } unset($groupDiscount, $groupDiscountIterator); if (!empty($discountCache)) $this->discountByUserCache[$cacheKey] = $discountCache; unset($discountCache); } } $this->discountIds = $this->discountByUserCache[$cacheKey]; unset($cacheKey, $userGroups); } /** * Load discount modules. * * @param string|int $discount Discount key. * @return bool * @throws Main\LoaderException */ protected function loadDiscountModules($discount) { $result = true; if (empty($this->cacheDiscountModules[$discount])) return $result; foreach ($this->cacheDiscountModules[$discount] as $moduleID) { if (!isset($this->loadedModules[$moduleID])) $this->loadedModules[$moduleID] = Main\Loader::includeModule($moduleID); if (!$this->loadedModules[$moduleID]) { $result = false; break; } } unset($moduleID); return $result; } /** * Load sale discount from database * * @return void * @throws Main\ArgumentException */ protected function loadDiscountList() { if (empty($this->discountIds)) { $this->discountIds = null; return; } $this->getDiscountModules(); $couponList = DiscountCouponsManager::getForApply(array('MODULE_ID' => 'sale', 'DISCOUNT_ID' => $this->discountIds), array(), true); $this->saleDiscountCacheKey = md5('D'.implode('_', $this->discountIds)); if (!empty($couponList)) $this->saleDiscountCacheKey .= '-C'.implode('_', array_keys($couponList)); $this->saleDiscountCacheKey .= '-MF'.implode('_', $this->executeModuleFilter); if (!isset($this->saleDiscountCache[$this->saleDiscountCacheKey])) { $currentList = RuntimeCache\DiscountCache::getInstance()->getDiscounts( $this->discountIds, $this->executeModuleFilter, $this->orderData['SITE_ID'], $couponList?: array() ); if (!empty($currentList)) { $evalCode = ''; foreach (array_keys($currentList) as $index) { $discount = $currentList[$index]; $code = 'sale'.$discount['ID']; if (!$this->loadDiscountModules($code)) { unset($currentList[$index]); continue; } if (isset($this->cacheDiscountModules[$code])) { $currentList[$index]['MODULES'] = $this->cacheDiscountModules[$code]; } if (!$this->enableCheckingPrediction) { if ($discount['UNPACK'] !== null) $evalCode .= '$currentList['.$index.'][\'UNPACK_EXECUTE\'] = '.$discount['UNPACK'].";\n"; } else { if ($discount['PREDICTIONS_APP'] !== null && $discount['PREDICTIONS_APP'] !== '') $evalCode .= '$currentList['.$index.'][\'PREDICTIONS_APP_EXECUTE\'] = '.$discount['PREDICTIONS_APP'].";\n"; } if ($discount['APPLICATION'] !== null) $evalCode .= '$currentList['.$index.'][\'APPLICATION_EXECUTE\'] = '.$discount['APPLICATION'].";\n"; } unset($code, $discount, $index); if ($evalCode !== '') { if (version_compare(PHP_VERSION, '7.0.0', '>=')) { try { eval($evalCode); } catch (\ParseError $e) { $this->showAdminError(); } } else { eval($evalCode); } } unset($evalCode); } $this->saleDiscountCache[$this->saleDiscountCacheKey] = $currentList; } unset($couponList); } /** * Execute sale discount list. * * @return Result */ protected function executeDiscountList() { $result = new Result; $roundApply = true; $saleDiscountOnly = $this->useOnlySaleDiscounts(); $useMode = $this->getUseMode(); if ($saleDiscountOnly) { if ($useMode == self::USE_MODE_FULL && $this->isRoundMode(self::ROUND_MODE_SALE_DISCOUNT)) $roundApply = false; } $this->discountIds = array(); if (empty($this->saleDiscountCacheKey) || empty($this->saleDiscountCache[$this->saleDiscountCacheKey])) { if (!$roundApply) { $this->roundFullBasketPriceByIndex(array( 'DISCOUNT_INDEX' => -1, 'DISCOUNT_ID' => 0 )); } return $result; } $currentList = $this->saleDiscountCache[$this->saleDiscountCacheKey]; $this->discountIds = array_keys($currentList); $this->extendOrderData(); Discount\Actions::clearAction(); $blackList = array( 'UNPACK_EXECUTE' => true, 'APPLICATION_EXECUTE' => true, 'PREDICTIONS_APP_EXECUTE' => true ); $index = -1; $skipPriorityLevel = null; foreach ($currentList as $discountIndex => $discount) { if($skipPriorityLevel == $discount['PRIORITY']) { continue; } $skipPriorityLevel = null; $this->fillCurrentStep(array( 'discount' => $discount, 'cacheIndex' => $discountIndex )); if (!$this->checkDiscountConditions()) continue; $index++; if (!$roundApply && $discount['EXECUTE_MODULE'] == 'sale') { $this->roundFullBasketPriceByIndex(array( 'DISCOUNT_INDEX' => $index, 'DISCOUNT_ID' => $discount['ID'] )); $roundApply = true; } if ($useMode == self::USE_MODE_FULL && !isset($this->fullDiscountList[$discount['ID']])) $this->fullDiscountList[$discount['ID']] = array_diff_key($discount, $blackList); $actionsResult = $this->applySaleDiscount(); if (!$actionsResult->isSuccess()) { $result->addErrors($actionsResult->getErrors()); unset($actionsResult); return $result; } if ($this->currentStep['stop']) break; if ($this->currentStep['stopLevel']) { $skipPriorityLevel = $discount['PRIORITY']; } } unset($discount, $currentList); $this->fillEmptyCurrentStep(); if (!$roundApply) { $index++; $this->roundFullBasketPriceByIndex(array( 'DISCOUNT_INDEX' => $index, 'DISCOUNT_ID' => 0 )); } return $result; } /** * Fill last discount flag for basket items. Only for basket or new order or refreshed order. * * @return void */ protected function fillBasketLastDiscount() { if ($this->getUseMode() != self::USE_MODE_FULL) return; $applyMode = self::getApplyMode(); if ($applyMode == self::APPLY_MODE_ADD) return; $codeList = array_keys($this->orderData['BASKET_ITEMS']); switch ($applyMode) { case self::APPLY_MODE_DISABLE: case self::APPLY_MODE_FULL_DISABLE: foreach ($codeList as &$code) { if (isset($this->basketDiscountList[$code]) && !empty($this->basketDiscountList[$code])) $this->orderData['BASKET_ITEMS'][$code]['LAST_DISCOUNT'] = 'Y'; } unset($code); break; case self::APPLY_MODE_LAST: case self::APPLY_MODE_FULL_LAST: foreach ($codeList as &$code) { if (!isset($this->basketDiscountList[$code]) || empty($this->basketDiscountList[$code])) continue; $lastDiscount = end($this->basketDiscountList[$code]); if (!empty($lastDiscount['LAST_DISCOUNT']) && $lastDiscount['LAST_DISCOUNT'] == 'Y') $this->orderData['BASKET_ITEMS'][$code]['LAST_DISCOUNT'] = 'Y'; } unset($code); break; } unset($codeList, $applyMode); } /** * Check last discount flag for basket items. Only for basket or new order or refreshed order. * * @return bool */ protected function isBasketLastDiscount() { $result = false; if ($this->getUseMode() != self::USE_MODE_FULL) return $result; $this->fillBasketLastDiscount(); $applyMode = self::getApplyMode(); if ($applyMode == self::APPLY_MODE_FULL_LAST || $applyMode == self::APPLY_MODE_FULL_DISABLE) { foreach ($this->orderData['BASKET_ITEMS'] as $basketItem) { if (isset($basketItem['LAST_DISCOUNT']) && $basketItem['LAST_DISCOUNT'] == 'Y') { $result = true; break; } } unset($basketItem); } unset($applyMode); return $result; } /* additional coupons tools */ /** * Clear coupons from already used discounts. * * @internal * @param array $coupons Coupons list from \Bitrix\Sale\DiscountCouponsManager::getForApply. * @return array */ protected function clearAdditionalCoupons(array $coupons) { if (empty($coupons)) return array(); if (empty($this->discountsCache)) return $coupons; $result = array(); foreach ($coupons as $couponCode => $couponData) { $found = false; foreach ($this->discountsCache as &$discount) { if ( $discount['MODULE_ID'] == $couponData['MODULE_ID'] && $discount['DISCOUNT_ID'] == $couponData['DISCOUNT_ID'] && $discount['USE_COUPONS'] == 'N' ) { $found = true; } } unset($discount); if (!$found && !empty($this->couponsCache)) { foreach ($this->couponsCache as $existCouponCode => $existCouponData) { $discount = $this->discountsCache[$existCouponData['ORDER_DISCOUNT_ID']]; if ( $discount['MODULE_ID'] != $couponData['MODULE_ID'] || $discount['DISCOUNT_ID'] != $couponData['DISCOUNT_ID'] ) continue; if ($couponCode == $existCouponCode) { if ( $existCouponData['ID'] > 0 || $existCouponData['TYPE'] == Internals\DiscountCouponTable::TYPE_BASKET_ROW ) $found = true; } else { if ( $existCouponData['TYPE'] != Internals\DiscountCouponTable::TYPE_BASKET_ROW || $couponData['TYPE'] != Internals\DiscountCouponTable::TYPE_BASKET_ROW ) { $found = true; } else { if ($discount['MODULE_ID'] == 'sale') $found = true; } } unset($discount); if ($found) break; } unset($existCouponCode, $existCouponData); } if (!$found) $result[$couponCode] = $couponData; unset($found); } unset($couponCode, $couponData); return $result; } /** * Return additional coupons for exist order. * * @internal * @param array $filter Coupons filter. * @return array */ protected function getAdditionalCoupons(array $filter = array()) { if ($this->useOnlySaleDiscounts()) { if (isset($filter['MODULE_ID']) && $filter['MODULE_ID'] != 'sale') return array(); if (isset($filter['!MODULE_ID']) && $filter['!MODULE_ID'] == 'sale') return array(); $filter['MODULE_ID'] = 'sale'; } $useOrderCoupons = DiscountCouponsManager::isUsedOrderCouponsForApply(); DiscountCouponsManager::useSavedCouponsForApply(false); $coupons = DiscountCouponsManager::getForApply($filter, array(), true); DiscountCouponsManager::useSavedCouponsForApply($useOrderCoupons); unset($useOrderCoupons); if (empty($coupons)) return array(); return $this->clearAdditionalCoupons($coupons); } /** * Calculate additional basket coupons. * * @param array $applyCoupons Apply discount coupons data. * @return Result */ protected function calculateApplyBasketAdditionalCoupons(array $applyCoupons) { $result = new Result; if ($this->useOnlySaleDiscounts()) return $result; if (empty($applyCoupons)) return $result; $applyBlock = &$this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['BASKET']; $applyExist = $this->isBasketApplyResultExist(); $basketCodeList = $this->getBasketCodes(false); foreach ($basketCodeList as &$basketCode) { if (array_key_exists($basketCode, $applyBlock)) unset($applyBlock[$basketCode]); if (empty($applyCoupons[$basketCode])) continue; $itemData = array( 'MODULE_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['MODULE'], 'PRODUCT_ID' => $this->orderData['BASKET_ITEMS'][$basketCode]['PRODUCT_ID'], 'BASKET_ID' => $basketCode ); $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE']; $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE']; foreach ($applyCoupons[$basketCode] as $index => $discount) { $discountResult = $this->convertDiscount($discount); if (!$discountResult->isSuccess()) { $result->addErrors($discountResult->getErrors()); unset($discountResult); return $result; } $orderDiscountId = $discountResult->getId(); $discountData = $discountResult->getData(); $applyCoupons[$basketCode][$index]['ORDER_DISCOUNT_ID'] = $orderDiscountId; if (empty($discount['COUPON'])) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('BX_SALE_DISCOUNT_ERR_DISCOUNT_WITHOUT_COUPON'), self::ERROR_ID )); return $result; } $couponResult = $this->convertCoupon($discount['COUPON'], $orderDiscountId); if (!$couponResult->isSuccess()) { $result->addErrors($couponResult->getErrors()); unset($couponResult); return $result; } $orderCouponId = $couponResult->getId(); DiscountCouponsManager::setApplyByProduct($itemData, array($orderCouponId)); unset($couponResult); unset($discountData, $discountResult); if (!isset($applyBlock[$basketCode])) $applyBlock[$basketCode] = array(); $applyBlock[$basketCode][$index] = array( 'DISCOUNT_ID' => $orderDiscountId, 'COUPON_ID' => $orderCouponId, 'RESULT' => array( 'APPLY' => 'Y', 'DESCR' => false, 'DESCR_DATA' => false ) ); $currentProduct = $this->orderData['BASKET_ITEMS'][$basketCode]; $orderApplication = ( !empty($this->discountsCache[$orderDiscountId]['APPLICATION']) ? $this->discountsCache[$orderDiscountId]['APPLICATION'] : null ); if (!empty($orderApplication)) { $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'] = ( !empty($this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA']) ? $this->discountsCache[$orderDiscountId]['ACTIONS_DESCR_DATA'] : false ); $applyProduct = null; eval('$applyProduct='.$orderApplication.';'); if (is_callable($applyProduct)) $applyProduct($this->orderData['BASKET_ITEMS'][$basketCode]); unset($applyProduct); if (!empty($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT'])) { $applyBlock[$basketCode][$index]['RESULT']['DESCR_DATA'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']['BASKET']; $applyBlock[$basketCode][$index]['RESULT']['DESCR'] = self::formatDescription($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_RESULT']); } unset($orderApplication); if ($applyExist && !$this->getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId)) { $this->orderData['BASKET_ITEMS'][$basketCode] = $currentProduct; $applyBlock[$basketCode][$index]['RESULT']['APPLY'] = 'N'; } unset($currentProduct); } unset($discount, $index); $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']; unset($this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']); } unset($basketCode, $basketCodeList); unset($applyBlock); return $result; } /** * Calculate additional sale coupons. * * @param array $applyCoupons Coupons data. * @return Result */ protected function calculateApplySaleAdditionalCoupons(array $applyCoupons) { $result = new Result; if (empty($applyCoupons)) return $result; $this->discountResult['APPLY_BLOCKS'][$this->discountResultCounter]['ORDER'] = array(); $discountId = array(); foreach ($applyCoupons as $coupon) $discountId[] = $coupon['DISCOUNT_ID']; unset($coupon); $currentUseMode = $this->getUseMode(); $this->setUseMode(self::USE_MODE_COUPONS); $this->loadDiscountByUserGroups(array('@DISCOUNT_ID' => $discountId)); unset($discountId); $basketCodeList = $this->getBasketCodes(false); foreach ($basketCodeList as $basketCode) { $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE']; $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE']; } unset($basketCode); $this->loadDiscountList(); $executeResult = $this->executeDiscountList(); if (!$executeResult->isSuccess()) $result->addErrors($executeResult->getErrors()); unset($executeResult); $this->setUseMode($currentUseMode); unset($currentUseMode); foreach ($basketCodeList as $basketCode) { $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']; unset($this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE_TMP']); } unset($basketCode); unset($basketCodeList); return $result; } /* additional coupons tools finish */ /* compatibility tools */ /** * Fill order fields for deprecated discount classes. * * @return void */ protected function fillCompatibleOrderFields() { $this->orderData['USE_BASE_PRICE'] = $this->saleOptions['USE_BASE_PRICE']; } /* apply flag tools */ /** * Return apply status for basket discount. * * @internal * @param string|int $basketCode Basket item code. * @param int $orderDiscountId Order discount id. * @param string $orderCouponId Coupon. * @return bool */ protected function getStatusApplyBasketDiscount($basketCode, $orderDiscountId, $orderCouponId) { $disable = false; if ( $orderCouponId != '' && !empty($this->applyResult['COUPON_LIST'][$orderCouponId]) && $this->applyResult['COUPON_LIST'][$orderCouponId] == 'N' ) { $disable = true; } else { if ( !empty($this->applyResult['DISCOUNT_LIST'][$orderDiscountId]) && $this->applyResult['DISCOUNT_LIST'][$orderDiscountId] == 'N' ) { $disable = true; } if (!empty($this->applyResult['BASKET'][$basketCode])) { if (is_string($this->applyResult['BASKET'][$basketCode])) $disable = ($this->applyResult['BASKET'][$basketCode] == 'N'); elseif (!empty($this->applyResult['BASKET'][$basketCode][$orderDiscountId])) $disable = ($this->applyResult['BASKET'][$basketCode][$orderDiscountId] == 'N'); } } return !$disable; } /* instance tools */ /** * Round and correct discount calculation results. * @internal * * @return void */ protected function normalizeDiscountResult() { $customPrice = isset($this->orderData['CUSTOM_PRICE_DELIVERY']) && $this->orderData['CUSTOM_PRICE_DELIVERY'] == 'Y'; /** @noinspection PhpInternalEntityUsedInspection */ $this->orderData['PRICE_DELIVERY_DIFF'] = (!$customPrice ? PriceMaths::roundPrecision($this->orderData['PRICE_DELIVERY_DIFF']) : 0 ); if (!$customPrice) { if ($this->orderData['PRICE_DELIVERY_DIFF'] > 0) $this->orderData['PRICE_DELIVERY'] = $this->orderData['BASE_PRICE_DELIVERY'] - $this->orderData['PRICE_DELIVERY_DIFF']; else $this->orderData['PRICE_DELIVERY'] = PriceMaths::roundPrecision($this->orderData['PRICE_DELIVERY']); } unset($customPrice); if (!empty($this->orderData['BASKET_ITEMS'])) { foreach (array_keys($this->orderData['BASKET_ITEMS']) as $basketCode) { $customPrice = $this->isCustomPriceByCode($basketCode); $basketItem = $this->orderData['BASKET_ITEMS'][$basketCode]; $basketItem['DISCOUNT_PRICE'] = (!$customPrice ? PriceMaths::roundPrecision($basketItem['DISCOUNT_PRICE']) : 0 ); if (!$customPrice) { if ($basketItem['DISCOUNT_PRICE'] > 0) $basketItem['PRICE'] = $basketItem['BASE_PRICE'] - $basketItem['DISCOUNT_PRICE']; else $basketItem['PRICE'] = PriceMaths::roundPrecision($basketItem['PRICE']); } $this->orderData['BASKET_ITEMS'][$basketCode] = $basketItem; } unset($basketItem, $customPrice, $basketCode); } } /** * Return instance index for order. * * @internal * @param Order $order Order. * @return string */ protected static function getInstanceIndexByOrder(Order $order) { return $order->getInternalId().'|0'.'|'.$order->getSiteId(); } /** * Return instance index for basket. * * @internal * * @param BasketBase $basket Basket. * @param Context\BaseContext|null $context * * @return string */ protected static function getInstanceIndexByBasket(BasketBase $basket, Context\BaseContext $context = null) { if (!$context) return '0|'.$basket->getFUserId(false).'|'.$basket->getSiteId(); return '0|-1|'.$basket->getSiteId().'|'.$context->getUserGroupsHash(); } /** * Return instance index for fuser. * * @internal * @param string|int $fuser Fuser id. * @param string $site Site id. * @return string */ protected static function getInstanceIndexByFuser($fuser, $site) { return '0|'.$fuser.'|'.$site; } /** * Return order property codes for translate to order fields. * * @return array */ protected static function getOrderPropertyCodes() { return array( 'DELIVERY_LOCATION' => 'IS_LOCATION', 'USER_EMAIL' => 'IS_EMAIL', 'PAYER_NAME' => 'IS_PAYER', 'PROFILE_NAME' => 'IS_PROFILE_NAME', 'DELIVERY_LOCATION_ZIP' => 'IS_ZIP' ); } /** * Return empty apply block * * @return array */ protected static function getEmptyApplyBlock() { return array( 'BASKET' => array(), 'BASKET_ROUND' => array(), 'ORDER' => array() ); } /** * Check use old api. * * @return bool */ private function isUsedDiscountCompatibility() { return (Compatible\DiscountCompatibility::isUsed() && Compatible\DiscountCompatibility::isInited()); } /** * Get description for old actions. * * @param array $stepResult Action results. * @return array */ private function getSimpleActionDescription(array $stepResult) { $result = array(); if (!empty($stepResult['BASKET'])) { $data = OrderDiscountManager::prepareDiscountDescription( OrderDiscountManager::DESCR_TYPE_SIMPLE, Loc::getMessage('BX_SALE_DISCOUNT_MESS_SIMPLE_DESCRIPTION_BASKET') ); if ($data->isSuccess()) { $result['BASKET'] = array( 0 => $data->getData() ); } unset($data); } if (!empty($stepResult['DELIVERY'])) { $data = OrderDiscountManager::prepareDiscountDescription( OrderDiscountManager::DESCR_TYPE_SIMPLE, Loc::getMessage('BX_SALE_DISCOUNT_MESS_SIMPLE_DESCRIPTION_DELIVERY') ); if ($data->isSuccess()) { $result['DELIVERY'] = array( 0 => $data->getData() ); } unset($data); } if (empty($result)) { $data = OrderDiscountManager::prepareDiscountDescription( OrderDiscountManager::DESCR_TYPE_SIMPLE, Loc::getMessage('BX_SALE_DISCOUNT_MESS_SIMPLE_DESCRIPTION_UNKNOWN') ); if ($data->isSuccess()) { $result['BASKET'] = array( 0 => $data->getData() ); } unset($data); } return $result; } private function showAdminError() { $iterator = \CAdminNotify::GetList( array(), array('MODULE_ID' => 'sale', 'TAG' => self::ERROR_ID) ); $notify = $iterator->Fetch(); unset($iterator); if (empty($notify)) { $defaultLang = ''; $messages = array(); $languages = Main\Localization\LanguageTable::getList(array( 'select' => array('ID', 'DEF'), 'filter' => array('=ACTIVE' => 'Y') )); while ($row = $languages->fetch()) { if ($row['DEF'] == 'Y') $defaultLang = $row['ID']; $languageId = $row['ID']; Main\Localization\Loc::loadLanguageFile( __FILE__, $languageId ); $messages[$languageId] = Main\Localization\Loc::getMessage( 'BX_SALE_DISCOUNT_ERR_PARSE_ERROR', array('#LINK#' => '/bitrix/admin/settings.php?lang='.$languageId.'&mid=sale'), $languageId ); } unset($row, $languages); \CAdminNotify::Add(array( 'MODULE_ID' => 'sale', 'TAG' => self::ERROR_ID, 'ENABLE_CLOSE' => 'N', 'NOTIFY_TYPE' => \CAdminNotify::TYPE_ERROR, 'MESSAGE' => $messages[$defaultLang], 'LANG' => $messages )); unset($messages, $defaultLang); } unset($notify); } /** * Calculate discount percent for public components. * * @param int|float $basePrice Base price. * @param int|float $discount Discount value (for an extra can be negative). * @return float|int|null */ public static function calculateDiscountPercent($basePrice, $discount) { $basePrice = (float)$basePrice; if ($basePrice <= 0) return null; $discount = (float)$discount; if ($discount > $basePrice) return null; $result = roundEx(100*$discount/$basePrice, 0); if ($result < 0) $result = 0; return $result; } /** * Returns show prices for public components. * * @return array */ public function getShowPrices() { if (!$this->isOrderNew() && empty($this->orderData)) { $this->initUseMode(); $this->loadOrderData(); } $result = array( 'BASKET' => array(), 'DELIVERY' => $this->getApplyDeliveryPrice() ); $checkRound = true; $useMode = $this->getUseMode(); switch ($useMode) { case self::USE_MODE_APPLY: case self::USE_MODE_MIXED: if ($this->convertedOrder) $checkRound = false; break; } if (!empty($this->orderData['BASKET_ITEMS'])) { $basket = $this->orderData['BASKET_ITEMS']; $order = $this->orderData; unset($order['BASKET_ITEMS']); switch ($useMode) { case self::USE_MODE_FULL: case self::USE_MODE_APPLY: if ($checkRound) { $basketCodeList = $this->getBasketCodes(true); $existRound = array(); $existRoundRules = array(); foreach ($basketCodeList as $basketCode) { if (!empty($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'])) { $existRound[$basketCode] = self::resetPrice($basket[$basketCode]); $existRoundRules[$basketCode] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']; } } if (!empty($existRound)) { $roundResult = OrderDiscountManager::roundBasket( $existRound, $existRoundRules, $order ); foreach ($roundResult as $basketCode => $row) { if (empty($row) || !is_array($row)) continue; if (!isset($existRound[$basketCode])) continue; $basket[$basketCode]['BASE_PRICE'] = $row['PRICE']; $basket[$basketCode]['DISCOUNT_PRICE'] = $basket[$basketCode]['BASE_PRICE'] - $basket[$basketCode]['PRICE']; } } unset($existRoundRules, $existRound); } break; case self::USE_MODE_MIXED: if ($checkRound) { $existRound = array(); $existRoundRules = array(); foreach ($basket as $basketCode => $item) { if ($this->isCustomPrice($item) || $this->isInSet($item)) continue; if (!empty($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'])) { $existRound[$basketCode] = self::resetPrice($basket[$basketCode]); $existRoundRules[$basketCode] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']; } } unset($code, $item); if (!empty($existRound)) { $roundResult = OrderDiscount::roundBasket( $existRound, $existRoundRules, $order ); foreach ($roundResult as $basketCode => $row) { if (empty($row) || !is_array($row)) continue; if (!isset($existRound[$basketCode])) continue; $basket[$basketCode]['BASE_PRICE'] = $row['PRICE']; $basket[$basketCode]['DISCOUNT_PRICE'] = $basket[$basketCode]['BASE_PRICE'] - $basket[$basketCode]['PRICE']; } } unset($existRoundRules, $existRound); } break; } foreach ($basket as $basketCode => $basketItem) { $result['BASKET'][$basketCode] = $this->getShowBasketItemPrice($basketCode, $basketItem); $result['BASKET'][$basketCode]['REAL_BASE_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['BASE_PRICE']; $result['BASKET'][$basketCode]['REAL_PRICE'] = $this->orderData['BASKET_ITEMS'][$basketCode]['PRICE']; $result['BASKET'][$basketCode]['REAL_DISCOUNT'] = $this->orderData['BASKET_ITEMS'][$basketCode]['DISCOUNT_PRICE']; } unset($basketCode, $basketItem); } return $result; } /** * Search round rule for base price. * @internal * * return void */ private function getRoundForBasePrices() { $mode = $this->getUseMode(); if ($mode != self::USE_MODE_FULL && $mode != self::USE_MODE_MIXED) return; $basketCodeList = $this->getBasketCodes(true); if (empty($basketCodeList)) return; $basket = array_intersect_key( $this->orderData['BASKET_ITEMS'], array_fill_keys($basketCodeList, true) ); if (empty($basket)) return; foreach ($basketCodeList as $basketCode) { if (!isset($this->basketItemsData[$basketCode])) $this->basketItemsData[$basketCode] = array(); $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND'] = $basket[$basketCode]['BASE_PRICE']; $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'] = array(); } unset($basketCode); foreach ($basket as &$basketItem) $basketItem = self::resetPrice($basketItem); unset($basketItem); $orderData = $this->orderData; unset($orderData['BASKET_ITEMS']); $result = OrderDiscountManager::roundBasket( $basket, array(), $orderData ); foreach ($basketCodeList as $basketCode) { if (empty($result[$basketCode]) || !is_array($result[$basketCode])) continue; $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND'] = $result[$basketCode]['PRICE']; $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'] = $result[$basketCode]['ROUND_RULE']; } unset($basketCode, $result); unset($storageClassName); unset($basket, $orderData); } /** * Returns basket item price for show in public components (basket, order, etc). * * @param string|int $basketCode Basket item code. * @param array $item Basket item. * @return array */ private function getShowBasketItemPrice($basketCode, array $item) { if ($this->isCustomPrice($item) || $this->isInSet($item)) { if ($item['BASE_PRICE'] <= $item['PRICE']) $result = $this->getShowPriceWithZeroDiscountPercent($item); else $result = $this->getShowPriceWithDiscountPercent($item); return $result; } if ($item['BASE_PRICE'] <= $item['PRICE']) return $this->getShowPriceWithZeroDiscountPercent($item); if ($this->isExistBasketItemDiscount($basketCode)) return $this->getShowPriceWithDiscountPercent($item); return $this->getShowPriceWithZeroDiscountPercent($item); } /** * Returns basket item price with rounded discount percent. Only for show. * * @param array $item Basket item (price fields). * @return array */ private function getShowPriceWithDiscountPercent(array $item) { return [ 'SHOW_BASE_PRICE' => $item['BASE_PRICE'], 'SHOW_PRICE' => $item['PRICE'], 'SHOW_DISCOUNT' => $item['DISCOUNT_PRICE'], 'SHOW_DISCOUNT_PERCENT' => $this->calculateDiscountPercent( $item['BASE_PRICE'], $item['DISCOUNT_PRICE'] ) ]; } /** * Returns basket item price without discount. Only for show. * * @param array $item Basket item (price fields). * @return array */ private function getShowPriceWithZeroDiscountPercent(array $item) { return [ 'SHOW_BASE_PRICE' => $item['PRICE'], 'SHOW_PRICE' => $item['PRICE'], 'SHOW_DISCOUNT' => 0, 'SHOW_DISCOUNT_PERCENT' => 0 ]; } /** * Checking the existence of the applied discount on the basket item. * * @param string|int $basketCode Basket item code. * @return bool */ private function isExistBasketItemDiscount($basketCode) { $result = false; if (!empty($this->discountResult['APPLY_BLOCKS'])) { foreach ($this->discountResult['APPLY_BLOCKS'] as $counter => $applyBlock) { if (isset($applyBlock['BASKET'][$basketCode])) { foreach ($applyBlock['BASKET'][$basketCode] as $discount) { if ($discount['RESULT']['APPLY'] == 'Y') $result = true; } unset($discount); } if (!empty($applyBlock['ORDER'])) { foreach ($applyBlock['ORDER'] as $discount) { if (!empty($discount['RESULT']['BASKET'])) { if (!isset($discount['RESULT']['BASKET'][$basketCode])) continue; if ($discount['RESULT']['BASKET'][$basketCode]['APPLY'] == 'Y') $result = true; } } unset($discount); } } unset($counter, $applyBlock); } return $result; } /** * Returns basket item stored data for save. * * @param string|int $basketCode Basket item code. * @return array|null */ private function prepareBasketItemStoredData($basketCode) { $result = []; if (isset($this->basketItemsData[$basketCode])) { if (isset($this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE'])) $result['BASE_PRICE_ROUND_RULE'] = $this->basketItemsData[$basketCode]['BASE_PRICE_ROUND_RULE']; } return (!empty($result) ? $result : null); } /** * Reset product price for discount calculation. * * @param array $item Basket item data. * @return array */ private static function resetPrice(array $item) { $item['PRICE'] = $item['BASE_PRICE']; $item['DISCOUNT_PRICE'] = 0; return $item; } /* deprecated methods */ /** * Set base price for basket item. * @deprecated * * @param int|string $code Basket code. * @param float $price Price. * @param string $currency Currency. * @return void */ public function setBasketItemBasePrice($code, $price, $currency) {} /** * Set base price for all basket items. * @deprecated * * @param array $basket Basket. * @return void */ public function setBasketBasePrice($basket) {} /** * Get base price for basket item. * @deprecated * * @param int|string $code Basket code. * @return float|null */ public function getBasketItemBasePrice($code) { return (isset($this->orderData[$code]) ? $this->orderData[$code]['BASE_PRICE'] : null); } /** * Set product discounts for basket item. * @deprecated * * @param int|string $code Basket code. * @param array $discountList Discount list. * @return void */ public function setBasketItemDiscounts($code, $discountList) {} /** * Set various basket item data. * @deprecated * * @param int|string $code Basket code. * @param array $providerData Product data from provider. * @return void */ public function setBasketItemData($code, $providerData) {} /** * Clear basket item data. * @deprecated * * @param int|string $code Basket code. * @return void */ public function clearBasketItemData($code) {} /* deprecated methods finish */ }