Михаил Базаров Разработка на 1С-Битрикс Михаил Базаров

Обновление цен и остатков в каталоге без перезагрузки страницы, в реальном времени

Просмотров: 15 Задача: в каталоге интернет-магазина обновлять цены и остатки товаров без перезагрузки страницы. Пришла новая цена от 1С, поменялся остаток на складе или сработало правило корзины - покупатель увидел это сразу, а не через F5.

Классическая ситуация: правим цены в админке, 1С выгрузила свежие остатки, а на витрине покупатель видит старую цену и запас, которого уже нет. Добавляет в корзину - а там сюрприз.

Штатный способ обновления - перезагрузить страницу. Но в 2026 году перезагружать страницу ради изменения пары цифр - это как гвозди микроскопом забивать.

Решение - Bitrix Push & Pull (модуль pull). Я сам недавно внедрил на проекте, где 1С гоняет цены каждые 15 минут. Проект для b2b, товары списком, что-то типа дашборда для закупщиков - постоянно держат страницу открытой что бы мониторить цены и остатки. Раньше люди видели актуальное только после F5, теперь - сразу.

Как это работает

СЕРВЕР (PHP) --[Event::add]--> PUSH-СЕРВЕР (Node.js, порт 8893) --[WebSocket]--> БРАУЗЕР (JS)
  • При изменении товара (цена/остаток) PHP отправляет событие через \Bitrix\Pull\Event::add()
  • Push-сервер ретранслирует его всем подписанным клиентам по WebSocket
  • JS в браузере ловит событие и обновляет DOM

Никаких AJAX-опросов (ajax pulling) каждые 5 секунд. Только реальные изменения по факту.

Серверная часть: отправляем события

Подключаем модули и пишем функции отправки:

use Bitrix\Main\Loader;
use Bitrix\Pull\Event;

Loader::includeModule('pull');
Loader::includeModule('catalog');

/**
 * Отправка события об изменении цены товара
 */
function sendPriceUpdateEvent(int $productId, float $newPrice, string $currency = 'RUB'): void
{
    Event::add(
        'catalog_price_update',
        'onPriceChanged',
        [
            'productId' => $productId,
            'price'     => $newPrice,
            'currency'  => $currency,
            'timestamp' => time(),
        ]
    );
}

/**
 * Отправка события об изменении остатка
 */
function sendStockUpdateEvent(int $productId, int $newQuantity): void
{
    Event::add(
        'catalog_stock_update',
        'onStockChanged',
        [
            'productId' => $productId,
            'quantity'  => $newQuantity,
            'timestamp' => time(),
        ]
    );
}

Дальше вешаем эти функции на D7-модели каталога. Добавляем в init.php:

use Bitrix\Main\EventManager;

$eventManager = EventManager::getInstance();

// При изменении цены через Bitrix\Catalog\Model\Price
$eventManager->addEventHandler('catalog', 'Bitrix\Catalog\Model\Price::OnAfterUpdate', function($event) {
    $id = $event->getParameter('id');
    $data = $event->getParameter('fields');

    if ($id > 0) {
        sendPriceUpdateEvent(
            (int)$data['PRODUCT_ID'],
            (float)$data['PRICE'],
            (string)$data['CURRENCY']
        );
    }
});

// При изменении товара (остатки, доступность)
$eventManager->addEventHandler('catalog', 'Bitrix\Catalog\Model\Product::OnAfterUpdate', function($event) {
    $id = $event->getParameter('id');
    $data = $event->getParameter('fields');

    if ($id > 0 && array_key_exists('QUANTITY', $data)) {
        sendStockUpdateEvent((int)$id, (int)$data['QUANTITY']);
    }
});

Клиентская часть: ловим в браузере

В шаблоне каталога или в script.js компонента подписываемся на каналы:

