В данной главе приведены наиболее яркие, распространенные примеры использования модуля.
Один из наиболее частых случаев применения модуля - реализация механизма Нарастающие скидки.
Решение данного примера вынесено в отдельный модуль, с возможностями которого можно познакомиться на странице модуля Покупай больше - плати меньше. Документация по модулю доступна на странице..
Механизм правил работы с корзиной в 1С-Битрикс дает возможность представлять скидки в зависимости от многих условий, таких как: тип плательщика, служба доставки, платежные системы и т.п. Но нет зависимости от выбранной точки самовывоза (склада). Но, как показывает практика, подобное очень часто требуется.
Внимание: Решение задачи рассмотрено на примере дефолтного шаблона компонента sale.order.ajax. В кастомизированных шаблонах решение некоторых моментов может отличаться.
В статье приведены фрагменты кода, описывающие принцип реализации, а не полностью рабочий продукт. Для внедрения рекомендуется обратиться к разработчикам.
Например, имеется 2 склада:
Требуется применить скидку на товары 10%, если покупатель выбрал точку самовывоза Основной склад.
Для этого необходимо: |
1) Создать соответствующее правило работы с корзиной.
Как видно из скриншота, в качестве 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
Если вы сделали все правильно, то настроенная вами скидка должна применяться при выборе нужной точки самовывоза.
Внимание: В статье приведены фрагменты кода, описывающие принцип реализации. Для внедрения рекомендуется обратиться к разработчикам.
В зависимости от задач функция может быть более сложной. В данном материале описан принцип реализации.
Вопрос предоставления разной величины скидки с учетом стоимости всех товаров в корзине по одному купону не так тривиален.
Например, необходимо решить следующую задачу:
Эта задача легко бы решалась, если не одно НО: они должны применяться только при активации купона, причем один купон на всех.
Для решения данной задачи необходимо создать 3 правила работы с корзиной с самими скидками (т.к. в нашем примере их 3) и еще одно правило под купон (назовем его служебным), последнее правило с нулевой скидкой.
В условиях 3-х правил указываем необходимые интервалы применения, а также дополнительное PHP-условие, которое будут доступно после установки модуля. В значении условия укажем функцию, например с названием __check_coupon(), к данной функции мы еще вернемся. Произвольное PHP условие правил работы с корзиной
Выглядит все это следующим образом:
Также создаем 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; }
В данной статье рассмотрены ряд важных тем:
Для реализации наиболее или наименее выгодной скидки с помощью правил потребуется в помочь наш модуль - Произвольное 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С-Битрикс Опросы, а также опроса сервиса marquiz.ру:
Для реализации скидки с помощью правил потребуется наш модуль - Произвольное PHP условие правил работы с корзиной. Свои (кастомные) условия в скидках.
I. Для модуля 1С-Битрикс. Опросы.
AddEventHandler("vote", "onAfterVoting", Array("CDev", "onAfterVotingHandler"));
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(""); } } }
(Bitrix\Main\Application::getInstance()->getContext()->getRequest()->getCookie("SCODER_VOTE_SUCCESS") == "Y")
II. Для квиза marquiz.ру (через Webhooks).
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 );
Предлагаем обрабатывать эти данные с помощью обработчика событий 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С-Битрикс.
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; } }
Разумеется, логику можно развивать. Например, предоставлять разные скидки или подарки в зависимости от квиза или вариантов ответов на вопросы...
На сайтах, где установлены модули Торговый каталог и Интернет-магазин есть несколько способов задать скидки:
Но как ограничить скидку на общую сумму заказа с учетом всех скидок? Не так, чтобы прекращать применять другие скидки, а применить до максимума.
Например:
В статье рассмотрен пример применения ограничения не к каждому товару, а именно к общей сумме. Для реализации задачи потребуется наш модуль - Произвольное 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), которая вызывается в дополнительных условиях правил работы с корзиной, отвечающих за наценку. Выглядит следующим образом: