Setting Up Inventory Count in 1C-Bitrix
Inventory breaks at the verification stage: system shows 50 units, physically there are 47. Someone manually adjusts inventory via b_catalog_product, bypassing document flow. After a week, discrepancies accumulate again, history disappears.
Inventory count document
Inventory in Bitrix is a document of type I in b_catalog_docs. Difference from receipt/shipment: document rows specify not delta, but actual quantity. The system itself calculates the difference against accounting balances and applies correction when conducting.
Fields of b_catalog_docs for inventory: DOC_TYPE = 'I', STORE_FROM and STORE_TO simultaneously point to one warehouse (inventory is tied to specific warehouse), STATUS.
Rows in b_catalog_docs_element: AMOUNT — physically counted quantity, AMOUNT_RESERVED — not used in inventory. When conducting, system reads current AMOUNT from b_catalog_store_product for this warehouse, calculates difference and applies it.
Creating an inventory document:
$result = \Bitrix\Catalog\StoreDocumentTable::add([
'DOC_TYPE' => \Bitrix\Catalog\StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
'STATUS' => 'N',
'STORE_TO' => 1,
'TITLE' => 'Inventory ' . date('m.d.Y'),
'DATE_DOCUMENT' => new \Bitrix\Main\Type\DateTime(),
]);
TYPE_STORE_ADJUSTMENT — constant for type I.
Collecting actual balances
The problem with inventory in Bitrix — no built-in mechanism for staged warehouse walk-through. Standard interface requires entering all items at once. For large warehouses this is inconvenient.
Solution — create document in N status and add rows as you count. While document is in draft, it doesn't affect balances. Rows are added via \Bitrix\Catalog\StoreDocumentElementTable::add(). Already added rows are updated via update() by ID.
To get list of all warehouse products with current accounting balances:
$storeItems = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['STORE_ID' => 1, '>AMOUNT' => 0],
'select' => ['PRODUCT_ID', 'AMOUNT', 'QUANTITY_RESERVED'],
'order' => ['PRODUCT_ID' => 'ASC'],
]);
This list serves as basis for printed form for counting. After physical count, actual quantities are entered in document rows.
Conducting and calculating discrepancies
When calling \Bitrix\Catalog\Document\DocManager::conductDocument($docId) for document type I, system for each row:
- Reads current accounting balance from
b_catalog_store_product. - Compares with actual (
AMOUNTfrom document row). - If actual less — creates shipment (decreases
AMOUNT). - If more — creates receipt (increases
AMOUNT). - Updates total
QUANTITYinb_catalog_product.
Products not in document rows are not affected by inventory. This allows partial inventory — only certain category or warehouse zone.
Zero balances after inventory
If inventory count shows 0 units, row must still be added to document with AMOUNT = 0. Without this, product remains with previous accounting balance. Many forget this — especially when importing actual balances from Excel.
Automatic adding zero rows for all warehouse products:
// Get all warehouse products
$existing = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['STORE_ID' => $storeId, '>AMOUNT' => 0],
'select' => ['PRODUCT_ID'],
])->fetchAll();
// Add zero rows for missing in document
foreach ($existing as $item) {
if (!in_array($item['PRODUCT_ID'], $scannedProductIds)) {
\Bitrix\Catalog\StoreDocumentElementTable::add([
'DOC_ID' => $docId,
'ELEMENT_ID' => $item['PRODUCT_ID'],
'STORE_TO' => $storeId,
'AMOUNT' => 0,
]);
}
}
History of adjustments
After inventory is conducted, document in b_catalog_docs remains with STATUS = 'Y' and serves as historical record. For audit of discrepancies, it's convenient to query documents by period with difference between accounting and actual quantity — this would require JOIN with b_catalog_store_product at time of conducting, which standard Bitrix doesn't save. For complete audit, you need to add custom table of balance snapshots before conducting.







