Разработка блока "хиты продаж" 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Разработка блока "хиты продаж" 1С-Битрикс
Средняя
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    747
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Разработка блока "хиты продаж" 1С-Битрикс

«Хиты продаж» — блок с товарами, которые покупают чаще всего. Работает на главной странице как витрина популярности, в каталоге — для ориентации покупателя, в карточке товара — как доверительный сигнал («другие выбирают это»). В Битрикс нет встроенного автоматического механизма расчёта хитов — ручная пометка свойства HIT не масштабируется. Нужна система, которая считает по данным.

Источники данных для расчёта

Хиты определяются по реальным продажам — из b_sale_basket + b_sale_order. Дополнительно можно учитывать просмотры карточек (если ведётся трекинг) с меньшим весом.

-- Топ продаваемых товаров за 30 дней
SELECT
    b.product_id,
    SUM(b.quantity)                  AS total_qty,
    COUNT(DISTINCT b.order_id)       AS total_orders,
    SUM(b.price * b.quantity)        AS total_revenue
FROM b_sale_basket b
JOIN b_sale_order o ON b.order_id = o.id
WHERE
    o.canceled  = 'N'
    AND o.date_insert >= DATE_SUB(NOW(), INTERVAL 30 DAY)
    AND b.product_id IS NOT NULL
GROUP BY b.product_id
ORDER BY total_orders DESC, total_qty DESC
LIMIT 100;

Ранжирование по total_orders (число заказов), а не по total_qty — иначе один заказ на 100 единиц поднимет товар выше, чем 50 разных заказов на 1 единицу. Количество уникальных заказов объективнее отражает популярность.

Многоуровневые хиты: весовая формула

Для более точного ранжирования — формула с несколькими факторами:

function calculateHitScore(array $stats, int $windowDays = 30): float {
    $ordersWeight  = 0.5;
    $revenueWeight = 0.3;
    $viewsWeight   = 0.2;

    // Нормализация: делим на максимальное значение в выборке
    $normOrders  = $stats['total_orders'] / ($stats['max_orders']  ?: 1);
    $normRevenue = $stats['total_revenue'] / ($stats['max_revenue'] ?: 1);
    $normViews   = $stats['total_views']   / ($stats['max_views']   ?: 1);

    // Временной коэффициент: недавние продажи ценнее
    $recencyBoost = 1.0;
    if ($stats['last_sale_days_ago'] <= 7) {
        $recencyBoost = 1.2;
    } elseif ($stats['last_sale_days_ago'] <= 14) {
        $recencyBoost = 1.1;
    }

    return ($normOrders * $ordersWeight + $normRevenue * $revenueWeight + $normViews * $viewsWeight)
        * $recencyBoost;
}

Таблица хитов и агент пересчёта

CREATE TABLE custom_hits (
    product_id    INT NOT NULL PRIMARY KEY,
    score         FLOAT NOT NULL,
    total_orders  INT DEFAULT 0,
    total_qty     INT DEFAULT 0,
    total_revenue DECIMAL(12,2) DEFAULT 0,
    category_rank INT,           -- ранг внутри категории
    is_hit        TINYINT DEFAULT 1,
    calculated_at DATETIME DEFAULT NOW(),
    INDEX idx_score (score DESC),
    INDEX idx_category_rank (category_rank)
);

Агент пересчитывает таблицу раз в сутки:

function RecalcHitsAgent(): string {
    $connection = \Bitrix\Main\Application::getConnection();

    // Очищаем таблицу и заполняем заново
    $connection->truncateTable('custom_hits');

    $data = calcSalesStats(30); // за 30 дней
    $max  = getMaxValues($data);

    foreach ($data as $productId => $stats) {
        $stats = array_merge($stats, $max);
        $score = calculateHitScore($stats);

        $connection->add('custom_hits', [
            'product_id'    => $productId,
            'score'         => $score,
            'total_orders'  => $stats['total_orders'],
            'total_qty'     => $stats['total_qty'],
            'total_revenue' => $stats['total_revenue'],
            'is_hit'        => $score > 0.1 ? 1 : 0,
            'calculated_at' => new \Bitrix\Main\Type\DateTime(),
        ]);
    }

    // Рассчитываем ранг внутри каждой категории
    updateCategoryRanks();

    return 'RecalcHitsAgent();';
}

Категориальные хиты

Помимо общего рейтинга — хиты по разделам каталога. В карточке товара блок показывает «Хиты в этой категории»:

function getCategoryHits(int $sectionId, int $limit = 8, int $excludeId = 0): array {
    // Получаем товары раздела, отсортированные по рангу внутри категории
    $hitIds = \Bitrix\Main\Application::getConnection()->query("
        SELECT ch.product_id
        FROM custom_hits ch
        JOIN b_iblock_element ie ON ch.product_id = ie.id
        WHERE ie.iblock_section_id = {$sectionId}
          AND ie.active = 'Y'
          AND ch.is_hit = 1
          AND ch.product_id != {$excludeId}
        ORDER BY ch.score DESC
        LIMIT {$limit}
    ")->fetchAll();

    return getProductsByIds(array_column($hitIds, 'product_id'));
}

Компонент и кеширование

// company:catalog.hits — component.php
$cacheKey = "hits_{$arParams['SECTION_ID']}_{$arParams['LIMIT']}";
$cache    = \Bitrix\Main\Data\Cache::createInstance();

if ($cache->initCache(3600 * 6, $cacheKey, '/catalog/hits')) {
    $arResult = $cache->getVars();
} elseif ($cache->startDataCache()) {
    $arResult = getCategoryHits(
        (int)$arParams['SECTION_ID'],
        (int)$arParams['LIMIT'],
        (int)$arParams['EXCLUDE_ID']
    );
    $cache->endDataCache($arResult);
}

Кеш на 6 часов — данные о хитах меняются раз в сутки, но частое обновление кеша не нужно.

Ручное управление: пометка хитов редактором

Помимо автоматических хитов — возможность ручной пометки. Менеджер заходит в карточку товара и ставит флаг «Редакционный хит». Такие товары всегда показываются в блоке, независимо от статистики. Используется для акционных и новых товаров.

// Свойство инфоблока EDITORIAL_HIT (тип: список, значения: Y/N)
// При формировании блока ручные хиты идут первыми
$manualHits = getEditorialHits($sectionId, $limit);
$autoHits   = getCategoryHits($sectionId, $limit - count($manualHits), $excludeIds);
$result     = array_merge($manualHits, $autoHits);

Отображение значка "Хит" на карточках

В шаблоне карточки товара и в листинге добавляем метку:

// В template.php листинга
if (!empty($arItem['PROPERTIES']['HIT']['VALUE'])) {
    echo '<span class="product-badge product-badge--hit">Хит продаж</span>';
}

// Или через таблицу хитов — не требует свойства в инфоблоке
$isHit = isProductHit($arItem['ID']); // читает из custom_hits

Сроки

Этап Срок
SQL расчёта + таблица хитов 1–2 дня
Агент пересчёта + весовая формула 2–3 дня
Компонент (общий + категориальный) 2–3 дня
Ручная пометка в административной части 1–2 дня
Значки на карточках и в листинге 1 день
Тестирование 1–2 дня

Итого: 1–1.5 недели.