Решение: Скрытие товаров без установленной цены для групп пользователей в Bitrix

    В системе существует каталог товаров, все позиции которого физически присутствуют на складе в достаточном количестве. Параллельно настроено порядка 30 различных типов цен, каждый из которых привязан к соответствующей группе пользователей.

    Проблема: Для конкретного покупателя значительная часть товаров отображается с пометкой «Нет в наличии». Причина - отсутствие установленной цены в том типе цен, который соответствует группе данного пользователя.

    Цель: Необходимо настроить систему таким образом, чтобы для пользователя автоматически скрывались товары, для которых не определена цена в типе цены его группы. Стандартные механизмы платформы Bitrix с этой задачей не справляются.

    Техническое решение

    Для реализации требуется добавить кастомный обработчик событий в файл /local/php_interface/init.php. Предложенный код выполняет следующую последовательность действий:

    1. Отключает стандартные фильтры каталога, которые мешают корректной работе.
    2. Определяет группу текущего пользователя и связанные с ней доступные типы цен.
    3. Фильтрует список товаров, оставляя только те, для которых существует активная цена в разрешённых для пользователя типах цен.
    4. Корректирует навигацию для отображения актуального количества найденных товаров.

    Код решения:

    // /local/php_interface/init.php
    use Bitrix\Main\EventManager;
    use Bitrix\Main\Loader;
    use Bitrix\Main\Application;
    
    $eventManager = EventManager::getInstance();
    
    // 1. Отключаем стандартные фильтры по наличию и ценам
    $eventManager->addEventHandler('iblock', 'OnBeforeIBlockElementGetList', function(&$arFilter) {
        $keysToRemove = [
            'CATALOG_AVAILABLE', 'AVAILABLE', '=CATALOG_AVAILABLE',
            'CATALOG_PRICE_', '>CATALOG_PRICE_', '!=CATALOG_PRICE_'
        ];
        
        foreach ($keysToRemove as $key) {
            foreach (array_keys($arFilter) as $filterKey) {
                if (strpos($filterKey, $key) === 0) {
                    unset($arFilter[$filterKey]);
                }
            }
        }
    });
    
    // 2. Отключаем стандартную проверку доступности товаров
    $eventManager->addEventHandler('catalog', 'OnGetAvailableItems', function() {
        return false;
    });
    
    // 3. Основная логика фильтрации после получения списка товаров
    $eventManager->addEventHandler('iblock', 'OnAfterIBlockElementGetList', function(&$arResult) {
        if (empty($arResult) || !Loader::includeModule('catalog') || !Loader::includeModule('sale')) {
            return;
        }
        
        global $USER;
        $userId = $USER->GetID();
        $userGroups = $USER->GetUserGroupArray();
        
        // Определяем типы цен, доступные группам пользователя
        $allowedPriceTypes = [];
        $priceTypeRes = \Bitrix\Catalog\GroupTable::getList([
            'select' => ['ID', 'NAME'],
            'filter' => ['=GROUP_ACCESS.GROUP_ID' => $userGroups]
        ]);
        
        while ($priceType = $priceTypeRes->fetch()) {
            $allowedPriceTypes[] = (int)$priceType['ID'];
        }
        
        // Если привязок нет - разрешаем все типы цен
        if (empty($allowedPriceTypes)) {
            $priceTypeRes = \Bitrix\Catalog\GroupTable::getList(['select' => ['ID']]);
            while ($priceType = $priceTypeRes->fetch()) {
                $allowedPriceTypes[] = (int)$priceType['ID'];
            }
        }
        
        if (empty($allowedPriceTypes)) {
            return;
        }
        
        // Собираем ID товаров из результата
        $elementIds = [];
        foreach ($arResult as $item) {
            if (isset($item['ID'])) {
                $elementIds[] = (int)$item['ID'];
            }
        }
        
        if (empty($elementIds)) {
            return;
        }
        
        // Ищем товары с ценами в разрешённых типах
        $connection = Application::getConnection();
        $sql = "
            SELECT DISTINCT cp.PRODUCT_ID 
            FROM b_catalog_price cp
            WHERE cp.PRODUCT_ID IN (" . implode(',', $elementIds) . ")
            AND cp.CATALOG_GROUP_ID IN (" . implode(',', $allowedPriceTypes) . ")
            AND cp.PRICE > 0
            AND (cp.QUANTITY_FROM IS NULL OR cp.QUANTITY_FROM = 0)
            AND (cp.QUANTITY_TO IS NULL OR cp.QUANTITY_TO = 0)
        ";
        
        $result = $connection->query($sql);
        $productsWithPrices = [];
        
        while ($row = $result->fetch()) {
            $productsWithPrices[(int)$row['PRODUCT_ID']] = true;
        }
        
        // Фильтруем итоговый массив
        $filteredResult = [];
        foreach ($arResult as $item) {
            if (isset($item['ID']) && isset($productsWithPrices[(int)$item['ID']])) {
                $filteredResult[] = $item;
            }
        }
        
        $arResult = $filteredResult;
        
        // Корректируем данные навигации
        if (isset($GLOBALS['NAV_RESULT']) && is_object($GLOBALS['NAV_RESULT'])) {
            $GLOBALS['NAV_RESULT']->NavRecordCount = count($filteredResult);
            $GLOBALS['NAV_RESULT']->SelectedRows = count($filteredResult);
        }
    });

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