Цены из 1С: типы цен, валюты, НДС, округление

Почему несопоставленный тип цены игнорируется молча, как считается НДС при выгрузке и откуда берётся расхождение в копейку между сайтом и учётной системой.

Дмитрий Мещеряков
Дмитрий Мещеряков
📅 12 октября 2026 г.📖 7 мин чтения

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

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

Разберём все четыре: где сопоставляются, как ломаются и как проверить, что цена, которую видит покупатель, — та же, что в учётной системе.

Что такое тип цены

Тип цены — это именованный набор цен для всех товаров каталога: розница, опт, закупка, цена для партнёров. Один товар имеет столько цен, сколько типов заведено, и каждая живёт своей жизнью.

Связь между типами цен 1С и Битрикса устроена ровно так же, как связь товаров и свойств, разобранная в статье про маппинг полей из 1С: по полю XML_ID. В нём хранится GUID типа цены из 1С, и обмен ищет соответствие только по нему. Числовой ID Битрикса в обмене не участвует.

xml
<Предложение>
  <Ид>f4e2...#9a71...</Ид>
  <Цены>
    <Цена>
      <ИдТипаЦены>b2d4c7a1-...</ИдТипаЦены>
      <ЦенаЗаЕдиницу>4990.00</ЦенаЗаЕдиницу>
      <Валюта>RUB</Валюта>
      <Единица>шт</Единица>
      <Коэффициент>1</Коэффициент>
    </Цена>
  </Цены>
  <Количество>17</Количество>
</Предложение>

Главная ловушка: молчаливое игнорирование

Вот поведение, которое стоит запомнить до того, как оно случится на вашем проекте.

Если ИдТипаЦены не совпал ни с одним XML_ID на сайте, обмен отбрасывает цену и продолжает работу. Не падает, не пишет предупреждение, не помечает товар. В журнале — ничего. Обмен завершается со статусом success, потому что с его точки зрения ошибки не было: пришли данные, которые сайту не нужны.

Со стороны бизнеса это выглядит так: «обмен зелёный, а цены старые». Разработчик идёт искать поломку в обмене, а обмена там нет — есть несопоставленный справочник.

⚠️ Важно

Сопоставление типов цен проверяется до первого боевого обмена, а не после жалобы. Особенно после любого изменения на стороне 1С: новый тип цены, переименование, перенос базы в другую информационную базу — всё это меняет GUID. Тип цены с прежним названием, но новым GUID для обмена не существует.

Как проверить сопоставление

Сначала смотрим, какие GUID реально приезжают:

bash
cd /home/site/www/upload/1c_catalog/

# Уникальные типы цен в выгрузке
grep -o "<ИдТипаЦены>[^<]*</ИдТипаЦены>" offers.xml \
  | sort -u

Затем — что заведено на сайте:

php
<?php
use Bitrix\Catalog\GroupTable;
use Bitrix\Main\Loader;

Loader::includeModule('catalog');

$rows = GroupTable::getList([
    'select' => ['ID', 'NAME', 'NAME_LANG', 'XML_ID', 'BASE'],
    'order'  => ['ID' => 'ASC'],
])->fetchAll();

foreach ($rows as $row) {
    printf(
        "%3d  %-20s base=%s  xml_id=%s\n",
        $row['ID'],
        $row['NAME'],
        $row['BASE'],
        $row['XML_ID'] ?: '— НЕ СОПОСТАВЛЕН —'
    );
}

Два списка должны сойтись. Каждый GUID из выгрузки обязан найтись в колонке xml_id — иначе эти цены на сайт не попадают.

💡 Совет

Базовая цена в Битриксе одна. Тип цены с флагом BASE = Y участвует в расчёте скидок и в большинстве типовых сценариев. Если 1С выгружает несколько типов, а базовым на сайте отмечен не тот, витрина будет показывать не ту цену, хотя в базе лежат все — и это ещё одна поломка, не оставляющая следов в журнале.

Валюты

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

