Выбрать курс
0  /  8

Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках

Примеры

В данной главе приведены наиболее яркие, распространенные примеры использования модуля.

Нарастающие скидки

Один из наиболее частых случаев применения модуля - реализация механизма Нарастающие скидки.

Решение данного примера вынесено в отдельный модуль, с возможностями которого можно познакомиться на странице модуля Покупай больше - плати меньше. Документация по модулю доступна на странице..

Применение скидки / наценки при выборе точек самовывоза в форме заказа

Механизм правил работы с корзиной в 1С-Битрикс дает возможность представлять скидки в зависимости от многих условий, таких как: тип плательщика, служба доставки, платежные системы и т.п. Но нет зависимости от выбранной точки самовывоза (склада). Но, как показывает практика, подобное очень часто требуется.

Внимание: Решение задачи рассмотрено на примере дефолтного шаблона компонента sale.order.ajax. В кастомизированных шаблонах решение некоторых моментов может отличаться.

В статье приведены фрагменты кода, описывающие принцип реализации, а не полностью рабочий продукт. Для внедрения рекомендуется обратиться к разработчикам.

Например, имеется 2 склада:

  • Основной склад. (идентификатор склада равно 2)
  • Дополнительный склад. (идентификатор склада равно 3)
Склады

Требуется применить скидку на товары 10%, если покупатель выбрал точку самовывоза Основной склад.

Для этого необходимо:

1) Создать соответствующее правило работы с корзиной.

Скидка 10%

Скидка 10% - условия применения

Как видно из скриншота, в качестве PHP-выражения указана обычная функция __scoder_check_stores(). Данная функция будет описана в файле init.php, к ней мы еще вернемся

2) Необходимо определить, какая на данный момент выбрана точка самовывоза. Для этого понадобится обработчик события OnSaleComponentOrderUserResult, подробнее в документации.

AddEventHandler("sale", "OnSaleComponentOrderUserResult", Array("CScoderClass", "OnSaleComponentOrderUserResultHandler"));
class CScoderClass
{
   const STORE_ID = 2;		//Ид точки самовывоза - Основной склад
   function OnSaleComponentOrderUserResultHandler(
	&$arUserResult,
	$request,
	&$arParams
   )
   {
         //если выбрана точка самовывоза - Основной склад
         if ($arUserResult['BUYER_STORE']==self::STORE_ID)
         {
               $_SESSION['IS_DISCOUNT'] = true;
         }
         else
         {
               $_SESSION['IS_DISCOUNT'] = false;
         }
   }
}

С помощью данного обработчика меняем признак сессии в зависимости от того какую точку самовывоза выбрал покупатель.

3. Описываем PHP-выражение - функцию __scoder_check_stores().

function __scoder_check_stores()
{
   $btrue = false;
   if ($_SESSION['IS_DISCOUNT']===true)
      $btrue = true;
   
   return $btrue;
}

Данная функция проверяет признак сессии и возвращает истину (true) или ложь (false), т.е. применять или нет скидку.

4) Остается доработать шаблон компонента формы заказа sale.order.ajax, чтобы покупатель видел скидку сразу после выбора точки самовывоза, предварительно скопировав шаблон.

По умолчанию, при выборе точки самовывоза пересчета в форме заказа не происходит, как это, например происходит при смене местоположения или службы, доставки. Поэтому покупатель при выборе точки самовывоза не сразу увидит скидку.

Чтобы это изменить, необходимо внести изменения в js-код формы заказа - в order_ajax.js. Пересчет осуществляется командой BX.Sale.OrderAjaxComponent.sendRequest(). Вызов данной команды необходимо разместить в 2-х местах.

В методе editFadePickUpContent.и в методе initMaps

phpcondition_js1.jpg

phpcondition_js2.jpg

Если вы сделали все правильно, то настроенная вами скидка должна применяться при выборе нужной точки самовывоза.

Один купон на разные скидки

Внимание: В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения рекомендуется обратиться к разработчикам.

В зависимости от задач функция может быть более сложной. В данном материале описан принцип реализации.

Вопрос предоставления разной величины скидки с учетом стоимости всех товаров в корзине по одному купону не так тривиален.

Например, необходимо решить следующую задачу:

  • 5% - при сумме от 500 руб. до 1 000 руб.
  • 10% - при сумме от 1 000 руб. до 5 000 руб.
  • 15% - при сумме выше 5 000 руб.

