Получить минимальную цену с учётом нескольких типов цен
Расчёт минимальной цены товара среди всех типов цен с учётом количества, скидок и доступности типов цен для пользователя.
Как использовать
- Подключите модуль catalog, получите все типы цен (CCatalogGroup), для каждого проверьте доступность для пользователя и цену через CPrice::GetList и GetOptimalPrice.
- Верните минимальную цену среди доступных типов с полями PRICE, CURRENCY, PRICE_TYPE_ID.
- Для неавторизованного передавайте userId = 0; для расчёта с количеством передайте quantity.
В каталоге 1С-Битрикс у товара может быть несколько типов цен (розница, опт, спеццены для групп). В списках и карточках обычно показывают минимальную доступную цену для текущего пользователя с учётом скидок. Проблема: если брать только базовый тип цены или первую попавшуюся, отображается не лучшая цена; при этом типы цен привязаны к группам пользователей, и не все типы доступны гостю или другой группе. Симптомы: в каталоге завышенная цена, скидка не учитывается, для части пользователей цена не та. Ниже — функция, которая перебирает все типы цен, проверяет доступность по группам пользователя, учитывает GetOptimalPrice (скидки, персональные цены) и возвращает минимальную цену и тип.
Решение
Получение минимальной цены товара среди всех доступных типов цен. Используйте в списках товаров, фильтрах, компонентах каталога.
use Bitrix\Main\Loader;
/**
* Получить минимальную цену среди всех типов цен
* @param int $productId ID товара
* @param int $userId ID пользователя (для проверки доступности типов цен)
* @param int $quantity Количество для расчёта
* @return array|false ['PRICE' => float, 'CURRENCY' => string, 'PRICE_TYPE_ID' => int] или false
*/
function getMinimalPriceFromAllTypes($productId, $userId = 0, $quantity = 1) {
if (!Loader::includeModule('catalog')) {
return false;
}
$priceTypes = CCatalogGroup::GetList([], ['BASE' => 'N']);
$allPriceTypes = [];
while ($type = $priceTypes->Fetch()) {
$allPriceTypes[] = $type['ID'];
}
$baseType = CCatalogGroup::GetBaseGroup();
if ($baseType) {
$allPriceTypes[] = $baseType['ID'];
}
$minimalPrice = null;
$minimalPriceData = null;
foreach ($allPriceTypes as $priceTypeId) {
if ($userId > 0) {
$userGroups = CUser::GetUserGroup($userId);
$priceType = CCatalogGroup::GetByID($priceTypeId);
if ($priceType && !empty($priceType['USER_GROUP'])) {
$allowedGroups = explode(',', $priceType['USER_GROUP']);
if (empty(array_intersect($userGroups, $allowedGroups))) {
continue;
}
}
}
$price = CPrice::GetList(
[],
[
'PRODUCT_ID' => $productId,
'CATALOG_GROUP_ID' => $priceTypeId
],
false,
false,
['PRICE', 'CURRENCY']
)->Fetch();
if ($price) {
$priceValue = (float)$price['PRICE'];
$optimalPrice = CCatalogProduct::GetOptimalPrice(
$productId,
$quantity,
$userId,
'N',
[],
false,
false,
[$priceTypeId]
);
if ($optimalPrice && isset($optimalPrice['PRICE']['PRICE'])) {
$priceValue = (float)$optimalPrice['PRICE']['PRICE'];
}
if ($minimalPrice === null || $priceValue < $minimalPrice) {
$minimalPrice = $priceValue;
$minimalPriceData = [
'PRICE' => $priceValue,
'CURRENCY' => $price['CURRENCY'],
'PRICE_TYPE_ID' => $priceTypeId
];
}
}
}
return $minimalPriceData ?: false;
}
Пример использования:
$userId = $GLOBALS['USER']->GetID();
$minPrice = getMinimalPriceFromAllTypes(12345, $userId, 1);
if ($minPrice) {
echo 'Минимальная цена: ' . $minPrice['PRICE'] . ' ' . $minPrice['CURRENCY'];
echo 'Тип цены ID: ' . $minPrice['PRICE_TYPE_ID'];
}
$minPrice = getMinimalPriceFromAllTypes(12345, 0, 1); // неавторизованный
Доступность типов цен проверяется по группам пользователя (USER_GROUP у типа цены). Для гостя учитываются только типы без ограничения по группам или с пустым USER_GROUP. GetOptimalPrice учитывает скидки и персональные цены.
Проверка
-
Товар с одним типом цены — вызовите функцию для товара с одной записью в b_catalog_price. Ожидаем массив с PRICE, CURRENCY, PRICE_TYPE_ID; цена должна совпадать с ценой в админке (с учётом скидки, если есть).
-
Товар с несколькими типами цен — создайте два типа цен и записи в b_catalog_price с разными суммами. Результат должен содержать минимальную из цен среди типов, доступных текущему пользователю.
-
Разные группы пользователя — для типа цены укажите ограничение по группе; залогиньтесь под пользователем из этой группы и без неё. В первом случае минимальная цена должна учитывать этот тип, во втором — нет (если других типов нет, вернётся false или цена другого типа).
-
Диагностика: всегда false — проверьте, что модуль catalog подключён, у товара есть хотя бы одна запись в b_catalog_price и для пользователя доступен хотя бы один тип цены (группы и USER_GROUP типа).
Типичные ошибки
- Учитывается только базовый тип — для каталога с несколькими типами цен нужно перебирать все типы и выбирать минимум; иначе оптовые/спеццены не отобразятся.
- Не проверяется доступность типа для группы — тип цены может быть привязан к группам (USER_GROUP); без проверки группам показывается цена, которая им не предназначена. Используйте CUser::GetUserGroup и сравнение с USER_GROUP типа.
- Скидки не учтены — базовая цена из CPrice::GetList без скидок. Для учёта скидок и персональных цен вызывайте CCatalogProduct::GetOptimalPrice с нужным типом цены.
- Нет цен у товара — если у товара нет ни одной записи в b_catalog_price или ни один тип недоступен, функция вернёт false. Обрабатывайте этот случай в шаблоне (не показывать цену или «по запросу»).
Где применять
- Prod / dev: списки товаров, карточка товара, фильтры по цене (минимальная цена для отображения и сортировки), API каталога.
- Личный кабинет и публичная часть: передавайте текущего пользователя ($GLOBALS[‘USER’]->GetID()) или 0 для гостя.
Связанные сниппеты: Список цен товара, Родительский товар по артикулу, Изменение статуса заказа D7 и save.