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

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Розробка блоку "хіти продажів" 1С-Бітрікс
Середня
~1-2 тижні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • 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
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Розробка блоку "хіти продажів" 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 тижні.