Разработка модуля подписки на товары 1С-Битрикс
«Уведомить, когда появится в наличии» — базовая потребность для интернет-магазина с товарами не всегда в наличии. Стандартный Битрикс не имеет такого механизма. Задачу часто закрывают формой обратной связи с ручной обработкой, что плохо масштабируется. Модуль подписки решает задачу системно: автоматическая постановка в очередь, отправка уведомлений при появлении остатка, статистика по востребованности позиций.
Модель данных
Модуль vendor.waitlist:
-
b_vendor_waitlist_subscription— подписки: id, user_id, email (для незарегистрированных), product_id, sku_id (торговое предложение), created_at, notified_at, status (active/notified/cancelled) -
b_vendor_waitlist_notify_log— журнал уведомлений: id, subscription_id, sent_at, result (sent/failed), error
Подписка на конкретное SKU
При нажатии «Уведомить о поступлении» клиент вводит email или подписывается через учётную запись:
class WaitlistService
{
public function subscribe(int $productId, ?int $skuId, int $userId, string $email): SubscribeResult
{
// Проверяем, нет ли уже активной подписки от этого пользователя/email на этот товар
$existing = SubscriptionTable::getList([
'filter' => [
'PRODUCT_ID' => $productId,
'SKU_ID' => $skuId,
'STATUS' => 'active',
'=EMAIL' => $email,
],
])->fetch();
if ($existing) {
return SubscribeResult::alreadySubscribed();
}
SubscriptionTable::add([
'USER_ID' => $userId ?: null,
'EMAIL' => $email,
'PRODUCT_ID' => $productId,
'SKU_ID' => $skuId,
'STATUS' => 'active',
]);
return SubscribeResult::success();
}
}
Триггер при поступлении товара
При изменении остатков через API или 1С-синхронизацию срабатывает событие OnProductStockChanged (кастомное событие модуля, вызывается из обработчика OnAfterIBlockElementUpdate):
AddEventHandler('iblock', 'OnAfterIBlockElementUpdate', ['\Vendor\Waitlist\StockWatcher', 'onElementUpdate']);
public static function onElementUpdate(array &$fields): void
{
// Проверяем: это торговое предложение? Изменился ли QUANTITY?
if (!isset($fields['PROPERTY_VALUES']['QUANTITY'])) return;
$newQty = (int)$fields['PROPERTY_VALUES']['QUANTITY'];
if ($newQty <= 0) return;
// Ищем активные подписки на этот SKU
$subscriptions = SubscriptionTable::getList([
'filter' => ['SKU_ID' => $fields['ID'], 'STATUS' => 'active'],
])->fetchAll();
foreach ($subscriptions as $sub) {
NotifyQueueTable::add(['SUBSCRIPTION_ID' => $sub['ID']]);
}
}
Уведомления отправляются из очереди агентом — не блокируя процесс сохранения элемента.
Агент отправки уведомлений
public static function run(): string
{
$queue = NotifyQueueTable::getList(['limit' => 100, 'filter' => ['STATUS' => 'pending']])->fetchAll();
foreach ($queue as $item) {
$sub = SubscriptionTable::getById($item['SUBSCRIPTION_ID'])->fetch();
// Повторно проверяем остаток: вдруг раскупили пока агент спал
$qty = \CIBlockElement::GetProperty($sub['SKU_ID'], false, 'QUANTITY', true);
if ($qty <= 0) {
NotifyQueueTable::delete($item['ID']);
continue;
}
$result = \Bitrix\Main\Mail\Event::send([
'EVENT_NAME' => 'WAITLIST_PRODUCT_AVAILABLE',
'LID' => SITE_ID,
'C_FIELDS' => [
'EMAIL' => $sub['EMAIL'],
'PRODUCT_ID' => $sub['PRODUCT_ID'],
'PRODUCT_URL' => \CIBlockElement::GetDetailPageUrl($sub['PRODUCT_ID']),
],
]);
SubscriptionTable::update($sub['ID'], ['STATUS' => 'notified', 'NOTIFIED_AT' => new DateTime()]);
}
return '\Vendor\Waitlist\NotifyAgent::run();';
}
Виджет на карточке товара
Компонент vendor:waitlist.button подключается к карточке товара. Определяет:
- Есть ли у текущего пользователя активная подписка на данный SKU
- Отображает кнопку «Уведомить о поступлении» или «Вы подписаны»
- Для незалогиненных — форма ввода email без регистрации
Статистика и аналитика
В административном интерфейсе:
- Топ товаров по количеству подписок — понимание реального спроса
- Конверсия: сколько пользователей купили после получения уведомления
- Среднее время от подписки до уведомления
- Список «вечных» ожидателей — подписки старше N дней без уведомления
Статистика спроса напрямую полезна для закупочной службы.
Сроки разработки
| Этап | Срок |
|---|---|
| ORM-таблицы, сервис подписки | 1 день |
| Триггер изменения остатков | 1 день |
| Очередь и агент уведомлений | 1 день |
| Виджет карточки товара | 1 день |
| Email-шаблон уведомления | 0.5 дня |
| Статистика и аналитика | 1 день |
| Административный интерфейс | 1 день |
| Тестирование | 0.5 дня |
Итого: 7 рабочих дней. Поддержка push-уведомлений как дополнительного канала — +1 день.







