Product Subscription Module Development for 1C-Bitrix
"Notify me when back in stock" is a basic requirement for online stores where products are not always available. Standard Bitrix has no such mechanism. The task is often solved with a feedback form handled manually, which does not scale. A subscription module addresses this systematically: automatic queue placement, notifications when stock arrives, statistics on item demand.
Data Model
Module vendor.waitlist:
-
b_vendor_waitlist_subscription— subscriptions: id, user_id, email (for non-registered users), product_id, sku_id (trade offer), created_at, notified_at, status (active/notified/cancelled) -
b_vendor_waitlist_notify_log— notification log: id, subscription_id, sent_at, result (sent/failed), error
Subscribing to a Specific SKU
When a customer clicks "Notify me when available", they enter their email or subscribe via their account:
class WaitlistService
{
public function subscribe(int $productId, ?int $skuId, int $userId, string $email): SubscribeResult
{
// Check for an existing active subscription from this user/email for this product
$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();
}
}
Trigger on Stock Arrival
When stock changes via the API or 1C synchronization, the OnProductStockChanged event fires (a custom module event triggered from the OnAfterIBlockElementUpdate handler):
AddEventHandler('iblock', 'OnAfterIBlockElementUpdate', ['\Vendor\Waitlist\StockWatcher', 'onElementUpdate']);
public static function onElementUpdate(array &$fields): void
{
// Check: is this a trade offer? Did QUANTITY change?
if (!isset($fields['PROPERTY_VALUES']['QUANTITY'])) return;
$newQty = (int)$fields['PROPERTY_VALUES']['QUANTITY'];
if ($newQty <= 0) return;
// Find active subscriptions for this SKU
$subscriptions = SubscriptionTable::getList([
'filter' => ['SKU_ID' => $fields['ID'], 'STATUS' => 'active'],
])->fetchAll();
foreach ($subscriptions as $sub) {
NotifyQueueTable::add(['SUBSCRIPTION_ID' => $sub['ID']]);
}
}
Notifications are sent from the queue by an agent — without blocking the element save process.
Notification Agent
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();
// Re-check stock: it may have sold out while the agent was sleeping
$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();';
}
Widget on the Product Card
The vendor:waitlist.button component is connected to the product card. It determines:
- Whether the current user has an active subscription for this SKU
- Displays the "Notify me when available" or "You are subscribed" button
- For non-logged-in users — an email input form without registration
Statistics and Analytics
In the admin interface:
- Top products by subscription count — understanding real demand
- Conversion: how many users purchased after receiving a notification
- Average time from subscription to notification
- List of "eternal waiters" — subscriptions older than N days without a notification
Demand statistics are directly useful for the purchasing department.
Development Timeline
| Stage | Duration |
|---|---|
| ORM tables, subscription service | 1 day |
| Stock change trigger | 1 day |
| Notification queue and agent | 1 day |
| Product card widget | 1 day |
| Notification email template | 0.5 days |
| Statistics and analytics | 1 day |
| Admin interface | 1 day |
| Testing | 0.5 days |
Total: 7 working days. Support for push notifications as an additional channel — +1 day.







