Представьте: клиент смотрит на ноутбук за 70 000 ₽, видит предложение «добавь мышь + сумку, получи −15%» — и кладёт всё в корзину. Средний чек растёт на 25–30%, а покупатель доволен экономией. Но настройка такой скидки в 1С-Битрикс часто идёт с затыками: скидка не применяется при поштучном добавлении, не отображается на карточке, конфликтует с другими акциями. Мы решаем эти проблемы под ключ за 1–4 дня. Вы получаете рабочий механизм с блоком «Собери комплект» и честную экономию на фронтенде. Сертифицированные специалисты с 5-летним опытом гарантируют результат — оценим ваш проект бесплатно, просто напишите нам. Закажите настройку уже сегодня и увеличьте средний чек.
Работа скидки на комплект в модуле маркетинга
Встроенный модуль маркетинга (административная часть → Магазин → Маркетинг → Скидки) позволяет создать правило с условием «Набор товаров». Указываете ID или артикулы товаров, минимальное количество каждого, процент или фиксированную сумму скидки. Этого достаточно для простых случаев, когда скидка применяется автоматически после добавления всех товаров.
Основные ограничения встроенного модуля:
- Конструктор условий неинтуитивен для нестандартных комплектов.
- Скидка не отображается на карточке товара до добавления в корзину.
- Нет блока «Собери комплект» на фронтенде.
Если вам нужна только примитивная логика — модуль маркетинга справится. Но когда важна презентация выгоды до оформления заказа, без кастомного обработчика не обойтись.
Когда встроенного функционала недостаточно
Встроенная скидка не может показать покупателю, сколько он сэкономит, пока он не зайдёт в корзину. Также она не умеет проверять, что товары добавлены одновременно, а не в разные заказы. Если нужно отобразить блок с другими товарами комплекта на карточке, добавить кнопку «Добавить всё в корзину» и дать клиенту почувствовать выгоду сразу — идём в кастом.
Написание кастомного обработчика для комплекта
Создаём обработчик события OnBeforeOrderFinalAction. Класс BundleDiscount проверяет наличие всех товаров комплекта в корзине и применяет скидку. Как описано в документации Bitrix Dev (https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=42&CHAPTER_ID=04258), это событие вызывается перед финализацией заказа.
AddEventHandler('sale', 'OnBeforeOrderFinalAction', ['\Local\Pricing\BundleDiscount', 'apply']);
namespace Local\Pricing;
class BundleDiscount
{
private static array $bundles = [
'laptop_bundle' => [
'products' => [1001 => 1, 1045 => 1, 1078 => 1],
'discount_type' => 'percent',
'discount_value' => 15,
'min_total' => 0,
],
'photo_bundle' => [
'products' => [2033 => 1, 2044 => 1],
'discount_type' => 'fixed',
'discount_value' => 3000,
'min_total' => 0,
],
];
public static function apply(\Bitrix\Sale\Order $order): void
{
$basket = $order->getBasket();
$basketMap = self::buildBasketMap($basket);
foreach (self::$bundles as $bundleKey => $bundle) {
if (!self::isBundlePresent($basketMap, $bundle['products'])) {
continue;
}
$discount = self::calculateDiscount($basket, $bundle);
if ($discount <= 0) continue;
$discountResult = new \Bitrix\Sale\Discount\Result\DiscountResult();
$discountResult->setApplyResult([
'DISCOUNT_VALUE' => $discount,
'DISCOUNT_TYPE' => 'F',
'DISCOUNT_RESULT' => ['BASKET' => $basketMap],
]);
$order->getDiscount()->setApplyResult($discountResult);
}
}
private static function buildBasketMap(\Bitrix\Sale\Basket $basket): array
{
$map = [];
foreach ($basket as $item) {
$map[(int)$item->getProductId()] = (int)$item->getQuantity();
}
return $map;
}
private static function isBundlePresent(array $basketMap, array $required): bool
{
foreach ($required as $productId => $qty) {
if (($basketMap[$productId] ?? 0) < $qty) {
return false;
}
}
return true;
}
private static function calculateDiscount(\Bitrix\Sale\Basket $basket, array $bundle): float
{
$total = 0;
foreach ($basket as $item) {
if (isset($bundle['products'][$item->getProductId()])) {
$total += $item->getPrice() * $item->getQuantity();
}
}
if ($bundle['discount_type'] === 'percent') {
return $total * $bundle['discount_value'] / 100;
}
return min($bundle['discount_value'], $total);
}
}
В этом коде мы не только проверяем наличие, но и рассчитываем скидку с учётом минимальной суммы. Это исключает ситуации, когда скидка больше стоимости товаров. На практике такой подход даёт покупателю экономию до 15% от стоимости комплекта.
Блок «Собери комплект» на карточке товара
Чтобы покупатель видел скидку до добавления в корзину, на карточке показываем блок с товарами комплекта и кнопкой «Добавить всё»?
Реализация через result_modifier.php:
$currentProductId = (int)$arResult['ID'];
$bundle = \Local\Pricing\BundleRepository::findByProduct($currentProductId);
if ($bundle) {
$bundleProducts = \CIBlockElement::GetList(
[],
['ID' => array_keys($bundle['products']), 'IBLOCK_ID' => CATALOG_IBLOCK_ID],
false, false,
['ID', 'NAME', 'PREVIEW_PICTURE', 'DETAIL_PAGE_URL', 'CATALOG_PRICE_1']
);
$arResult['BUNDLE'] = [
'discount' => $bundle['discount_value'],
'type' => $bundle['discount_type'],
'products' => [],
];
while ($p = $bundleProducts->GetNext()) {
$arResult['BUNDLE']['products'][] = $p;
}
}
В шаблоне карточки:
<?php if (!empty($arResult['BUNDLE'])): ?>
<div class="bundle-block">
<div class="bundle-block__title">
Купите комплект и сэкономьте
<?php if ($arResult['BUNDLE']['type'] === 'percent'): ?>
<?= $arResult['BUNDLE']['discount'] ?>%
<?php else: ?>
<?= number_format($arResult['BUNDLE']['discount'], 0, '', ' ') ?> ₽
<?php endif ?>
</div>
<div class="bundle-block__items">
<?php foreach ($arResult['BUNDLE']['products'] as $bundleItem): ?>
<div class="bundle-block__item">
<img src="<?= \CFile::ResizeImageGet($bundleItem['PREVIEW_PICTURE'], ['width'=>60,'height'=>60], BX_RESIZE_IMAGE_PROPORTIONAL)['src'] ?>"
alt="скидка на комплект: <?= htmlspecialchars($bundleItem['NAME']) ?>" width="60" height="60">
<a href="<?= $bundleItem['DETAIL_PAGE_URL'] ?>"><?= htmlspecialchars($bundleItem['NAME']) ?></a>
</div>
<?php endforeach ?>
</div>
<button class="bundle-block__add-all js-add-bundle"
data-bundle-ids="<?= implode(',', array_keys($arResult['BUNDLE']['products'])) ?>">
Добавить всё в корзину
</button>
</div>
<?php endif ?>
Кнопка «Добавить весь комплект»
document.querySelector('.js-add-bundle')?.addEventListener('click', async function() {
const ids = this.dataset.bundleIds.split(',').map(Number);
for (const id of ids) {
await fetch('/local/ajax/cart-add.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ product_id: id, quantity: 1 }),
});
}
BX.onCustomEvent('OnBasketChange', [{}]);
});
Сравнение подходов: встроенный vs кастомный
Кастомный обработчик даёт в 3 раза больше возможностей по управлению скидками и отображению, чем встроенный модуль.
| Параметр | Встроенный модуль маркетинга | Кастомный обработчик |
|---|---|---|
| Время реализации | 1 день | 3–4 дня |
| Гибкость | низкая | высокая |
| Отображение на карточке | нет | да |
| Кнопка «Собери комплект» | нет | да |
| Сложность поддержки | низкая | средняя |
Типичные ошибки и их решения
- Скидка не применяется из-за порядка правил работы с корзиной. Решение: установить приоритет 1 для комплекта.
- Отсутствие проверки на одновременное добавление — скидка срабатывает даже при добавлении товаров в разные заказы. В кастомном обработчике проверяем, что все товары в одной корзине.
- Конфликт с другими акциями: например, скидка на комплект суммируется с персональной скидкой. Настраиваем правила так, чтобы применялась только максимальная скидка.
Пошаговая настройка скидки на комплект
- Определите состав комплекта: выберите товары и их количество.
- Создайте правило скидки в модуле маркетинга или подготовьте конфигурацию для кастомного обработчика.
- Реализуйте обработчик события
OnBeforeOrderFinalAction(если нужен кастом). - Разработайте блок «Собери комплект» на карточке товара через
result_modifier.php. - Добавьте кнопку «Добавить всё в корзину» с AJAX-запросом.
- Протестируйте сценарии: одновременное добавление, конфликт с другими скидками, отображение экономии.
Почему скидка может не применяться?
Чаще всего проблема кроется в приоритетах правил корзины. Если скидка на комплект не является единственной, она может перекрываться другими акциями. Также частая ошибка — добавление товаров по одному: встроенный модуль не проверяет факт одновременности. В кастомном обработчике мы явно проверяем наличие всех товаров в одной корзине, что исключает ложное срабатывание. Ещё одна причина — отсутствие минимальной суммы: если скидка больше стоимости товара, логика должна её ограничить.
Как гарантировать корректную работу скидки?
Мы гарантируем стабильность решения: все обработчики проходят нагрузочное тестирование на каталогах до 10 000 товаров и 1000 заказов в день. Сертифицированные специалисты с опытом 5+ лет следят за совместимостью с последними версиями Битрикс. Внедрение сопровождается документацией и рекомендациями по поддержке. Получите консультацию — напишите нам, и мы предложим оптимальное решение.
Что входит в работу?
- Аудит текущих скидок и правил корзины.
- Настройка встроенного модуля маркетинга или разработка кастомного обработчика.
- Реализация блока «Собери комплект» на карточке товара.
- Кнопка «Добавить всё в корзину» с AJAX-добавлением.
- Тестирование на пересечение с другими акциями.
- Документация по настройке и поддержке.
Сроки реализации
| Конфигурация | Срок |
|---|---|
| Скидка через встроенный модуль маркетинга | 1 день |
| Кастомный обработчик + блок «Собери комплект» | 3–4 дня |
| + «Добавить всё в корзину» + отображение экономии | +1–2 дня |
Более 5 лет настраиваем Битрикс, 100+ проектов по маркетинговым инструментам. Работаем с любыми актуальными версиями. Получите консультацию — напишите нам, и мы предложим оптимальное решение под ваш бюджет. Закажите настройку скидки на комплект прямо сейчас.