Валюта заведена в модуле валют. Код из выгрузки (RUB, USD, EUR) должен существовать в справочнике валют сайта.

Для валюты есть актуальный курс. Пересчёт делается по курсу на текущую дату. Курса нет — пересчёта нет, и результат зависит от того, какой курс окажется последним доступным.

php
<?php
use Bitrix\Currency\CurrencyTable;
use Bitrix\Main\Loader;

Loader::includeModule('currency');

$rows = CurrencyTable::getList([
    'select' => ['CURRENCY', 'BASE', 'AMOUNT', 'DATE_UPDATE'],
])->fetchAll();

foreach ($rows as $row) {
    printf(
        "%s  base=%s  обновлено: %s\n",
        $row['CURRENCY'],
        $row['BASE'],
        $row['DATE_UPDATE'] ? $row['DATE_UPDATE']->format('d.m.Y') : 'никогда'
    );
}

Строка «обновлено: никогда» у валюты, в которой приезжают цены, — это уже инцидент, даже если жалоб пока нет. Автоматическая загрузка курсов в Битриксе делается агентом, а агенты, как разобрано в статье про расписание обмена, без cron ночью не выполняются. Классическая связка: курс не обновился, потому что не было посетителей, а цены пересчитались по прошлогоднему.

НДС

Здесь ломается тоньше всего, потому что налог описывается двумя независимыми фактами, и рассинхронизация между ними даёт правдоподобно выглядящий, но неверный результат.

Первый факт — ставка. Приезжает в товаре:

xml
<Товар>
  <Ид>f4e2...</Ид>
  <СтавкиНалогов>
    <СтавкаНалога>
      <Наименование>НДС</Наименование>
      <Ставка>20</Ставка>
    </СтавкаНалога>
  </СтавкиНалогов>
</Товар>

Второй — включён ли налог в переданное число. Цена 4990 может означать и «4990 с НДС», и «4990 плюс НДС сверху». Разница — 998 рублей, и никакой алгоритм не выведет её из самого числа.

На стороне Битрикса этим двум фактам соответствуют ставка НДС товара и флаг «цена включает НДС». Три сценария поломки:

Что в 1СЧто на сайтеРезультат
Цена с НДСФлаг «включает НДС» снятНДС начислится повторно, цена вырастет на 20%
Цена без НДСФлаг «включает НДС» стоитНалог потеряется, цена занижена
Ставка не сопоставлена—Товар считается без НДС, чек уйдёт с неверной ставкой

Третий случай опаснее первых двух: на витрине всё выглядит правильно, а расхождение всплывает в фискальных документах — то есть там, где ошибка стоит дороже всего.

⚠️ Важно

Проверяйте НДС на товаре с некруглой ценой. На цене 1000 рублей ошибка в ставке даёт 1200 — заметно. На цене 4990 — 5988, и число выглядит достаточно правдоподобно, чтобы пройти беглую проверку. Для сверки берите товар с ценой вроде 4990,50 и считайте вручную.

Округление

Расхождение «на сайте 4990, в 1С 4990,17» — самая частая претензия к обмену и почти всегда ложная тревога. В базу цена приезжает с полной точностью. Округление применяется при показе, по правилам, которые настраиваются отдельно для каждого типа цены в модуле торгового каталога.

Отсюда порядок разбирательства, который экономит час:

php
<?php
use Bitrix\Catalog\PriceTable;
use Bitrix\Main\Loader;

Loader::includeModule('catalog');

// Что реально лежит в базе для товара — без округления и пересчётов
$rows = PriceTable::getList([
    'select' => ['PRODUCT_ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY'],
    'filter' => ['=PRODUCT_ID' => 4821],
])->fetchAll();

print_r($rows);

Совпало с 1С — обмен работает, вопрос к настройкам округления или к валюте вывода. Не совпало — вот теперь имеет смысл смотреть обмен.

Второй источник копеечных расхождений — конвертация валют. Цена в долларах, пересчитанная в рубли и округлённая, при обратном пересчёте не даст исходное число. Это свойство арифметики, а не ошибка: для мультивалютных каталогов сверять нужно цену в валюте хранения.

