Как выгрузить номенклатуру в другую базу 1С и избежать типичных ошибок синхронизации

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

При настройке обмена данными между различными конфигурациями 1С (например, между «1С-Рарус: Торговый комплекс» и «1С:Розница») разработчики часто сталкиваются с необходимостью синхронизировать справочник Номенклатура в реальном времени. На первый взгляд задача кажется простой: перехватить момент создания карточки товара, сформировать файл обмена и отправить его в базу-приемник. Однако при реализации «в лоб» возникает множество подводных камней — от зависания интерфейса пользователей до рассинхронизации данных из-за отката транзакций.

В этой статье мы подробно разберем, почему популярный подход с выгрузкой в событии ПередЗаписью является критической ошибкой, выясним, как правильно читать реквизиты объектов, и рассмотрим наиболее надежные и современные способы преодоления несовместимости версий платформ (например, при взаимодействии серверов на платформах 8.3.13 и 8.3.16).

Почему выгрузка в событии «ПередЗаписью» — это архитектурный антипаттерн

Попытка создать файл выгрузки непосредственно в предопределенном обработчике ПередЗаписью модуля объекта или в соответствующей подписке на событие несет в себе три серьезные угрозы для стабильности информационной системы.

  1. Нарушение транзакционности (фантомные данные). Событие ПередЗаписью выполняется внутри транзакции записи. База данных еще не зафиксировала изменения окончательно. Если после выгрузки файла в этой же транзакции произойдет сбой (например, не сработает проверка заполнения реквизитов на уровне платформы, или заблокируется проведение связанных регистров), транзакция откатится. При этом в базе-источнике карточка товара создана не будет, а файл обмена уже запишется на диск. База-приемник обработает этот файл и создаст у себя несуществующий в источнике товар.
  2. Отсутствие ссылки у новых объектов. Когда пользователь впервые создает карточку номенклатуры, в событии ПередЗаписью у нее еще нет ссылки в базе данных (метод Ссылка.Пустая() вернет истину). Без реальной ссылки невозможно прочитать данные из подчиненных регистров сведений, например, штрихкоды или цены номенклатуры, которые записываются строго после самого объекта.
  3. Блокировка интерфейса. Запись файла на диск (особенно если это сетевой ресурс или внешний FTP-сервер) — операция сравнительно медленная и зависимая от сетевой инфраструктуры. Выполняя эту операцию внутри транзакции записи, мы заставляем пользователя ждать окончания процесса записи файла. Если сеть «моргнет», сеанс пользователя просто зависнет до истечения таймаута блокировок.

Правильное решение: выгрузка должна происходить асинхронно. В транзакции записи мы должны лишь зарегистрировать факт изменения объекта (например, с помощью планов обмена), а саму отправку данных выполнять в фоновом режиме с помощью регламентного задания.

Выборка или запрос: как правильно собирать данные для выгрузки

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

Если мы все же вынуждены собирать данные непосредственно в момент записи (в событии ПриЗаписи или ПослеЗаписи), то для обращения к реквизитам самого объекта нам не нужны ни запросы, ни выборки. Все данные уже находятся в оперативной памяти сервера. Мы можем обращаться к ним напрямую через переменную Источник (в подписке на событие) или ключевое слово ЭтотОбъект:


НаименованиеТовара = Источник.Наименование;
АртикулТовара = Источник.Артикул;

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

Проанализируем пример правильного получения связанных данных с помощью запроса в событии ПриЗаписи:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Штрихкоды.Штрихкод КАК Штрихкод,
    |	Штрихкоды.ТипШтрихкода КАК ТипШтрихкода
    |ИЗ
    |	РегистрСведений.ШтрихкодыНоменклатуры КАК Штрихкоды
    |ГДЕ
    |	Штрихкоды.Номенклатура = &Номенклатура";

Запрос.УстановитьПараметр("Номенклатура", Источник.Ссылка);
РезультатЗапроса = Запрос.Выполнить();

