Штатный обмен 1С-Битрикс и 1С: механика, потолок и что ломается на практике

12 мин20.08.2026

Мы восемнадцать лет работаем с 1С-Битрикс и с 2021 года пишем собственные модули. За это время штатный обмен сайта с 1С прошёл через наши руки достаточно раз, чтобы мы перестали верить фразе «интеграция работает из коробки». Простые функции действительно запускаются за час-два. Дальше начинается то, ради чего написана эта статья.

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

Как устроен штатный обмен

Кто с кем разговаривает

Первое, что стоит зафиксировать: инициатор обмена — всегда 1С. Сайт не ходит в 1С и вообще про неё ничего не знает. В терминах сети 1С — это клиент, сайт — сервер. 1С «открывает» служебную страницу /bitrix/admin/1c_exchange.php, шлёт запросы методами GET и POST и читает текстовый ответ. По сути 1С ведёт себя как браузер, и это можно повторить руками через curl — мы так и отлаживаем.

Протокол синхронный: 1С отправляет следующий запрос только после ответа на предыдущий или после таймаута. Данные передаются в двух форматах. Ответы сайта — простой текст: первая строка success, progress, error или failure. Содержимое каталога и заказов — XML по стандарту CommerceML 2.0 (актуальная версия схемы — 2.10, пространство имён urn:1C.ru:commerceml_2; формат поддерживается начиная с «1С:Предприятие 8.1» и «1С-Битрикс: Управление сайтом» 6.5).

Точка входа одна, а поведение задаётся двумя GET-параметрами:

  • type — что обмениваем: catalog (товары), sale (заказы и контрагенты), reference (справочники, HL-блоки);
  • mode — этап обмена: checkauth, init, file, import, deactivate, complete, query, success, info.

Выгрузка каталога по шагам

Разберём самый частый сценарий — 1С выгружает каталог на сайт (type=catalog).

Шаг 1. checkauth — авторизация. 1С логинится по HTTP Basic. Если всё хорошо, сайт возвращает имя и значение куки авторизации, идентификатор сессии и timestamp — текущее серверное время. Эту метку 1С запомнит и вернёт в конце.

bash
curl -s -c cookie.txt \
  'https://example.ru/bitrix/admin/1c_exchange.php?type=catalog&mode=checkauth' \
  -H "Authorization: Basic $BASIC_AUTH"
# success
# PHPSESSID
# c84bba7587de83c2b7f88a837ffc4237
# sessid=641ec5e7f1547d4934458141ec237512
# timestamp=1623238486

Шаг 2. init — настройки приёмника. 1С спрашивает, что умеет сайт, и получает два параметра:

zip=yes
file_limit=204800

zip=yes означает, что сайт распакует архив, — 1С будет слать файлы в zip, экономя трафик и время. file_limit — максимальный размер одного HTTP-запроса с файлом в байтах (в интерфейсе Битрикса это «Размер единовременно загружаемой части файла», по умолчанию 204800). Если файл больше, 1С режет его на части. Заодно init чистит папку обмена (по умолчанию /upload/1c_catalog/).

Шаг 3. file — передача файла (повторяется). Файл льётся сырым POST в теле запроса. На стороне сайта данные читаются не из $_FILES, а из php://input — то есть всё тело и есть файл. Битрикс дописывает куски в файл, имя которого пришло в filename, пока 1С не передаст все части. Здесь передаются import*.xml (разделы, товары, свойства), offers*.xml (торговые предложения), prices*.xml (цены), rests*.xml (остатки), картинки.

Шаг 4. import — обработка (повторяется много раз). Вот здесь начинается неочевидное. GET-параметр mode=import один и тот же, но за ним прячется до одиннадцати внутренних операций. Битрикс держит прогресс в сессии, в $_SESSION["BX_CML2_IMPORT"], и на каждый запрос делает столько, сколько успевает за отведённое время шага, отвечая progress. 1С повторяет запрос, пока не получит success.

