%PDF- %PDF-
Direktori : /proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/ |
Current File : //proc/self/root/home/bitrix/www/bitrix/modules/sale/lib/orderdiscountbase.php |
<?php namespace Bitrix\Sale; use Bitrix\Main, Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); abstract class OrderDiscountBase { const EVENT_ON_BUILD_DISCOUNT_PROVIDERS = 'onBuildDiscountProviders'; const ERROR_ID = 'BX_SALE_ORDER_DISCOUNT'; const PROVIDER_ACTION_PREPARE_DISCOUNT = 'prepareData'; const PROVIDER_ACTION_GET_URL = 'getEditUrl'; const PROVIDER_ACTION_APPLY_COUPON = 'calculateApplyCoupons'; const PROVIDER_ACTION_ROUND_ITEM_PRICE = 'roundPrice'; const PROVIDER_ACTION_ROUND_BASKET_PRICES = 'roundBasket'; const STORAGE_TYPE_DISCOUNT_ACTION_DATA = 'ACTION_DATA'; const STORAGE_TYPE_ORDER_CONFIG = 'ORDER_CONFIG'; const STORAGE_TYPE_ROUND_CONFIG = 'ROUND_CONFIG'; const STORAGE_TYPE_BASKET_ITEM = 'BASKET_ITEM'; protected static $init = false; protected static $errors = array(); private static $discountProviders = array(); private static $managerConfig = array(); private static $discountCache = array(); /** * Initial discount manager. * * @return void */ public static function init() { if (self::$init) return; static::initDiscountProviders(); self::$init = true; } /** * Set manager params. * * @param array $config Manager params (site, currency, etc). * @return bool */ public static function setManagerConfig($config) { if (empty($config) || empty($config['SITE_ID'])) return false; if (empty($config['CURRENCY'])) $config['CURRENCY'] = Internals\SiteCurrencyTable::getSiteCurrency($config['SITE_ID']); if (!isset($config['USE_BASE_PRICE']) || ($config['USE_BASE_PRICE'] != 'Y' && $config['USE_BASE_PRICE'] != 'N')) $config['USE_BASE_PRICE'] = ((string)Main\Config\Option::get('sale', 'get_discount_percent_from_base_price') == 'Y' ? 'Y' : 'N'); if (empty($config['BASKET_ITEM'])) $config['BASKET_ITEM'] = '$basketItem'; self::$managerConfig = $config; return true; } /** * Return current manager params. * * @return array */ public static function getManagerConfig() { return self::$managerConfig; } /** * Convert and save discount. * * @param array $discount Discount data. * @param bool $extResult Result extended result data. * @return Result */ public static function saveDiscount(array $discount, $extResult = false) { static::init(); $result = new Result(); $extResult = ($extResult === true); $process = true; $internal = null; $discountData = false; $fields = false; $emptyData = array( 'ID' => 0, 'DISCOUNT_ID' => 0, 'NAME' => '', 'ORDER_DISCOUNT_ID' => 0, 'ORDER_COUPON_ID' => 0, 'USE_COUPONS' => '', 'LAST_DISCOUNT' => '', 'MODULE_ID' => '', 'EDIT_PAGE_URL' => '', 'ACTIONS_DESCR' => array() ); if ($extResult) { $emptyData['RAW_DATA'] = array(); $emptyData['PREPARED_DATA'] = array(); } $resultData = $emptyData; $config = static::getManagerConfig(); if (empty($config)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_EMPTY_MANAGER_PARAMS'), self::ERROR_ID )); } if (empty($discount) || empty($discount['MODULE_ID'])) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_EMPTY_DISCOUNT'), self::ERROR_ID )); } if ($process) { if (!static::isNativeModule($discount['MODULE_ID'])) { if (!static::checkDiscountProvider($discount['MODULE_ID'])) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_DISCOUNT_MODULE'), self::ERROR_ID )); } else { $discountData = static::executeDiscountProvider( array('MODULE_ID' => $discount['MODULE_ID'], 'METHOD' => self::PROVIDER_ACTION_PREPARE_DISCOUNT), array($discount, $config) ); } } else { $discountData = static::prepareData($discount); } if (empty($discountData) || !is_array($discountData)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_PREPARE_DISCOUNT'), self::ERROR_ID )); } } if ($process) { $fields = static::normalizeDiscountFields($discountData); if (empty($fields) || !is_array($fields)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_PREPARE_DISCOUNT'), self::ERROR_ID )); } elseif ($fields['DISCOUNT_HASH'] === null) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_DISCOUNT_HASH'), self::ERROR_ID )); } } if ($process) { $existDiscount = static::searchDiscount($fields['DISCOUNT_HASH']); if ($existDiscount === null) { /** @var Result $internalResult */ $internalResult = static::addDiscount($fields, $discountData); if ($internalResult->isSuccess()) { $existDiscount = static::searchDiscount($fields['DISCOUNT_HASH']); } else { $process = false; $result->addErrors($internalResult->getErrors()); } unset($internalResult); } if ($existDiscount !== null) { $resultData = $existDiscount; $result->setId($resultData['ID']); } } if ($process) { $resultData['EDIT_PAGE_URL'] = $discountData['EDIT_PAGE_URL']; if ($extResult) { $resultData['RAW_DATA'] = $discount; $resultData['PREPARED_DATA'] = $discountData; } $result->setData($resultData); } unset($resultData, $process); return $result; } /** * Save coupon. * * @param array $coupon Coupon data. * @return Result */ public static function saveCoupon($coupon) { static::init(); $result = new Result(); $process = true; $resultData = array( 'ID' => 0, 'ORDER_ID' => 0, 'ORDER_DISCOUNT_ID' => 0, 'COUPON' => '', 'TYPE' => 0, 'COUPON_ID' => 0, 'DATA' => array() ); if (empty($coupon) || !is_array($coupon)) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_EMPTY_COUPON'), self::ERROR_ID )); } if ($process) { if (empty($coupon['ORDER_DISCOUNT_ID']) || (int)$coupon['ORDER_DISCOUNT_ID'] <= 0) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_EMPTY_COUPON'), self::ERROR_ID )); } if (empty($coupon['COUPON'])) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_COUPON_CODE_ABSENT'), self::ERROR_ID )); } if (!isset($coupon['TYPE'])) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage( 'SALE_ORDER_DISCOUNT_ERR_COUPON_TYPE_ABSENT', array('#COUPON#' => $coupon['COUPON']) ), self::ERROR_ID )); } } if ($process) { $validateResult = static::validateCoupon($coupon); if (!$validateResult->isSuccess()) { $process = false; $result->addErrors($validateResult->getErrors()); } unset($validateResult); } if ($process) { $iterator = static::getOrderCouponIterator(array( 'select' => array('*'), 'filter' => array('=COUPON' => $coupon['COUPON'], '=ORDER_ID' => $coupon['ORDER_ID']) )); if ($row = $iterator->fetch()) { $resultData = $row; } else { $internalResult = static::addCoupon($coupon); if ($internalResult->isSuccess()) { $resultData = $internalResult->getData(); $resultData['ID'] = $internalResult->getId(); } else { $process = false; $result->addErrors($internalResult->getErrors()); } unset($internalResult); } unset($row, $iterator); } if ($process) { $result->setId($resultData['ID']); $result->setData($resultData); } unset($process, $resultData); return $result; } /** * Check apply discount. * * @param string $module Discount module. * @param array $discount Discount data. * @param array $basket Basket data. * @param array $params Calculate params. * @return bool|array */ public static function calculateApplyCoupons($module, $discount, $basket, $params) { static::init(); $module = (string)$module; if (static::isNativeModule($module)) return false; return static::executeDiscountProvider( array('MODULE_ID' => $module, 'METHOD' => self::PROVIDER_ACTION_APPLY_COUPON), array($discount, $basket, $params) ); } /** * Round basket item price. * * @param array $basketItem Basket item data. * @param array $roundData Round data. * @return array */ public static function roundPrice(array $basketItem, array $roundData = array()) { static::init(); if (empty($basketItem)) return array(); $result = static::executeDiscountProvider( array('MODULE_ID' => $basketItem['MODULE'], 'METHOD' => self::PROVIDER_ACTION_ROUND_ITEM_PRICE), array($basketItem, $roundData) ); if (empty($result)) return array(); if (!isset($result['PRICE']) || !isset($result['DISCOUNT_PRICE'])) return array(); if (!isset($result['ROUND_RULE'])) return array(); return $result; } /** * Round basket prices. * * @param array $basket Basket. * @param array $roundData Round data. * @param array $orderData Order (without basket). * @return array */ public static function roundBasket(array $basket, array $roundData = array(), array $orderData = array()) { static::init(); if (empty($basket)) return array(); $result = array(); $basketByModules = array(); $roundByModules = array(); foreach ($basket as $basketCode => $basketItem) { if (!isset($basketItem['MODULE'])) continue; $module = $basketItem['MODULE']; if (!isset($basketByModules[$module])) { $basketByModules[$module] = array(); $roundByModules[$module] = array(); } $basketByModules[$module][$basketCode] = $basketItem; $roundByModules[$module][$basketCode] = (isset($roundData[$basketCode]) ? $roundData[$basketCode] : array()); } unset($basketCode, $basketItem); foreach ($basketByModules as $module => $moduleItems) { $moduleResult = static::executeDiscountProvider( array('MODULE_ID' => $module, 'METHOD' => self::PROVIDER_ACTION_ROUND_BASKET_PRICES), array($moduleItems, $roundByModules[$module], $orderData) ); if ($moduleResult === false) { $moduleResult = array(); foreach ($moduleItems as $basketCode => $basketItem) { $itemResult = static::roundPrice($basketItem, $roundByModules[$module][$basketCode]); if (!empty($itemResult)) $moduleResult[$basketCode] = $itemResult; } } if (empty($moduleResult)) continue; foreach (array_keys($moduleResult) as $basketCode) $result[$basketCode] = $moduleResult[$basketCode]; unset($moduleResult); } unset($moduleResult, $module, $moduleItems); return $result; } /** * Check existing discount provider for module. * * @param string $module Module id. * @return bool */ public static function checkDiscountProvider($module) { static::init(); $module = (string)$module; if (static::isNativeModule($module)) return true; return ($module != '' && isset(self::$discountProviders[$module])); } /** * Return url for edit sale discount. * * @param array $discount Discount data. * @return string */ public static function getEditUrl(array $discount) { $result = ''; if (!empty($discount['ID'])) $result = '/bitrix/admin/sale_discount_edit.php?lang='.LANGUAGE_ID.'&ID='.$discount['ID']; return $result; } /** * Clear discount cache. * * @return void */ public static function clearCache() { $entity = get_called_class(); if (!isset(self::$discountCache[$entity])) return; unset(self::$discountCache[$entity]); } /** * Load discount result for order. * * @param int $order Order id. * @param array|bool $basketList Correspondence between basket ids and basket codes. * @param array $basketData Basket data. * @return Result */ public static function loadResultFromDb($order, array $basketList = [], array $basketData = []) { static::init(); $result = new Result; /** @var Discount $discountClassName */ $discountClassName = static::getDiscountClassName(); $emptyApplyBlock = $discountClassName::getEmptyApplyBlock(); $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } $resultData = [ 'APPLY_BLOCKS' => [], 'DISCOUNT_LIST' => [], 'COUPON_LIST' => [], 'STORED_ACTION_DATA' => [] ]; $applyBlocks = []; $orderDiscountIndex = []; $orderDiscountLink = []; $discountList = []; $discountSort = []; $couponList = []; $resultData['COUPON_LIST'] = static::loadCouponsFromDb($order); if (!empty($resultData['COUPON_LIST'])) { foreach ($resultData['COUPON_LIST'] as $coupon) $couponList[$coupon['ID']] = $coupon['COUPON']; unset($coupon); } $ruleIterator = static::getResultIterator([ 'filter' => ['=ORDER_ID' => $order], 'order' => ['ID' => 'ASC'] ]); while ($rule = $ruleIterator->fetch()) { $rule['ID'] = (int)$rule['ID']; $rule['ORDER_DISCOUNT_ID'] = (int)$rule['ORDER_DISCOUNT_ID']; $rule['ORDER_COUPON_ID'] = (int)$rule['COUPON_ID']; $rule['ENTITY_ID'] = (int)$rule['ENTITY_ID']; if ($rule['ORDER_COUPON_ID'] > 0) { if (!isset($couponList[$rule['COUPON_ID']])) { $result->addError(new Main\Entity\EntityError( Loc::getMessage( 'SALE_ORDER_DISCOUNT_ERR_RULE_COUPON_NOT_FOUND', array('#ID#' => $rule['ID'], '#COUPON_ID#' => $rule['COUPON_ID']) ) )); } else { $rule['COUPON_ID'] = $couponList[$rule['ORDER_COUPON_ID']]; } } if (!isset($rule['RULE_DESCR_ID'])) $rule['RULE_DESCR_ID'] = 0; $rule['RULE_DESCR_ID'] = (int)$rule['RULE_DESCR_ID']; $rule['APPLY_BLOCK_COUNTER'] = (int)$rule['APPLY_BLOCK_COUNTER']; $blockCounter = $rule['APPLY_BLOCK_COUNTER']; if (!isset($applyBlocks[$blockCounter])) $applyBlocks[$blockCounter] = $emptyApplyBlock; if (!isset($orderDiscountIndex[$blockCounter])) $orderDiscountIndex[$blockCounter] = 0; if (static::isNativeModule($rule['MODULE_ID'])) { $discountId = (int)$rule['ORDER_DISCOUNT_ID']; if (!isset($orderDiscountLink[$discountId])) { $applyBlocks[$blockCounter]['ORDER'][$orderDiscountIndex[$blockCounter]] = static::formatSaleRuleResult($rule); $orderDiscountLink[$discountId] = &$applyBlocks[$blockCounter]['ORDER'][$orderDiscountIndex[$blockCounter]]; $orderDiscountIndex[$blockCounter]++; } $ruleItem = static::formatSaleItemRuleResult($rule); switch (static::getResultEntityFromInternal($rule['ENTITY_TYPE'])) { case $discountClassName::ENTITY_BASKET_ITEM: $index = static::transferEntityCodeFromInternal($rule, $basketList); if ($index == '') continue; $ruleItem['BASKET_ID'] = static::transferEntityCodeFromInternal($rule, []); static::fillRuleProductFields($ruleItem, $basketData, $index); if (!isset($orderDiscountLink[$discountId]['RESULT']['BASKET'])) $orderDiscountLink[$discountId]['RESULT']['BASKET'] = []; $orderDiscountLink[$discountId]['RESULT']['BASKET'][$index] = $ruleItem; if ($ruleItem['ACTION_BLOCK_LIST'] === null) $orderDiscountLink[$discountId]['ACTION_BLOCK_LIST'] = false; break; case $discountClassName::ENTITY_DELIVERY: if (!isset($orderDiscountLink[$discountId]['RESULT']['DELIVERY'])) $orderDiscountLink[$discountId]['RESULT']['DELIVERY'] = []; $ruleItem['DELIVERY_ID'] = static::transferEntityCodeFromInternal($rule, []); $orderDiscountLink[$discountId]['RESULT']['DELIVERY'] = $ruleItem; break; } unset($ruleItem, $discountId); } else { if ( $rule['ENTITY_ID'] <= 0 || static::getResultEntityFromInternal($rule['ENTITY_TYPE']) != $discountClassName::ENTITY_BASKET_ITEM ) continue; $index = static::transferEntityCodeFromInternal($rule, $basketList); if ($index == '') continue; $ruleResult = static::formatBasketRuleResult($rule); static::fillRuleProductFields($ruleResult, $basketData, $index); if (!isset($applyBlocks[$blockCounter]['BASKET'][$index])) $applyBlocks[$blockCounter]['BASKET'][$index] = []; $applyBlocks[$blockCounter]['BASKET'][$index][] = $ruleResult; unset($ruleResult); } if (!isset($discountList[$rule['ORDER_DISCOUNT_ID']])) { $discountList[$rule['ORDER_DISCOUNT_ID']] = $rule['ORDER_DISCOUNT_ID']; $discountSort[] = $rule['ORDER_DISCOUNT_ID']; } } unset($rule, $ruleIterator); unset($couponList); unset($orderDiscountLink, $orderDiscountIndex); if (!empty($discountList)) { $resultData['DISCOUNT_LIST'] = static::loadOrderDiscountFromDb($discountList, $discountSort); if ($resultData['DISCOUNT_LIST'] === null) $resultData['DISCOUNT_LIST'] = []; } unset($discountSort, $discountList); $actionsData = static::loadOrderStoredDataFromDb( $order, self::STORAGE_TYPE_DISCOUNT_ACTION_DATA ); if ($actionsData !== null) $resultData['STORED_ACTION_DATA'] = $actionsData; unset($actionsData); $dataIterator = static::getRoundResultIterator([ 'select' => ['*'], 'filter' => [ '=ORDER_ID' => $order, '=ENTITY_TYPE' => static::getRoundEntityInternal($discountClassName::ENTITY_BASKET_ITEM) ] ]); while ($data = $dataIterator->fetch()) { $data['APPLY_BLOCK_COUNTER'] = (int)$data['APPLY_BLOCK_COUNTER']; $blockCounter = $data['APPLY_BLOCK_COUNTER']; if (!isset($applyBlocks[$blockCounter])) $applyBlocks[$blockCounter] = $emptyApplyBlock; $basketCode = static::transferEntityCodeFromInternal($data, $basketList); if ($basketCode == '') continue; $applyBlocks[$blockCounter]['BASKET_ROUND'][$basketCode] = array( 'RULE_ID' => (int)$data['ID'], 'APPLY' => $data['APPLY'], 'ROUND_RULE' => $data['ROUND_RULE'] ); unset($basketCode, $blockCounter); } unset($data, $dataIterator); if (!empty($applyBlocks)) ksort($applyBlocks); $resultData['APPLY_BLOCKS'] = $applyBlocks; unset($applyBlocks); $result->setData($resultData); unset($resultData); unset($emptyApplyBlock, $discountClassName); return $result; } /** * Load applied discount list. * * @param array $discountIds * @param array $discountOrder * @return array|null */ protected static function loadOrderDiscountFromDb(array $discountIds, array $discountOrder) { if (empty($discountIds) || empty($discountOrder)) return null; $result = []; $list = []; $iterator = static::getOrderDiscountIterator([ 'select' => ['*'], 'filter' => ['@ID' => $discountIds] ]); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $row['ORDER_DISCOUNT_ID'] = $row['ID']; $row['MODULES'] = []; $row['SIMPLE_ACTION'] = true; if (static::isNativeModule($row['MODULE_ID'])) $row['SIMPLE_ACTION'] = self::isSimpleAction($row['APPLICATION']); $list[$row['ID']] = $row; } unset($row, $iterator); if (!empty($list)) { foreach ($discountOrder as $id) { if (!isset($list[$id])) continue; $result[$id] = $list[$id]; } unset($id); } unset($list); if (!empty($result)) { $resultIds = array_keys($result); $discountModules = static::loadModulesFromDb($resultIds); if ($discountModules !== null) { foreach ($discountModules as $id => $modules) $result[$id]['MODULES'] = $modules; unset($id, $modules); } unset($discountModules); foreach ($resultIds as $id) { $discount = $result[$id]; if (static::isNativeModule($discount['MODULE_ID'])) { $result[$id]['EDIT_PAGE_URL'] = static::getEditUrl(['ID' => $discount['DISCOUNT_ID']]); } else { $result[$id]['EDIT_PAGE_URL'] = (string)static::executeDiscountProvider( ['MODULE_ID' => $discount['MODULE_ID'], 'METHOD' => self::PROVIDER_ACTION_GET_URL], [ ['ID' => $discount['DISCOUNT_ID'], 'MODULE_ID' => $discount['MODULE_ID']] ] ); } } unset($discount, $id); } return (!empty($result) ? $result : null); } /** * Load stored data collection for order. * @internal * * @param int $order Order id. * @param string $storageType Storage type (only simple value, no mixed). * @param array $additionalFilter Additional filter for internal getList. * @return array|null */ public static function loadStoredDataFromDb($order, $storageType, array $additionalFilter = array()) { $result = null; $order = (int)$order; if ($order <= 0) return $result; $storageType = static::getStorageTypeInternal($storageType); if ($storageType === null) return $result; $filter = [ '=ORDER_ID' => $order, '=ENTITY_TYPE' => $storageType, ]; if (!empty($additionalFilter)) $filter = $filter + $additionalFilter; $list = []; $iterator = static::getStoredDataIterator(array( 'select' => ['*'], 'filter' => $filter )); while ($row = $iterator->fetch()) { if (empty($row['ENTITY_DATA']) || !is_array($row['ENTITY_DATA'])) continue; $index = static::getEntityIndex($row); $list[$index] = $row['ENTITY_DATA']; } unset($index, $row, $iterator); if (!empty($list)) $result = $list; unset($list); return $result; } /** * Load order stored data row. * @internal * * @param int $order Order id. * @param string $storageType Storage type (only simple value, no mixed). * @return array|null */ public static function loadOrderStoredDataFromDb($order, $storageType) { $result = null; $data = static::loadStoredDataFromDb( $order, $storageType, ['=ENTITY_ID' => $order] ); if ($data === null) return $result; if (count($data) > 1) return $result; if (isset($data[$order])) $result = $data[$order]; unset($data); return $result; } /** * Save order stored data. * * @param int $order * @param string $storageType * @param array $data * @param array $options * @return Result */ public static function saveOrderStoredData($order, $storageType, array $data, array $options = array()) { return static::saveStoredDataBlock( $order, $storageType, [$order => [ 'ENTITY_ID' => $order, 'ENTITY_VALUE' => $order, 'ENTITY_DATA' => $data ]], $options ); } /** * Save stored data for entities. * * @param int $order * @param string $storageType * @param array $block * @param array $options * @return Result * @throws Main\Db\SqlQueryException */ public static function saveStoredDataBlock($order, $storageType, array $block, array $options = array()) { $result = new Result; $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } $storageType = static::getStorageTypeInternal($storageType); if ($storageType === null) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_STORAGE_TYPE'), self::ERROR_ID )); return $result; } $allowUpdate = (isset($options['ALLOW_UPDATE']) && $options['ALLOW_UPDATE'] === 'Y'); $deleteMissing = (isset($options['DELETE_MISSING']) && $options['DELETE_MISSING'] === 'Y'); $collection = []; $deleteList = []; $iterator = static::getStoredDataIterator([ 'select' => ['*'], 'filter' => [ '=ORDER_ID' => $order, '=ENTITY_TYPE' => $storageType, ] ]); while ($row = $iterator->fetch()) { $index = static::getEntityIndex($row); $collection[$index] = $row; $deleteList[$index] = $row['ID']; } unset($row, $iterator); $existError = false; foreach ($block as $index => $row) { if (isset($deleteList[$index])) unset($deleteList[$index]); if (!empty($collection[$index]) && !$allowUpdate) { $existError = true; continue; } if (empty($collection[$index])) { $row['ORDER_ID'] = $order; $row['ENTITY_TYPE'] = $storageType; $resultInternal = static::addStoredDataInternal($row); } else { $resultInternal = static::updateStoredDataInternal( $collection[$index]['ID'], ['ENTITY_DATA' => $row['ENTITY_DATA']] ); } if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $index, $row); if ($existError) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_ORDER_STORED_DATA_ALREADY_EXISTS'), self::ERROR_ID )); } unset($existError); if ($result->isSuccess()) { if ($deleteMissing && !empty($deleteList)) static::deleteRowsByIndex(static::getStoredDataTableInternal(), 'ID', $deleteList); } unset($deleteList, $deleteMissing); return $result; } public static function addResultBlock($order, array $block) { $result = new Result(); $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } if (empty($block)) return $result; foreach ($block as $row) { $row['ORDER_ID'] = $order; $row['ENTITY_TYPE'] = static::getResultEntityInternal($row['ENTITY_TYPE']); if (!isset($row['APPLY_BLOCK_COUNTER']) || $row['APPLY_BLOCK_COUNTER'] < 0) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_APPLY_BLOCK_COUNTER') )); return $result; } $resultInternal = static::addResultRow($row); if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $row); return $result; } public static function updateResultBlock($order, array $block) { $result = new Result(); $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } $deleteList = array(); $iterator = static::getResultIterator(array( 'select' => array('ID'), 'filter' => array('=ORDER_ID' => $order), 'order' => array('ID' => 'ASC') )); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $deleteList[$row['ID']] = $row['ID']; } unset($row, $iterator); if (!empty($block)) { foreach ($block as $row) { $id = null; if (isset($row['RULE_ID']) && $row['RULE_ID'] > 0) $id = $row['RULE_ID']; unset($row['RULE_ID']); if ($id === null) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_RESULT_ROW_ID_IS_ABSENT'), self::ERROR_ID )); continue; } if (!isset($deleteList[$id])) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_RESULT_ROW_ID'), self::ERROR_ID )); continue; } unset($deleteList[$id]); $resultInternal = static::updateResultRow($id, $row); if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $row); } if (!$result->isSuccess()) return $result; if (!empty($deleteList)) { self::deleteRowsByIndex(static::getResultTableNameInternal(), 'ID', $deleteList); self::deleteRowsByIndex(static::getResultDescriptionTableNameInternal(), 'RULE_ID', $deleteList); } return $result; } public static function addRoundBlock($order, array $block) { $result = new Result(); $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } if (empty($block)) return $result; foreach ($block as $row) { $row['ORDER_ID'] = $order; $row['ENTITY_TYPE'] = static::getRoundEntityInternal($row['ENTITY_TYPE']); $row['ORDER_ROUND'] = 'N'; if (!isset($row['APPLY_BLOCK_COUNTER']) || $row['APPLY_BLOCK_COUNTER'] < 0) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_APPLY_BLOCK_COUNTER') )); return $result; } $resultInternal = static::addRoundResultInternal($row); if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $row); return $result; } public static function updateRoundBlock($order, array $block) { $result = new Result(); $order = (int)$order; if ($order <= 0) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ORDER_ID'), self::ERROR_ID )); return $result; } $deleteList = array(); $iterator = static::getRoundResultIterator([ 'select' => ['ID'], 'filter' => ['=ORDER_ID' => $order] ]); while ($row = $iterator->fetch()) { $row['ID'] = (int)$row['ID']; $deleteList[$row['ID']] = $row['ID']; } unset($row, $iterator); if (!empty($block)) { foreach ($block as $row) { $id = null; if (isset($row['RULE_ID']) && $row['RULE_ID'] > 0) $id = $row['RULE_ID']; if ($id === null) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_ROUND_ROW_ID_IS_ABSENT'), self::ERROR_ID )); continue; } if (!isset($deleteList[$id])) { $result->addError(new Main\Error( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_BAD_ROUND_ROW_ID'), self::ERROR_ID )); continue; } unset($deleteList[$id]); unset($row['RULE_ID']); $resultInternal = static::updateRoundResultInternal($id, $row); if (!$resultInternal->isSuccess()) $result->addErrors($resultInternal->getErrors()); } unset($resultInternal, $row); } if (!$result->isSuccess()) return $result; if (!empty($deleteList)) self::deleteRowsByIndex(static::getRoundTableNameInternal(), 'ID', $deleteList); return $result; } /** * Delete all data by order. * * @param int $order Order id. * @return void */ public static function deleteByOrder($order) {} /** * Return parent entity type. The method must be overridden in the derived class. * @internal * * @return string * @throws Main\NotImplementedException */ public static function getRegistryType() { throw new Main\NotImplementedException(); } protected static function getDiscountClassName() { $registry = Registry::getInstance(static::getRegistryType()); return $registry->getDiscountClassName(); } /** * Returns true, if discount from module sale. * * @param string $module Module id. * @return bool */ protected static function isNativeModule($module) { return ($module === 'sale'); } /** * Return valid provider action list. * * @return array */ protected static function getDiscountProviderActions() { return array( self::PROVIDER_ACTION_PREPARE_DISCOUNT, self::PROVIDER_ACTION_GET_URL, self::PROVIDER_ACTION_APPLY_COUPON, self::PROVIDER_ACTION_ROUND_ITEM_PRICE, self::PROVIDER_ACTION_ROUND_BASKET_PRICES ); } /** * Initialization discount providers. * * @return void */ protected static function initDiscountProviders() { self::$discountProviders = array(); $event = new Main\Event('sale', self::EVENT_ON_BUILD_DISCOUNT_PROVIDERS, array()); $event->send(); $resultList = $event->getResults(); if (empty($resultList) || !is_array($resultList)) return; $actionList = static::getDiscountProviderActions(); /** @var Main\EventResult $eventResult */ foreach ($resultList as $eventResult) { if ($eventResult->getType() != Main\EventResult::SUCCESS) continue; $module = (string)$eventResult->getModuleId(); $provider = $eventResult->getParameters(); if (empty($provider) || !is_array($provider)) continue; if (!isset($provider[self::PROVIDER_ACTION_PREPARE_DISCOUNT])) continue; self::$discountProviders[$module] = array( 'module' => $module ); foreach ($actionList as $action) { if (isset($provider[$action])) self::$discountProviders[$module][$action] = $provider[$action]; } } unset($provider, $module, $actionList, $eventResult, $resultList, $event); } /** * Execute discount provider. * * @param array $provider Provider info * keys are case sensitive: * <ul> * <li>string MODULE Provider module id * <li>string METHOD Prodider method id * </ul>. * @param array $data Data for execute. * @return mixed */ protected static function executeDiscountProvider(array $provider, array $data) { $module = $provider['MODULE_ID']; $method = $provider['METHOD']; if (!isset(self::$discountProviders[$module]) || !isset(self::$discountProviders[$module][$method])) return false; return call_user_func_array( self::$discountProviders[$module][$method], $data ); } /** * Prepare sale discount before saving. * * @param array $discount Discount data. * @return array|bool */ protected static function prepareData($discount) { $fields = static::fillAbsentDiscountFields($discount); if ($fields === null) return false; $discountId = (int)$fields['ID']; if (!isset($fields['NAME']) || (string)$fields['NAME'] == '') $fields['NAME'] = Loc::getMessage('SALE_ORDER_DISCOUNT_NAME_TEMPLATE', array('#ID#' => $fields['ID'])); $fields['DISCOUNT_ID'] = $discountId; $fields['EDIT_PAGE_URL'] = static::getEditUrl(array('ID' => $discountId)); unset($fields['ID']); return $fields; } /** * Get absent discount fields from database. * * @param array $fields Current discount state. * @return array|null */ protected static function fillAbsentDiscountFields(array $fields) { if (empty($fields) || empty($fields['ID'])) return null; $discountId = (int)$fields['ID']; if ($discountId <= 0) return null; $requiredFields = static::checkRequiredOrderDiscountFields($fields); if (!empty($requiredFields)) { if (in_array('ACTIONS_DESCR', $requiredFields)) return null; $requiredFields[] = 'ID'; $iterator = static::getDiscountIterator(array( 'select' => $requiredFields, 'filter' => array('=ID' => $discountId) )); $row = $iterator->fetch(); unset($iterator); if (empty($row)) return null; foreach ($row as $field => $value) { if (isset($fields[$field])) continue; $fields[$field] = $value; } unset($field, $value); } unset($requiredFields); return $fields; } /** * Clear raw data and calculate discount hash. * * @param array $rawFields Discount information. * @return array|null */ protected static function normalizeDiscountFields(array $rawFields) { $result = static::normalizeOrderDiscountFieldsInternal($rawFields); if (!is_array($result)) return null; $result['DISCOUNT_HASH'] = static::calculateOrderDiscountHashInternal($result); return $result; } /** * Returns exists discount for discount hash (cached). * * @param string $hash Discount hash. * @return array|null */ protected static function searchDiscount($hash) { $hash = (string)$hash; if ($hash === '') return null; $entity = get_called_class(); if (!isset(self::$discountCache[$entity])) self::$discountCache[$entity] = array(); if (!isset(self::$discountCache[$entity][$hash])) { $iterator = static::getOrderDiscountIterator(array( 'select' => array('*'), 'filter' => array('=DISCOUNT_HASH' => $hash) )); $row = $iterator->fetch(); if (!empty($row)) self::setCacheItem($entity, $row); unset($row, $iterator); } return (isset(self::$discountCache[$entity][$hash]) ? self::$discountCache[$entity][$hash] : null); } /** * Save exist discount to cache. * * @param string $entity Entity id (class name). * @param array $fields Discount. * @return void */ private static function setCacheItem($entity, array $fields) { $fields['ID'] = (int)$fields['ID']; $fields['NAME'] = (string)$fields['NAME']; $fields['ORDER_DISCOUNT_ID'] = $fields['ID']; self::$discountCache[$entity][$fields['DISCOUNT_HASH']] = $fields; } /** * Validate coupon. * * @param array $fields Coupon data. * @return Result */ protected static function validateCoupon(array $fields) { $result = new Result(); if ( !static::isValidCouponTypeInternal($fields['TYPE']) ) { $result->addError(new Main\Entity\EntityError( Loc::getMessage( 'SALE_ORDER_DISCOUNT_ERR_COUPON_TYPE_BAD', array('#COUPON#' => $fields['COUPON']) ), self::ERROR_ID )); } if (empty($fields['COUPON_ID']) || (int)$fields['COUPON_ID'] <= 0) { $result->addError(new Main\Entity\EntityError( Loc::getMessage( 'SALE_ORDER_DISCOUNT_ERR_COUPON_ID_BAD', array('#COUPON#' => $fields['COUPON']) ), self::ERROR_ID )); } return $result; } /** * Add new coupon for order. * * @param array $fields Tablet fields. * @return Result|null */ protected static function addCoupon(array $fields) { $result = new Result(); if (array_key_exists('ID', $fields)) unset($fields['ID']); $tabletResult = static::addOrderCouponInternal($fields); if ($tabletResult->isSuccess()) { $fields['ID'] = $tabletResult->getId(); $result->setId($fields['ID']); $result->setData($fields); } else { $result->addErrors($tabletResult->getErrors()); } unset($tabletResult); return $result; } /** * Add new unique order discount. * * @param array $fields Tablet fields. * @param array $rawFields Additional fields. * @return Result|null */ protected static function addDiscount(array $fields, array $rawFields) { $result = new Result; $process = true; $orderDiscountId = null; $tabletResult = static::addOrderDiscountInternal($fields); if ($tabletResult->isSuccess()) { $orderDiscountId = (int)$tabletResult->getId(); } else { $process = false; $result->addErrors($tabletResult->getErrors()); } unset($tabletResult); if ($process) { $moduleList = static::prepareDiscountModules($rawFields); if (!empty($moduleList)) { $resultModule = static::saveOrderDiscountModulesInternal( $orderDiscountId, $moduleList ); if (!$resultModule) { $process = false; $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_SAVE_DISCOUNT_MODULES'), self::ERROR_ID )); } unset($resultModule); } unset($moduleList); } if ($process) $result->setId($orderDiscountId); return $result; } /** * Load discount modules. * * @param array $discountIds Order discount id list. * @return array|null */ protected static function loadModulesFromDb(array $discountIds) { if (empty($discountIds)) return null; Main\Type\Collection::normalizeArrayValuesByInt($discountIds, true); if (empty($discountIds)) return null; $result = array(); $iterator = static::getOrderDiscountModuleIterator(array( 'select' => array('MODULE_ID', 'ORDER_DISCOUNT_ID'), 'filter' => array('@ORDER_DISCOUNT_ID' => $discountIds) )); while ($row = $iterator->fetch()) { $orderDiscountId = (int)$row['ORDER_DISCOUNT_ID']; if (!isset($result[$orderDiscountId])) $result[$orderDiscountId] = array(); $result[$orderDiscountId][] = $row['MODULE_ID']; } unset($row, $iterator); return (!empty($result) ? $result : null); } protected static function prepareDiscountModules(array $discount) { $result = array(); $needDiscountModules = array(); if (!empty($discount['MODULES'])) { $needDiscountModules = ( !is_array($discount['MODULES']) ? array($discount['MODULES']) : $discount['MODULES'] ); } elseif (!empty($discount['HANDLERS'])) { if (!empty($discount['HANDLERS']['MODULES'])) { $needDiscountModules = ( !is_array($discount['HANDLERS']['MODULES']) ? array($discount['HANDLERS']['MODULES']) : $discount['HANDLERS']['MODULES'] ); } } if (!empty($needDiscountModules)) { foreach ($needDiscountModules as &$module) { $module = trim((string)$module); if (!empty($module)) $result[$module] = $module; } unset($module); $result = array_values($result); } return $result; } /** * Returns entity code for discount and round results. * * @param array $row Result row. * @param array $transferList Transfer table (for example, basket id to basket code). * @return int|string */ protected static function transferEntityCodeFromInternal(array $row, array $transferList) { $code = ''; if (empty($row)) return $code; $row['ENTITY_VALUE'] = (string)$row['ENTITY_VALUE']; if (!empty($transferList)) { if ($row['ENTITY_ID'] > 0 && isset($transferList[$row['ENTITY_ID']])) $code = $transferList[$row['ENTITY_ID']]; elseif ($row['ENTITY_VALUE'] !== '' && isset($transferList[$row['ENTITY_VALUE']])) $code = $transferList[$row['ENTITY_VALUE']]; } else { $code = ($row['ENTITY_ID'] > 0 ? $row['ENTITY_ID'] : $row['ENTITY_VALUE']); } return $code; } /** * Format rule result for basket discount. * * @param array $rule Rule result from database. * @return array */ protected static function formatBasketRuleResult(array $rule) { $ruleResult = [ 'BASKET_ID' => $rule['ENTITY_ID'], 'RULE_ID' => $rule['ID'], 'ORDER_ID' => $rule['ORDER_ID'], 'DISCOUNT_ID' => $rule['ORDER_DISCOUNT_ID'], 'ORDER_COUPON_ID' => $rule['ORDER_COUPON_ID'], 'COUPON_ID' => ($rule['ORDER_COUPON_ID'] > 0 ? $rule['COUPON_ID'] : ''), 'RESULT' => ['APPLY' => $rule['APPLY']], 'RULE_DESCR_ID' => $rule['RULE_DESCR_ID'], 'ACTION_BLOCK_LIST' => (isset($rule['ACTION_BLOCK_LIST']) ? $rule['ACTION_BLOCK_LIST'] : null) ]; if (!empty($rule['RULE_DESCR']) && is_array($rule['RULE_DESCR'])) { $ruleResult['RESULT']['DESCR_DATA'] = $rule['RULE_DESCR']; $ruleResult['RESULT']['DESCR'] = Discount\Formatter::formatList($rule['RULE_DESCR']); $ruleResult['DESCR_ID'] = $rule['RULE_DESCR_ID']; } return $ruleResult; } /** * Format rule result for sale discount. * * @param array $rule Rule result from database. * @return array */ protected static function formatSaleRuleResult(array $rule) { return [ 'ORDER_ID' => $rule['ORDER_ID'], 'DISCOUNT_ID' => $rule['ORDER_DISCOUNT_ID'], 'ORDER_COUPON_ID' => $rule['ORDER_COUPON_ID'], 'COUPON_ID' => ($rule['ORDER_COUPON_ID'] > 0 ? $rule['COUPON_ID'] : ''), 'RESULT' => [], 'ACTION_BLOCK_LIST' => true ]; } /** * Format rule item result for sale discount. * * @param array $rule Rule result from database. * @return array */ protected static function formatSaleItemRuleResult(array $rule) { $ruleItem = array( 'RULE_ID' => $rule['ID'], 'APPLY' => $rule['APPLY'], 'RULE_DESCR_ID' => $rule['RULE_DESCR_ID'], 'ACTION_BLOCK_LIST' => ( !empty($rule['ACTION_BLOCK_LIST']) && is_array($rule['ACTION_BLOCK_LIST']) ? $rule['ACTION_BLOCK_LIST'] : null ) ); if (!empty($rule['RULE_DESCR']) && is_array($rule['RULE_DESCR'])) { $ruleItem['DESCR_DATA'] = $rule['RULE_DESCR']; $ruleItem['DESCR'] = Discount\Formatter::formatList($rule['RULE_DESCR']); $ruleItem['DESCR_ID'] = $rule['RULE_DESCR_ID']; } return $ruleItem; } /** * Fill product fields in rule result. * * @param array &$result Rule result. * @param array $basketData Basket data. * @param int|string $index Basket index. * @return void */ protected static function fillRuleProductFields(array &$result, array $basketData, $index) { if (!empty($basketData[$index])) { $result['MODULE'] = $basketData[$index]['MODULE']; $result['PRODUCT_ID'] = $basketData[$index]['PRODUCT_ID']; } } /* discounts */ /** * Discount getList (prototype). * * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getDiscountIterator(array $parameters) { return null; } /* discounts end */ /* coupons */ /** * Check coupon type. * * @param int $type Coupon type. * @return bool */ protected static function isValidCouponTypeInternal($type) { return false; } /* coupons end */ /* order discounts */ /** * Order discount getList (prototype). * * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getOrderDiscountIterator(array $parameters) { return null; } /** * Low-level method add new discount for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addOrderDiscountInternal(array $fields) { return null; } /** * Returns the list of missing discount fields (prototype). * * @param array $fields Discount fields. * @return array */ protected static function checkRequiredOrderDiscountFields(array $fields) { return []; } /** * Clear raw order discount data (prototype). * * @param array $rawFields Discount information. * @return array|null */ protected static function normalizeOrderDiscountFieldsInternal(array $rawFields) { return null; } /** * Calculate order discount hash (prototype). * * @param array $fields Discount information. * @return string|null */ protected static function calculateOrderDiscountHashInternal(array $fields) { return null; } /* order discounts end */ /* order coupons */ /** * Order coupons getList (prototype). * * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ public static function getOrderCouponIterator(array $parameters) { return null; } /** * Low-level method add new coupon for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addOrderCouponInternal(array $fields) { return null; } /** * Load coupons for order. * * @param int $order Order id. * @return array */ protected static function loadCouponsFromDb($order) { $result = array(); $couponIterator = static::getOrderCouponIterator(array( 'select' => array('*'), 'filter' => array('=ORDER_ID' => $order), 'order' => array('ID' => 'ASC') )); while ($coupon = $couponIterator->fetch()) { $coupon['ID'] = (int)$coupon['ID']; $coupon['ORDER_ID'] = (int)$coupon['ORDER_ID']; $coupon['ORDER_DISCOUNT_ID'] = (int)$coupon['ORDER_DISCOUNT_ID']; $result[$coupon['COUPON']] = $coupon; } unset($coupon, $couponIterator); return $result; } /* order coupons end */ /* order discount modules */ /** * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getOrderDiscountModuleIterator(array $parameters) { return null; } /** * Low-level method save order discount modules. * * @param int $orderDiscountId * @param array $modules * @return bool */ protected static function saveOrderDiscountModulesInternal($orderDiscountId, array $modules) { return false; } /* order discount modules end */ /* discount results */ /** * Converts the discount result entity identifier to the database table format (prototype). * * @param string $entity * @return null|int */ protected static function getResultEntityInternal($entity) { return null; } /** * Converts the discount result entity identifier from the database table format (prototype). * * @param int $entity * @return null|string */ protected static function getResultEntityFromInternal($entity) { return null; } /** * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getResultIterator(array $parameters) { return null; } /** * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getResultDescriptionIterator(array $parameters) { return null; } /** * Low-level method add new result discount for order. * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addResultRow(array $fields) { if (array_key_exists('ID', $fields)) unset($fields['ID']); $resultFields = static::checkResultTableWhiteList($fields); if (empty($resultFields)) { $result = new Main\Entity\AddResult(); $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_RESULT_ROW_IS_EMPTY') )); return $result; } $result = static::addResultInternal($resultFields); unset($resultFields); if (!$result->isSuccess()) return $result; $fields['RULE_ID'] = (int)$result->getId(); $descriptionFields = static::checkResultDescriptionTableWhiteList($fields); if (empty($descriptionFields)) { $result->addError(new Main\Entity\EntityError( Loc::getMessage('SALE_ORDER_DISCOUNT_ERR_RESULT_ROW_DESCRIPTION_IS_EMPTY') )); return $result; } $descriptionResult = static::addResultDescriptionInternal($descriptionFields); unset($descriptionFields); if (!$descriptionResult->isSuccess()) $result->addErrors($descriptionResult->getErrors()); unset($descriptionResult); return $result; } /** * Low-level method update result discount for order. * * @param int $id Tablet row id. * @param array $fields Tablet fields. * @return Main\Entity\UpdateResult|null */ protected static function updateResultRow($id, array $fields) { $rowUpdate = ['APPLY' => $fields['APPLY']]; if (isset($fields['ACTION_BLOCK_LIST'])) $rowUpdate['ACTION_BLOCK_LIST'] = $fields['ACTION_BLOCK_LIST']; $result = static::updateResultInternal($id, $rowUpdate); unset($rowUpdate); if (!$result->isSuccess()) return $result; $descrId = null; if (isset($fields['DESCR_ID']) && $fields['DESCR_ID'] > 0) $descrId = $fields['DESCR_ID']; if ($descrId === null) { $iterator = static::getResultDescriptionIterator([ 'select' => ['ID'], 'filter' => ['=RULE_ID' => $id] ]); $row = $iterator->fetch(); if (!empty($row['ID'])) $descrId = (int)$row['ID']; unset($row, $iterator); } if ($descrId === null) { $iterator = static::getResultIterator([ 'select' => ['MODULE_ID', 'ORDER_DISCOUNT_ID', 'ORDER_ID'], 'filter' => ['=ID' => $id], 'order' => [] ]); $row = $iterator->fetch(); unset($iterator); $row['RULE_ID'] = $id; $row['DESCR'] = $fields['DESCR']; $resultDescr = static::addResultDescriptionInternal($row); unset($row); } else { $resultDescr = static::updateResultDescriptionInternal( $fields['DESCR_ID'], array('DESCR' => $fields['DESCR']) ); } if (!$resultDescr->isSuccess()) $result->addErrors($resultDescr->getErrors()); unset($resultDescr); return $result; } /** * Low-level method returns result table name (prototype). * * @return string|null */ protected static function getResultTableNameInternal() { return null; } /** * Low-level method returns result description table name (prototype). * * @return string|null */ protected static function getResultDescriptionTableNameInternal() { return null; } /** * Low-level method returns only those fields that are in the result table (prototype). * * @param array $fields * @return array|null */ protected static function checkResultTableWhiteList(array $fields) { return null; } /** * Low-level method returns only those fields that are in the result description table (prototype). * * @param array $fields * @return array|null */ protected static function checkResultDescriptionTableWhiteList(array $fields) { return null; } /** * Low-level method add new result discount for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addResultInternal(array $fields) { return null; } /** * Low-level method add new result description for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addResultDescriptionInternal(array $fields) { return null; } /** * Low-level method update result discount for order (prototype). * * @param int $id Primary key. * @param array $fields Tablet fields. * @return Main\Entity\UpdateResult|null */ protected static function updateResultInternal($id, array $fields) { return null; } /** * Low-level method update result description for order. * * @param int $id Primary key. * @param array $fields Tablet fields. * @return Main\Entity\UpdateResult|null */ protected static function updateResultDescriptionInternal($id, array $fields) { return null; } /* discount results end */ /* round result */ /** * Converts the rounded entity identifier to the database table format (prototype). * * @param string $entity * @return null|int */ protected static function getRoundEntityInternal($entity) { return null; } /** * Converts the rounded entity identifier from the database table format (prototype). * * @param int $entity * @return null|string */ protected static function getRoundEntityFromInternal($entity) { return null; } /** * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getRoundResultIterator(array $parameters) { return null; } /** * Low-level method add new round result for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addRoundResultInternal(array $fields) { return null; } /** * Low-level method update round result for order (prototype). * * @param int $id Tablet row id. * @param array $fields Tablet fields. * @return Main\Entity\UpdateResult|null */ protected static function updateRoundResultInternal($id, array $fields) { return null; } /** * low-level method returns the round-results table name (prototype). * * @return string|null */ protected static function getRoundTableNameInternal() { return null; } /* round result end */ /* data storage */ /** * Low-level method for convert storage types to internal format. * * @param string $storageType Abstract storage type. * @return int|null */ protected static function getStorageTypeInternal($storageType) { return null; } /** * @param array $parameters \Bitrix\Main\Entity\DataManager::getList parameters. * @return Main\DB\Result|null */ protected static function getStoredDataIterator(array $parameters) { return null; } /** * Low-level method add stored data for order (prototype). * * @param array $fields Tablet fields. * @return Main\Entity\AddResult|null */ protected static function addStoredDataInternal(array $fields) { return null; } /** * Low-level method update stored data for order (prototype). * * @param int $id Tablet row id. * @param array $fields Tablet fields. * @return Main\Entity\UpdateResult|null */ protected static function updateStoredDataInternal($id, array $fields) { return null; } /** * low-level method returns the order stored data table name (prototype). * * @return string|null */ protected static function getStoredDataTableInternal() { return null; } /* data storage end */ /** * Return flag simple action in discount. * * @internal * @param string $action Discount action. * @return bool */ private static function isSimpleAction($action) { $result = true; $action = (string)$action; if ($action == '') return $result; $action = trim(substr($action, 8)); $action = substr($action, 2); $key = strpos($action, ')'); if ($key === false) return $result; $orderName = '\\'.substr($action, 0, $key); preg_match_all("/".$orderName."(?:,|\))/".BX_UTF_PCRE_MODIFIER, $action, $list); if (isset($list[0]) && is_array($list[0])) $result = count($list[0]) <= 2; return $result; } /** * Remove old result rows without events. * * @param string $tableName * @param string $indexField * @param array $ids * @throws Main\Db\SqlQueryException */ private static function deleteRowsByIndex($tableName, $indexField, array $ids) { $tableName = (string)$tableName; if ($tableName === '') return; $indexField = (string)$indexField; if ($indexField === '') return; if (empty($ids)) return; Main\Type\Collection::normalizeArrayValuesByInt($ids, true); if (empty($ids)) return; $conn = Main\Application::getConnection(); $helper = $conn->getSqlHelper(); $query = 'delete from '.$helper->quote($tableName).' where '.$helper->quote($indexField); foreach (array_chunk($ids, 500) as $page) { $conn->queryExecute($query.' in ('.implode(', ', $page).')'); } unset($page, $query); unset($helper, $conn); } private static function getEntityIndex(array $row) { return (isset($row['ENTITY_ID']) ? $row['ENTITY_ID'] : $row['ENTITY_VALUE']); } }