Setting up an abandoned product view trigger in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    565
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    980

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 AbandonedViewAgent in b_agent with needed interval
  • Query to b_catalog_viewed_product accounting 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