Эта задача легко бы решалась, если не одно НО: они должны применяться только при активации купона, причем один купон на всех.

Для решения данной задачи необходимо создать 3 правила работы с корзиной с самими скидками (т.к. в нашем примере их 3) и еще одно правило под купон (назовем его служебным), последнее правило с нулевой скидкой.

В условиях 3-х правил указываем необходимые интервалы применения, а также дополнительное PHP-условие, которое будут доступно после установки модуля. В значении условия укажем функцию, например с названием __check_coupon(), к данной функции мы еще вернемся. Произвольное PHP условие правил работы с корзиной

Выглядит все это следующим образом:

I. Скидка 5%

Один купон на все скидки - 5%

II. Скидка 10%

Один купон на все скидки - 10%

III. Скидка 15%

Один купон на все скидки - 15%

Также создаем 4-е (служебное) правило, скидку для неё указываем 0% и создаем нужный нам купон, например, SL-WI8UK-QU3ENAW

Служебное правило с купоном

Остается прописать функцию __check_coupon() в файле init.php. В данной функции будем проверять применен или нет наш купон и возвращать истину (true) или ложь (false).

function __check_coupon()
{
	$btrue = false;
	$coupon = "SL-WI8UK-QU3ENAW";	//TODO код купона
	
	//Проверяем примененные купону для текучего пользователя
	$arCoupons = Bitrix\Sale\DiscountCouponsManager::get(true, array(), true, true);
	if (is_array($arCoupons) && array_key_exists($coupon, $arCoupons))
		$btrue = true;
	
	return $btrue;
}

Одноразовый многоразовый купон

В 1С-Битрикс имеются как одноразовые купоны, так и многоразовые.

Как быть, если многоразовый купон необходимо сделать одноразовым? Как это понимать?

Очень просто, бывает стоит задача создать какой то один купон с кодом, например СКИДКА и ограничить его использование одним и тем же пользователем много раз. Т.е. если покупатель уже воспользовался купоном и создал заказ, то чтобы более не мог его использовать. Деактивировать купон или сделать его одноразовым нельзя, т.к. этим же купоном должны иметь возможность воспользоваться другие покупатели.

Внимание: В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения рекомендуется обратиться к разработчикам.

В зависимости от задач функция может быть более сложной. В данном материале описан принцип реализации.

Конечно, задачу можно решить не одним способом. Но мы предлагаем воспользоваться модулем Произвольное PHP условие правил работы с корзиной.

Принцип реализации не сложен. Также необходимо создать правило для работы с корзиной, создать нужный многоразовый купон. На дополнительных условиях правила прописать произвольную PHP-функцию, которая будет проверять совершал ли покупатель с данным купоном заказ или нет, и возвращать ИСТИНУ (true) или ЛОЖЬ (false). Например функция __sc_check_multi_coupon() (это всего лишь пример, как может выглядеть функция, для внедрения рекомендуется обратиться к разработчикам.):

function __sc_check_multi_coupon()
{
	$btrue = true;
	$ar_orders = array();
	$arFilter = Array(
	   "USER_ID" => $GLOBALS["USER"]->GetID(),
	   "!CANCELED" => "Y",
	);

	$db_sales = CSaleOrder::GetList(
		array("DATE_INSERT" => "desc"), 
		$arFilter,
		false,
		false,
		array("ID")
	);
	while ($ar_sales = $db_sales->Fetch())
	{
		$ar_orders[] = $ar_sales["ID"];
		
	}
	if (count($ar_orders)>0)
	{
		$str_coupons = "КУПОН1,КУПОН2,...,КУПОНN";		//TODO заменить на свои купоны
		$ar = explode(",",$str_coupons);
		if (is_array($ar) && count($ar)>0)
		{
			$ar_coupons = array();
			foreach ($ar as $key => $coupon)
			{
				if (strlen(trim($coupon))>0)
					$ar_coupons[] = trim($coupon);
			}
		}
		if (is_array($ar_coupons))
		{
			$couponList = \Bitrix\Sale\Internals\OrderCouponsTable::getList(array(
				'select' => array('COUPON'),
				'filter' => array(
					'=ORDER_ID' => $ar_orders,
					'COUPON' => $ar_coupons
				)
			));
			if ($coupon = $couponList->fetch())
			{
				$btrue = false;
			}
		}
	}
	
	return $btrue;
}

