Configuring Abandoned Cart Tracking in 1C-Bitrix
An abandoned cart is when a product has been added but the order was never completed. Statistically, 65–75% of carts are abandoned. Recovering even 10% of them means increasing revenue without increasing traffic. The first step is to set up tracking: understand how many carts are being abandoned, who these users are, and what they left behind.
Where Cart Data Is Stored in Bitrix
The cart in 1C-Bitrix is linked to a virtual user (fuser) — an anonymous session identifier.
-
b_sale_fuser— a record for each visitor (anonymous and authenticated) -
b_sale_basket— cart items linked toFUSER_ID -
b_sale_basket.DATE_INSERT,b_sale_basket.DATE_UPDATE— date of addition and last modification
On login, the session's FUSER_ID is associated with the real user: b_sale_fuser.USER_ID. Before login, USER_ID = null.
Defining an "Abandoned" Cart
A cart is considered abandoned if:
-
b_sale_basketcontains items - No completed order exists for that
FUSER_IDwithin the last N hours - The cart's last update (
DATE_UPDATE) was more than M hours ago
// Select abandoned carts: items added more than 1 hour ago, no order placed
$cutoffTime = new \Bitrix\Main\Type\DateTime();
$cutoffTime->add('-1 hour');
$abandonedFusers = \Bitrix\Sale\BasketTable::getList([
'filter' => [
'<DATE_UPDATE' => $cutoffTime,
'=ORDER_ID' => false, // not linked to an order
],
'group' => ['FUSER_ID'],
'select' => ['FUSER_ID'],
])->fetchAll();
After obtaining the FUSER_ID, check whether it has a USER_ID (authenticated user):
foreach ($abandonedFusers as $row) {
$fuser = \Bitrix\Sale\FuserTable::getList([
'filter' => ['=ID' => $row['FUSER_ID']],
'select' => ['USER_ID', 'DATE_UPDATE'],
])->fetch();
if (!$fuser || !$fuser['USER_ID']) continue; // anonymous — no contact for communication
// Get the user's email
$user = \Bitrix\Main\UserTable::getById($fuser['USER_ID'])->fetch();
$email = $user['EMAIL'] ?? '';
if (!$email) continue;
// Add to the notification queue
AbandonedCartQueue::push($fuser['USER_ID'], $row['FUSER_ID']);
}
Tracking via Analytics (GA4 / Yandex.Metrica)
In parallel with server-side tracking, configure client-side events:
// When a product is added to the cart
BX.addCustomEvent('onBasketItemAdded', function(data) {
gtag('event', 'add_to_cart', {
currency: 'RUB',
value: data.price * data.quantity,
items: [{
item_id: data.productId,
item_name: data.productName,
price: data.price,
quantity: data.quantity,
}],
});
ym(METRIKA_ID, 'reachGoal', 'add_to_cart');
});
// When a product is removed from the cart
BX.addCustomEvent('onBasketItemDeleted', function(data) {
gtag('event', 'remove_from_cart', { /* ... */ });
});
In GA4: Reports → Monetisation → Checkout journey — shows the funnel from add-to-cart to purchase with drop-off percentages at each step.
Agent for Regular Abandoned Cart Detection
Run the agent every 30–60 minutes:
// In /local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main', 'OnBeforeLocalRedirect', // or register via agents
function() {}
);
// Agent (Settings → Agents)
function checkAbandonedCarts(): string
{
AbandonedCartService::processNew();
return __FUNCTION__ . '();'; // return for recurring execution
}
AbandonedCartService::processNew() — your class that selects newly abandoned carts (not yet marked as processed) and adds them to the notification queue.
Storing Processing Status
Create a table to store abandoned cart status:
class AbandonedCartTable extends \Bitrix\Main\ORM\Data\DataManager
{
public static function getTableName(): string { return 'local_abandoned_cart'; }
public static function getMap(): array
{
return [
new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
new \Bitrix\Main\ORM\Fields\IntegerField('USER_ID'),
new \Bitrix\Main\ORM\Fields\IntegerField('FUSER_ID'),
new \Bitrix\Main\ORM\Fields\StringField('STATUS'), // new, email_sent, sms_sent, recovered
new \Bitrix\Main\ORM\Fields\DatetimeField('DETECTED_AT'),
new \Bitrix\Main\ORM\Fields\DatetimeField('EMAIL_SENT_AT'),
new \Bitrix\Main\ORM\Fields\DatetimeField('SMS_SENT_AT'),
new \Bitrix\Main\ORM\Fields\FloatField('CART_VALUE'),
];
}
}
When a user places an order, update the status to recovered via the OnSaleOrderSaved event.
Reporting: How Many Are Recovered
After a few weeks of operation you have data: how many carts were detected, how many communications were sent, and how many conversions occurred after the communication. Build a simple report:
SELECT
DATE(detected_at) AS date,
COUNT(*) AS detected,
SUM(CASE WHEN status = 'recovered' THEN 1 ELSE 0 END) AS recovered,
ROUND(SUM(CASE WHEN status = 'recovered' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) AS recovery_rate
FROM local_abandoned_cart
GROUP BY DATE(detected_at)
ORDER BY date DESC;
Timelines: basic server-side tracking and agent — 1–2 days. Client-side GA4/Metrica events — 4 hours. Status table and reporting — an additional 1 day.







