Setting Up Warehouse Operations Management in 1C-Bitrix
The warehouse system in Bitrix starts to fail at a specific moment: when the number of warehouses exceeds one, and the reservation logic is configured at the store level rather than the warehouse level. Orders reserve inventory globally, but the manager sees "in stock" when the product isn't physically available at the required warehouse.
Warehouse accounting architecture in the catalog module
Warehouse accounting is implemented through the catalog module. Each warehouse is a record in the b_catalog_store table. Balances are stored in b_catalog_store_product with fields STORE_ID, PRODUCT_ID, AMOUNT, QUANTITY_RESERVED. The connection to warehouse is through STORE_ID.
Product movement is recorded in b_catalog_docs_element — rows of receipt/shipment/transfer documents. The document itself is in b_catalog_docs with fields DOC_TYPE (A — receipt, S — shipment, M — transfer, I — inventory count) and STATUS (N — draft, Y — conducted).
API for working with warehouses:
// Get balances by warehouse
$storeProducts = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['STORE_ID' => 3, '>AMOUNT' => 0],
'select' => ['PRODUCT_ID', 'AMOUNT', 'QUANTITY_RESERVED'],
]);
// Available balance = AMOUNT - QUANTITY_RESERVED
Reservation and its conflicts
Reservation is managed through the sale module settings — parameter allow_reservation in b_option. Reserve can be tied to a specific order warehouse or to any warehouse with available inventory. The second option is the source of problems in multi-warehouse setup.
When an order is created, reserve is created via \Bitrix\Sale\Internals\ReserveTable method. Records link ORDER_ID, BASKET_ITEM_ID, STORE_ID and QUANTITY. If warehouse is not explicitly specified in the order, Bitrix takes the first warehouse with sufficient balance — the order is determined by SORT in b_catalog_store.
Proper multi-warehouse configuration requires enabling "warehouse selection during checkout" in the bitrix:sale.order.ajax component settings. The component parameter ALLOW_STORE_AMOUNT should be Y, otherwise customer won't see warehouse distribution.
Warehouse operations document flow
Warehouse documents are created and conducted via \Bitrix\Catalog\Document\DocManager. Conducting a document is the conduct() method, rollback is cancel(). When conducting, balances in b_catalog_store_product are recalculated and the total balance in b_catalog_product (field QUANTITY) is updated.
Manual creation of shipment document:
$doc = new \Bitrix\Catalog\Document\DocBuilder();
$doc->setDocType(\Bitrix\Catalog\StoreDocumentTable::TYPE_SALES_ORDERS);
$doc->setStoreFrom(3); // shipment warehouse
$doc->addItem($productId, $quantity);
$result = $doc->save();
if ($result->isSuccess()) {
\Bitrix\Catalog\Document\DocManager::conductDocument($result->getId());
}
Integration with order statuses
Automatic warehouse inventory deduction on order status change is configured via the OnOrderStatusChange event handler in the sale module. Standard mechanism — configuration in Store → Settings → Warehouses: for each order status, you can set automatic warehouse document conducting.
However, this mechanism doesn't work correctly for partial shipments. If an order of 10 units is shipped in two batches, the standard handler will conduct shipment for all 10 on the first status change. For partial shipment, you need a custom handler that creates a document for the actually shipped quantity from b_sale_order_delivery_basket.
Synchronization with 1C
When syncing inventory via CommerceML (bitrix:catalog.import.1c), balance updates go through b_catalog_store_product directly, bypassing document flow. This means b_catalog_docs doesn't contain change history from 1C — only operations created inside Bitrix. If you need full movement history, synchronization should create documents rather than update balances directly.