BX.ready(function() {

    // Подписка на изменение цен
    BX.PULL.subscribe({
        moduleId: 'catalog_price_update',
        command: 'onPriceChanged',
        callback: function(params) {
            var priceElement = document.querySelector(
                '[data-product-id="' + params.productId + '"] .product-price'
            );
            if (priceElement) {
                priceElement.textContent = new Intl.NumberFormat('ru-RU', {
                    style: 'currency',
                    currency: params.currency || 'RUB'
                }).format(params.price);

                // Подсветка изменения
                priceElement.classList.add('price-updated');
                setTimeout(function() {
                    priceElement.classList.remove('price-updated');
                }, 2000);
            }
        }
    });

    // Подписка на изменение остатков
    BX.PULL.subscribe({
        moduleId: 'catalog_stock_update',
        command: 'onStockChanged',
        callback: function(params) {
            var stockElement = document.querySelector(
                '[data-product-id="' + params.productId + '"] .product-stock'
            );
            if (stockElement) {
                stockElement.textContent = params.quantity > 0
                    ? 'В наличии: ' + params.quantity + ' шт.'
                    : 'Нет в наличии';
                stockElement.className = 'product-stock ' +
                    (params.quantity > 0 ? 'in-stock' : 'out-of-stock');
            }
        }
    });

});

Чтобы JS находил элементы, в шаблоне компонента добавляем data-атрибуты:

<div class="product-card" data-product-id="<?= $arItem['ID'] ?>">
    <span class="product-price"><?= $arItem['PRICE'] ?> руб.</span>
    <span class="product-stock in-stock">В наличии: <?= $arItem['QUANTITY'] ?> шт.</span>
</div>

И немного CSS для анимации:

.price-updated {
    animation: priceFlash 0.6s ease-in-out 3;
}