Отладка: сверка цены за три шага

Порядок тот же, что в общем разборе отладки обмена — от источника к витрине.

bash
# 1. Что прислала 1С для конкретного товара
grep -A 20 "f4e2c1a9" /home/site/www/upload/1c_catalog/offers.xml
php
<?php
// 2. Что лежит в базе (код выше) — сравниваем с XML
text
3. Что показывает витрина — сравниваем с базой.
   Разошлось между 1 и 2 → обмен, сопоставление типа цены.
   Разошлось между 2 и 3 → округление, валюта, кэш или скидка.

Третий пункт стоит развернуть. Расхождение между базой и витриной чаще всего означает не цену, а кэш каталога: данные обновились, а компонент отдаёт сохранённый результат. Как устроены сроки жизни кэша каталога и что сбрасывать после обмена, разобрано в статье про политику кэширования каталога. Отдельный сорт той же проблемы — залипание цен при обновлении через CPrice, ему посвящён разбор конкретного бага.

Итоги

Тип цены сопоставляется по XML_ID и молча игнорируется, если не сопоставлен. Это главный источник проблем с ценами, и он не оставляет следов в журнале. Проверять — до обмена, а не после.

Любое изменение в 1С меняет GUID. Новая информационная база, новый тип цены, миграция — сопоставление нужно перепроверить. Совпадение названий ничего не значит.

Валюта без актуального курса пересчитывается непредсказуемо. Проверьте дату последнего обновления курсов — и заодно то, что агент обновления вообще выполняется.

НДС — это два независимых факта. Ставка и признак «включён в цену». Ошибка в любом из них даёт правдоподобное число.

Расхождение в копейках — почти всегда округление при показе. Сверяйте значение в базе, а не то, что нарисовано в карточке.

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

Частые вопросы

Почему цены из 1С не обновляются на сайте?

Три причины в порядке частоты. Тип цены не сопоставлен: ИдТипаЦены из offers.xml не совпал ни с одним XML_ID типов цен на сайте, и данные отброшены без записи в журнал. Обмен вообще не дошёл до offers.xml — видно по логу веб-сервера. Цены обновились в базе, но витрина отдаёт кэш каталога, который никто не сбрасывал после обмена.

Как сопоставить тип цены из 1С с типом цены в Битриксе?

Найти GUID типа цены в offers.xml (тег ИдТипаЦены или блок ТипыЦен) и записать его в поле XML_ID нужного типа цены на сайте — в справочнике типов цен модуля торгового каталога. После этого обмен найдёт соответствие сам. Числовой ID Битрикса при этом не используется вообще: связь держится только на GUID.

Цена на сайте отличается от 1С на копейку. Где ошибка?

Как правило, нигде: в базу цена приехала точно такой, какой её отправила 1С, а расхождение возникает при показе. На это работают два механизма — правила округления цен, настраиваемые отдельно для каждого типа цены, и пересчёт по курсу, если валюта цены отличается от валюты вывода. Сверять нужно значение в базе, а не то, что нарисовано в карточке товара.

Как передаётся НДС при обмене с 1С?

Ставка налога приезжает в товаре блоком СтавкиНалогов, а признак того, включён ли налог в переданную цену, — атрибутом самой цены. На стороне Битрикса товару соответствуют ставка НДС и флаг «цена включает НДС». Рассинхронизация этих двух настроек и есть источник классической ошибки, когда НДС оказывается посчитан дважды или потерян целиком.

Что делать, если 1С выгружает цены в валюте, отличной от валюты сайта?

Убедиться, что валюта из выгрузки заведена в модуле валют и для неё есть актуальный курс. Битрикс хранит цену в той валюте, в которой она приехала, и пересчитывает её при показе по курсу на текущую дату. Без загруженного курса пересчёт даст либо ноль, либо цену по курсу годичной давности — и то и другое произойдёт молча.