Что происходит внутри import по порядку: распаковка архива → удаление старых временных таблиц → создание временной таблицы b_xml_tree → пошаговое чтение XML в эту таблицу → индексация → импорт метаданных (инфоблоки, свойства, типы цен, склады, единицы измерения) → импорт разделов (сопоставление по XML_ID) → пересчёт дерева разделов (LEFT_MARGIN, RIGHT_MARGIN) → импорт товаров → деактивация/удаление старых товаров → завершение с событием OnSuccessCatalogImport1C.

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

Шаг 5. deactivate — деактивация непришедшего. 1С передаёт сохранённый на шаге 1 timestamp. Битрикс деактивирует все товары и разделы, не тронутые в этой сессии (время их изменения раньше метки). Этот шаг выполняется только при полной выгрузке.

Шаг 6. complete — служебное завершение.

Схема целиком:

1С                          Сайт (1c_exchange.php)
 checkauth   ───────────▶   success + cookie + timestamp
 init        ───────────▶   zip=yes, file_limit=204800
 file (×N)   ───POST────▶   success
 import (×M) ───────────▶   progress … progress … success
 deactivate  ───────────▶   success (деактивировано)
 complete    ───────────▶   success

Приём заказов (type=sale) устроен зеркально: checkauthinitquery (сайт отдаёт заказы в CommerceML) → success (1С подтверждает получение) → при обратной загрузке статусов file/import. Есть ещё info — сайт отдаёт справочники магазина (статусы, способы оплаты и доставки), чтобы в 1С их сопоставить.

Документацию по каждому XML-узлу мы пересказывать не будем — она есть на dev.1c-bitrix.ru и на v8.1c.ru. Дальше про то, чего в документации нет.

Что ломается на практике

Что ломается после обновлений и почему замечают поздно

Общая беда всех поломок обмена в том, что он работает в фоне по расписанию. Никто не смотрит на него, пока в отделе продаж не спросят, почему заказы за два дня не попали в 1С, или пока клиент не пожалуется на неверный остаток. Между поломкой и обнаружением проходят часы, а то и дни.

Рассинхрон версий модуля обмена. Модуль обмена в 1С и версия обработчика в Битриксе должны быть совместимы. Обновили одну сторону — вторая может начать отдавать поля, которых другая не ждёт. Но опаснее другое: обновление меняет и сам обработчик на стороне сайта. Файл cml2.php переписывался между версиями не раз, и вместе с ним менялась цена одного шага импорта — при том, что XML остался прежним, настройки прежними и «ничего не меняли».

Сброс SEO-кэша на большом каталоге. Это как раз тот случай, когда цена шага выросла незаметно.

Загляните в cml2.php: после импорта каждого элемента вызывается \Bitrix\Iblock\InheritedProperty\ElementValues::clearValues(), а после каждого изменённого раздела — SectionValues::clearValues(). Теперь загляните в сам clearValues() у раздела: там DELETE FROM b_iblock_element_iprop с подзапросом, который отбирает элементы не этого раздела, а всего поддерева под ним — границы берутся из LEFT_MARGIN и RIGHT_MARGIN.

На каталоге из сотни разделов такой запрос не виден в профиле. На дереве из десятков тысяч разделов и сотен тысяч товаров он перебирает миллионы строк — и делается это на каждый изменённый раздел за шаг.

Выгрузка, годами укладывавшаяся в отведённое время, начинает упираться в таймаут веб-сервера. В логе обмена появляется 504 Gateway Time-out от nginx — и ни слова про SEO, из-за которого всё встало.

Обмен работал ровно до обновления и «вдруг» перестал — вот эта внезапность и есть ловушка. Проверять надо не XML, а время шага: сравните, сколько занимал import до обновления и сколько занимает после.

Смена домена и переход на HTTPS. Адрес скрипта обмена прописан в настройках узла в 1С. Переехали на новый домен или включили обязательный HTTPS — 1С продолжает стучаться на старый адрес и не может установить соединение. Со стороны сайта «ничего не меняли», а обмен встал.

