На сайтах, где установлены модули Торговый каталог и Интернет-магазин есть несколько способов задать скидки:
Но как ограничить скидку на общую сумму заказа с учетом всех скидок? Не так, чтобы прекращать применять другие скидки, а применить до максимума.
Например:
В статье рассмотрен пример применения ограничения не к каждому товару, а именно к общей сумме. Для реализации задачи потребуется наш модуль - Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках.
Внимание! В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения рекомендуется обратиться к разработчикам.
Мы также можем в этом помощь, но за отдельное вознаграждение. Обратиться за помощью можно в наш чат в правом нижнем углу нашего сайта (в рабочее время отвечаем достаточно оперативно). Но оставляем за собой право отказать в доработке без объяснения причин.
Основная идея заключается в том, чтобы вернуть итоговую цену до максимально разрешенной скидки с помощью наценок. Причем приоритет у этих наценок должен быть самый низкий, ниже всех по уровню с остальными скидками.
Шаги для реализации следующие:
Необходимо будет заготовить правила работы с корзиной с действием "наценка" с шагом единица, т.е. 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), которая вызывается в дополнительных условиях правил работы с корзиной, отвечающих за наценку. Выглядит следующим образом:
