Доброго всем дня.
Сегодня я представлю на обозрение свой велосипед по реализации логики работы скидок в корзине.
Суть задачи была такова:
Имеется три вида скидок: скидка товара в традиционном понимании, назовём её "собственная скидка товара", скидка правила работы с корзиной и скидка по купону.
Если в корзине будет присутствовать более одного вида скидок, то логика работы должна быть такова: для каждого товара в корзине смотрим какая скидка является наибольшей, её и применяем, остальные скидки не применяются.
Например:
Ситуация №1
Ситуация, когда в корзине присутствует скидка правила работы с корзиной (5% если товаров на сумму более 2000 р.) и собственная скидка товара (45%):
Предыдущее состояние было таким: присутствовали только собственные скидки товаров (2% и 45%):
Соответственно скидка правила работы с корзиной (5%) перекрыла скидку 2%, но не тронула 45%.
Ситуация №2
В корзине будет присутствовать скидка по купону.
Исходное состояние корзины, только собственные скидки товаров:
Применён купон, дающий скидку 20%
Применён купон (4%) и правило работы с корзиной (5%):
Предыдущее состояние, активно только правило работы с корзиной (5%):
Применён купон, дающий скидку 20%, он перекрыл правило работы с корзиной (5%):
Правило работы с корзиной (5%) и собственная скидка товара (45%):
Применён купон, дающий скидку 50%. Он перекрыл и собственные скидки товаров и правило работы с корзиной, т.к. размеры их скидок были меньше
Применён купон, дающий скидку 20%. Скидка 45% осталась нетронутой, на остальные товары скидка купона повлияла.
Применён купон 4% скидки, но он не перекрыл ни одну их скидок:
Вообщем, всю суть вы поняли. Теперь настройки в админке.
Настройки купонов:
Настройки правил работы с корзиной:
Для элементов инфоблока с товарами было создано поле, в которое помещалось значение собственной скидки товара:
Теперь программная реализация.
Вспомогательные методы были помещены в хелпер
use Bitrix\Main\Application; use Bitrix\Main\Web\Uri; /** * Class Helper */ class Helper { //вспомогательные функции для работы со скидками в корзине public static function getDiscountHandlers($discountList) { $result = array(); if (!empty($discountList) && is_array($discountList)) { $moduleList = \Bitrix\Sale\Internals\DiscountModuleTable::getByDiscount($discountList); if (!empty($moduleList)) { foreach ($moduleList as $discount => $discountModule) { $result[$discount] = array( 'MODULES' => $discountModule, 'EXT_FILES' => array() ); } unset($discount, $discountModule, $moduleList); } } return $result; } public static function __Unpack($arOrder, $strUnpack) { $checkOrder = null; if (empty($strUnpack)) return false; eval('$checkOrder='.$strUnpack.';'); if (!is_callable($checkOrder)) return false; $boolRes = $checkOrder($arOrder); unset($checkOrder); return $boolRes; } public static function __ApplyActions(&$arOrder, $strActions) { $applyOrder = null; if (!empty($strActions)) { eval('$applyOrder='.$strActions.';'); if (is_callable($applyOrder)) $applyOrder($arOrder); } } public static function getDiscountResult(&$oldOrder, &$currentOrder, $extMode = false) { $extMode = ($extMode === true); $result = array(); if (isset($oldOrder['PRICE_DELIVERY']) && isset($currentOrder['PRICE_DELIVERY'])) { if ($oldOrder['PRICE_DELIVERY'] != $currentOrder['PRICE_DELIVERY']) { $absValue = $oldOrder['PRICE_DELIVERY'] - $currentOrder['PRICE_DELIVERY']; $fullValue = ($extMode && isset($currentOrder['PRICE_DELIVERY_ORIG']) ? $currentOrder['PRICE_DELIVERY_ORIG'] : $oldOrder['PRICE_DELIVERY']); $percValue = ($fullValue != 0 ? $absValue*100/$fullValue : 0); $result['DELIVERY'] = array( 'TYPE' => 'D', 'DISCOUNT_TYPE' => ($currentOrder['PRICE_DELIVERY'] < $oldOrder['PRICE_DELIVERY'] ? 'D' : 'M'), 'VALUE' => $absValue, 'VALUE_PERCENT' => $percValue, 'DELIVERY_ID' => (isset($currentOrder['DELIVERY_ID']) ? $currentOrder['DELIVERY_ID'] : false) ); unset($percValue, $fullValue, $absValue); } } if (!empty($oldOrder['BASKET_ITEMS']) && !empty($currentOrder['BASKET_ITEMS'])) { foreach ($oldOrder['BASKET_ITEMS'] as $key => $item) { if (!isset($currentOrder['BASKET_ITEMS'][$key])) continue; if ($item['PRICE'] != $currentOrder['BASKET_ITEMS'][$key]['PRICE']) { $newItem = &$currentOrder['BASKET_ITEMS'][$key]; $absValue = $item['PRICE'] - $newItem['PRICE']; $fullValue = ($extMode && isset($newItem['PRICE_ORIG']) ? $newItem['PRICE_ORIG'] : $item['PRICE']); $percValue = ($fullValue != 0 ? $absValue*100/$fullValue : 0); if (!isset($result['BASKET'])) $result['BASKET'] = array(); $result['BASKET'][] = array( 'TYPE' => 'B', 'DISCOUNT_TYPE' => ($newItem['PRICE'] < $item['PRICE'] ? 'D' : 'M'), 'VALUE' => $absValue, 'VALUE_PERCENT' => $percValue, 'BASKET_NUM' => $key, 'BASKET_ID' => (isset($newItem['ID']) ? $newItem['ID'] : '0'), 'BASKET_PRODUCT_XML_ID' => (isset($newItem['PRODUCT_XML_ID']) && $newItem['PRODUCT_XML_ID'] != '' ? $newItem['PRODUCT_XML_ID'] : false), 'PRODUCT_ID' => $newItem['PRODUCT_ID'], 'MODULE' => $newItem['MODULE'] ); unset($percValue, $fullValue, $absValue, $newItem); } } } return $result; } } |
Поставлен обработчик события:
Main\EventManager::getInstance()->addEventHandler( 'catalog', 'OnGetDiscountResult', 'myOnGetDiscountResult' ); function myOnGetDiscountResult(&$arFields) { //======================== //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// $bShowReady = False; $bShowDelay = False; $bShowSubscribe = False; $bShowNotAvail = False; $allSum = 0; $allSumOld = 0; $allWeight = 0; $allCurrency = CSaleLang::GetLangCurrency(SITE_ID); $allVATSum = 0; $arResult["ITEMS"]["AnDelCanBuy"] = Array(); $arResult["ITEMS"]["DelDelCanBuy"] = Array(); $arResult["ITEMS"]["nAnCanBuy"] = Array(); $arResult["ITEMS"]["ProdSubscribe"] = Array(); $DISCOUNT_PRICE_ALL = 0; $arBasketItems = array(); $dbBasketItems = CSaleBasket::GetList( array( "NAME" => "ASC", "ID" => "ASC" ), array( "FUSER_ID" => CSaleBasket::GetBasketUserID(), "LID" => SITE_ID, "ORDER_ID" => "NULL" ), false, false, array("ID", "NAME", "CALLBACK_FUNC", "MODULE", "PRODUCT_ID", "QUANTITY", "DELAY", "CAN_BUY", "PRICE", "WEIGHT", "DETAIL_PAGE_URL", "NOTES", "CURRENCY", "VAT_RATE", "CATALOG_XML_ID", "PRODUCT_XML_ID", "SUBSCRIBE", "DISCOUNT_PRICE", "PRODUCT_PROVIDER_CLASS") ); $arItemsIDsForPhoto = array(); $arItemsInBasketIDs = array(); $oldSumAll = 0; while ($arItems = $dbBasketItems->GetNext()) { $arItemsIDsForPhoto[] = $arItems["PRODUCT_ID"]; $arItemsInBasketIDs[$arItems["PRODUCT_ID"]][] = $arItems["ID"]; $QUANTITY = 0; $arBasketItems['MAX_QUANTITY'] = $QUANTITY; $arItems['MAX_QUANTITY'] = $QUANTITY; $arItems["PROPS"] = Array(); if (in_array("PROPS", $arParams["COLUMNS_LIST"])) { $dbProp = CSaleBasket::GetPropsList(Array("SORT" => "ASC", "ID" => "ASC"), Array("BASKET_ID" => $arItems["ID"], "!CODE" => array("CATALOG.XML_ID", "PRODUCT.XML_ID"))); while ($arProp = $dbProp->GetNext()) $arItems["PROPS"][] = $arProp; } $arItems["PRICE_VAT_VALUE"] = (($arItems["PRICE"] / ($arItems["VAT_RATE"] + 1)) * $arItems["VAT_RATE"]); $arItems["PRICE_FORMATED"] = SaleFormatCurrency($arItems["PRICE"], $arItems["CURRENCY"]); $arItems["WEIGHT"] = DoubleVal($arItems["WEIGHT"]); $arItems["WEIGHT_FORMATED"] = roundEx(DoubleVal($arItems["WEIGHT"] / $arParams["WEIGHT_KOEF"]), SALE_VALUE_PRECISION) . " " . $arParams["WEIGHT_UNIT"]; if ($arItems["DELAY"] == "N" && $arItems["CAN_BUY"] == "Y") { $allSum += ($arItems["PRICE"] * $arItems["QUANTITY"]); $allWeight += ($arItems["WEIGHT"] * $arItems["QUANTITY"]); $allVATSum += roundEx($arItems["PRICE_VAT_VALUE"] * $arItems["QUANTITY"], SALE_VALUE_PRECISION); } if ($arItems["DELAY"] == "N" && $arItems["CAN_BUY"] == "Y") { //$oldSumAll = $oldSumAll + $bShowReady = True; if (DoubleVal($arItems["DISCOUNT_PRICE"]) > 0) { $arItems["DISCOUNT_PRICE_PERCENT"] = $arItems["DISCOUNT_PRICE"] * 100 / ($arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]); $arItems["DISCOUNT_PRICE_PERCENT_FORMATED"] = roundEx($arItems["DISCOUNT_PRICE_PERCENT"], SALE_VALUE_PRECISION) . "%"; $DISCOUNT_PRICE_ALL += $arItems["DISCOUNT_PRICE"] * $arItems["QUANTITY"]; $arItems["FULL_PRICE"] = $arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]; $arItems["FULL_PRICE_FORMATED"] = SaleFormatCurrency($arItems["FULL_PRICE"], $arItems["CURRENCY"]); } $arResult["ITEMS"]["AnDelCanBuy"][] = $arItems; } elseif ($arItems["DELAY"] == "Y" && $arItems["CAN_BUY"] == "Y") { $bShowDelay = True; if (DoubleVal($arItems["DISCOUNT_PRICE"]) > 0) { $arItems["DISCOUNT_PRICE_PERCENT"] = $arItems["DISCOUNT_PRICE"] * 100 / ($arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]); $arItems["DISCOUNT_PRICE_PERCENT_FORMATED"] = roundEx($arItems["DISCOUNT_PRICE_PERCENT"], SALE_VALUE_PRECISION) . "%"; $DISCOUNT_PRICE_ALL += $arItems["DISCOUNT_PRICE"] * $arItems["QUANTITY"]; $arItems["FULL_PRICE"] = $arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]; $arItems["FULL_PRICE_FORMATED"] = SaleFormatCurrency($arItems["FULL_PRICE"], $arItems["CURRENCY"]); } $arResult["ITEMS"]["DelDelCanBuy"][] = $arItems; } elseif ($arItems["CAN_BUY"] == "N" && $arItems["SUBSCRIBE"] == "Y") { $bShowSubscribe = True; if (DoubleVal($arItems["DISCOUNT_PRICE"]) > 0) { $arItems["DISCOUNT_PRICE_PERCENT"] = $arItems["DISCOUNT_PRICE"] * 100 / ($arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]); $arItems["DISCOUNT_PRICE_PERCENT_FORMATED"] = roundEx($arItems["DISCOUNT_PRICE_PERCENT"], SALE_VALUE_PRECISION) . "%"; $DISCOUNT_PRICE_ALL += $arItems["DISCOUNT_PRICE"] * $arItems["QUANTITY"]; $arItems["FULL_PRICE"] = $arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]; $arItems["FULL_PRICE_FORMATED"] = SaleFormatCurrency($arItems["FULL_PRICE"], $arItems["CURRENCY"]); } $arResult["ITEMS"]["ProdSubscribe"][] = $arItems; } else { $bShowNotAvail = True; if (DoubleVal($arItems["DISCOUNT_PRICE"]) > 0) { $arItems["DISCOUNT_PRICE_PERCENT"] = $arItems["DISCOUNT_PRICE"] * 100 / ($arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]); $arItems["DISCOUNT_PRICE_PERCENT_FORMATED"] = roundEx($arItems["DISCOUNT_PRICE_PERCENT"], SALE_VALUE_PRECISION) . "%"; $DISCOUNT_PRICE_ALL += $arItems["DISCOUNT_PRICE"] * $arItems["QUANTITY"]; $arItems["FULL_PRICE"] = $arItems["DISCOUNT_PRICE"] + $arItems["PRICE"]; $arItems["FULL_PRICE_FORMATED"] = SaleFormatCurrency($arItems["FULL_PRICE"], $arItems["CURRENCY"]); } $arResult["ITEMS"]["nAnCanBuy"][] = $arItems; } $allSumOld += ($arItems["FULL_PRICE"] * $arItems["QUANTITY"]); $arBasketItems[] = $arItems; } global $USER; $arOrder = array( 'SITE_ID' => SITE_ID, 'USER_ID' => $USER->GetID(), 'ORDER_PRICE' => $allSum, 'ORDER_WEIGHT' => $allWeight, 'BASKET_ITEMS' => $arResult["ITEMS"]["AnDelCanBuy"] ); $arOptions = array(); $arErrors = array(); //CSaleDiscount::DoProcessOrder($arOrder, $arOptions, $arErrors); /******************************/ $isOrderConverted = \Bitrix\Main\Config\Option::get("main", "~sale_converted_15", 'N'); $oldDelivery = ''; $checkIds = true; $arIDS = array(); if ($isOrderConverted == 'Y') { if (isset($arOrder['DELIVERY_ID']) && $arOrder['DELIVERY_ID'] != '') { $oldDelivery = $arOrder['DELIVERY_ID']; $arOrder['DELIVERY_ID'] = \CSaleDelivery::getIdByCode($arOrder['DELIVERY_ID']); } $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true); if ($adminSection) { $mode = Sale\Compatible\DiscountCompatibility::MODE_MANAGER; $modeParams = array(); if (isset($arOrder['CURRENCY'])) $modeParams['CURRENCY'] = $arOrder['CURRENCY']; if (isset($arOrder['SITE_ID'])) { $modeParams['SITE_ID'] = $arOrder['SITE_ID']; if (!isset($modeParams['CURRENCY'])) $modeParams['CURRENCY'] = Sale\Internals\SiteCurrencyTable::getSiteCurrency($modeParams['SITE_ID']); } } else { $mode = \Bitrix\Sale\Compatible\DiscountCompatibility::MODE_CLIENT; $modeParams = array( 'SITE_ID' => SITE_ID, 'CURRENCY' => \Bitrix\Sale\Internals\SiteCurrencyTable::getSiteCurrency(SITE_ID) ); $basketIdList = array(); foreach ($arOrder['BASKET_ITEMS'] as $basketId => $basketItem) { if (!isset($basketItem['PRODUCT_PRICE_ID']) && isset($basketItem['ID'])) { $basketIdList[$basketItem['ID']] = $basketId; } } unset($basketId, $basketItem); if (!empty($basketIdList)) { $iterator = \Bitrix\Sale\Internals\BasketTable::getList(array( 'select' => array('ID', 'PRODUCT_PRICE_ID'), 'filter' => array('@ID' => array_keys($basketIdList)) )); while ($row = $iterator->fetch()) { if (!isset($basketIdList[$row['ID']])) continue; $index = $basketIdList[$row['ID']]; $arOrder['BASKET_ITEMS'][$index]['PRODUCT_PRICE_ID'] = $row['PRODUCT_PRICE_ID']; unset($index); } unset($row, $iterator); } } unset($adminSection); if (!empty($modeParams)) { \Bitrix\Sale\Discount\Actions::setUseMode( \Bitrix\Sale\Discount\Actions::MODE_CALCULATE, array( 'USE_BASE_PRICE' => \Bitrix\Main\Config\Option::get('sale', 'get_discount_percent_from_base_price'), 'SITE_ID' => $modeParams['SITE_ID'], 'CURRENCY' => $modeParams['CURRENCY'] ) ); } if (!\Bitrix\Sale\Compatible\DiscountCompatibility::isInited()) { if (!empty($modeParams)) \Bitrix\Sale\Compatible\DiscountCompatibility::init($mode, $modeParams); } unset($modeParams, $mode); \Bitrix\Sale\Compatible\DiscountCompatibility::clearDiscountResult(); \Bitrix\Sale\Compatible\DiscountCompatibility::fillBasketData($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::calculateBasketDiscounts($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::roundPrices($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::setApplyMode($arOrder['BASKET_ITEMS']); $applyMode = \Bitrix\Sale\Discount::getApplyMode(); if ($applyMode == \Bitrix\Sale\Discount::APPLY_MODE_FULL_LAST || $applyMode == \Bitrix\Sale\Discount::APPLY_MODE_FULL_DISABLE) { foreach ($arOrder['BASKET_ITEMS'] as &$basketItem) { if (isset($basketItem['LAST_DISCOUNT']) && $basketItem['LAST_DISCOUNT'] == 'Y') { $checkIds = false; break; } } unset($basketItem); } } if ($checkIds) { $groupDiscountIterator = \Bitrix\Sale\Internals\DiscountGroupTable::getList(array( 'select' => array('DISCOUNT_ID'), 'filter' => array('@GROUP_ID' => CUser::GetUserGroup($arOrder['USER_ID']), '=ACTIVE' => 'Y') )); while ($groupDiscount = $groupDiscountIterator->fetch()) { $groupDiscount['DISCOUNT_ID'] = (int)$groupDiscount['DISCOUNT_ID']; if ($groupDiscount['DISCOUNT_ID'] > 0) $arIDS[$groupDiscount['DISCOUNT_ID']] = true; } } if (!empty($arIDS)) { $arIDS = array_keys($arIDS); $couponList = \Bitrix\Sale\DiscountCouponsManager::getForApply(array('MODULE_ID' => 'sale', 'DISCOUNT_ID' => $arIDS), array(), true); //TODO: fix this condition $useProps = true; $iblockPropList = array(); $entityList = \Bitrix\Sale\Internals\DiscountEntitiesTable::getByDiscount( $arIDS, array( '=MODULE_ID' => 'catalog', '=ENTITY' => 'ELEMENT_PROPERTY' ) ); if (empty($entityList)) { $useProps = false; } else { if (empty($entityList['catalog']['ELEMENT_PROPERTY'])) { $useProps = false; } else { foreach ($entityList['catalog']['ELEMENT_PROPERTY'] as $entity) { $entityField = explode(':', $entity['FIELD_TABLE']); if (isset($entityField[1])) { $propId = (int)$entityField[1]; if ($propId > 0) $iblockPropList[$propId] = $propId; unset($propId); } unset($entityField); } unset($entity); if (empty($iblockPropList)) $useProps = false; } } $arExtend = array( 'catalog' => array( 'fields' => true, 'props' => $useProps, ), ); if ($useProps) $arExtend['iblock']['props'] = $iblockPropList; unset($iblockPropList, $useProps); foreach (GetModuleEvents('sale', 'OnExtendBasketItems', true) as $arEvent) ExecuteModuleEventEx($arEvent, array(&$arOrder['BASKET_ITEMS'], $arExtend)); foreach ($arOrder['BASKET_ITEMS'] as &$arOneItem) { if ( array_key_exists('PRODUCT_PROVIDER_CLASS', $arOneItem) && empty($arOneItem['PRODUCT_PROVIDER_CLASS']) && array_key_exists('CALLBACK_FUNC', $arOneItem) && empty($arOneItem['CALLBACK_FUNC']) && (!isset($arOneItem['CUSTOM_PRICE']) || $arOneItem['CUSTOM_PRICE'] != 'Y') ) { if (isset($arOneItem['DISCOUNT_PRICE'])) { $arOneItem['PRICE'] += $arOneItem['DISCOUNT_PRICE']; $arOneItem['DISCOUNT_PRICE'] = 0; $arOneItem['BASE_PRICE'] = $arOneItem['PRICE']; } } } if (isset($arOneItem)) unset($arOneItem); $cacheDiscountHandlers = array(); $usedModules = array(); if (empty($cacheDiscountHandlers)) { $cacheDiscountHandlers = Helper::getDiscountHandlers($arIDS); } else { $needDiscountHandlers = array(); foreach ($arIDS as &$discountID) { if (!isset($cacheDiscountHandlers[$discountID])) $needDiscountHandlers[] = $discountID; } unset($discountID); if (!empty($needDiscountHandlers)) { $discountHandlersList = CSaleDiscount::getDiscountHandlers($needDiscountHandlers); if (!empty($discountHandlersList)) { foreach ($discountHandlersList as $discountID => $discountHandlers) { $cacheDiscountHandlers[$discountID] = $discountHandlers; } unset($discountHandlers, $discountID); } unset($discountHandlersList); } unset($needDiscountHandlers); } $currentDatetime = new Main\Type\DateTime(); $discountSelect = array( 'ID', 'PRIORITY', 'SORT', 'LAST_DISCOUNT', 'UNPACK', 'APPLICATION', 'USE_COUPONS', 'EXECUTE_MODULE', 'NAME', 'CONDITIONS_LIST', 'ACTIONS_LIST' ); $discountOrder = array('PRIORITY' => 'DESC', 'SORT' => 'ASC', 'ID' => 'ASC'); $discountFilter = array( '@ID' => $arIDS, '=LID' => $arOrder['SITE_ID'], array( 'LOGIC' => 'OR', 'ACTIVE_FROM' => '', '<=ACTIVE_FROM' => $currentDatetime ), array( 'LOGIC' => 'OR', 'ACTIVE_TO' => '', '>=ACTIVE_TO' => $currentDatetime ) ); if (empty($couponList)) { $discountFilter['=USE_COUPONS'] = 'N'; } else { $discountFilter[] = array( 'LOGIC' => 'OR', '=USE_COUPONS' => 'N', array( '=USE_COUPONS' => 'Y', '=COUPON.COUPON' => array_keys($couponList) ) ); $discountSelect['DISCOUNT_COUPON'] = 'COUPON.COUPON'; } $discountIterator = \Bitrix\Sale\Internals\DiscountTable::getList(array( 'select' => $discountSelect, 'filter' => $discountFilter, 'order' => $discountOrder )); $discountApply = array(); $resultDiscountFullList = array(); $resultDiscountList = array(); $resultDiscountKeys = array(); $resultDiscountIndex = 0; $discountSale = 0; //скидка правила работы с корзиной while ($discount = $discountIterator->fetch()) { $discount['ID'] = (int)$discount['ID']; if (isset($discountApply[$discount['ID']])) continue; $discount['MODULE'] = 'sale'; $discount['MODULE_ID'] = 'sale'; if ($discount['USE_COUPONS'] == 'Y') $discount['COUPON'] = $couponList[$discount['DISCOUNT_COUPON']]; $discountApply[$discount['ID']] = true; $applyFlag = true; if (isset($cacheDiscountHandlers[$discount['ID']])) { $moduleList = $cacheDiscountHandlers[$discount['ID']]['MODULES']; if (!empty($moduleList)) { foreach ($moduleList as &$moduleID) { if (!isset($usedModules[$moduleID])) { $usedModules[$moduleID] = Bitrix\Main\Loader::includeModule($moduleID); } if (!$usedModules[$moduleID]) { $applyFlag = false; break; } } unset($moduleID); if ($applyFlag) $discount['MODULES'] = $moduleList; } unset($moduleList); } if ($isOrderConverted == 'Y') Bitrix\Sale\Compatible\DiscountCompatibility::setOrderData($arOrder); if ($applyFlag && Helper::__Unpack($arOrder, $discount['UNPACK'])) { $oldOrder = $arOrder; if ($isOrderConverted == 'Y') Bitrix\Sale\Discount\Actions::clearAction(); Helper::__ApplyActions($arOrder, $discount['APPLICATION']); if ($isOrderConverted == 'Y') { if($discountSale == 0 && $discount["ACTIONS_LIST"]["CHILDREN"][0]["CLASS_ID"] == "ActSaleBsktGrp" && $discount["ACTIONS_LIST"]["CHILDREN"][0]["DATA"]["Unit"] == "Perc"){ $discountSale = $discount["ACTIONS_LIST"]["CHILDREN"][0]["DATA"]["Value"]; } //////////////////////////// $resultDiscountFullList[] = $discount; if (Bitrix\Sale\Compatible\DiscountCompatibility::calculateSaleDiscount($arOrder, $discount)) { $resultDiscountList[$resultDiscountIndex] = array( 'MODULE_ID' => $discount['MODULE_ID'], 'ID' => $discount['ID'], 'NAME' => $discount['NAME'], 'PRIORITY' => $discount['PRIORITY'], 'SORT' => $discount['SORT'], 'LAST_DISCOUNT' => $discount['LAST_DISCOUNT'], 'CONDITIONS' => serialize($discount['CONDITIONS_LIST']), 'UNPACK' => $discount['UNPACK'], 'ACTIONS' => serialize($discount['ACTIONS_LIST']), 'APPLICATION' => $discount['APPLICATION'], 'RESULT' => Helper::getDiscountResult($oldOrder, $arOrder, false), 'HANDLERS' => $cacheDiscountHandlers[$discount['ID']], 'USE_COUPONS' => $discount['USE_COUPONS'], 'COUPON' => ($discount['USE_COUPONS'] == 'Y' ? $couponList[$discount['DISCOUNT_COUPON']] : false) ); $resultDiscountKeys[$discount['ID']] = $resultDiscountIndex; $resultDiscountIndex++; if ($discount['LAST_DISCOUNT'] == 'Y') break; } Sale\Discount\Actions::clearAction(); //////////////////////////// } else { $discountResult = Helper::getDiscountResult($oldOrder, $arOrder, false); if (!empty($discountResult['DELIVERY']) || !empty($discountResult['BASKET'])) { if ($discount['USE_COUPONS'] == 'Y' && !empty($discount['DISCOUNT_COUPON'])) { if ($couponList[$discount['DISCOUNT_COUPON']]['TYPE'] == Sale\Internals\DiscountCouponTable::TYPE_BASKET_ROW) self::changeDiscountResult($oldOrder, $arOrder, $discountResult); $couponApply = Sale\DiscountCouponsManager::setApply($discount['DISCOUNT_COUPON'], $discountResult); unset($couponApply); } $resultDiscountList[$resultDiscountIndex] = array( 'MODULE_ID' => $discount['MODULE_ID'], 'ID' => $discount['ID'], 'NAME' => $discount['NAME'], 'PRIORITY' => $discount['PRIORITY'], 'SORT' => $discount['SORT'], 'LAST_DISCOUNT' => $discount['LAST_DISCOUNT'], 'CONDITIONS' => serialize($discount['CONDITIONS_LIST']), 'UNPACK' => $discount['UNPACK'], 'ACTIONS' => serialize($discount['ACTIONS_LIST']), 'APPLICATION' => $discount['APPLICATION'], 'RESULT' => $discountResult, 'HANDLERS' => self::$cacheDiscountHandlers[$discount['ID']], 'USE_COUPONS' => $discount['USE_COUPONS'], 'COUPON' => ($discount['USE_COUPONS'] == 'Y' ? $couponList[$discount['DISCOUNT_COUPON']] : false) ); $resultDiscountKeys[$discount['ID']] = $resultDiscountIndex; $resultDiscountIndex++; if ($discount['LAST_DISCOUNT'] == 'Y') break; } unset($discountResult); } } } unset($discount, $discountIterator); $arOrder['DISCOUNT_LIST'] = $resultDiscountList; $arOrder['FULL_DISCOUNT_LIST'] = $resultDiscountFullList; if ($isOrderConverted == 'Y') \Bitrix\Sale\Compatible\DiscountCompatibility::setOldDiscountResult($resultDiscountList); } $arOrder["ORDER_PRICE"] = 0; $arOrder["ORDER_WEIGHT"] = 0; $arOrder["USE_VAT"] = false; $arOrder["VAT_RATE"] = 0; $arOrder["VAT_SUM"] = 0; $arOrder["DISCOUNT_PRICE"] = 0.0; $arOrder["DISCOUNT_VALUE"] = $arOrder["DISCOUNT_PRICE"]; $arOrder["PRICE_DELIVERY"] = roundEx($arOrder["PRICE_DELIVERY"], SALE_VALUE_PRECISION); $arOrder["DELIVERY_PRICE"] = $arOrder["PRICE_DELIVERY"]; foreach ($arOrder['BASKET_ITEMS'] as &$arShoppingCartItem) { if (isset($arShoppingCartItem['CATALOG'])) unset($arShoppingCartItem['CATALOG']); if (!CSaleBasketHelper::isSetItem($arShoppingCartItem)) { $customPrice = isset($arShoppingCartItem['CUSTOM_PRICE']) && $arShoppingCartItem['CUSTOM_PRICE'] = 'Y'; if (!$customPrice) { $arShoppingCartItem['DISCOUNT_PRICE'] = roundEx($arShoppingCartItem['DISCOUNT_PRICE'], SALE_VALUE_PRECISION); if ($arShoppingCartItem['DISCOUNT_PRICE'] > 0) $arShoppingCartItem['PRICE'] = $arShoppingCartItem['BASE_PRICE'] - $arShoppingCartItem['DISCOUNT_PRICE']; else $arShoppingCartItem['PRICE'] = roundEx($arShoppingCartItem['PRICE'], SALE_VALUE_PRECISION); } else { $arShoppingCartItem['DISCOUNT_PRICE'] = 0; } if (isset($arShoppingCartItem['VAT_RATE'])) { $vatRate = (float)$arShoppingCartItem['VAT_RATE']; if ($vatRate > 0) $arShoppingCartItem['VAT_VALUE'] = (($arShoppingCartItem['PRICE'] / ($vatRate + 1)) * $vatRate); unset($vatRate); } $arOrder["ORDER_PRICE"] += $arShoppingCartItem["PRICE"] * $arShoppingCartItem["QUANTITY"]; $arOrder["ORDER_WEIGHT"] += $arShoppingCartItem["WEIGHT"] * $arShoppingCartItem["QUANTITY"]; $arShoppingCartItem["PRICE_FORMATED"] = CCurrencyLang::CurrencyFormat($arShoppingCartItem["PRICE"], $arShoppingCartItem["CURRENCY"], true); $arShoppingCartItem["DISCOUNT_PRICE_PERCENT"] = 0; if ($arShoppingCartItem["DISCOUNT_PRICE"] + $arShoppingCartItem["PRICE"] > 0) $arShoppingCartItem["DISCOUNT_PRICE_PERCENT"] = $arShoppingCartItem["DISCOUNT_PRICE"]*100 / ($arShoppingCartItem["DISCOUNT_PRICE"] + $arShoppingCartItem["PRICE"]); $arShoppingCartItem["DISCOUNT_PRICE_PERCENT_FORMATED"] = roundEx($arShoppingCartItem["DISCOUNT_PRICE_PERCENT"], SALE_VALUE_PRECISION)."%"; if ($arShoppingCartItem["VAT_RATE"] > 0) { $arOrder["USE_VAT"] = true; if ($arShoppingCartItem["VAT_RATE"] > $arOrder["VAT_RATE"]) $arOrder["VAT_RATE"] = $arShoppingCartItem["VAT_RATE"]; $arOrder["VAT_SUM"] += $arShoppingCartItem["VAT_VALUE"] * $arShoppingCartItem["QUANTITY"]; } } } unset($arShoppingCartItem); if ($isOrderConverted == 'Y' && $oldDelivery != '') $arOrder['DELIVERY_ID'] = $oldDelivery; $arOrder["ORDER_PRICE"] = roundEx($arOrder["ORDER_PRICE"], SALE_VALUE_PRECISION); /******************************/ //узнать сумму товаров в корзине с учётом собственных скидок товаров CModule::IncludeModule("sale"); $dbBasketItems = CSaleBasket::GetList( array( "NAME" => "ASC", "ID" => "ASC" ), array( "FUSER_ID" => CSaleBasket::GetBasketUserID(), "LID" => SITE_ID, "ORDER_ID" => "NULL" ), false, false, array("ID", "NAME", "CALLBACK_FUNC", "MODULE", "PRODUCT_ID", "QUANTITY", "DELAY", "CAN_BUY", "PRICE", "WEIGHT", "DETAIL_PAGE_URL", "NOTES", "CURRENCY", "VAT_RATE", "CATALOG_XML_ID", "PRODUCT_XML_ID", "SUBSCRIBE", "DISCOUNT_PRICE", "PRODUCT_PROVIDER_CLASS") ); $sumAll = 0; $discountOne = 0; while ($arItems = $dbBasketItems->GetNext()) { $sumAll += ($arItems["PRICE"] * $arItems["QUANTITY"]); } if (!empty($arFields)) { $maxTmp = 0; foreach ($arFields as $key => $value) { if ($value["VALUE"] > $maxTmp) { $maxTmp = $value["VALUE"]; } } //собственная скидка товара $currentDiscount = intval($maxTmp); if($discountSale > $currentDiscount){ $arFields=array(); // если скидка правила работы с корзиной больше //собственной скидки товара, то убиваем собственную скидку товара } } //======================== } |
Кастомизирован компонент корзины, файл component.php
Находим строку
CSaleDiscount::DoProcessOrder($arOrder, $arOptions, $arErrors); |
$couponDiscount = 0; //узнать параметры применённого купона $couponList = \Bitrix\Sale\DiscountCouponsManager::get( true, array(), false, false ); if(!empty($couponList)){ $arCouponDiscount = CCatalogDiscount::GetByID(current($couponList)["DISCOUNT_ID"]); $couponDiscount = intval($arCouponDiscount["VALUE"]);//скидка по купону //достать скидку правила работы с корзиной //$arDiscountsSaleTmp = \Bitrix\Sale\Internals\DiscountTable::getList(); /******************************/ $isOrderConverted = \Bitrix\Main\Config\Option::get("main", "~sale_converted_15", 'N'); $oldDelivery = ''; $checkIds = true; $arIDS = array(); if ($isOrderConverted == 'Y') { if (isset($arOrder['DELIVERY_ID']) && $arOrder['DELIVERY_ID'] != '') { $oldDelivery = $arOrder['DELIVERY_ID']; $arOrder['DELIVERY_ID'] = \CSaleDelivery::getIdByCode($arOrder['DELIVERY_ID']); } $adminSection = (defined('ADMIN_SECTION') && ADMIN_SECTION === true); if ($adminSection) { $mode = Sale\Compatible\DiscountCompatibility::MODE_MANAGER; $modeParams = array(); if (isset($arOrder['CURRENCY'])) $modeParams['CURRENCY'] = $arOrder['CURRENCY']; if (isset($arOrder['SITE_ID'])) { $modeParams['SITE_ID'] = $arOrder['SITE_ID']; if (!isset($modeParams['CURRENCY'])) $modeParams['CURRENCY'] = Sale\Internals\SiteCurrencyTable::getSiteCurrency($modeParams['SITE_ID']); } } else { $mode = \Bitrix\Sale\Compatible\DiscountCompatibility::MODE_CLIENT; $modeParams = array( 'SITE_ID' => SITE_ID, 'CURRENCY' => \Bitrix\Sale\Internals\SiteCurrencyTable::getSiteCurrency(SITE_ID) ); $basketIdList = array(); foreach ($arOrder['BASKET_ITEMS'] as $basketId => $basketItem) { if (!isset($basketItem['PRODUCT_PRICE_ID']) && isset($basketItem['ID'])) { $basketIdList[$basketItem['ID']] = $basketId; } } unset($basketId, $basketItem); if (!empty($basketIdList)) { $iterator = \Bitrix\Sale\Internals\BasketTable::getList(array( 'select' => array('ID', 'PRODUCT_PRICE_ID'), 'filter' => array('@ID' => array_keys($basketIdList)) )); while ($row = $iterator->fetch()) { if (!isset($basketIdList[$row['ID']])) continue; $index = $basketIdList[$row['ID']]; $arOrder['BASKET_ITEMS'][$index]['PRODUCT_PRICE_ID'] = $row['PRODUCT_PRICE_ID']; unset($index); } unset($row, $iterator); } } unset($adminSection); if (!empty($modeParams)) { \Bitrix\Sale\Discount\Actions::setUseMode( \Bitrix\Sale\Discount\Actions::MODE_CALCULATE, array( 'USE_BASE_PRICE' => \Bitrix\Main\Config\Option::get('sale', 'get_discount_percent_from_base_price'), 'SITE_ID' => $modeParams['SITE_ID'], 'CURRENCY' => $modeParams['CURRENCY'] ) ); } if (!\Bitrix\Sale\Compatible\DiscountCompatibility::isInited()) { if (!empty($modeParams)) \Bitrix\Sale\Compatible\DiscountCompatibility::init($mode, $modeParams); } unset($modeParams, $mode); \Bitrix\Sale\Compatible\DiscountCompatibility::clearDiscountResult(); \Bitrix\Sale\Compatible\DiscountCompatibility::fillBasketData($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::calculateBasketDiscounts($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::roundPrices($arOrder['BASKET_ITEMS']); \Bitrix\Sale\Compatible\DiscountCompatibility::setApplyMode($arOrder['BASKET_ITEMS']); $applyMode = \Bitrix\Sale\Discount::getApplyMode(); if ($applyMode == \Bitrix\Sale\Discount::APPLY_MODE_FULL_LAST || $applyMode == \Bitrix\Sale\Discount::APPLY_MODE_FULL_DISABLE) { foreach ($arOrder['BASKET_ITEMS'] as &$basketItem) { if (isset($basketItem['LAST_DISCOUNT']) && $basketItem['LAST_DISCOUNT'] == 'Y') { $checkIds = false; break; } } unset($basketItem); } } if ($checkIds) { $groupDiscountIterator = \Bitrix\Sale\Internals\DiscountGroupTable::getList(array( 'select' => array('DISCOUNT_ID'), 'filter' => array('@GROUP_ID' => CUser::GetUserGroup($arOrder['USER_ID']), '=ACTIVE' => 'Y') )); while ($groupDiscount = $groupDiscountIterator->fetch()) { $groupDiscount['DISCOUNT_ID'] = (int)$groupDiscount['DISCOUNT_ID']; if ($groupDiscount['DISCOUNT_ID'] > 0) $arIDS[$groupDiscount['DISCOUNT_ID']] = true; } } if (!empty($arIDS)) { $arIDS = array_keys($arIDS); $couponList = \Bitrix\Sale\DiscountCouponsManager::getForApply(array('MODULE_ID' => 'sale', 'DISCOUNT_ID' => $arIDS), array(), true); //TODO: fix this condition $useProps = true; $iblockPropList = array(); $entityList = \Bitrix\Sale\Internals\DiscountEntitiesTable::getByDiscount( $arIDS, array( '=MODULE_ID' => 'catalog', '=ENTITY' => 'ELEMENT_PROPERTY' ) ); if (empty($entityList)) { $useProps = false; } else { if (empty($entityList['catalog']['ELEMENT_PROPERTY'])) { $useProps = false; } else { foreach ($entityList['catalog']['ELEMENT_PROPERTY'] as $entity) { $entityField = explode(':', $entity['FIELD_TABLE']); if (isset($entityField[1])) { $propId = (int)$entityField[1]; if ($propId > 0) $iblockPropList[$propId] = $propId; unset($propId); } unset($entityField); } unset($entity); if (empty($iblockPropList)) $useProps = false; } } $arExtend = array( 'catalog' => array( 'fields' => true, 'props' => $useProps, ), ); if ($useProps) $arExtend['iblock']['props'] = $iblockPropList; unset($iblockPropList, $useProps); foreach (GetModuleEvents('sale', 'OnExtendBasketItems', true) as $arEvent) ExecuteModuleEventEx($arEvent, array(&$arOrder['BASKET_ITEMS'], $arExtend)); if (isset($arOneItem)) unset($arOneItem); $cacheDiscountHandlers = array(); $usedModules = array(); if (empty($cacheDiscountHandlers)) { $cacheDiscountHandlers = Helper::getDiscountHandlers($arIDS); } else { $needDiscountHandlers = array(); foreach ($arIDS as &$discountID) { if (!isset($cacheDiscountHandlers[$discountID])) $needDiscountHandlers[] = $discountID; } unset($discountID); if (!empty($needDiscountHandlers)) { $discountHandlersList = CSaleDiscount::getDiscountHandlers($needDiscountHandlers); if (!empty($discountHandlersList)) { foreach ($discountHandlersList as $discountID => $discountHandlers) { $cacheDiscountHandlers[$discountID] = $discountHandlers; } unset($discountHandlers, $discountID); } unset($discountHandlersList); } unset($needDiscountHandlers); } $currentDatetime = new Bitrix\Main\Type\DateTime(); $discountSelect = array( 'ID', 'PRIORITY', 'SORT', 'LAST_DISCOUNT', 'UNPACK', 'APPLICATION', 'USE_COUPONS', 'EXECUTE_MODULE', 'NAME', 'CONDITIONS_LIST', 'ACTIONS_LIST' ); $discountOrder = array('PRIORITY' => 'DESC', 'SORT' => 'ASC', 'ID' => 'ASC'); $discountFilter = array( '@ID' => $arIDS, '=LID' => $arOrder['SITE_ID'], array( 'LOGIC' => 'OR', 'ACTIVE_FROM' => '', '<=ACTIVE_FROM' => $currentDatetime ), array( 'LOGIC' => 'OR', 'ACTIVE_TO' => '', '>=ACTIVE_TO' => $currentDatetime ) ); if (empty($couponList)) { $discountFilter['=USE_COUPONS'] = 'N'; } else { $discountFilter[] = array( 'LOGIC' => 'OR', '=USE_COUPONS' => 'N', array( '=USE_COUPONS' => 'Y', '=COUPON.COUPON' => array_keys($couponList) ) ); $discountSelect['DISCOUNT_COUPON'] = 'COUPON.COUPON'; } $discountIterator = \Bitrix\Sale\Internals\DiscountTable::getList(array( 'select' => $discountSelect, 'filter' => $discountFilter, 'order' => $discountOrder )); $discountApply = array(); $resultDiscountFullList = array(); $resultDiscountList = array(); $resultDiscountKeys = array(); $resultDiscountIndex = 0; $discountSale = 0; //скидка правила работы с корзиной while ($discount = $discountIterator->fetch()) { $discount['ID'] = (int)$discount['ID']; if (isset($discountApply[$discount['ID']])) continue; $discount['MODULE'] = 'sale'; $discount['MODULE_ID'] = 'sale'; if ($discount['USE_COUPONS'] == 'Y') $discount['COUPON'] = $couponList[$discount['DISCOUNT_COUPON']]; $discountApply[$discount['ID']] = true; $applyFlag = true; if (isset($cacheDiscountHandlers[$discount['ID']])) { $moduleList = $cacheDiscountHandlers[$discount['ID']]['MODULES']; if (!empty($moduleList)) { foreach ($moduleList as &$moduleID) { if (!isset($usedModules[$moduleID])) { $usedModules[$moduleID] = Bitrix\Main\Loader::includeModule($moduleID); } if (!$usedModules[$moduleID]) { $applyFlag = false; break; } } unset($moduleID); if ($applyFlag) $discount['MODULES'] = $moduleList; } unset($moduleList); } if ($isOrderConverted == 'Y') Bitrix\Sale\Compatible\DiscountCompatibility::setOrderData($arOrder); if ($applyFlag && Helper::__Unpack($arOrder, $discount['UNPACK'])) { $oldOrder = $arOrder; if ($isOrderConverted == 'Y') { if($discountSale == 0 && $discount["ACTIONS_LIST"]["CHILDREN"][0]["CLASS_ID"] == "ActSaleBsktGrp" && $discount["ACTIONS_LIST"]["CHILDREN"][0]["DATA"]["Unit"] == "Perc"){ $discountSale = $discount["ACTIONS_LIST"]["CHILDREN"][0]["DATA"]["Value"]; } } else { $discountResult = Helper::getDiscountResult($oldOrder, $arOrder, false); if (!empty($discountResult['DELIVERY']) || !empty($discountResult['BASKET'])) { if ($discount['USE_COUPONS'] == 'Y' && !empty($discount['DISCOUNT_COUPON'])) { if ($couponList[$discount['DISCOUNT_COUPON']]['TYPE'] == Sale\Internals\DiscountCouponTable::TYPE_BASKET_ROW) self::changeDiscountResult($oldOrder, $arOrder, $discountResult); $couponApply = Sale\DiscountCouponsManager::setApply($discount['DISCOUNT_COUPON'], $discountResult); unset($couponApply); } $resultDiscountList[$resultDiscountIndex] = array( 'MODULE_ID' => $discount['MODULE_ID'], 'ID' => $discount['ID'], 'NAME' => $discount['NAME'], 'PRIORITY' => $discount['PRIORITY'], 'SORT' => $discount['SORT'], 'LAST_DISCOUNT' => $discount['LAST_DISCOUNT'], 'CONDITIONS' => serialize($discount['CONDITIONS_LIST']), 'UNPACK' => $discount['UNPACK'], 'ACTIONS' => serialize($discount['ACTIONS_LIST']), 'APPLICATION' => $discount['APPLICATION'], 'RESULT' => $discountResult, 'HANDLERS' => self::$cacheDiscountHandlers[$discount['ID']], 'USE_COUPONS' => $discount['USE_COUPONS'], 'COUPON' => ($discount['USE_COUPONS'] == 'Y' ? $couponList[$discount['DISCOUNT_COUPON']] : false) ); $resultDiscountKeys[$discount['ID']] = $resultDiscountIndex; $resultDiscountIndex++; if ($discount['LAST_DISCOUNT'] == 'Y') break; } unset($discountResult); } } } unset($discount, $discountIterator); $arOrder['DISCOUNT_LIST'] = $resultDiscountList; $arOrder['FULL_DISCOUNT_LIST'] = $resultDiscountFullList; if ($isOrderConverted == 'Y') \Bitrix\Sale\Compatible\DiscountCompatibility::setOldDiscountResult($resultDiscountList); } /******************************/ //если скидка купона меньше скидки правила работы с корзиной, то применяем скидку правила if($couponDiscount < $discountSale){ CSaleDiscount::DoProcessOrder($arOrder, $arOptions, $arErrors); } }else{ CSaleDiscount::DoProcessOrder($arOrder, $arOptions, $arErrors); } |
В цикл
foreach ($arOrder['BASKET_ITEMS'] as &$arOneItem) |
//определить, какие товары имеют собственную скидку $arDiscounts = CCatalogDiscount::GetDiscountByProduct( $arOneItem["PRODUCT_ID"], $USER->GetUserGroupArray(), "N", 2, SITE_ID ); $mxResult = CCatalogSku::GetProductInfo( $arOneItem["PRODUCT_ID"] ); if (is_array($mxResult)){ //торговое предложение $PRODUCT_ID = $mxResult['ID']; }else{ //не торговое предложение $PRODUCT_ID = $arOneItem["PRODUCT_ID"]; } if(!empty($arDiscounts)){ $maxTmp = 0; foreach($arDiscounts as $key => $value){ if($value["VALUE"] > $maxTmp){ $maxTmp = $value["VALUE"]; } } CIBlockElement::SetPropertyValueCode($PRODUCT_ID, "IS_SALE", intval($maxTmp)); }else{ CIBlockElement::SetPropertyValueCode($PRODUCT_ID, "IS_SALE", "0"); } |
Собственно, усё.