Кодировка. Штатный обмен идёт в windows-1251. Если сайт перевели в UTF-8, а обработчик выгрузки заказов отдаёт CML в 1251 (или наоборот, база 1С в UTF-8), в заказах и товарах появляются «кракозябры» в русских полях. Ломается не весь обмен, а конкретные поля — и это замечают ещё позже, потому что цифры (цены, остатки) приходят правильно, а бьётся текст.

Пропущенные обязательные поля. У справочника, например, узел <Наименование> фактически не используется, но если его нет в файле — Битрикс выдаёт ошибку. Такие вещи всплывают не при настройке, а когда 1С однажды выгрузит запись без нужного узла.

Одновременный запуск двух обменов. Классика: к боевому сайту подключили тестовую 1С и забыли выключить в ней периодический обмен. Оба процесса пишут во временную таблицу b_xml_tree, которая чистится в начале каждого обмена. Второй процесс её обнуляет, первый получает Table 'b_xml_tree' doesn't exist и падает. Ошибка плавающая, воспроизводится не всегда — и потому диагностируется тяжело.

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

Задвоение номенклатуры

Это самая дорогая в исправлении проблема, потому что к моменту обнаружения к неправильным элементам уже привязаны заказы.

Сопоставление товаров и разделов идёт по внешнему коду. 1С выгружает свой идентификатор (GUID) ко всем сущностям — товарам, свойствам, значениям свойств, — а Битрикс кладёт его в поле XML_ID. Всё сопоставление построено на XML_ID. Если товар с таким XML_ID на сайте есть — он обновляется, если нет — создаётся новый.

Отсюда вытекает механика дублей. Как только XML_ID на сайте и внешний код в 1С перестают совпадать, обмен вместо обновления карточки создаёт рядом вторую. Типичные поводы:

  • слияние или пересоздание базы 1С — у номенклатуры меняются GUID, и весь каталог задваивается;
  • пересоздание инфоблока — обмен начинает лить в новый инфоблок, старый остаётся с заказами;
  • два товара с одинаковым <Ид> в одном файле — на сайт попадёт только последний, он затрёт предыдущий.

Отдельная ловушка — сопоставление по названию. Если внешнего кода нет и товары матчатся по наименованию, любая правка названия в 1С («Труба ⌀20» → «Труба д.20») превращается для сайта в новый товар. Старый деактивируется, новый создаётся с нуля.

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

  • к старому элементу уже привязаны заказы. Просто удалить дубль нельзя: потеряется история;
  • остатки размазаны между двумя карточками, и ни одна не показывает правду;
  • поисковый индекс и фильтр содержат обе версии, покупатель видит два одинаковых товара.

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

Большие каталоги: где выгрузка «на всё сразу» перестаёт работать

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

Точную границу называют по-разному, и это честный разброс, а не единая цифра.

  • До 5 тысяч товаров и 10 тысяч предложений — оценка ЮвелирСофт: дальше выгрузка занимает от трёх до шести часов.
  • Больше 3–5 тысяч позиций — оценка kalinkindev.ru: выгрузка за один запрос перестаёт укладываться в предел времени и обрывается на середине.

Граница зависит от сервера, поэтому надёжнее ориентироваться не на число товаров, а на то, во что упирается процесс.

А упирается он вот во что:

Ограничение Что происходит Симптом
max_execution_time шаг импорта не укладывается в лимит PHP обрыв на середине, неполный каталог
memory_limit не хватает памяти на разбор XML или большой заказ Fatal error: Allowed memory size … exhausted
Таймаут веб-сервера nginx рвёт долгий запрос раньше PHP 504 Gateway Time-out в логе обмена
Размер XML / file_limit файл больше лимита передаётся частями лишние круги на шаге file
Блокировка b_xml_tree параллельный обмен чистит временную таблицу Table 'b_xml_tree' doesn't exist

Упирается не только каталог, но и отдельный тяжёлый заказ. Вот жалоба с форума dev.1c-bitrix.ru:

