Integrating Returns with 1C in 1C-Bitrix
When a buyer submits a return request on the website, the back office needs to create several related documents: a sales reversal, a discrepancy act, and a corrective invoice. If Bitrix and 1C operate independently, all of this is done manually, and the likelihood of stock and settlement discrepancies grows with each passing week.
Returns integration is a separate contour within the 1C–Bitrix exchange, because the standard sale module exchanges orders — not corrective documents. These must be described separately and explicitly.
How Returns Work in Bitrix
A return in Bitrix is represented by the class \Bitrix\Sale\Payment\PaymentReturn in the context of a payment, or \Bitrix\Sale\Shipment\ShipmentReturn in the context of a shipment. From a database perspective, these are records in the following tables:
-
b_sale_order_return— the return request -
b_sale_order_return_item— return line items -
b_sale_order_return_shipment— link to the shipment
Return statuses (RETURN_STATUS): NEW, PROCESSED, COMPLETED, REJECTED. The trigger for transmitting to 1C should be the transition to COMPLETED — prior to this point, the data may still change.
Exchange Scheme
The standard bitrix.1c module transmits orders via sale.order in CommerceML 2. Returns are not described in CommerceML — this is a custom node that must be added to the schema manually.
Bitrix: return transitions to COMPLETED
→ OnSaleReturnComplete event handler
→ Build XML node <Return>
→ Write to exchange queue (b_agent / custom table)
→ Next 1C sync session
→ 1C creates document "Customer Return"
→ Confirmation from 1C → return status updated
Use the OnSaleReturnComplete event from the sale module:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleReturnComplete',
function (\Bitrix\Main\Event $event) {
$return = $event->getParameter('ENTITY');
ReturnExchangeQueue::push($return->getId());
}
);
XML Structure for Transmission to 1C
1C works with CommerceML, but it is more convenient to pass returns as a separate node inside <КоммерческаяИнформация>:
<Возврат>
<Ид>RETURN-{return_id}</Ид>
<НомерДокумента>R-{return_id}</НомерДокумента>
<Дата>{date}</Дата>
<ЗаказИд>{order_id}</ЗаказИд>
<Контрагент>
<Ид>{user_id}</Ид>
<Наименование>{user_name}</Наименование>
</Контрагент>
<Товары>
<Товар>
<Ид>{product_id}</Ид>
<Наименование>{product_name}</Наименование>
<Количество>{qty}</Количество>
<ЦенаЗаЕдиницу>{price}</ЦенаЗаЕдиницу>
<Сумма>{sum}</Сумма>
</Товар>
</Товары>
<СуммаВозврата>{total}</СуммаВозврата>
<Причина>{reason}</Причина>
</Возврат>
On the 1C side, a handler is configured in the configuration that reads this node and creates a "Customer Return" document with the correct reference to the original transaction using ЗаказИд.
Stock Synchronization After a Return
After the return is successfully posted in 1C, stock levels are updated. It is important that Bitrix receives this signal and updates b_catalog_store_product. The standard stock exchange (offers.xml) will handle this in the next session, but if sessions are infrequent, a separate request is needed:
// 1C calls this URL after posting the return
// /bitrix/admin/1c_exchange.php?type=catalog&mode=checkauth&return_confirm=Y&return_id=123
$returnId = (int)$_GET['return_id'];
ReturnSyncService::updateStocksFromReturn($returnId);
In updateStocksFromReturn — direct update of b_catalog_store_product via \Bitrix\Catalog\StoreProductTable, with a call to \Bitrix\Catalog\StoreBatchService if batch recalculation is needed.
Sending Back the Status Confirmation
After the document is created in 1C, a confirmation must be returned to Bitrix:
| Situation | Status in Bitrix |
|---|---|
| Document posted in 1C | COMPLETED, flag 1C_SYNCED = Y |
| Items not available for reversal | ERROR_1C, manager notification |
| Return rejected in 1C | REJECTED, comment from 1C |
Updating the status on the Bitrix side:
$return = \Bitrix\Sale\OrderReturn::load($returnId);
$return->setField('STATUS', 'COMPLETED');
$return->setField('COMMENTS', '1C: document #' . $doc1cId);
$return->save();
Case Study: Wholesale Supplier, 150+ Returns per Month
Client — a construction chemicals distributor. Returns came through two channels: via the dealer's personal account on the website and directly from logistics. Problem: the 1C accounting department did not see returns from the website and posted documents manually with a 3–5 day delay. This led to incorrect warehouse stock in Bitrix and conflicts on subsequent orders.
What was done:
- The
OnSaleReturnCompleteevent writes the return to thelocal_1c_return_queuetable. - An agent (every 10 minutes) builds the XML and transmits it to the 1C endpoint via HTTP to the standard exchange handler.
- 1C posts the document, updates stock, and sends a confirmation to
/api/1c/return-confirm/{returnId}. - The webhook in Bitrix updates the return status and recalculates
b_catalog_store_productfor the affected SKUs.
| Metric | Before | After |
|---|---|---|
| Delay in reflecting return in 1C | 3–5 days | < 15 minutes |
| Stock discrepancies (weekly reconciliation) | 8–12 items | 0–1 item |
| Manual operations in 1C | ~150/month | < 5/month (exceptions) |
Scope of Work
- Analysis of the 1C configuration: "Customer Return" document type, order reference fields
- Development of the XML schema and PHP generator for Bitrix
-
OnSaleReturnCompleteevent handler, exchange queue - Webhook endpoint for confirmation from 1C
- Stock update in Bitrix after confirmation
- Testing across multiple scenarios: partial return, return without shipment, return with multiple warehouses
Timeline: basic integration (return transmission + confirmation) — 3–4 weeks. With real-time stock synchronization and non-standard scenario handling — 6–8 weeks.







