Разработка фильтрации по наличию на складе 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Разработка фильтрации по наличию на складе 1С-Битрикс
Средняя
~1-2 недели
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    745
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Разработка фильтрации по наличию на складе 1С-Битрикс

Фильтр «Только в наличии» — один из наиболее часто запрашиваемых после ценового диапазона. На первый взгляд просто: добавить чекбокс и условие в фильтр. Сложность появляется когда нужно учитывать несколько складов, торговые предложения (SKU), зарезервированные остатки и реальные данные из 1С с задержкой синхронизации.

Модель хранения остатков в 1С-Битрикс

Остатки хранятся в таблице b_catalog_store_product (многоскладская модель) и b_catalog_product (поле QUANTITY). Модуль каталога предоставляет класс CCatalogStoreProduct для работы с многоскладскими остатками.

Для торговых предложений — остатки хранятся у предложений, не у родительского товара. Фильтр «в наличии» должен находить товары, у которых хоть одно предложение имеет ненулевой остаток.

Базовый фильтр по наличию

// Простой случай: одиночные товары без торговых предложений
if (!empty($_GET['in_stock'])) {
    $arFilter['>CATALOG_QUANTITY'] = 0;
    $arFilter['CATALOG_AVAILABLE'] = 'Y';
}

CATALOG_AVAILABLE = 'Y' — флаг доступности, который учитывает не только количество, но и настройки доступности товара (можно купить при нулевом остатке или нет).

Фильтрация с учётом торговых предложений

function getInStockProductIds(int $catalogIblockId, int $offersIblockId): array
{
    // Товары с прямыми остатками
    $directIds = [];
    $res = CIBlockElement::GetList(
        [],
        [
            'IBLOCK_ID'         => $catalogIblockId,
            'ACTIVE'            => 'Y',
            '>CATALOG_QUANTITY' => 0,
            'CATALOG_AVAILABLE' => 'Y',
        ],
        false,
        false,
        ['ID']
    );
    while ($row = $res->GetNext()) {
        $directIds[] = $row['ID'];
    }

    // Товары через предложения с остатком
    $offerParentIds = [];
    $res = CIBlockElement::GetList(
        [],
        [
            'IBLOCK_ID'         => $offersIblockId,
            'ACTIVE'            => 'Y',
            '>CATALOG_QUANTITY' => 0,
            'CATALOG_AVAILABLE' => 'Y',
        ],
        false,
        false,
        ['PROPERTY_CML2_LINK']
    );
    while ($row = $res->GetNext()) {
        if ($row['PROPERTY_CML2_LINK_VALUE']) {
            $offerParentIds[] = intval($row['PROPERTY_CML2_LINK_VALUE']);
        }
    }

    return array_unique(array_merge($directIds, $offerParentIds));
}

// Применение в фильтре каталога
if (!empty($_GET['in_stock'])) {
    $inStockIds = getInStockProductIds(CATALOG_IBLOCK_ID, OFFERS_IBLOCK_ID);
    $arFilter['ID'] = !empty($inStockIds) ? $inStockIds : [0];
}

Многоскладской учёт

При нескольких складах — фильтрация по конкретному складу или по суммарному остатку:

function getProductIdsByWarehouse(int $warehouseId, int $minQty = 1): array
{
    $connection = \Bitrix\Main\Application::getConnection();

    $sql = "
        SELECT DISTINCT sp.PRODUCT_ID
        FROM b_catalog_store_product sp
        INNER JOIN b_iblock_element ie ON ie.ID = sp.PRODUCT_ID
        WHERE sp.STORE_ID = " . intval($warehouseId) . "
          AND sp.AMOUNT >= " . intval($minQty) . "
          AND ie.ACTIVE = 'Y'
    ";

    $res = $connection->query($sql);
    $ids = [];
    while ($row = $res->fetch()) {
        $ids[] = $row['PRODUCT_ID'];
    }
    return $ids;
}

// Фильтр по конкретному складу
if (!empty($_GET['warehouse_id'])) {
    $warehouseId = intval($_GET['warehouse_id']);
    $ids = getProductIdsByWarehouse($warehouseId);
    $arFilter['ID'] = !empty($ids) ? $ids : [0];
}

UI фильтра с количеством на складе

// Получение складов для UI
$warehouses = [];
$res = CCatalogStore::GetList(['SORT' => 'ASC'], ['ACTIVE' => 'Y'], false, false, ['ID', 'TITLE', 'ADDRESS']);
while ($wh = $res->Fetch()) {
    $warehouses[$wh['ID']] = $wh;
}

// Подсчёт для каждого склада
foreach ($warehouses as $whId => $wh) {
    $count = count(getProductIdsByWarehouse($whId));
    $warehouses[$whId]['COUNT'] = $count;
}
?>
<div class="filter-block filter-block--warehouse">
    <label class="filter-toggle">
        <input type="checkbox" name="in_stock" value="1"
               <?= !empty($_GET['in_stock']) ? 'checked' : '' ?>>
        <span>Только в наличии</span>
    </label>

    <?php if (count($warehouses) > 1): ?>
    <div class="warehouse-filter">
        <span class="filter-label">Склад:</span>
        <?php foreach ($warehouses as $whId => $wh): ?>
        <label>
            <input type="radio" name="warehouse_id" value="<?= $whId ?>"
                   <?= ($_GET['warehouse_id'] ?? '') == $whId ? 'checked' : '' ?>>
            <?= htmlspecialchars($wh['TITLE']) ?> (<?= $wh['COUNT'] ?>)
        </label>
        <?php endforeach; ?>
    </div>
    <?php endif; ?>
</div>

Кеширование для производительности

Запрос всех товаров в наличии при каждом обращении к каталогу — дорогостоящая операция на больших каталогах. Кешируем список ID:

$cacheKey = 'in_stock_ids_' . CATALOG_IBLOCK_ID;
$cacheTime = 300; // 5 минут

$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache($cacheTime, $cacheKey, '/catalog/filter/')) {
    $inStockIds = $cache->getVars();
} else {
    $inStockIds = getInStockProductIds(CATALOG_IBLOCK_ID, OFFERS_IBLOCK_ID);
    $cache->startDataCache();
    $cache->endDataCache($inStockIds);
}

Кеш инвалидируется при изменении остатков через обработчик события OnCatalogStoreDocumentUpdate.

Сроки выполнения

Базовый фильтр «в наличии» без торговых предложений — 3–5 часов. Полная реализация с торговыми предложениями, многоскладским учётом, кешированием и UI выбора склада — 2–3 рабочих дня.