Configuring Online and Offline Order Synchronization in 1C-Bitrix
In a retail network, orders come from three places: the online store, POS terminals at locations, and operator calls. Without synchronization, there are three isolated streams: the same stock is reserved three times, the office manager cannot see online orders, and a courier delivers a product that is already out of stock. Online/offline synchronization in Bitrix is primarily about working with b_sale_order, warehouse stock levels, and the data transfer channel to 1C or another accounting system.
Order Stream Architecture
The central accounting system defines all logic. Typical options:
1C as master — 1C is the source of truth for orders and stock. Bitrix passes online orders to 1C; offline sales are recorded there too, and stock levels are synchronized back.
Bitrix as master — all orders (online and offline via POS) are aggregated in Bitrix; 1C receives data for accounting.
A hybrid scheme with no clear master is a source of chaos. The choice is made before development begins.
Synchronization via CommerceML
The standard 1C–Bitrix exchange (/bitrix/admin/1c_exchange.php) covers the basic scenario: orders from Bitrix go to 1C, and statuses come back. Configuration is in the sale module:
Administration → Online Store → Settings → "1C" tab
Limitation: the standard exchange is batch-based, running every N minutes. For real-time synchronization, webhooks or queues are required.
Offline Orders: How to Push Them into Bitrix from a POS
When a cashier processes a sale through a POS (Evotor, 1C:Retail), the options are:
- Create an order in Bitrix via API and immediately mark it as paid
- Or only deduct stock without creating an order (if offline sales do not need to appear in the online store history)
Creating an order via the Bitrix REST API (/rest/v1/sale.order.add) or via PHP:
// Creating an offline order in Bitrix
\Bitrix\Main\Loader::includeModule('sale');
\Bitrix\Main\Loader::includeModule('catalog');
$order = \Bitrix\Sale\Order::create(SITE_ID, $userId);
$order->setField('CURRENCY', 'RUB');
$order->setField('USER_DESCRIPTION', 'In-store sale: ' . $storeName);
$basket = \Bitrix\Sale\Basket::create(SITE_ID);
foreach ($items as $item) {
$basketItem = $basket->createItem('catalog', $item['PRODUCT_ID']);
$basketItem->setFields([
'QUANTITY' => $item['QUANTITY'],
'CURRENCY' => 'RUB',
'LID' => SITE_ID,
'PRODUCT_PROVIDER_CLASS' => '\CCatalogProductProvider',
]);
}
$order->setBasket($basket);
// Mark the order source
$order->setField('STATUS_ID', 'F'); // Completed — offline sale is finalized
$order->setField('ADDITIONAL_INFO', json_encode([
'source' => 'offline',
'store' => $storeId,
'pos_transaction_id' => $transactionId,
]));
$result = $order->save();
Stock Reservation for Online Orders
The critical question: when to reserve a product for an online order? On add-to-cart — too early (many abandoned carts). On payment — risk of selling something already taken from the store shelf.
Bitrix manages reservations via b_catalog_store_product and b_sale_product_reserve:
// Reserve product at the time of order placement
\Bitrix\Catalog\StoreProductTable::reserveProduct(
$productId,
$storeId,
$quantity,
$orderId
);
// Release reservation on cancellation
\Bitrix\Catalog\StoreProductTable::releaseProductReserve(
$productId,
$orderId
);
For Click & Collect the logic differs: reserve at a specific warehouse (pickup location), not from the general pool.
Real-Time Stock Synchronization
A stock change on any side (online order, POS sale, supplier receipt) must be reflected everywhere immediately. For this — an event queue:
// On stock change — push to queue
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'catalog', 'OnProductUpdate',
function (\Bitrix\Main\Event $event) {
$productId = $event->getParameter('ID');
$fields = $event->getParameter('FIELDS');
if (isset($fields['QUANTITY'])) {
// Queue for synchronization with 1C
\Local\Queue\InventorySync::push([
'product_id' => $productId,
'quantity' => $fields['QUANTITY'],
'timestamp' => time(),
]);
}
}
);
The queue is processed by a Bitrix agent or a worker (supervisor + PHP script), which sends changes in batches to 1C or another accounting system.
Conflicts: Simultaneous Sales
Stock = 1, an online order and a POS sale are placed simultaneously. Both streams see stock = 1 and accept the order. The solution — optimistic locking at the DB level:
-- Atomic deduction with a check
UPDATE b_catalog_store_product
SET QUANTITY = QUANTITY - 1
WHERE PRODUCT_ID = ? AND STORE_ID = ? AND QUANTITY >= 1;
-- If affected_rows = 0 — no stock, reject the order
In Bitrix this is implemented via CCatalogProductProvider with quantity checked inside a transaction.
Setup Timeline
Setting up bidirectional order exchange (online ↔ offline) with stock reservation and queue-based synchronization — 5–10 business days depending on the POS type and accounting system.







