Розробка функціоналу зворотного відліку (таймер акції) 1С-Bitrix
Акція без дедлайну — це не акція, а постійна скидка. Таймер зворотного відліку створює ощущення обмеженості пропозиції й спонукає до рішення. Але в Bitrix немає штатного компонента зворотного відліку. Стандартний механізм скидок умить задавати період дії, а ось візуальний таймер на фронте — завдання для кастомної розробки. Розглянемо, як пов'язати таймер з реальними правилами скидок, щоб дата закінчення бралася з бази, а не була захардкожена в шаблоні.
Джерело даних: звідки брати дату закінчення
Таймер повинен показувати реальний строк акції, а не декоративні цифри. У Bitrix дати акцій зберігаються в кількох місцях:
Скидки каталогу — таблиця b_catalog_discount, поля ACTIVE_FROM й ACTIVE_TO. Отримуємо через \Bitrix\Catalog\DiscountTable::getList() з фільтром ACTIVE = Y й ACTIVE_TO > NOW().
Правила кошика — таблиця b_sale_discount, аналогічні поля. API: \Bitrix\Sale\Internals\DiscountTable::getList().
Властивість товара — можна створити властивість інфоблока PROMO_END_DATE типу «Дата/Час» й заповнювати вручну або автоматично при прив'язанні товара до акції.
Вибір джерела залежить від сценарію. Якщо таймер показується на карточці товара — зручна властивість або скидка каталогу. Якщо таймер глобальний (банер на головній) — правило кошика або окремий інфоблок акцій.
Архітектура компонента
Створюємо кастомний компонент local:sale.countdown в /local/components/local/sale.countdown/. Структура стандартна:
-
class.php— логіка вибірки даних. -
templates/.default/template.php— HTML-розмітка. -
templates/.default/script.js— JavaScript-логіка зворотного відліку. -
.parameters.php— налаштовувані параметри компонента.
Параметри компонента:
| Параметр | Тип | Описання |
|---|---|---|
| SOURCE_TYPE | list | Джерело: catalog_discount, sale_discount, iblock_property |
| DISCOUNT_ID | int | ID скидки (для catalog/sale) |
| IBLOCK_ID | int | ID інфоблока (для властивості товара) |
| ELEMENT_ID | int | ID елемента (для властивості товара) |
| PROPERTY_CODE | string | Код властивості з датою закінчення |
| DISPLAY_FORMAT | list | Формат: дні+години+хвилини+секунди або години+хвилини+секунди |
| ACTION_ON_EXPIRE | list | Дія при закінченні: приховати / показати повідомлення |
| CACHE_TIME | int | Час кешування |
У class.php компонент отримує дату закінчення з обраного джерела й передає в шаблон timestamp:
$this->arResult['TIMESTAMP_END'] = (new \Bitrix\Main\Type\DateTime($endDate))->getTimestamp();
JavaScript: клієнтський відлік
Таймер працює повністю на клієнті. Серверна частина дає лише timestamp закінчення. Це принципово: AJAX-запити кожну секунду — навантаження без сенсу.
Ключовий момент — синхронізація часу. Клієнтські годинники можуть відрізнятися від серверних. Рішення: сервер передає не лише TIMESTAMP_END, але й TIMESTAMP_SERVER — поточний серверний час. JavaScript обчислює дельту й коректує відлік:
const serverNow = parseInt(container.dataset.serverTime);
const clientNow = Math.floor(Date.now() / 1000);
const drift = serverNow - clientNow;
const remaining = endTime - (Math.floor(Date.now() / 1000) + drift);
Оновлення DOM кожну секунду через setInterval — робочий варіант. Але для кількох таймерів на сторінці (листинг товарів з акціями) краще один requestAnimationFrame-цикл, який оновлює всі таймери за один прохід.
Дія при закінченні. Коли remaining <= 0, таймер не повинен просто зупинитися. Варіанти: приховати блок акції, замінити кнопку «Купити зі скидкою» на звичайну, показати повідомлення «Акція завершена». Для зміни кнопки — AJAX-запит до сервера для перевірки актуальності скидки й перерендер блока ціни.
Інтеграція з composite-кешем
Composite cache (автокомпозит) Bitrix кешує HTML-сторінку цілком. Таймер із серверним timestamp в HTML зламає кеш: кожний хіт буде унікальним.
Рішення — винести блок таймера в динамічну область. У template.php:
$frame = new \Bitrix\Main\Page\Frame('countdown_' . $this->arParams['DISCOUNT_ID']);
$frame->begin();
// HTML таймера
$frame->end();
Всередину динамічної області HTML оновлюється при кожному запиті, а решта сторінки отдається з кешу.
Альтернатива — не виводити timestamp в HTML взагалі. Замість цього зберігати його в окремому endpoint (/ajax/countdown.php), який JavaScript запитує один раз при завантаженні. Сторінка кешується повністю, дані таймера завантажуються окремо.
Прив'язка до правил скидок
Таймер сам по собі — лише візуал. Він повинен бути синхронізований з реальним правилом скидки. Якщо менеджер продовжив акцію в адміні — таймер має оновитися автоматично.
Для цього компонент при кожному скиданні кешу заново запитує ACTIVE_TO з таблиці скидок. Час кешування компонента ставимо невеликим — 300-600 секунд. Компроміс між актуальністю й навантаженням.
Додатково: обробник OnAfterCatalogDiscountUpdate / OnAfterSaleDiscountUpdate для скидання кешу компонента при зміні скидки. Теггований кеш (\Bitrix\Main\Data\TaggedCache) з тегом catalog_discount_{ID} розв'язує завдання точково.
Таймер на листингу товарів
Окреме завдання — показати таймери на сторінці каталогу, де 20-50 товарів. Кожен може мати свою акцію з окремим строком. Компонент викликається в циклі catalog.section — це кілька SQL-запитів.
Оптимізація: у class.php реалізуємо batch-режим. Компонент приймає масив ELEMENT_IDS, одним запитом отримує всі дати й повертає масив timestamp'ів. У шаблоні catalog.section — один виклик замість N.
Строки реалізації
| Варіант | Склад | Строк |
|---|---|---|
| Простий таймер | Один компонент, одна скидка, статичний endpoint | 3-4 дні |
| Повне рішення | Batch-режим, composite-сумісність, синхронізація з правилами, автоскидання кешу | 7-10 днів |