Как применить наиболее или наименее выгодную скидку в корзине в интернет-магазине на ЦМС 1С-Битрикс

В данной статье рассмотрены ряд важных тем:

  • Как вернуть примененные правила работы в текущей корзине покупателя с помощью АПИ.
  • Как рассчитать итоговую сумму корзины покупателя по отдельности по каждому правилу с помощью АПИ.
  • Как применить только одну наиболее выгодную скидку из актуальных правил работы с корзиной.
Внимание! Рассмотрены такие действия, как скидка в процентах (%) и фиксированные скидки; подарки НЕ взяты в расчёт.

В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрение описанного подхода потребуется помощь программистов.

Мы также можем в этом помощь, но за отдельное вознаграждение. Обратиться за помощью можно в наш чат в правом нижнем углу нашего сайта (в рабочее время отвечаем достаточно оперативно). Но оставляем за собой право отказать в доработке без объяснения причин.

Для реализации наиболее или наименее выгодной скидки с помощью правил потребуется в помочь наш модуль - Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках.

Шаги следующие:

  1. С помощью АПИ 1С-Битрикс до вывода корзины (подключения компонента bitrix:sale.basket.basket) определяем какие правила работы применились для текущего покупателя.
  2. Проходим по найденным правилам работы с корзиной и применяем каждое по отдельности. Таким образом определяем сумму корзины на случай, если бы каждое правило применилось по отдельности.
  3. Находим правило с наибольшей (с максимальной выгодой для покупателя) сумме или наименьшей (менее всего выгодной для покупателя), в зависимости от требуемого. Записываем ИД найденного правила в глобальную переменную для дальнейшего использования в модуле. В нашем примере ниже рассмотрен случай с максимальной выгодой.
  4. С помощью модуля Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках отменяем (отсекаем) правила, которые НЕ нужно применять.

Ниже рассмотрен код, который необходимо разместить в init.php. В коде есть множество коммитов, которые отражает техническую сторону реализации и будет очень полезно для ознакомления разработчикам.

AddEventHandler("main", "OnBeforeProlog", Array("CDev", "OnBeforePrologHandler"));
class CDev
{
	//вспомогательная функция: определяет правило с масимальной выгодой
	public static function __sc_discounts_calculation()
	{
		if (!CModule::IncludeModule('sale') && !CModule::IncludeModule('catalog'))
			return false;
		
		$max_cond_id = $max_price = 0;
		
		//возвращаем текущую корзину покупателя
		$basket = \Bitrix\Sale\Basket::loadItemsForFUser(
		   \Bitrix\Sale\Fuser::getId(),
		   \Bitrix\Main\Context::getCurrent()->getSite()
		);
		
		//узнаем примененные правила работы с корзиной в корзине
		$discounts = \Bitrix\Sale\Discount::loadByBasket($basket);		
		$res = $discounts->calculate();
		$result = $discounts->getApplyResult(true);
		
		//если есть применные правила
		if (!empty($result["DISCOUNT_LIST"]) && is_array($result["DISCOUNT_LIST"]))
		{
			foreach ($result["DISCOUNT_LIST"] as $id => $item)
			{
				//по очереди исключаем правило и рассчитываем окончательную сумму в корзине
				$discounts->setApplyResult([
				   'DISCOUNT_LIST' => [
					  $id => 'N'
				   ]
				]);
				$res = $discounts->calculate();
				$sub_result = $discounts->getApplyResult(true);
				if (!empty($sub_result["PRICES"]["BASKET"]) && is_array($sub_result["PRICES"]["BASKET"]))
				{
					foreach ($sub_result["PRICES"]["BASKET"] as $basket_price)
					{
						//определяем правило с максимальной скидкой
						if ($basket_price["PRICE"] > $max_price)
						{
							$max_price = $basket_price["PRICE"];
							
							//реальный ИД правила работы с корзиной с максимальной скидкой
							$max_cond_id = $item["REAL_DISCOUNT_ID"];
						}
					}
				}
			}
			//если нашли реальный ИД правила работы с корзиной с максимальной скидкой
			if ($max_cond_id > 0)
			{
				//пишем в глобальную переменную для использования во внутренних обработчиках событий
				$GLOBALS["SCODER_MAXCOND_ID"] = $max_cond_id;
			}
		}
	}
	//Событие "OnBeforeProlog" вызывается в выполняемой части пролога сайта
	public static function OnBeforePrologHandler()
	{
		global $APPLICATION;
		if (CModule::IncludeModule('sale') && CModule::IncludeModule('catalog'))
		{
			///personal/cart/ - если у вас корзина находится по другому пути, то нужно указать его
			if (strpos($APPLICATION->GetCurPage(false),"/personal/cart/") === 0)
			{
				CDev::__sc_discounts_calculation();
			}
		}
	//вспомогательная функция: определяет какое правило можно применять. ФУНКЦИЯ СПЕЦИАЛЬНЫМ ОБРАЗОМ УКАЗЫВАЕТСЯ В ДОПОЛНИТЕЛЬНЫХ НАСТРОЙКАХ ПРАВИЛ РАБОТЫ С КОРЗИНОЙ
	public static function __check_best_offer($arOrder = array(), $cond_id = 0)
	{
		if ($cond_id > 0
			&& !empty($GLOBALS["SCODER_MAXCOND_ID"]))
		{
			//если реальный ИД правила работы с корзиной с максимальной скидкой "НЕ равно" нашему правилу, то отменяем правило
			if ($GLOBALS["SCODER_MAXCOND_ID"] != $cond_id)
				return false;
		}
		return true;
	}
}

Немного о функции CDev::__check_best_offer($arOrder,732), которая вызывается в дополнительных условиях каждого правила работы с корзиной. Выглядит это следующим образом:

Разумеется, когда правил уже достаточно много создано, то дописывать подобное условие не очень удобно. Но это тоже можно пройтись с помощью скрипта и дописать. С этим может относительно легко справиться любой программист, специализирующийся на ЦМС 1С-Битрикс, или вы можете обратиться к нам (см. выше).

Скидка на товары после опроса на ЦМС 1С-Битрикс

В данной статье рассмотрены варианты применения скидок после успешного прохождения опроса с помощью модуля 1С-Битрикс Опросы, а также опроса сервиса marquiz.ру:

Внимание! В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения отраженного подхода потребуется помощь программистов.

Мы также можем в этом помощь, но за отдельное вознаграждение. Обратиться за помощью можно в наш чат в правом нижнем углу нашего сайта (в рабочее время отвечаем достаточно оперативно). Но оставляем за собой право отказать в доработке без объяснения причин.

Для реализации скидки с помощью правил потребуется наш модуль - Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках.

I. Для модуля 1С-Битрикс. Опросы.

