Настройка правил начисления кэшбэка по категориям 1С-Битрикс
Маркетолог хочет давать 5% кэшбэка на электронику, 2% на бытовую химию и 0% на акционные товары. При этом правила должны комбинироваться: карта лояльности «Золото» даёт +1% к базовой ставке в любой категории. Стандартный модуль скидок (catalog.discount) не подходит — он оперирует снижением цены, а не начислением на счёт. Нужна отдельная система правил.
Архитектура правил
Правила хранятся в таблице local_cashback_rules. Минимальная структура:
CREATE TABLE local_cashback_rules (
ID INT AUTO_INCREMENT PRIMARY KEY,
RULE_TYPE ENUM('category','product','user_group','promo') NOT NULL,
ENTITY_ID INT, -- ID раздела инфоблока, товара, группы
CASHBACK_PCT DECIMAL(5,2), -- процент начисления
PRIORITY INT DEFAULT 10,-- чем меньше, тем выше приоритет
DATE_FROM DATE,
DATE_TO DATE,
ACTIVE CHAR(1) DEFAULT 'Y'
);
Правила с RULE_TYPE = 'category' привязаны к разделам инфоблока каталога. При начислении кэшбэка по заказу нужно определить, к какому разделу относится каждый товар, и найти подходящее правило.
Определение категории товара
Товар может быть привязан к нескольким разделам (множественная привязка). Для выбора правила берём «основной» раздел:
function getCashbackRateForProduct(int $productId): float
{
// Основной раздел через b_iblock_element
$element = \CIBlockElement::GetByID($productId)->Fetch();
$sectionId = (int)$element['IBLOCK_SECTION_ID'];
// Ищем правило: сначала по точному разделу, потом по родителям
while ($sectionId > 0) {
$rule = CashbackRuleTable::getActiveRuleForSection($sectionId);
if ($rule) {
return (float)$rule['CASHBACK_PCT'];
}
// Идём вверх по дереву разделов
$section = \CIBlockSection::GetByID($sectionId)->Fetch();
$sectionId = (int)$section['IBLOCK_SECTION_ID'];
}
// Правило по умолчанию
return CashbackConfig::getDefaultRate();
}
Наследование правил по дереву разделов: если на «Электроника» установлено 5%, а на «Ноутбуки» нет явного правила — ноутбуки получат 5% от родительского раздела. Явное правило на дочернем разделе всегда перекрывает родительское.
Приоритет и комбинирование правил
Сложная логика с несколькими одновременно активными правилами — через приоритеты:
function resolveCashbackRate(int $productId, int $userId): float
{
$baseRate = getCashbackRateForProduct($productId);
// Дополнительные правила по группе пользователя
$userGroups = CUser::GetUserGroup($userId);
$bonusRates = [];
foreach ($userGroups as $groupId) {
$rule = CashbackRuleTable::getQuery()
->setFilter([
'RULE_TYPE' => 'user_group',
'ENTITY_ID' => $groupId,
'ACTIVE' => 'Y',
'<=DATE_FROM' => new \Bitrix\Main\Type\Date(),
'>=DATE_TO' => new \Bitrix\Main\Type\Date(),
])
->setOrder(['PRIORITY' => 'ASC'])
->fetchObject();
if ($rule) {
$bonusRates[] = (float)$rule->getCashbackPct();
}
}
// Стратегия: берём максимальный бонус от группы + базовая категорийная ставка
$bonusRate = empty($bonusRates) ? 0 : max($bonusRates);
return $baseRate + $bonusRate;
}
Стратегию сложения или замены выбирает бизнес. Для большинства программ лояльности: категорийная ставка + групповой бонус (сложение), но не более максимально допустимого процента.
Исключения: акционные товары и промо-периоды
Нулевая ставка на акционные товары реализуется правилом с CASHBACK_PCT = 0 и максимальным приоритетом (минимальное число в поле PRIORITY). Товар определяется как акционный, если на него действует скидка через механизм catalog.discount или через пользовательское свойство IS_PROMO = Y.
Промо-периоды — те же правила с DATE_FROM и DATE_TO. Автоматическое включение/отключение без вмешательства разработчика.
Начисление по завершению заказа
Кэшбэк начисляется при смене статуса заказа на «Выполнен» (не при оплате — чтобы не начислять на возвращённые товары):
AddEventHandler('sale', 'OnSaleStatusOrder', function(string $statusId, \Bitrix\Sale\Order $order) {
if ($statusId !== 'F') { // F = Выполнен
return;
}
$userId = $order->getUserId();
$totalCashback = 0;
foreach ($order->getBasket() as $item) {
$productId = (int)$item->getProductId();
$rate = resolveCashbackRate($productId, $userId);
$cashback = $item->getPrice() * $item->getQuantity() * ($rate / 100);
$totalCashback += $cashback;
// Фиксируем по позиции для детальной истории
CashbackTransactionTable::add([
'USER_ID' => $userId,
'ORDER_ID' => $order->getId(),
'PRODUCT_ID' => $productId,
'AMOUNT' => $cashback,
'RATE' => $rate,
'TYPE' => 'accrual',
]);
}
CashbackBalanceTable::credit($userId, $totalCashback);
});
Управление правилами из админки
Интерфейс управления правилами строится на CAdminList + CAdminForm или React-компонентом в разделе /local/admin/. Минимальный набор: список правил с фильтром по типу/активности, форма редактирования с деревом разделов каталога для выбора категории.
Сроки
| Задача | Срок |
|---|---|
| Таблицы правил, базовая категорийная логика | 3–5 дней |
| Групповые бонусы, исключения, промо-периоды | 3–5 дней |
| Начисление по заказу с детальной историей | 2–3 дня |
| Интерфейс управления правилами в админке | 3–5 дней |
| Полный комплекс | 2–3 недели |







