Настройка наличия товаров по магазинам 1С-Битрикс
Клиент выбирает товар на сайте, едет в магазин — и оказывается, что там его нет. На складе числится 3 единицы, но они в другом филиале. Это случается, когда на сайте показывается суммарный остаток по всем складам вместо актуального наличия в конкретной точке.
Архитектура многоскладского учёта
Модуль catalog поддерживает склады начиная с Битрикс 16.x. Остатки по складам хранятся в b_catalog_store_product:
-
PRODUCT_ID— ID элемента каталога -
STORE_ID— ID склада изb_catalog_store -
AMOUNT— количество на складе -
QUANTITY_RESERVED— зарезервировано под заказы
b_catalog_store — это таблица складов каталога. Она отличается от b_sale_store (торговых точек для самовывоза). Связь «торговая точка → склад» устанавливается через пользовательское поле или вручную — в стандартной схеме Битрикс эти две сущности разделены.
Суммарный остаток в b_catalog_product.QUANTITY — это агрегат всех складов. При включённом многоскладском учёте (CCatalogStore::IsStoreProductQuantityEnable()) поле QUANTITY обновляется автоматически при изменении b_catalog_store_product.
Привязка склада к торговой точке
Стандартная задача: каждой торговой точке (b_sale_store) соответствует один или несколько складов (b_catalog_store). Создаём пользовательское поле у b_catalog_store:
CUserTypeEntity::Add([
'ENTITY_ID' => 'CAT_STORE',
'FIELD_NAME' => 'UF_SALE_STORE_ID',
'USER_TYPE_ID' => 'integer',
'MANDATORY' => 'N',
]);
После этого при запросе наличия по торговой точке фильтруем b_catalog_store_product через JOIN с b_catalog_store по UF_SALE_STORE_ID.
Отображение наличия на карточке товара
Запрос наличия товара по всем связанным складам:
$availability = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => [
'PRODUCT_ID' => $productId,
'>AMOUNT' => 0,
],
'select' => ['STORE_ID', 'AMOUNT', 'QUANTITY_RESERVED'],
])->fetchAll();
// Получаем ID торговых точек через UF_SALE_STORE_ID
$storeIds = array_column($availability, 'STORE_ID');
$saleStores = \Bitrix\Catalog\StoreTable::getList([
'filter' => ['ID' => $storeIds],
'select' => ['ID', 'UF_SALE_STORE_ID'],
])->fetchAll();
Доступное количество: AMOUNT - QUANTITY_RESERVED. Если результат ≤ 0 — товар зарезервирован, физически на складе есть, но для продажи недоступен.
Резервирование при оформлении заказа
Когда покупатель добавляет товар в корзину или оформляет заказ, должна создаваться резервация. Это делается через \Bitrix\Catalog\StoreProductTable::update() с увеличением QUANTITY_RESERVED. Стандартный механизм Битрикс делает это при смене статуса заказа, но не при добавлении в корзину.
Если нужно резервировать уже на этапе корзины — добавляем обработчик OnSaleBasketItemAdd:
AddEventHandler('sale', 'OnSaleBasketItemAdd', function(\Bitrix\Main\Event $event) {
$item = $event->getParameter('ENTITY');
// Увеличиваем QUANTITY_RESERVED на складе, привязанном к ближайшей точке
// с учётом геолокации пользователя или выбранного им магазина
});
Синхронизация с 1С
Остатки по складам приходят из 1С через стандартный обмен или через REST API. При обмене через файл CommerceML/XML данные пишутся в b_catalog_store_product через CCatalogStore::UpdateProductQuantity(). При прямом REST — через метод catalog.storeproduct.update.
Важно: при обмене 1С может присылать полный остаток (перезапись) или дельту. При дельте нужно защититься от отрицательных значений AMOUNT: добавить CHECK-constraint на уровне БД или проверку в коде перед update().
Что настраиваем
- Проверку включения многоскладского учёта и заполнение
b_catalog_store_product - Пользовательское поле
UF_SALE_STORE_IDвb_catalog_storeдля связи со складами каталога - Отображение наличия по торговым точкам на карточке товара
- Расчёт доступного количества с учётом
QUANTITY_RESERVED - Обработчик резервирования при добавлении в корзину (если требуется)
- Схему синхронизации остатков из 1С с защитой от отрицательных значений