При обратной загрузке большого заказа с 1С в Битрикс выпадает ошибка «Allowed memory size of 536870912 bytes exhausted». Добавил оперативки до 6 ГБ, memory_limit 3072, execute time 900 поставил. Ошибка при загрузке большого заказа (около 400 позиций) сохранилась.

512 МБ памяти и поднятые пределы не спасли заказ на четыре сотни строк. Узкое место было не в размере предела, а в самом коде обработки — поднимать лимиты дальше бессмысленно.

Что делают вместо полной выгрузки:

  1. Выгрузка только изменений. В 1С включается режим «Только изменения» — на сайт едет не весь каталог, а дельта. Скорость обмена растёт кардинально. Это первое, что нужно включить на большом каталоге.
  2. Разделение потоков. Товары меняются редко, остатки и цены — постоянно. Их разводят на отдельные обмены с разной частотой: каталог, скажем, раз в сутки, остатки и цены — по своему расписанию. Остатки в принципе не про реалтайм: их гоняют периодически, регулируя объём пакета.
  3. Уменьшение размера пакета. В настройках обмена уменьшают число элементов в шаге, чтобы каждый запрос гарантированно укладывался в лимиты.
  4. Очередь и фоновая обработка. Для действительно больших каталогов пошаговую обработку выносят в фон (агенты или cron), чтобы не зависеть от max_execution_time одного HTTP-запроса.

Ключевая мысль: увеличить max_execution_time и memory_limit — это лечение симптома. Оно выигрывает время, но растущий каталог рано или поздно снова упрётся в потолок. Архитектурное решение — перестать возить всё целиком.

Потеря заказов при обрыве связи (коротко)

Этому сюжету посвящён отдельный разбор — здесь только суть.

Заказы уезжают в 1С в два такта: query — сайт отдаёт заказы, success — 1С подтверждает получение. Отметка «эти заказы выгружены» ставится не на query, а только на success: на шаге успеха модуль sale записывает во внутреннее свойство (last_export_time_committed_…) время момента запроса заказов. Если 1С получила файл, но ответ success не дошёл из-за обрыва, на сайте эта отметка не сдвинулась. При следующем сеансе те же заказы снова попадут под фильтр «изменённые после последней выгрузки» (они выбираются с флагами EXTERNAL_ORDER = "N" и UPDATED_1C = "N") и уедут повторно.

Отдельного идемпотентного ключа у штатного обмена нет. От размножения документов спасает только сопоставление на стороне 1С по узлам <Ид> и <Номер> документа заказа — если оно настроено верно, повторный заказ обновит существующий документ, а не создаст дубль. Отсюда и вывод: повторная отправка «в лоб», без надёжного сопоставления, плодит дубли заказов. Как строить приём заказов, устойчивый к обрывам, — в отдельном разборе.

Итог

Почему это не настраивается за день

Соберём картину. Четыре свойства штатного обмена, из-за которых он и не настраивается за день:

  • Синхронный протокол из нескольких этапов. У каждого этапа свои режимы и свои способы сломаться.
  • Сопоставление держится на одном поле. Любое рассогласование XML_ID оборачивается дублями, которые дорого чинить задним числом.
  • Полная выгрузка упирается в пределы. Время выполнения, память, таймаут веб-сервера — на растущем каталоге выгрузку приходится перестраивать.
  • Заказы при обрыве связи выгружаются повторно. Защита от дублей лежит на правильной настройке сопоставления в 1С, своего ключа у обмена нет.

Ничего из этого не всплывает в первый день. Всплывает через недели после запуска — когда каталог подрос, домен переехал, а Битрикс обновился. Поэтому обмен и не настраивается за день: настроить первую успешную выгрузку — это час, а сделать так, чтобы она не рассыпалась на реальных данных и объёмах, — совсем другая работа.

Если вы дочитали до этого места и узнали свой обмен — задвоенную номенклатуру, выгрузку, которая падает по таймауту, заказы, что задваиваются после сбоя, — это ровно те задачи, которые мы разбираем и приводим в порядок. Что мы делаем и как, описано на странице интеграции сайта с 1С.

В статье использованы материалы

← Все статьи