@keyframes priceFlash {
    0%, 100% { background-color: transparent; }
    50%      { background-color: #fff3cd; }
}

.product-stock.in-stock    { 
    color: #28a745; 
}
.product-stock.out-of-stock { 
    color: #dc3545; 
}

Продвинутый сценарий: перерисовка карточки целиком

Если нужно обновить не только цену, а всю карточку (картинка, описание, свойства) - делаем AJAX-запрос при получении Pull-события:

BX.PULL.subscribe({
    moduleId: 'catalog_price_update',
    command: 'onPriceChanged',
    callback: function(params) {
        BX.ajax.runComponentAction('bitrix:catalog', 'getProductData', {
            mode: 'class',
            data: { productId: params.productId }
        }).then(function(response) {
            var card = document.querySelector(
                '[data-product-id="' + params.productId + '"]'
            );
            if (card) {
                card.querySelector('.product-price').textContent = response.data.price + ' руб.';
                card.querySelector('.product-stock').textContent =
                    'В наличии: ' + response.data.quantity + ' шт.';
            }
        });
    }
});

Проверка что Push-сервер работает

Без запущенного Push-сервера события будут писаться в базу, но до клиента не дойдут. Поднять его можно двумя способами:

  • Штатное веб-окружение Битрикс - в нём Push-сервер уже предустановлен и настроен, нужно только проверить что запущен.
  • Вручную на любом сервере - как описано в заметке про установку CRM Битрикс на Ubuntu, там подробно разобрана настройка Push-сервера.

Базовая проверка:

# Проверить статус
systemctl status push-server

# Запустить если надо
/opt/bitrix/php/bin/php /bitrix/modules/pull/push_server/push-server.php start

# Проверить порт
netstat -tlnp | grep 8893

Из PHP можно проверить доступность:

function isPushServerAvailable(): bool
{
    $config = \Bitrix\Pull\Config::get();
    return !empty($config['server']);
}

Важные моменты реализации

  • Без запущенного Push-сервера ничего не работает. Event::add() сохранит событие в БД, но WebSocket-соединения нет - доставки не будет.
  • Композитный кеш (Composite). Если сайт на композите - обновление DOM через JS может конфликтовать с кешированным HTML. Цены и остатки лучше вынести в динамические области.
  • Доставка не гарантируется. При нестабильном соединении пакет может не дойти. Для критичных операций (оформление заказа) всегда делайте серверную проверку.
  • Pull не создаёт подписки автоматом. Каталог регистрируется в Pull через событие onGetDependentModule, но подписки на каналы нужно писать руками.

Куда что класть: карта файлов для штатного каталога

Пути ниже - для стандартного компонента bitrix:catalog с шаблоном .default. У себя замените название шаблона и компонента на свои.

  • Функции sendPriceUpdateEvent(), sendStockUpdateEvent(), isPushServerAvailable() - в /bitrix/php_interface/init.php или /local/php_interface/init.php. Добавить в конец файла. Либо вынести в отдельный /local/php_interface/pull_events.php и подключить через require_once в init.php - так код чище.
  • Регистрация обработчиков событий через EventManager - туда же, в init.php. Добавить после функций, порядок не важен.
  • JS-код подписки на Pull - в /bitrix/templates/.default/components/bitrix/catalog/.default/script.js. Заменить .default на название своего шаблона и компонента. Либо разместить в template.php внутри <script>-тега.
  • data-атрибут data-product-id - в /bitrix/templates/.default/components/bitrix/catalog/.default/template.php или component_epilog.php. Добавить к контейнеру товара: <div class="product-card" data-product-id="<?= $arItem['ID'] ?>">
  • CSS для анимации price-updated - в /bitrix/templates/.default/components/bitrix/catalog/.default/style.css. Можно добавить и в общий template_styles.css шаблона - тоже сработает.

Для других компонентов вывода (catalog.section, catalog.element, news.list и т.д.): принцип тот же - JS в script.js компонента, data-атрибуты в template.php, стили в style.css.

Проверка после установки:

  • Открыть страницу каталога на витрине
  • В консоли браузера (F12) проверить что BX.PULL определён - значит модуль подключен
  • В админке изменить цену любого товара
  • На витрине цена должна моргнуть жёлтым и обновиться без перезагрузки
  • Если не работает - проверить запущен ли Push-сервер (см. раздел выше)

Собрать обновление цен и остатков в реальном времени на Bitrix Push & Pull - час работы, если всё уже настроено. Задача не сложная, но результат даёт вау-эффект: цены меняются на глазах у покупателя, никаких F5, никаких просроченных остатков в корзине.

Где это можно использовать и зачем

  • Акций и распродаж (цена меняется автоматически по правилам корзины)
  • 1С-обмена (выгрузили новые остатки - они сразу на витрине)
  • Мультискладского учёта (резервирование на одном складе видно на всех)
  • Аукционов и динамического ценообразования

Модуль pull есть в любой редакции Битрикс начиная с "Малый бизнес". Единственное что Push-сервер нужно поднимать отдельно. Но если сайт уже использует живую ленту или мессенджер - он уже работает, просто подпишитесь на свои каналы.

Услуги Стоимость разработки на 1С-Битрикс

Стоимость разработки сайта зависит от объёма и сложности проекта. Ниже приведены ориентировочные цены, как правило не выходят за обозначенные рамки. Срок разработки зависит от сложности проекта: как правило называю сроки с запасом.
Финальная стоимость и сроки разработки обговариваются на этапе обсуждения. Скачайте опросник на разработку, заполните как можно подробнее и вышлите удобным способом. После ознакомления смогу задать уточняющие вопросы и оценить проект.
Поддержка и доработки проектов
от 3 000 руб. от 1 часа

Выполнение доработок любой сложности. Поддержка, модернизация и расширение функционала существующих проектов. Решение задач: от мелких правок вёрстки до разработки новых модулей.

Подробнее
Сайт на готовом решении 1С-Битрикс
от 70 000 руб. от 5-ти дней

Вариант для тех, кто не хочет тратить много средств на индивидуальный проект, и не имеет серьезных требований к сайту. Магазин, быстро запускается на базе одного из 200-та готовых решений.

Подробнее
Индивидуальная разработка магазина
от 400 000 руб. от 5-ти недель

Разработка магазина на 1С-Битрикс с нуля. Дизайн, сборка и оптимизация производительности под конкретный проект и требования. Реализация любого функционала без ограничений готовых решений.

Подробнее
Мобильное приложение
от 300 000 руб. от 4-х недель

Разработка кроссплатформенного мобильного приложения, которое не уступает нативным решениям как в производительности, так и пользовательском опыте. Публикуется в AppStore, GooglePlay и RuStore

Подробнее
Инфоресурс
от 170 000 руб. от 3-х недель

Информационный ресурс любой сложности. Сайт для СМИ, городской портал или многопользовательская доска объявлений. Внутренние форумы, блоги- по необходимости.

Подробнее
Сайт компании
от 150 000 руб. от 2-х недель

Корпоративный сайт с информационными разделами, каталогом товаров или услуг. Включает формы обратной связи карточек каталога, любое количество статичных и динамичных разделов.

Подробнее

Включено в стоимость разработки:

  • Лицензия на 1С-Битрикс необходимой редакции, дополнительные модули, для реализации функционала и видео-инструкции по работе с готовым проектом
  • Оптимизация программной части проекта и конфигурации сервера под максимальную скорость работы. Базовая СЕО оптимизация и добавление сайта в поисковые системы.

Блог-note Заметки по 1С-Битрикс