Setting up abandoned product view trigger in 1C-Bitrix
A user opened a product card, spent 40 seconds on it and left without adding to cart. This is a hot lead: there was interest, no decision. The abandoned view trigger captures this moment and launches automation — email, push or task to manager. In Bitrix this is implemented via the sale module combined with marketing automation triggers in CRM or via custom agent.
Where view data is stored
The catalog module writes view history to the b_catalog_viewed_product table. Structure: USER_ID, PRODUCT_ID, SITE_ID, DATE_VISIT. The table is updated on each hit to the detail page via the bitrix:catalog.element component — inside it calls CCatalogViewedProduct::Add().
Anonymous users are recorded with USER_ID equal to FUSER_ID from b_sale_fuser — this is important: the field in b_catalog_viewed_product is called USER_ID, but for guests it stores fake user ID, not real b_user.ID.
Determining "abandonment"
A view is considered abandoned if three conditions are met: the product was viewed, within N minutes after the view the product was not added to cart (b_sale_basket), and the user did not complete an order with this product (b_sale_order_basket). Time threshold is a configurable parameter, usually 30–60 minutes.
Query to find abandoned views in the last 2 hours:
SELECT v.USER_ID, v.PRODUCT_ID, v.DATE_VISIT
FROM b_catalog_viewed_product v
LEFT JOIN b_sale_basket b
ON b.FUSER_ID = v.USER_ID
AND b.PRODUCT_ID = v.PRODUCT_ID
AND b.DATE_INSERT > v.DATE_VISIT
WHERE v.DATE_VISIT > NOW() - INTERVAL '2 hours'
AND b.ID IS NULL;
Implementation via Bitrix agent
Standard mechanism — an agent in b_agent that runs every 15–30 minutes. The agent checks b_catalog_viewed_product table, finds candidates based on conditions above, and for each creates an event in marketing automation or sends email.
function AbandonedViewAgent(): string
{
$cutoffTime = new \Bitrix\Main\Type\DateTime();
$cutoffTime->add('-60 minutes');
$thresholdTime = new \Bitrix\Main\Type\DateTime();
$thresholdTime->add('-30 minutes');
$viewed = \Bitrix\Catalog\ViewedProductTable::getList([
'filter' => [
'>=DATE_VISIT' => $cutoffTime,
'<=DATE_VISIT' => $thresholdTime,
],
'select' => ['USER_ID', 'PRODUCT_ID', 'DATE_VISIT'],
])->fetchAll();
foreach ($viewed as $row) {
// Check if product is in cart after view
$inBasket = \Bitrix\Sale\BasketTable::getList([
'filter' => [
'FUSER_ID' => $row['USER_ID'],
'PRODUCT_ID' => $row['PRODUCT_ID'],
'>=DATE_INSERT' => $row['DATE_VISIT'],
],
'limit' => 1,
])->fetch();
if (!$inBasket) {
// Launch automation scenario
\Bitrix\Marketing\Automation\Trigger\BaseTrigger::send(
'CATALOG_ABANDONED_VIEW',
['PRODUCT_ID' => $row['PRODUCT_ID'], 'FUSER_ID' => $row['USER_ID']]
);
}
}
return 'AbandonedViewAgent();';
}
Integration with Marketing Automation
In the marketing module (Bitrix24 On-Premise) triggers are created via "Automation → Triggers". For a custom trigger, you need to register a class inheriting from \Bitrix\Marketing\Automation\Trigger\BaseTrigger and write it in b_marketing_trigger. Trigger takes PRODUCT_ID and FUSER_ID, builds a segment, and launches the mailing scenario.
If Bitrix24 is not used, alternative — subscribe module: create an event via \Bitrix\Main\Mail\Event::send() with email type CATALOG_ABANDONED_VIEW, in the template of which product data from the infoblock is substituted.
Deduplication of triggers
Agent should remember what it has already processed. Otherwise each run will find the same records again. Solution: separate table bl_abandoned_view_sent with fields (fuser_id, product_id, sent_at). Before sending — check for record presence. Unique index on (fuser_id, product_id) protects against duplicates during parallel agent runs.
What we configure
- Registration of
AbandonedViewAgentinb_agentwith needed interval - Query to
b_catalog_viewed_productaccounting for FUSER/USER difference for anonymous users - Deduplication table
bl_abandoned_view_sent - Email or push notification template with viewed product data
- Logic excluding users who already added product to cart or bought it