  1. В init.php регистрируем обработчик событий onAfterVoting.:
    AddEventHandler("vote", "onAfterVoting", Array("CDev", "onAfterVotingHandler"));
    
  2. В функции-обработчике событий успешной фиксации голоса прописываем логику. Например, мы записываем в куки, что опрос пройден.:
    class CDev
    {
    	public static function onAfterVotingHandler($voteId, $eventId, $userId)
    	{
    		if ($voteId > 0)
    		{
    			$application = Bitrix\Main\Application::getInstance();
    			$context = $application->getContext();
    			
    			$cookie = new Cookie("SCODER_VOTE_SUCCESS", "Y", time() + 60*60*24);
    			$cookie->setDomain($context->getServer()->getHttpHost());
    			$cookie->setHttpOnly(false);
    			Bitrix\Main\Application::getInstance()->getContext()->getResponse()->addCookie($cookie);
    			$context->getResponse()->flush("");
    		}
    	}
    }
    
  3. В нужном правиле работе с корзиной в PHP условии указать условие проверки куки:
    (Bitrix\Main\Application::getInstance()->getContext()->getRequest()->getCookie("SCODER_VOTE_SUCCESS") == "Y")

II. Для квиза marquiz.ру (через Webhooks).

  1. Передаем дополнительный кастомный параметр в квиз. В нашем случае это FUSER_ID. В обработчике событий OnPageStart размещаем следующий код:
    Bitrix\Main\Page\Asset::getInstance()->addString(
    	'< script type="text/javascript">
    		document.addEventListener("marquizLoaded", function() {
    			Marquiz.addParam("FUSER_ID", "'. \Bitrix\Sale\Fuser::getId(). '");
    		});
    	< /script>',
    	true,
    	\Bitrix\Main\Page\AssetLocation::AFTER_JS_KERNEL
    );
    
  2. Если опрос пройден, и в личном кабинете сервиса у вас настроен вебхук обратно на ваш веб-сайт, то сервис отправляет запрос с данными, подробнее в документации - Как интегрировать квиз через Webhooks.

