Настройка персональных скидок по группам клиентов 1С-Битрикс
Менеджер создаёт «скидку 15% для VIP-клиентов» в админке Битрикса, назначает её группе пользователей. Заходит тестовый VIP-аккаунт — скидка не применяется. Причина: скидка создана в «Торговом каталоге», но пользователь не входит в нужную группу, потому что менеджер перепутал «группу покупателей» (модуль sale) с «группой пользователей» (модуль main). В Битриксе это разные сущности, и путаница между ними — источник большинства проблем с персональными скидками.
Архитектура скидок в Битриксе
Модуль sale управляет скидками через \Bitrix\Sale\Discount. Скидки могут применяться к:
- Группам пользователей (
b_user_group— таблица main-модуля) - Отдельным пользователям (через расширенные условия)
- Покупателям с определённой историей заказов (накопительные скидки)
Для назначения скидки группе: в административном интерфейсе «Магазин → Правила работы с ценами» — выбрать тип «Скидка на товар» или «Скидка на заказ», в условиях указать «Покупатель состоит в группе». Группы берутся из b_user_group.
Создание группы и привязка пользователей
Группы пользователей создаются в «Настройки → Группы пользователей». Программно:
$group = new \CGroup();
$groupId = $group->Add([
'NAME' => 'VIP-клиенты',
'DESCRIPTION' => 'Скидка 15% на весь каталог',
'ACTIVE' => 'Y',
'SORT' => 100,
]);
Добавить пользователя в группу:
$user = new \CUser();
$user->Update($userId, ['GROUP_ID' => array_merge($currentGroups, [$groupId])]);
Или через CUser::SetUserGroup($userId, $groups) — перезаписывает все группы, поэтому обязательно передавать текущие группы вместе с новой.
Автоматическое назначение группы по условиям
Для автоматического перевода покупателя в VIP-группу при достижении порога суммы заказов — обработчик события OnSaleOrderSaved:
AddEventHandler('sale', 'OnSaleOrderSaved', function(\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
$userId = (int)$order->getUserId();
if (!$userId) return;
// Считаем сумму оплаченных заказов
$total = 0;
$res = \Bitrix\Sale\Order::getList([
'filter' => ['USER_ID' => $userId, 'PAYED' => 'Y'],
'select' => ['PRICE'],
]);
while ($row = $res->fetch()) {
$total += $row['PRICE'];
}
$vipThreshold = 50000; // рублей
$vipGroupId = 5;
$dbUser = \CUser::GetByID($userId)->Fetch();
$currentGroups = array_map('intval', explode(',', $dbUser['GROUP_IDS'] ?? ''));
if ($total >= $vipThreshold && !in_array($vipGroupId, $currentGroups)) {
$currentGroups[] = $vipGroupId;
(new \CUser())->Update($userId, ['GROUP_ID' => $currentGroups]);
}
});
Скидки на торговые предложения (SKU)
Если скидка назначена на товар-родитель, она применяется и к торговым предложениям. Но если нужна скидка только на конкретный SKU — условие в правиле цены должно указывать на инфоблок предложений. В настройках правила: «Тип инфоблока» → выбрать инфоблок предложений, раздел или конкретные элементы.
Отображение персональной цены
Стандартный компонент catalog.element отображает цену через $arResult['CATALOG_PRICE_*'], где * — тип цены. Скидки для групп применяются через \CCatalogProduct::GetOptimalPrice(), который учитывает правила цен и группы текущего пользователя. Если страница кешируется — цена в кеше может быть без скидки.
Решение: выносить блок цены в отдельный некешируемый компонент или запрашивать актуальную цену через AJAX после загрузки страницы. Второй подход лучше для производительности при высоком трафике.







