При использовании торговых предложений в Битриксе при разработке каталога возникает логичный вопрос: как отсортировать/отфильтровать товары с торговыми предложениями по наличии и по цене? Из-за того, что для таких товаров цена и наличие записывается не у товаров, а у предложений, скрытие из листингов товаров с нулевым остатком приводит к исчезновению из листинга товаров с торговыми предложениями.
Пока разработчики битрикса не добавили такие возможности в ядро(но они обещают доработать этот функционал), используем свой код для реализации этих функций.
Наличие на складе
Для сортировки и фильтрации по наличию используем стандартное поле модуля catalog QUANTITY(CATALOG_QUANTITY), при изменении наличия торговых предложений находим остальные предложения у товара и обновляем его доступное количество:
AddEventHandler("catalog", "OnProductAdd","UpdateProductQuantity");
AddEventHandler("catalog", "OnProductUpdate","UpdateProductQuantity");
function UpdateProductQuantity($id, $arFields)
{
$quantity = $arFields['QUANTITY'];
if (CModule::IncludeModule('catalog') && CModule::IncludeModule('iblock')) {
$arProductInfo = CCatalogSKU::GetProductInfo($id);
if (is_array($arProductInfo)) {
$arOffersInfo = CCatalogSKU::GetInfoByProductIBlock($arProductInfo['IBLOCK_ID']);
$arFilter = array(
'IBLOCK_ID' => OFFERS_IBLOCK_ID,
"PROPERTY_CML2_LINK" => $arProductInfo['ID'],
"!ID" => $id,
);
$obOffersList = CIBlockElement::GetList(array("SORT"=>"ASC"), $arFilter, false, false, array("CATALOG_QUANTITY"));
while ($arOffers = $obOffersList->Fetch()) {
$quantity += $arOffers["CATALOG_QUANTITY"];
}
$arFieldsProduct = array(
"QUANTITY" => $quantity,
);
CCatalogProduct::Update($arProductInfo['ID'], $arFieldsProduct);
}
}
}
После этого мы может товар с торговыми предложениями наравне с обычными товарами сортировать или фильтровать по полям CATALOG_QUANTITY и CATALOG_AVAILABLE, а также использовать параметр HIDE_NOT_AVAILABLE компонента bitrix:catalog или bitrix:catalog.section:
Скрыть отсутствующие товары:
"HIDE_NOT_AVAILABLE" => "N",
Отсортировать по популярности, но показать сначала товары в наличии, затем отсутствующие:
"ELEMENT_SORT_FIELD" => "CATALOG_AVAILABLE",
"ELEMENT_SORT_ORDER" => "DESC",
"ELEMENT_SORT_FIELD2" => "SHOWS",
"ELEMENT_SORT_ORDER2" => "DESC",
Отсортировать по наличию:
"ELEMENT_SORT_FIELD" => "CATALOG_AVAILABLE",
"ELEMENT_SORT_ORDER" => "DESC",
Цена
Для сортировки и фильтрации по цене товаров используем свойство MINIMUM_PRICE, в нём уже из коробки записывается минимальная цена среди торговых предложений. Если у всех торговых предложений одинаковая цена, можно воспользоваться облегчённым вариантом, который при изменении цены товара/торгового предложения обновляет свойство PRICE у товара(свойство нужно создать самому):
AddEventHandler("catalog", "OnPriceAdd", "UpdateProductPropertyPrice");
AddEventHandler("catalog", "OnPriceUpdate", "UpdateProductPropertyPrice");
function UpdateProductPropertyPrice($id, $arFields)
{
if (CModule::IncludeModule("iblock") && CModule::IncludeModule("catalog")) {
$elementId = $arFields["PRODUCT_ID"];
$resElement = CIBlockElement::GetByID($elementId);
if($arElement = $resElement->GetNext()) {
if ($arElement["IBLOCK_ID"] == OFFERS_IBLOCK_ID) {
$resOffer = CIBlockElement::GetProperty(OFFERS_IBLOCK_ID, $elementId, "sort", "asc", array("CODE" => "CML2_LINK"));
if ($arOffer = $resOffer->GetNext()) {
$elementId = $arOffer["VALUE"];
}
}
}
CIBlockElement::SetPropertyValuesEx($elementId, CATALOG_IBLOCK_ID, array("PRICE" => $arFields["PRICE"]));
}
}
Для сортировки по цене, например, по убыванию указываем параметры bitrix:catalog или bitrix:catalog.section:
"ELEMENT_SORT_FIELD" => "PROPERTY_PRICE",
"ELEMENT_SORT_ORDER" => "DESC",
В кусках кода используются константы CATALOG_IBLOCK_ID и OFFERS_IBLOCK_ID, их нужно объявить или заменить на ID соответственно инфоблока товаров и товарных предложений.
Я не претендую на уникальность или безупречность приведенных решений, код представлен как есть.