Setting up stock arrival trigger in 1C-Bitrix
Product is out of stock, user clicks "Notify me when available" and... nothing. Email never arrives because no one configured the connection between stock replenishment event and the waiting list. This is a common case: the subscription form exists, stock is replenished, but automation isn't launched.
Where inventory and how it's updated
In Bitrix inventory lives in two places depending on configuration:
Simple catalog (without warehouse tracking): field CATALOG_QUANTITY in b_catalog_product. Updated directly via CCatalogProduct::Update() or via 1C exchange.
Multi-warehouse tracking (module catalog + warehouses): table b_catalog_store_product with fields PRODUCT_ID, STORE_ID, AMOUNT. Total inventory is aggregated. On 1C exchange via sale.crm.lead module or standard exchange data is written to b_catalog_store_product.
Inventory change event — OnProductUpdate in catalog module. It fires on any change to b_catalog_product record, including quantity:
AddEventHandler('catalog', 'OnProductUpdate', function($id, $fields) {
if (isset($fields['QUANTITY']) && $fields['QUANTITY'] > 0) {
// Product arrived at warehouse — was zero before
checkAndNotifyWaitlist($id);
}
});
Problem: OnProductUpdate fires on any product change, not only on stock replenishment. To filter only "appeared from zero" event you need to compare previous value. Before PHP event, data is still in DB, so read old value in OnBeforeProductUpdate:
AddEventHandler('catalog', 'OnBeforeProductUpdate', function($id, &$fields) {
$old = \Bitrix\Catalog\ProductTable::getByPrimary($id, ['select' => ['QUANTITY']])->fetch();
$fields['_OLD_QUANTITY'] = (float)($old['QUANTITY'] ?? 0);
});
Storing stock arrival subscriptions
The catalog module has no built-in mechanism for "notify on arrival". You need your own table:
CREATE TABLE bl_stock_notify (
id SERIAL PRIMARY KEY,
product_id INT NOT NULL,
user_id INT,
email VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
notified_at TIMESTAMP,
UNIQUE (product_id, email)
);
Form on site writes to this table. Unique key (product_id, email) protects against duplicates on resubscription.
Replenishment handler
function checkAndNotifyWaitlist(int $productId): void
{
$connection = \Bitrix\Main\Application::getConnection();
$waitlist = $connection->query(
"SELECT * FROM bl_stock_notify WHERE product_id = {$productId} AND notified_at IS NULL"
)->fetchAll();
if (empty($waitlist)) {
return;
}
$product = \CIBlockElement::GetByID($productId)->GetNextElement();
$name = $product->GetField('NAME');
$url = $product->GetField('DETAIL_PAGE_URL');
foreach ($waitlist as $row) {
\Bitrix\Main\Mail\Event::send([
'EVENT_NAME' => 'STOCK_ARRIVED',
'LID' => SITE_ID,
'C_FIELDS' => [
'EMAIL' => $row['email'],
'PRODUCT_NAME' => $name,
'PRODUCT_URL' => 'https://' . $_SERVER['SERVER_NAME'] . $url,
],
]);
$connection->queryExecute(
"UPDATE bl_stock_notify SET notified_at = NOW() WHERE id = {$row['id']}"
);
}
}
Integration with multi-warehouse tracking
With multi-warehouse tracking, OnProductUpdate doesn't fire on b_catalog_store_product changes. For warehouse operations you need to subscribe to deeper-level events in catalog module — either use hook after warehouse document record via \Bitrix\Catalog\Document\DocumentTable.
Alternative approach: agent that checks b_catalog_store_product every 5 minutes for non-zero balances on products in waitlist. Less elegant but more stable with non-standard inventory update schemes (e.g., direct UPDATE via 1C connector).
What we configure
- Handlers
OnBeforeProductUpdateandOnProductUpdatewith check for "0 → N" transition - Table
bl_stock_notifyand subscription form on site - Email template
STOCK_ARRIVEDin administrative mail events section - For multi-warehouse configuration — agent or warehouse document handler
- Logic for partial arrival: if 2 units arrived and 10 subscribed — notify all or first two?