    Предлагаем обрабатывать эти данные с помощью обработчика событий OnBeforeProlog. Код функции будет выглядеть следующим образом:

    $data = json_decode(file_get_contents('php://input'), true);
    if (is_array($data)
    	&& !empty($data['extra']['FUSER_ID'])
    		&& $data['extra']['FUSER_ID'] > 0
    )
    {
    	$fuser_id = $data['extra']['FUSER_ID'];
    
    	$load = array(
    		"FUSER_ID" => $fuser_id,
    		"DATE_INSERT" => new \Bitrix\Main\Type\DateTime(),
    		"QUIZE_NAME" => $data['quiz']['name'],
    		"QUIZE_ID" => $data['quiz']['id'],
    	);
    	
    	$filter = array(
    		"FUSER_ID" => $fuser_id,
    	);
    	$dbResultList = Scoder\Tools\FusersTable::getList(array(
    		'filter' => $filter,
    		'select' => array("ID","FUSER_ID"),
    	));
    	if ($fields = $dbResultList->Fetch())
    	{
    		$result = Scoder\Tools\FusersTable::update($fields["ID"], $load);
    		// if ($result->isSuccess()){}
    	}
    	else
    	{
    		$result = Scoder\Tools\FusersTable::add($load);
    		//if ($result->isSuccess()){}
    	}
    }
    

    Scoder\Tools\FusersTable - это класс работы с таблицей в базе данных, описанном в произвольном кастомном модуле. Таблица создается следующим образом:

    create table if not exists scoder_tools_marquiz_fusers
    (
    	ID int NOT NULL auto_increment,
    	FUSER_ID int not null,
    	DATE_INSERT datetime not null,
    	QUIZE_NAME varchar(255) not null,
    	QUIZE_ID varchar(255) not null,
    	PRIMARY KEY(ID)
    );
    

    По реализации в виде модуля можете обратиться к нам. Или же, в качестве альтернативы, можете использовать инфоблоки или highload-блоки ЦМС 1С-Битрикс.

  3. В нужном правиле работе с корзиной в PHP условии указать условие функцию CModule::IncludeModule('scoder.tools') && CScoderTools::__sc_vote_success().
    Class CScoderTools
    {	
        public static function __sc_vote_success()
        {
    		$btrue = false;
    		
    		$filter = array(
    			"FUSER_ID" => \Bitrix\Sale\Fuser::getId(),
    		);
    		$dbResultList = Scoder\Tools\FusersTable::getList(array(
    			'filter' => $filter,
    			'select' => array("ID","FUSER_ID"),
    		));
    		if ($fields = $dbResultList->Fetch())
    		{	
    			$btrue = true;
    		}
    		return $btrue;
    	}
    }
    

Разумеется, логику можно развивать. Например, предоставлять разные скидки или подарки в зависимости от квиза или вариантов ответов на вопросы...

Ограничение скидок в корзине веб-сайта на ЦМС 1С-Битрикс

На сайтах, где установлены модули Торговый каталог и Интернет-магазин есть несколько способов задать скидки:

  1. С помощью правил работы с корзиной.
  2. С помощью скидок на товары.
  3. Есть и иные: накопительные скидки, подарки. Но в данной статье они не взяты в расчет.

Но как ограничить скидку на общую сумму заказа с учетом всех скидок? Не так, чтобы прекращать применять другие скидки, а применить до максимума.

Например:

  • Скидка на товары в корзине по правилу работы равно 20%, а скидка на товар равно 15%.
  • 20 + 15 = 35%. Нужно поставить ограничение = 30%.
  • Полученную скидку снизить до ограничения 35 - 5 = 30%.

В статье рассмотрен пример применения ограничения не к каждому товару, а именно к общей сумме. Для реализации задачи потребуется наш модуль - Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках.

Внимание! В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения отраженного подхода потребуется помощь программистов.

Мы также можем в этом помощь, но за отдельное вознаграждение. Обратиться за помощью можно в наш чат в правом нижнем углу нашего сайта (в рабочее время отвечаем достаточно оперативно). Но оставляем за собой право отказать в доработке без объяснения причин.

Основная идея заключается в том, чтобы вернуть итоговую цену до максимально разрешенной скидки с помощью наценок. Причем приоритет у этих наценок должен быть самый низкий, ниже всех по уровню с остальными скидками.

Шаги для реализации следующие:

