Обмен заказами изнутри - документ, связь, перезапись
Разбираем сам документ заказа: из чего он собран и чем опознаётся на второй стороне обмена. Дальше смотрим, какие части заказа перезаписывает загрузка и что при этом остаётся за сайтом.
Механика
Документ заказа устроен иначе, чем файлы обмена каталогом. Каталог описывает справочник и едет в одну сторону, а заказ - это документ, который ходит туда и обратно. Одну и ту же запись правят две системы, и порядок правок согласуют до запуска обмена.
Сеанс обмена заказами идёт теми же шагами, что и обмен каталогом: проверка доступа, инициализация, передача файла, разбор. Шаги подробно разобраны в протоколе обмена, здесь речь только о содержимом самого документа.
Внутри пакета заказ лежит элементом с хозяйственной операцией и ролью стороны. Хозяйственная операция отличает документ заказа от документа оплаты или отгрузки, а роль говорит, кем выступает отправитель пакета.
Опознаётся заказ двумя номерами сразу, а не одним. Сайт кладёт свой номер в поле номера документа, учётная система возвращает туда же собственный номер отдельным полем. Первый успешный обмен связывает эти номера, дальше связь живёт на обеих сторонах независимо.
Потеря этой связи всегда даёт дубль, а не ошибку разбора. Сменившаяся нумерация на сайте или очищенная таблица идентификаторов объектов делает прежний заказ невидимым, и учётная система заводит новый документ рядом со старым.
Метка версии документа решает, разбирать его или пропустить. Принимающая сторона сравнивает метку с той, что запомнила на прошлом сеансе, и пропускает документ без изменений, оставляя в журнале отдельную строку об этом.
Загрузка заказа на сайт заменяет части документа целиком, а не дополняет их. Состав позиций, оплаты и отгрузки приезжают из учётной системы готовым набором и вытесняют то, что было заведено на сайте руками. Собственный идентификатор заказа, его покупатель и дата создания при этом остаются за сайтом.
Статусы и признаки едут не полями документа, а плоским списком реквизитов. Каждый реквизит - это пара из наименования и значения, и разбор ищет в списке знакомые ему наименования. Незнакомое наименование отбрасывается молча, поэтому состав реквизитов задаёт модуль обмена учётной системы, а не сайт.
Позиции документа сопоставляются с корзиной заказа по внешнему коду товара. Строка с пустым кодом приезжает неопознанной, а при совпадении внешних кодов в двух каталогах одного сайта в корзину подбирается не тот товар.
Загрузка идёт штатным сохранением заказа, а не прямой записью в таблицы. События модуля продаж срабатывают при обмене ровно так же, как при правке заказа из админки, и на них удобно вешать собственную логику.
Шаги
- Найти файл прошедшего сеанса в каталоге загрузки и открыть в нём нужный документ.
- Сверить номер документа с номером заказа на сайте, а номер учётной системы - с её документом.
- Прочитать список реквизитов и выписать оттуда статус, признаки проведения и отмены.
- Сравнить позиции документа с корзиной заказа по внешним кодам товаров и количествам.
- Повесить обработчик на сохранение заказа и посмотреть, что именно меняет загрузка.
Код
Смотрим, какие файлы заказов остались на диске:
ls -lt /home/bitrix/www/upload/1c_exchange/ | head -5# документы заказов приезжают файлами вида Documents___<идентификатор>.xml# файл остаётся на диске и после обрыва сеанса обменаgrep -c '<Документ>' /home/bitrix/www/upload/1c_exchange/Documents___*.xml# число документов в файле показывает объём одного сеанса обменаКаталог загрузки - самый быстрый способ увидеть, что именно приехало на сайт. Файл переживает и обрыв связи, и ошибку разбора, поэтому разбор полезно начинать именно с него.
Читаем шапку документа заказа:
<Документ> <Ид>Q-20030</Ид> <!-- ключ документа внутри пакета --> <НомерВерсии>AAAAAAWUUTs=</НомерВерсии> <!-- метка изменения документа --> <ПометкаУдаления>false</ПометкаУдаления> <!-- пометка удаления в учётной системе --> <Номер>Q-20030</Номер> <!-- номер заказа на стороне сайта --> <Номер1С>ХРCB-ХР0510</Номер1С> <!-- номер документа в учётной системе --> <Дата>2018-09-12</Дата> <!-- дата документа, время лежит отдельно --> <ХозОперация>Заказ товара</ХозОперация> <!-- вид документа: заказ, оплата, отгрузка --> <Роль>Продавец</Роль> <!-- роль отправителя пакета обмена --> <Валюта>руб</Валюта> <Курс>1.0000</Курс> <Сумма>23000</Сумма> <!-- итоговая сумма документа --></Документ>Два номера в шапке и есть вся связь между системами. Метка версии рядом с ними решает судьбу документа: не изменилась - разбор пропустит его целиком.
Реквизиты везут статус и признаки:
<ЗначенияРеквизитов> <ЗначениеРеквизита> <Наименование>Статуса заказа ИД</Наименование> <Значение>N</Значение> <!-- код статуса, названия не используются --> </ЗначениеРеквизита> <ЗначениеРеквизита> <Наименование>Проведен</Наименование> <Значение>true</Значение> <!-- документ проведён в учётной системе --> </ЗначениеРеквизита> <ЗначениеРеквизита> <Наименование>Отменен</Наименование> <Значение>true</Значение> <!-- отмена едет отдельно от статуса --> </ЗначениеРеквизита> <ЗначениеРеквизита> <Наименование>Метод доставки ИД</Наименование> <Значение>3</Значение> <!-- код службы доставки на сайте --> </ЗначениеРеквизита></ЗначенияРеквизитов>Статус, отмена, проведение и доставка лежат в одном плоском списке. Именно поэтому признак отмены приезжает без смены статуса, а статус - без признака отмены: согласовывает их отправитель, а не сайт.
Позиции документа несут свои реквизиты:
<Товары> <Товар> <Ид>a3f1c0de-1111-4b2c-9c11-7e0d2f4a55c1</Ид> <!-- внешний код товара --> <Наименование>Чайник электрический</Наименование> <ЦенаЗаЕдиницу>2450</ЦенаЗаЕдиницу> <Количество>2</Количество> <Сумма>4900</Сумма> <ЗначенияРеквизитов> <ЗначениеРеквизита> <Наименование>СвойствоКорзины#PROFILE_XML_ID</Наименование> <Значение>MATERIAL-STEEL</Значение> <!-- свойство позиции корзины --> </ЗначениеРеквизита> </ЗначенияРеквизитов> </Товар></Товары>Внешний код позиции - единственный ключ подбора номенклатуры при разборе. Свойства корзины уезжают отдельными реквизитами позиции, и принимающая сторона сопоставляет их по наименованию.
Контрагент опознаётся составным ключом:
<Контрагент> <Ид>10#arka@example.com#Аркадий Аркадьев</Ид> <!-- код, почта и имя разом --> <ПолноеНаименование>Аркадий Аркадьев</ПолноеНаименование> <Роль>Покупатель</Роль> <!-- у документа заказа роль всегда покупатель --> <ИНН/><КПП/> <!-- для юридического лица ключом служит ИНН --></Контрагент>Ключ покупателя собран из нескольких частей, и смена почты меняет его целиком. Незаполненный ИНН у юридического лица плодит карточки контрагентов в учётной системе.
Сверяем связь заказа с учётной системой:
\Bitrix\Main\Loader::includeModule('sale');$row = \Bitrix\Sale\Internals\OrderTable::getRow([ 'select' => ['ID', 'ACCOUNT_NUMBER', 'ID_1C', 'VERSION_1C', 'UPDATED_1C'], 'filter' => ['=ID' => $orderId],]);printf("номер=%s ключ=%s версия=%s отдан=%s\n", $row['ACCOUNT_NUMBER'], $row['ID_1C'] ?: '-', $row['VERSION_1C'] ?: '-', $row['UPDATED_1C']);// ACCOUNT_NUMBER - номер, по которому заказ ищет учётная система// ID_1C и VERSION_1C заполняются после разбора приехавшего документа// UPDATED_1C - признак того, что заказ уже отдан в одном из сеансовЗаполненные ключ и версия означают, что заказ уже побывал в учётной системе. Пустые поля при работающем обмене говорят, что связь по этому заказу так и не установилась.
Смотрим отметку последнего запроса заказов:
$key = 'last_export_time_committed_/bitrix/admin/1c_excha';printf("последний запрос: %s\n", \Bitrix\Main\Config\Option::get('sale', $key, '-'));// имя настройки обрезано по длине, поэтому адрес точки обмена в нём неполный\Bitrix\Main\Config\Option::set('sale', $key, '2026-08-01 00:00:00');// сдвинутая назад дата вернёт в выгрузку заказы нужного периодаПовторный запрос отдаёт пустой файл именно из-за этой отметки. Сдвиг даты поднимает старые заказы, не трогая ни их поля, ни признак выгрузки.
Ловим момент, когда загрузка меняет заказ:
$em = \Bitrix\Main\EventManager::getInstance();$em->addEventHandler('sale', 'OnSaleOrderBeforeSaved', static function (\Bitrix\Main\Event $event) { $order = $event->getParameter('ENTITY'); // состояние до применения документа $line = $order->getId() . ' ' . $order->getField('STATUS_ID') . "\n"; file_put_contents('/tmp/1c.log', $line, FILE_APPEND); });// печатать что-либо в вывод здесь нельзя: ответ точки обмена станет нечитаемымОбмен сохраняет заказ штатным способом, поэтому события срабатывают как обычно. Пишем только в файл: посторонний вывод на адресе обмена ломает разбор ответа на стороне учётной системы.
Сравниваем позиции документа с корзиной:
$order = \Bitrix\Sale\Order::load($orderId);foreach ($order->getBasket() as $item) { printf("%-28s товар=%d количество=%s цена=%s\n", $item->getField('NAME'), $item->getProductId(), $item->getQuantity(), $item->getPrice());}// getProductId - товар сайта, подобранный по внешнему коду из документа// расхождение количеств означает, что состав заказа переписан загрузкойЧисло строк и количества обязаны совпасть с тем, что лежит в приехавшем документе. Расхождение означает, что позицию не удалось опознать по внешнему коду при разборе.
Ограничения
Состав реквизитов документа задаёт модуль обмена учётной системы, а не сайт. Поле, которого нет в её конфигурации, не появится в документе ни при каких настройках сайта.
Перезапись частей заказа при загрузке - штатное поведение платформы, а не сбой. Отключается она только отказом от встречной выгрузки заказов на стороне узла обмена учётной системы.
Правка приехавшего файла руками ничего не даёт: следующий сеанс перепишет его заново. Поведение разбора меняют настройками профиля обмена и обработчиками событий модуля продаж.
Два одновременных сеанса обмена конфликтуют на временных таблицах и способны повредить данные. Расписание узла и ручной запуск обмена не должны пересекаться по времени.
Журнал ошибок остаётся пустым, пока в общих настройках выключено хранение информации об ошибках. Без этого флага ход разбора виден только по остаткам файлов в каталоге загрузки.
Типичные проблемы
После обмена из заказа пропали заведённые на сайте оплаты и отгрузки.
Встречная загрузка заменяет эти части заказа тем набором, который приехал из учётной системы. Чтобы они сохранялись, платежи и реализации заводят в учётной системе, а не в админке сайта.
Учётная система возвращает заказ в прежний статус уже после оплаты на сайте.
В документе едет реквизит статуса, и каждый сеанс применяет его значение заново. Источник состояния заказа выбирают один: либо сайт, либо учётная система.
Повторный запрос заказов отдаёт пустой файл выгрузки.
Отметка времени последнего запроса уже сдвинута вперёд прошедшим сеансом обмена. Чтобы отдать заказы заново, эту отметку в настройках модуля продаж переводят назад.
В журнале строка о том, что документ не менялся и будет пропущен.
Метка версии документа совпала с той, что запомнилась на прошлом сеансе обмена. Заказ правят на сайте или сбрасывают признак выгрузки, чтобы метка изменилась.
Признак отмены приехал в документе, а заказ на сайте не отменён.
Реквизит с отменой разобран, но статус в том же документе пришёл прежним значением. Отмену и статус учётная система шлёт независимо, и согласуют их на её стороне.
В заказ из учётной системы подобрались совсем не те товары.
Внешние коды товаров совпадают у двух разных каталогов одного и того же сайта. Сопоставление идёт только по коду, и в корзину попадает первый найденный товар.
Частые вопросы
Как получить файл выгрузки заказов в xml формате?
Обратиться к точке обмена запросом типа sale в режиме запроса документов, сохранив куку проверки доступа. Файл того же сеанса остаётся и в каталоге /upload/1c_exchange/.
Как добавить на сайте в заказ поле XML_ID, которое создалось в 1С?
Штатный документ везёт номер учётной системы, и он попадает в служебные поля заказа. Отдельного внешнего кода заказа платформа не ведёт, его дописывают обработчиком события сохранения.
Почему поле наименование в выгружаемом документе пустое?
Профиль обмена не сопоставил свойство заказа с наименованием контрагента. Сопоставление правят в профиле обмена на сайте, а не в самом файле выгрузки.
Как передать свойство корзины в реквизит товара 1С?
Свойство позиции уезжает реквизитом с наименованием вида «СвойствоКорзины#КОД». Принимающая сторона сопоставляет это наименование со своим реквизитом номенклатуры.
Есть ли у Битрикса журнал выгрузок заказов в 1С?
Отдельного журнала выгрузок нет, есть отметка времени последнего запроса и лог обмена. Лог заказов пишется только при включённом протоколировании в настройках модуля продаж.
Смежное
- Обмен: заказы - оглавление подтемы
- Обмен с 1С и HTTP - устройство интеграций целиком
- Протокол обмена с 1С: шаги, параметры, ответы, отладка - сеанс, в котором едет документ
- Статусы заказа при обмене: возврат из 1С - что делать, когда статус не применился
- Состав заказа и контрагенты при обмене: позиции, покупатели, дубли - настройка сопоставления позиций
- Отмены и возвраты при обмене: что уезжает в учётную систему - отмена со стороны сайта
- Не выгружаются заказы в 1С: разбор причин - когда документ не уезжает вовсе
- Импорт каталога изнутри: файлы, сопоставление, перезапись - тот же разбор для каталога
- Заказ изнутри: коллекции, пересчёт, сохранение, события - что именно перезаписывает загрузка
- Свойства заказа: свои поля, обязательность и вывод - откуда берутся реквизиты документа