В процессе обмена данными в распределенной информационной базе (РИБ) разработчики нередко сталкиваются с ситуацией, когда при формировании исходящего сообщения (на этапе выгрузки) поток управления неожиданно попадает в обработчик события ПриЗаписи или ПередЗаписью справочника или документа. Если в этом событии реализована дополнительная логика — например, интеграция с внешними FTP-серверами, отправка HTTP-запросов или проверка уникальности, — сеанс обмена может завершаться критической ошибкой, зависать или бесконечно зацикливать выгрузку одних и тех же объектов.
Разберем детально, почему возникает такое поведение, как локализовать место вызова и как правильно переписать алгоритмы, чтобы исключить сбои в механизмах обмена.
В типовой архитектуре 1С:Предприятия при выгрузке данных из узла РИБ платформа выполняет только сериализацию зарегистрированных изменений (чтение данных из базы). Вызов событий записи объекта (ПередЗаписью, ПриЗаписи) штатно происходит исключительно при загрузке входящего сообщения в базу-приемник. При этом на этапе загрузки платформа автоматически выставляет свойство объекта ОбменДанными.Загрузка = Истина.
Если же событие записи срабатывает при выгрузке, это свидетельствует о наличии нестандартного программного вызова метода Записать() в коде конфигурации. На этапе выгрузки свойство ОбменДанными.Загрузка всегда равно Ложь, из-за чего любые проверки вида НЕ ЭтотОбъект.ОбменДанными.Загрузка успешно пропускают выполнение тяжелых и опасных алгоритмов.
Рассмотрим типовой пример некорректно размещенного интеграционного кода в модуле объекта справочника:
Процедура ПриЗаписи(Отказ)
Если ЭтотОбъект.ТипКарты = Перечисления.ТипыИнформационныхКарт.Бонусная И НЕ ЭтотОбъект.ОбменДанными.Загрузка Тогда
FTPСоединение = пск_ФТП.ПодключениеКФТП(Константы.пск_ЛогинФТП.Получить(), Константы.пск_ПарольФТП.Получить(), Константы.пск_УРЛ.Получить(), Константы.пск_ПапкаФТП.Получить());
Если FTPСоединение <> Неопределено Тогда
Если НЕ пск_ФТП.ОбработкаЗапросаБонусныхКарт(ЭтотОбъект, "ПроверкаСуществованияКарты", FTPСоединение) Тогда
НачатьТранзакцию();
Если НЕ пск_ФТП.ОбработкаЗапросаБонусныхКарт(ЭтотОбъект, "СозданиеФайлаКарты", FTPСоединение,, ЭтотОбъект.ВладелецКарты) Тогда
Отказ = Истина;
КонецЕсли;
Если Отказ Тогда
ОтменитьТранзакцию();
КонецЕсли;
КонецЕсли;
Иначе
Сообщить("Отсутствует связь с ФТП сервером! Карта не создана!");
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Такой подход несет в себе критические риски:
Отказ = Истина, что полностью прерывает процесс выгрузки данных узла.Записать() во время формирования пакета обмена автоматически регистрирует этот же объект на узлах плана обмена заново. В результате объект будет бесконечно включаться в каждое последующее сообщение обмена.Чтобы выяснить, какая процедура инициирует модификацию объекта при выгрузке, выполним следующие шаги:
ПриЗаписи нужного справочника. Запустим процесс выгрузки обмена данными. При остановке откроем окно «Стек вызовов» и пройдем вверх по цепочке вызовов до исходной точки.ПриОтправкеДанныхГлавному и ПриОтправкеДанныхПодчиненному. Нередко в них ошибочно встраивают служебную фиксацию статуса или даты выгрузки с вызовом Объект.Записать().ПодпискаНаСобытие. Источником может быть подписка на выгрузку данных, регистрацию изменений или событие записи связанных объектов (регистров сведений, подчиненных справочников, документов), которая попутно модифицирует реквизиты исходного справочника.Разберем основные подходы к исправлению логики в зависимости от архитектурных задач конфигурации.
События модуля объекта (ПередЗаписью, ПриЗаписи) должны отвечать исключительно за целостность данных в информационной базе 1С. Внешние сетевые вызовы (FTP, HTTP, веб-сервисы) необходимо выносить в один из следующих уровней:
ПослеЗаписиНаСервере).Если логику проверки необходимо временно оставить в модуле объекта, добавим обязательное сравнение текущих реквизитов со значениями, уже записанными в базе данных, чтобы исключить сетевые запросы при повторной технической перезаписи:
Процедура ПриЗаписи(Отказ)
Если ЭтотОбъект.ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Выполняем действия только для новых элементов или при изменении владельца/типа
ЭтоНовыйОбъект = ЭтоНовый();
Если НЕ ЭтоНовыйОбъект Тогда
РеквизитыБазы = Ссылка.ПолучитьОбъект();
Если РеквизитыБазы.ТипКарты = ЭтотОбъект.ТипКарты
И РеквизитыБазы.ВладелецКарты = ЭтотОбъект.ВладелецКарты Тогда
// Реквизиты не изменились, повторное обращение к FTP не требуется
Возврат;
КонецЕсли;
КонецЕсли;
Если ЭтотОбъект.ТипКарты = Перечисления.ТипыИнформационныхКарт.Бонусная Тогда
// Код работы с внешним сервисом
КонецЕсли;
КонецПроцедуры
Если в процессе выгрузки или служебной обработки требуется программно перезаписать объект без повторной отправки в узел обмена, перед вызовом метода Записать() необходимо отключить авторегистрацию изменений:
ОбъектСправочника = ВыбраннаяСсылка.ПолучитьОбъект();
ОбъектСправочника.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
ОбъектСправочника.ОбменДанными.Получатели.Очистить();
ОбъектСправочника.Записать();
Очистка коллекции получателей предотвращает повторную постановку объекта в очередь на выгрузку и разрывает бесконечный цикл обмена данными.