PHP
#bitrix#catalog#price#php#cPrice#discount

Получить цены товара и выбрать актуальную

Получение всех цен товара через CPrice::GetList с фильтрацией по типу цены и выбором актуальной цены с учётом скидок.

Как использовать

  1. Все цены: getProductPrices($productId) или getProductPrices($productId, $priceTypeId) для одного типа.
  2. Актуальная цена со скидками: getActualProductPrice($productId, $quantity, $userId); для гостя передайте userId = 0.
  3. Модули catalog и для скидок sale должны быть подключены.

В каталоге 1С-Битрикс у товара может быть несколько записей цен (разные типы, пороги количества). Для отображения в карточке и корзине нужны либо все цены по типам, либо одна актуальная с учётом скидок и количества. Проблема: если брать только первую запись из CPrice без учёта скидок, пользователь видит цену без скидки; при нескольких типах цен без фильтра сложно показать «от» или цену нужного типа. Симптомы: завышенная цена, скидка не отображается, неверная валюта. Ниже — получение списка цен через CPrice::GetList (с опциональной фильтрацией по типу) и получение актуальной цены через GetOptimalPrice (скидки, количество, пользователь); проверка и типичные ошибки.

Решение

Получение цен товара из каталога с выбором актуальной цены. Используйте в компонентах каталога, корзины, детальных страницах.

use Bitrix\Main\Loader;

/**
 * Получить все цены товара
 * @param int $productId ID товара
 * @param int $priceTypeId ID типа цены (опционально)
 * @return array Массив цен
 */
function getProductPrices($productId, $priceTypeId = null) {
    if (!Loader::includeModule('catalog')) {
        return [];
    }
    
    $filter = ['PRODUCT_ID' => $productId];
    if ($priceTypeId) {
        $filter['CATALOG_GROUP_ID'] = $priceTypeId;
    }
    
    $res = CPrice::GetList(
        ['CATALOG_GROUP_ID' => 'ASC'],
        $filter
    );
    
    $prices = [];
    while ($price = $res->Fetch()) {
        $prices[] = [
            'ID' => $price['ID'],
            'PRICE' => $price['PRICE'],
            'CURRENCY' => $price['CURRENCY'],
            'PRICE_TYPE_ID' => $price['CATALOG_GROUP_ID'],
            'QUANTITY_FROM' => $price['QUANTITY_FROM'],
            'QUANTITY_TO' => $price['QUANTITY_TO']
        ];
    }
    
    return $prices;
}

/**
 * Получить актуальную цену товара с учётом скидок
 * @param int $productId ID товара
 * @param int $quantity Количество для расчёта (по умолчанию 1)
 * @param int $userId ID пользователя (для персональных скидок)
 * @return array|false ['PRICE', 'CURRENCY', 'DISCOUNT_PRICE', 'DISCOUNT_PERCENT', 'BASE_PRICE'] или false
 */
function getActualProductPrice($productId, $quantity = 1, $userId = 0) {
    if (!Loader::includeModule('catalog') || !Loader::includeModule('sale')) {
        return false;
    }
    
    $prices = getProductPrices($productId);
    if (empty($prices)) {
        return false;
    }
    
    $basePrice = $prices[0];
    $currentUserId = $userId ?: ($GLOBALS['USER']->IsAuthorized() ? $GLOBALS['USER']->GetID() : 0);
    
    $arPrice = CCatalogProduct::GetOptimalPrice(
        $productId,
        $quantity,
        $currentUserId,
        'N'
    );
    
    if ($arPrice && isset($arPrice['PRICE']['PRICE'])) {
        return [
            'PRICE' => (float)$arPrice['PRICE']['PRICE'],
            'CURRENCY' => $arPrice['PRICE']['CURRENCY'],
            'DISCOUNT_PRICE' => isset($arPrice['DISCOUNT']['PRICE']) ? (float)$arPrice['DISCOUNT']['PRICE'] : null,
            'DISCOUNT_PERCENT' => isset($arPrice['DISCOUNT']['PERCENT']) ? (float)$arPrice['DISCOUNT']['PERCENT'] : null,
            'BASE_PRICE' => (float)$basePrice['PRICE']
        ];
    }
    
    return [
        'PRICE' => (float)$basePrice['PRICE'],
        'CURRENCY' => $basePrice['CURRENCY'],
        'DISCOUNT_PRICE' => null,
        'BASE_PRICE' => (float)$basePrice['PRICE']
    ];
}

Пример использования:

$prices = getProductPrices(12345);
foreach ($prices as $price) {
    echo $price['PRICE'] . ' ' . $price['CURRENCY'];
}

$actualPrice = getActualProductPrice(12345, 1, $USER->GetID());
if ($actualPrice) {
    if ($actualPrice['DISCOUNT_PRICE']) {
        echo 'Старая цена: ' . $actualPrice['BASE_PRICE'];
        echo 'Цена со скидкой: ' . $actualPrice['PRICE'];
    } else {
        echo 'Цена: ' . $actualPrice['PRICE'];
    }
}

GetOptimalPrice учитывает скидки, персональные цены и количество. Для скидок нужен модуль sale. Для неавторизованного передавайте userId = 0.

Проверка

  1. Товар с одной ценой — getProductPrices($productId) должен вернуть массив с одним элементом; getActualProductPrice — массив с PRICE, CURRENCY, BASE_PRICE. Цена должна совпадать с карточкой товара в админке.

  2. Товар со скидкой — настройте скидку на товар или корзину, вызовите getActualProductPrice. Ожидаем PRICE меньше BASE_PRICE, DISCOUNT_PRICE или DISCOUNT_PERCENT заполнены (если модуль скидок возвращает их).

  3. Фильтр по типу цены — getProductPrices($productId, $priceTypeId) должен вернуть только записи с указанным CATALOG_GROUP_ID. Сверьте с типами цен в настройках каталога.

  4. Товар без цен — для элемента без записей в b_catalog_price getProductPrices вернёт [], getActualProductPrice — false. Обрабатывайте в шаблоне (не показывать цену или «по запросу»).

Типичные ошибки

  • Скидка не применяется — подключите модуль sale и передавайте актуальный userId в GetOptimalPrice. Для гостя передавайте 0; проверьте, что скидки активны и применимы к товару/корзине.
  • Передан ID SKU вместо товара — GetOptimalPrice и CPrice работают с PRODUCT_ID: для торгового предложения это ID родительского товара в привязке каталога. Если в компоненте приходит ID SKU, сначала получите родителя (например по SKU) и передайте PRODUCT_ID в функции цен.
  • Несколько типов цен — getProductPrices возвращает все типы; для «минимальной по всем типам» используйте отдельный сниппет с учётом групп пользователя.

Где применять

  • Prod / dev: карточка товара, корзина, списки каталога, API цен, расчёт «старая цена / со скидкой».
  • Компоненты: передавайте productId из элемента каталога (для SKU — ID родительского товара, если цены привязаны к товару).

Связанные сниппеты: Минимальная цена по типам цен, Родительский товар по SKU, Список элементов с пагинацией.