Налаштування інвентаризації у 1C-Bitrix
Інвентаризація ламається на етапі звірки: система показує 50 одиниць, фізично 47. Хтось коригує остаток вручну через b_catalog_product, мімо документообігу. Через тиждень розбіжності накопичуються знову, історія зникає.
Документ інвентаризації
Інвентаризація у Bitrix — документ типу I у b_catalog_docs. Відмінність від надходження/видачі: у рядках документа вказується не дельта, а фактична кількість. Система сама обчислює різницю з урахуванням облікових остатків та при проведенні застосовує коригування.
Поля b_catalog_docs для інвентаризації: DOC_TYPE = 'I', STORE_FROM та STORE_TO одночасно вказують на один склад (інвентаризація привʼязана до конкретного складу), STATUS.
Рядки в b_catalog_docs_element: AMOUNT — фактично перелічена кількість, AMOUNT_RESERVED — не використовується при інвентаризації. При проведенні система читає поточний AMOUNT з b_catalog_store_product для цього складу, обчислює різницю та застосовує її.
Створення документа інвентаризації:
$result = \Bitrix\Catalog\StoreDocumentTable::add([
'DOC_TYPE' => \Bitrix\Catalog\StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
'STATUS' => 'N',
'STORE_TO' => 1,
'TITLE' => 'Інвентаризація ' . date('d.m.Y'),
'DATE_DOCUMENT' => new \Bitrix\Main\Type\DateTime(),
]);
TYPE_STORE_ADJUSTMENT — константа для типу I.
Збір фактичних остатків
Проблема інвентаризації у Bitrix — немає вбудованого механізму для поетапного обходу складу. Стандартний інтерфейс вимагає вносити всі позиції одразу. Для великих складів це незручно.
Рішення — створювати документ у статусі N та доповнювати його рядки при перелічуванні. Поки документ у чернетці, він не впливає на остатки. Рядки добавляються через \Bitrix\Catalog\StoreDocumentElementTable::add(). Вже добавлені рядки оновлюються через update() за ID.
Для отримання списку всіх товарів на складі з поточними обліковими остатками:
$storeItems = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['STORE_ID' => 1, '>AMOUNT' => 0],
'select' => ['PRODUCT_ID', 'AMOUNT', 'QUANTITY_RESERVED'],
'order' => ['PRODUCT_ID' => 'ASC'],
]);
Цей список служить основою для печатної форми під перелічування. Після фізичного перелічування фактичні кількості вносяться в рядки документа.
Проведення та розрахунок розбіжностей
При виклику \Bitrix\Catalog\Document\DocManager::conductDocument($docId) для документа типу I система для кожного рядка:
- Читає поточний обліковий остаток з
b_catalog_store_product. - Порівнює з фактичним (
AMOUNTз рядка документа). - Якщо фактичне менше — створює видачу (зменшує
AMOUNT). - Якщо більше — створює надходження (збільшує
AMOUNT). - Оновлює загальну
QUANTITYуb_catalog_product.
Товари, яких немає в рядках документа, при інвентаризації не торкаються. Це дозволяє проводити часткову інвентаризацію — лише певну категорію або зону складу.
Нульові остатки після інвентаризації
Якщо при інвентаризації фізично виявлено 0 одиниць, рядок у документ все рівно потрібно додати з AMOUNT = 0. Без цього товар залишиться з попереднім обліковим остатком. Багато хто забувають про це — особливо при імпорті фактичних остатків з Excel.
Автоматичне додавання нульових рядків для всіх товарів складу:
// Отримати всі товари складу
$existing = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => ['STORE_ID' => $storeId, '>AMOUNT' => 0],
'select' => ['PRODUCT_ID'],
])->fetchAll();
// Додати нульові рядки для відсутніх у документі
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,
]);
}
}
Історія коригувань
Після проведення інвентаризації документ у b_catalog_docs залишається з STATUS = 'Y' та служить історичною записом. Для аудиту розбіжностей зручно запитувати документи за період з різницею облікового та фактичного кількості — це потребує JOIN з b_catalog_store_product на момент проведення, що стандартний Bitrix не зберігає. При потребі повного аудиту потрібно додати кастомну таблицю знімків остатків перед проведенням.