  1. С помощью АПИ 1С-Битрикс до вывода корзины (подключения компонента bitrix:sale.basket.basket) определяем какие правила работы применились для текущего покупателя.
  2. Вычисляем итоговую величину скидки в корзине.
  3. Если скидка выше допустимой, накидываем наценку, чтобы привести скидку к максимально допустимой.

    Необходимо будет заготовить правила работы с корзиной с действием "наценка" с шагом единица, т.е. 1%, 2%, 3%,...100% (вручную или скриптом).
    Указать в дополнительных условиях PHP-условие CDev::__check_markup($arOrder, $markup_action), где $markup_action - это величина процента скидки.

Ниже рассмотрен код, который необходимо разместить в init.php. В коде есть множество коммитов, которые отражает техническую сторону реализации и будет очень полезно для ознакомления разработчикам.

Class CDev
{
	public static function OnBeforePrologHandler()
	{
		global $APPLICATION;
		if (CModule::IncludeModule('sale') && CModule::IncludeModule('catalog'))
		{
			///personal/cart/ - если у вас корзина или форма заказа находятся по другому пути, то нужно указать его
			if (strpos($APPLICATION->GetCurPage(false),"/personal/cart/") === 0
				|| strpos($APPLICATION->GetCurPage(false),"/personal/order/make/") === 0
			)
			{
				CDev::__sc_markup_calculation();
			}
		}
	}
	//вспомогательная функция: определяет величину наценки, на котрую нужно поднять получившуюся цену, чтобы получить максимально допустимую цену
	public static function __sc_markup_calculation()
	{
		global $APPLICATION;
		if (!CModule::IncludeModule('sale') && !CModule::IncludeModule('catalog'))
			return false;
		
		$max_discount = 30;		//максимально допустимая скидка, например 30%. Рекомендуем вынести в константу или в опции
		
		$fullBasket = \Bitrix\Sale\Basket::loadItemsForFUser(\Bitrix\Sale\Fuser::getId(), \Bitrix\Main\Context::getCurrent()->getSite());
		$basketClone = $fullBasket->createClone();
		$orderableBasket = $basketClone->getOrderableItems();
		unset($basketClone);

		//Использовать только правила корзины
		$onlySaleDiscounts = (string) Bitrix\Main\Config\Option::get('sale', 'use_sale_discount_only') == 'Y';
		if (!$onlySaleDiscounts)
		{
			$orderableBasket->refresh(\Bitrix\Sale\Basket\RefreshFactory::create(\Bitrix\Sale\Basket\RefreshFactory::TYPE_FULL));
		}

		$discounts = \Bitrix\Sale\Discount::buildFromBasket($orderableBasket, new \Bitrix\Sale\Discount\Context\Fuser($orderableBasket->getFUserId(true)));
		if ($discounts)
		{
			$discounts->calculate();

			$result = $discounts->getApplyResult(true);

			$prices = $result['PRICES']['BASKET'];
			if (!empty($result["PRICES"]["BASKET"]))
			{
				$base_price = $price = 0;
				foreach ($result["PRICES"]["BASKET"] as $item)
				{
					$base_price += $item["BASE_PRICE"];
					$price += $item["PRICE"];
				}
				if ($price > 0 && $base_price > $price)
				{
					$total_discount = ($price * 100) / $base_price;		//скидка, которая применилась
					if ($total_discount > $max_discount)
					{
						$max_price = $base_price * (100 - $max_discount) / 100;		//определяем какая должны была быть максимально допустимая цена, с учетомт скидок
						$GLOBALS["SCODER_MARKUP"] = (int) round(($max_price - $price) * 100 / $price);		//НАЦЕНКА, на какой процент поднять, чтобы получить максимально допустимую цену
					}
				}
			}
		}
	}
	//вспомогательная функция: определяет, накидывать ли наценку для ограничения скидок
	public static function __check_markup($arOrder = array(), $markup_action = 0)
	{
		$markup_action = (int) $markup_action;
		if ($markup_action > 0
				&& $GLOBALS["SCODER_MARKUP"] > 0
			)
		{
			//если величины наценок совпадают
			if ($GLOBALS["SCODER_MARKUP"] == $markup_action)
			{
				return true;
			}
		}
		return false;
	}
}

Немного о функции CDev::__check_markup($arOrder, $markup_action), которая вызывается в дополнительных условиях правил работы с корзиной, отвечающих за наценку. Выглядит следующим образом: