Почему при выгрузке данных в РИБ срабатывает событие ПриЗаписи и как это исправить

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

В процессе обмена данными в распределенной информационной базе (РИБ) разработчики нередко сталкиваются с ситуацией, когда при формировании исходящего сообщения (на этапе выгрузки) поток управления неожиданно попадает в обработчик события ПриЗаписи или ПередЗаписью справочника или документа. Если в этом событии реализована дополнительная логика — например, интеграция с внешними FTP-серверами, отправка HTTP-запросов или проверка уникальности, — сеанс обмена может завершаться критической ошибкой, зависать или бесконечно зацикливать выгрузку одних и тех же объектов.

Разберем детально, почему возникает такое поведение, как локализовать место вызова и как правильно переписать алгоритмы, чтобы исключить сбои в механизмах обмена.

Штатное поведение платформы при обмене РИБ

В типовой архитектуре 1С:Предприятия при выгрузке данных из узла РИБ платформа выполняет только сериализацию зарегистрированных изменений (чтение данных из базы). Вызов событий записи объекта (ПередЗаписью, ПриЗаписи) штатно происходит исключительно при загрузке входящего сообщения в базу-приемник. При этом на этапе загрузки платформа автоматически выставляет свойство объекта ОбменДанными.Загрузка = Истина.

Если же событие записи срабатывает при выгрузке, это свидетельствует о наличии нестандартного программного вызова метода Записать() в коде конфигурации. На этапе выгрузки свойство ОбменДанными.Загрузка всегда равно Ложь, из-за чего любые проверки вида НЕ ЭтотОбъект.ОбменДанными.Загрузка успешно пропускают выполнение тяжелых и опасных алгоритмов.

Опасности выполнения внешних операций в событиях объекта

Рассмотрим типовой пример некорректно размещенного интеграционного кода в модуле объекта справочника:


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

Такой подход несет в себе критические риски:

  1. Срыв регламентного обмена: выгрузка пакетов РИБ выполняется в транзакциях. Любая сетевая задержка, таймаут или недоступность FTP-сервера приводит к установке Отказ = Истина, что полностью прерывает процесс выгрузки данных узла.
  2. Зацикливание выгрузки (бесконечный обмен): вызов метода Записать() во время формирования пакета обмена автоматически регистрирует этот же объект на узлах плана обмена заново. В результате объект будет бесконечно включаться в каждое последующее сообщение обмена.

Пошаговый поиск источника неявной записи

Чтобы выяснить, какая процедура инициирует модификацию объекта при выгрузке, выполним следующие шаги:

  1. Анализ стека вызовов в отладчике: установим точку останова в первой строке процедуры ПриЗаписи нужного справочника. Запустим процесс выгрузки обмена данными. При остановке откроем окно «Стек вызовов» и пройдем вверх по цепочке вызовов до исходной точки.
  2. Проверка обработчиков плана обмена: проанализируем модуль плана обмена, по которому строится РИБ. Особое внимание обратим на процедуры ПриОтправкеДанныхГлавному и ПриОтправкеДанныхПодчиненному. Нередко в них ошибочно встраивают служебную фиксацию статуса или даты выгрузки с вызовом Объект.Записать().
  3. Проверка подписок на события: выполним поиск по метаданным конфигурации среди объектов типа ПодпискаНаСобытие. Источником может быть подписка на выгрузку данных, регистрацию изменений или событие записи связанных объектов (регистров сведений, подчиненных справочников, документов), которая попутно модифицирует реквизиты исходного справочника.
  4. Проверка правил конвертации и регистрации (при использовании правил): если для обмена применяются правила (ПВД/ПКО), проверим обработчики «Перед выгрузкой», «При выгрузке» и «После выгрузки» на наличие кода, модифицирующего свойства объекта.

Варианты правильного исправления проблемы

Разберем основные подходы к исправлению логики в зависимости от архитектурных задач конфигурации.

1. Вынос сетевой интеграции из модуля объекта

События модуля объекта (ПередЗаписью, ПриЗаписи) должны отвечать исключительно за целостность данных в информационной базе 1С. Внешние сетевые вызовы (FTP, HTTP, веб-сервисы) необходимо выносить в один из следующих уровней:

  1. События формы: если создание карты на внешнем сервере требуется только при интерактивной работе пользователя, перенесем вызов интеграции в модуль формы (например, в обработчик команды записи или ПослеЗаписиНаСервере).
  2. Регламентные задания: при необходимости фоновой синхронизации создадим отдельный регистр сведений (очередь отправки) и фоновое задание, которое пачками передает созданные карты на FTP-сервер без замедления основных транзакций и блокировок обмена.

2. Проверка модификации ключевых реквизитов

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


Процедура ПриЗаписи(Отказ)
    Если ЭтотОбъект.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Выполняем действия только для новых элементов или при изменении владельца/типа
    ЭтоНовыйОбъект = ЭтоНовый();
    Если НЕ ЭтоНовыйОбъект Тогда
        РеквизитыБазы = Ссылка.ПолучитьОбъект();
        Если РеквизитыБазы.ТипКарты = ЭтотОбъект.ТипКарты 
            И РеквизитыБазы.ВладелецКарты = ЭтотОбъект.ВладелецКарты Тогда
            // Реквизиты не изменились, повторное обращение к FTP не требуется
            Возврат;
        КонецЕсли;
    КонецЕсли;

    Если ЭтотОбъект.ТипКарты = Перечисления.ТипыИнформационныхКарт.Бонусная Тогда
        // Код работы с внешним сервисом
    КонецЕсли;
КонецПроцедуры

3. Корректная программная запись без повторной регистрации в РИБ

Если в процессе выгрузки или служебной обработки требуется программно перезаписать объект без повторной отправки в узел обмена, перед вызовом метода Записать() необходимо отключить авторегистрацию изменений:


ОбъектСправочника = ВыбраннаяСсылка.ПолучитьОбъект();
ОбъектСправочника.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
ОбъектСправочника.ОбменДанными.Получатели.Очистить();
ОбъектСправочника.Записать();

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

← На главную