Цена товара: правильно выбираем Add или Update

Цена товара не выводится, а импорт каждый раз создаёт новую запись. Обычно код обновляет по PRODUCT_ID, хотя update ожидает ID самой записи цены. Исправление — сначала найти запись по товару и типу цены, затем вызвать add или update по результату.

Это код для защищённого административного скрипта или CLI на тестовой копии. Не делайте из него публичный URL, который принимает сумму и ID товара.

Условия перед запуском

Пример рассчитан на одну цену без диапазонов количества. PRODUCT_ID — ID товара или SKU, CATALOG_GROUP_ID — ID существующего типа цены, а ID — ID строки цены. Для оптовых диапазонов сначала выбирайте запись с точными QUANTITY_FROM и QUANTITY_TO; нельзя обновлять первую попавшуюся цену.

Поиск, добавление или обновление

<?php
use Bitrix\Catalog\Model\Price;
use Bitrix\Main\Loader;

if (!Loader::includeModule('catalog')) {
    throw new RuntimeException('Не подключён модуль catalog');
}

$productId = 123;   // ID существующего товара или SKU.
$priceTypeId = 1;   // ID существующего типа цены.
$newPrice = 1500.00;
$currency = 'RUB';  // Код существующей валюты.

$price = Price::getRow(array(
    'select' => array('ID', 'PRODUCT_ID', 'CATALOG_GROUP_ID'),
    'filter' => array(
        '=PRODUCT_ID' => $productId,
        '=CATALOG_GROUP_ID' => $priceTypeId,
        '=QUANTITY_FROM' => false,
        '=QUANTITY_TO' => false,
    ),
));

$data = array('fields' => array(
    'PRODUCT_ID' => $productId,
    'CATALOG_GROUP_ID' => $priceTypeId,
    'PRICE' => $newPrice,
    'CURRENCY' => $currency,
    'QUANTITY_FROM' => null,
    'QUANTITY_TO' => null,
));

$result = $price
    ? Price::update((int)$price['ID'], $data)
    : Price::add($data);

if (!$result->isSuccess()) {
    throw new RuntimeException(implode('; ', $result->getErrorMessages()));
}

echo $price ? 'Цена обновлена' : 'Цена добавлена';

В фильтре false выбирает только запись без границ количества; при записи модель получает null, как требует D7-документация для отсутствующих границ. Так код не перезапишет первую найденную цену диапазона. Price::getRow — официальный метод, который возвращает первую строку ORM-выборки или null. Price::add возвращает результат с ID созданной цены, а Price::update принимает ID цены больше нуля и возвращает объект с ошибками или успехом. Не игнорируйте isSuccess(): тогда импорт не сообщит «готово» после неудачной записи.

Перед запуском проверьте, что ID указывает на товар, а не на родительский товар вариации. Цена, созданная для родителя вместо SKU, может сохраниться успешно, но не попасть в карточку выбранного торгового предложения.

Проверьте, что исправлена нужная запись

  1. Запускайте код только в административном контексте или CLI тестовой копии с правом менять каталог.
  2. До запуска сохраните ID товара, тип цены, прежнюю цену и валюту.
  3. После успеха прочитайте цену тем же фильтром и убедитесь, что изменился ожидаемый ID.
  4. Откройте нужный SKU под обычным посетителем и проверьте тип цены, валюту, право просмотра и количество.

Если цена приходит из 1С, ручное исправление может быть перезаписано следующим обменом. В этом случае включайте описанную логику в согласованный импорт либо меняйте первичный источник, а не шаблон товара.

Код сверён с официальными сигнатурами 4 октября 2026 года. На изолированном стенде Битрикс с тестовым SKU цена создалась как 101.00 и обновилась до 202.00; повторное чтение подтвердило тот же ID записи. См. Price::getRow, Price::add, Price::update и PriceTable. Исходный вопрос и ответ от 11 марта 2026 года: форум «1С-Битрикс».