Решение: Скрытие товаров без установленной цены для групп пользователей в Bitrix
В системе существует каталог товаров, все позиции которого физически присутствуют на складе в достаточном количестве. Параллельно настроено порядка 30 различных типов цен, каждый из которых привязан к соответствующей группе пользователей.
Проблема: Для конкретного покупателя значительная часть товаров отображается с пометкой «Нет в наличии». Причина - отсутствие установленной цены в том типе цен, который соответствует группе данного пользователя.
Цель: Необходимо настроить систему таким образом, чтобы для пользователя автоматически скрывались товары, для которых не определена цена в типе цены его группы. Стандартные механизмы платформы Bitrix с этой задачей не справляются.
Техническое решение
Для реализации требуется добавить кастомный обработчик событий в файл /local/php_interface/init.php. Предложенный код выполняет следующую последовательность действий:
- Отключает стандартные фильтры каталога, которые мешают корректной работе.
- Определяет группу текущего пользователя и связанные с ней доступные типы цен.
- Фильтрует список товаров, оставляя только те, для которых существует активная цена в разрешённых для пользователя типах цен.
- Корректирует навигацию для отображения актуального количества найденных товаров.
Код решения:
// /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);
}
});Данное решение позволяет гибко управлять видимостью товарных позиций в зависимости от ценовой политики для разных сегментов клиентов, решая проблему некорректного отображения «отсутствия в наличии».