Использование запроса позволяет минимизировать количество обращений к СУБД и мгновенно получить всю связанную информацию по конкретной ссылке.

Преодоление несовместимости платформ (8.3.13 и 8.3.16)

Когда базы данных работают на разных релизах платформы (например, одна на 8.3.13, а другая на 8.3.16), прямое подключение через COM-соединение (V83.COMConnector) становится невозможным. Компонента COM-соединения требует абсолютного совпадения версий клиентской и серверной частей. Рассмотрим три надежных способа обойти это ограничение.

Способ 1. Использование планов обмена и файлов XML через КД 2.0

Это классический и проверенный временем подход. Мы регистрируем изменения объектов в специализированном Плане обмена. Выгрузка происходит во внешний файл XML по правилам, настроенным в программе «Конвертация данных 2.0».

Разберем логику работы этого механизма по шагам:

  1. В базе-источнике создается план обмена (например, ПланОбмена.СинхронизацияСРозницей), в состав которого входит справочник Номенклатура.
  2. При записи элемента справочника платформа автоматически регистрирует изменение для узла-приемника.
  3. Регламентное задание по расписанию запускает обработку выгрузки. Она считывает правила обмена, выгружает зарегистрированные изменения в XML-файл и сбрасывает регистрацию изменений.
  4. База-приемник аналогично по расписанию забирает этот файл из папки обмена (или по FTP) и загружает данные к себе. Разница в версиях платформ при этом не имеет никакого значения. При необходимости автоматизировать регулярный обмен данными между базами 1С можно использовать внешнее решение, которое передаёт сообщения через веб-сервис или COM-соединение по расписанию.

Способ 2. Легковесный файловый обмен через формат JSON

Если структура баз не слишком сложная и использовать тяжелую «Конвертацию данных» не хочется, отличной альтернативой XML станет формат JSON. Он поддерживается платформой 1С нативно начиная с версии 8.3.6.

Посмотрим на пример кода, который сериализует данные карточки товара в JSON-строку для последующей записи в файл:


ДанныеДляОтправки = Новый Структура;
ДанныеДляОтправки.Вставить("UUID", Строка(Источник.Ссылка.УникальныйИдентификатор()));
ДанныеДляОтправки.Вставить("Наименование", Источник.Наименование);
ДанныеДляОтправки.Вставить("Артикул", Источник.Артикул);

ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.ОткрытьФайл("C:\Exchange\new_item.json",,, ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, ДанныеДляОтправки);
ЗаписьJSON.Закрыть();

В базе-приемнике прочитать этот файл так же просто с помощью объекта ЧтениеJSON. Это решает проблему несовпадения реквизитов: вы сами управляете именами ключей в структуре, приводя их к формату приемника.

Способ 3. HTTP-сервисы — интеграция без файлов

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

Схема реализации выглядит следующим образом:

  1. В базе «1С:Розница» создается и публикуется на веб-сервере (IIS или Apache) простейший HTTP-сервис. У сервиса определяется шаблон пути и метод POST для приема данных номенклатуры.
  2. В базе-источнике ТКПТ при записи номенклатуры (в фоновом задании) формируется JSON-пакет с данными товара и отправляется напрямую на веб-адрес розничной базы.

Рассмотрим пример отправки данных через HTTP-соединение из базы-источника:


Соединение = Новый HTTPСоединение("server-retail",,,,,, Новый ЗащищенноеСоединениеOpenSSL());
ЗапросHTTP = Новый HTTPЗапрос("/retail_db/hs/exchange/nomenclature");
ЗапросHTTP.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеМаркераПоследовательностиБайтов.НеИспользовать);

Попытка
    Ответ = Соединение.ОтправитьДляОбъекта(ЗапросHTTP);
    Если Ответ.КодСостояния = 200 Тогда
        // Данные успешно приняты и обработаны приемником
    КонецЕсли;
Исключение
    // Запись ошибки в Журнал регистрации
КонецПопытки;

Чек-лист для создания надежного обмена

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

← На главную