При настройке обмена данными между различными конфигурациями 1С (например, между «1С-Рарус: Торговый комплекс» и «1С:Розница») разработчики часто сталкиваются с необходимостью синхронизировать справочник Номенклатура в реальном времени. На первый взгляд задача кажется простой: перехватить момент создания карточки товара, сформировать файл обмена и отправить его в базу-приемник. Однако при реализации «в лоб» возникает множество подводных камней — от зависания интерфейса пользователей до рассинхронизации данных из-за отката транзакций.
В этой статье мы подробно разберем, почему популярный подход с выгрузкой в событии ПередЗаписью является критической ошибкой, выясним, как правильно читать реквизиты объектов, и рассмотрим наиболее надежные и современные способы преодоления несовместимости версий платформ (например, при взаимодействии серверов на платформах 8.3.13 и 8.3.16).
Попытка создать файл выгрузки непосредственно в предопределенном обработчике ПередЗаписью модуля объекта или в соответствующей подписке на событие несет в себе три серьезные угрозы для стабильности информационной системы.
ПередЗаписью выполняется внутри транзакции записи. База данных еще не зафиксировала изменения окончательно. Если после выгрузки файла в этой же транзакции произойдет сбой (например, не сработает проверка заполнения реквизитов на уровне платформы, или заблокируется проведение связанных регистров), транзакция откатится. При этом в базе-источнике карточка товара создана не будет, а файл обмена уже запишется на диск. База-приемник обработает этот файл и создаст у себя несуществующий в источнике товар.ПередЗаписью у нее еще нет ссылки в базе данных (метод Ссылка.Пустая() вернет истину). Без реальной ссылки невозможно прочитать данные из подчиненных регистров сведений, например, штрихкоды или цены номенклатуры, которые записываются строго после самого объекта.Правильное решение: выгрузка должна происходить асинхронно. В транзакции записи мы должны лишь зарегистрировать факт изменения объекта (например, с помощью планов обмена), а саму отправку данных выполнять в фоновом режиме с помощью регламентного задания.
Разберем ситуацию, когда нам необходимо сформировать пакет данных для передачи. Как эффективнее получить реквизиты карточки товара и связанные с ней сведения? Если объекты в разных базах были заведены вручную и имеют разные ссылки, пригодится обработка для переноса данных между похожими конфигурациями 1С, позволяющая использовать произвольные поля поиска.
Если мы все же вынуждены собирать данные непосредственно в момент записи (в событии ПриЗаписи или ПослеЗаписи), то для обращения к реквизитам самого объекта нам не нужны ни запросы, ни выборки. Все данные уже находятся в оперативной памяти сервера. Мы можем обращаться к ним напрямую через переменную Источник (в подписке на событие) или ключевое слово ЭтотОбъект:
НаименованиеТовара = Источник.Наименование;
АртикулТовара = Источник.Артикул;
Однако номенклатура редко переносится «пустой». Как правило, вместе с ней требуется передать базовую единицу измерения, штрихкоды и ставки НДС. Штрихкоды обычно хранятся в независимом регистре сведений ШтрихкодыНоменклатуры. Пытаться получить их перебором выборки — крайне неэффективный путь.
Проанализируем пример правильного получения связанных данных с помощью запроса в событии ПриЗаписи:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Штрихкоды.Штрихкод КАК Штрихкод,
| Штрихкоды.ТипШтрихкода КАК ТипШтрихкода
|ИЗ
| РегистрСведений.ШтрихкодыНоменклатуры КАК Штрихкоды
|ГДЕ
| Штрихкоды.Номенклатура = &Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", Источник.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
Использование запроса позволяет минимизировать количество обращений к СУБД и мгновенно получить всю связанную информацию по конкретной ссылке.
Когда базы данных работают на разных релизах платформы (например, одна на 8.3.13, а другая на 8.3.16), прямое подключение через COM-соединение (V83.COMConnector) становится невозможным. Компонента COM-соединения требует абсолютного совпадения версий клиентской и серверной частей. Рассмотрим три надежных способа обойти это ограничение.
Это классический и проверенный временем подход. Мы регистрируем изменения объектов в специализированном Плане обмена. Выгрузка происходит во внешний файл XML по правилам, настроенным в программе «Конвертация данных 2.0».
Разберем логику работы этого механизма по шагам:
ПланОбмена.СинхронизацияСРозницей), в состав которого входит справочник Номенклатура.Если структура баз не слишком сложная и использовать тяжелую «Конвертацию данных» не хочется, отличной альтернативой XML станет формат JSON. Он поддерживается платформой 1С нативно начиная с версии 8.3.6.
Посмотрим на пример кода, который сериализует данные карточки товара в JSON-строку для последующей записи в файл:
ДанныеДляОтправки = Новый Структура;
ДанныеДляОтправки.Вставить("UUID", Строка(Источник.Ссылка.УникальныйИдентификатор()));
ДанныеДляОтправки.Вставить("Наименование", Источник.Наименование);
ДанныеДляОтправки.Вставить("Артикул", Источник.Артикул);
ЗаписьJSON = Новый ЗаписьJSON;
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет);
ЗаписьJSON.ОткрытьФайл("C:\Exchange\new_item.json",,, ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, ДанныеДляОтправки);
ЗаписьJSON.Закрыть();
В базе-приемнике прочитать этот файл так же просто с помощью объекта ЧтениеJSON. Это решает проблему несовпадения реквизитов: вы сами управляете именами ключей в структуре, приводя их к формату приемника.
Наиболее современный и быстрый способ интеграции, полностью нивелирующий различия платформ и не требующий создания промежуточных файлов и настройки сетевых папок обмена.
Схема реализации выглядит следующим образом:
POST для приема данных номенклатуры.Рассмотрим пример отправки данных через HTTP-соединение из базы-источника:
Соединение = Новый HTTPСоединение("server-retail",,,,,, Новый ЗащищенноеСоединениеOpenSSL());
ЗапросHTTP = Новый HTTPЗапрос("/retail_db/hs/exchange/nomenclature");
ЗапросHTTP.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеМаркераПоследовательностиБайтов.НеИспользовать);
Попытка
Ответ = Соединение.ОтправитьДляОбъекта(ЗапросHTTP);
Если Ответ.КодСостояния = 200 Тогда
// Данные успешно приняты и обработаны приемником
КонецЕсли;
Исключение
// Запись ошибки в Журнал регистрации
КонецПопытки;
Для того чтобы ваш обмен работал без сбоев и не приводил к порче данных, обязательно соблюдайте следующие правила:
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
UUID). В базе-приемнике поиск выполняйте методом:
СсылкаПриемник = Справочники.Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(UUIDИзФайла));
УдалениеОбъекта), что позволяет корректно транслировать эти изменения в базу-приемник.