Как зарегистрировать объект к выгрузке непосредственно в процессе его загрузки из другой базы

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

При настройке двустороннего обмена данными между базами «1С:Предприятие» часто возникает задача: при получении объекта из «Базы 1» в «Базу 2» необходимо произвести в нем изменения (например, сменить статус) и тут же отправить его обратно в «Базу 1». Однако стандартные методы регистрации изменений, такие как ПланыОбмена.ЗарегистрироватьИзменения(), часто не срабатывают в обработчиках загрузки. Проанализируем ситуацию и разберем, как правильно реализовать этот механизм, избежав типичных ошибок.

Выясним причину: почему обычная регистрация не работает?

Разберем, что происходит «под капотом» платформы в момент выполнения обмена. Когда «База 2» читает сообщение, пришедшее из «Базы 1», платформа автоматически устанавливает свойству ОбменДанными.Отправитель значение, соответствующее узлу обмена «Базы 1». Это встроенный механизм защиты от бесконечного цикла или так называемого «эха».

Логика платформы проста: если объект пришел из Узла А, значит, Узел А уже знает об этом объекте. Следовательно, регистрировать этот же объект к выгрузке обратно на Узел А в рамках той же транзакции запрещено. Платформа просто игнорирует вызов метода ЗарегистрироватьИзменения для этого узла, чтобы данные не начали бесконечно циркулировать между базами при каждом обмене.

Кроме того, во время загрузки свойство ОбменДанными.Загрузка установлено в значение Истина. Это отключает стандартные подписки на события и механизмы автоматической регистрации, встроенные в логику конфигурации.

Решение №1: Использование коллекции «Получатели» и очистка Отправителя

Самый эффективный способ зарегистрировать объект прямо в обработчике «После загрузки» (например, в правилах конвертации данных 2.0) — это прямое управление коллекцией получателей объекта. Рассмотрим по шагам, как это сделать правильно.

Сначала нам необходимо «обмануть» систему, очистив информацию о том, откуда пришел объект. Только после этого мы сможем добавить нужный узел в список получателей. Проанализируем пример кода:


// 1. Находим ссылку на узел, куда хотим вернуть данные
УзелОбменаСсылка = ПланыОбмена.ОбменВзаиморасчетами.НайтиПоКоду("PK");

// 2. Очищаем отправителя, чтобы снять блокировку платформы на регистрацию "эха"
Объект.ОбменДанными.Отправитель = Неопределено;

// 3. Добавляем наш узел в коллекцию Получатели
// Важно использовать метод Добавить(), так как коллекция доступна только для чтения на запись целиком
Объект.ОбменДанными.Получатели.Добавить(УзелОбменаСсылка);

Обратите внимание: если вы пытаетесь присвоить коллекции новый массив (ОбменДанными.Получатели = МассивПолучателей), система выдаст ошибку «Поле объекта недоступно для записи». Коллекция Получатели инициализируется платформой, и в нее можно только добавлять элементы методом Добавить().

Решение №2: Регистрация через дополнительные свойства и подписки

Иногда прямое изменение коллекции Получатели в правилах обмена может привести к трудноуловимым ошибкам или конфликтам с логикой конфигурации. Более надежным и архитектурно верным подходом считается использование механизма ДополнительныеСвойства.

Рассмотрим этот алгоритм подробнее:

  1. В правилах обмена (обработчик «После загрузки») мы не регистрируем объект сразу, а лишь ставим «метку» о необходимости регистрации.
  2. В базе-приемнике создаем подписку на событие ПриЗаписи для нужного типа документа.
  3. В обработчике подписки проверяем наличие метки и выполняем регистрацию.

Пример кода в обработчике «После загрузки» правил конвертации:


// Устанавливаем признак для последующей регистрации
Объект.ДополнительныеСвойства.Вставить("ТребуетсяОбратнаяРегистрация", Истина);

Пример кода в модуле подписки на событие ПриЗаписи:


Процедура ПриЗаписиДокументаДляОбмена(Источник, Отказ) Экспорт
    
    // Проверяем, есть ли наше специальное свойство
    Если Источник.ДополнительныеСвойства.Свойство("ТребуетсяОбратнаяРегистрация") Тогда
        УзелСсылка = ПланыОбмена.ОбменВзаиморасчетами.НайтиПоКоду("PK");
        
        // Используем метод регистрации. В подписке он отработает корректно, 
        // если мы правильно управляем свойством Отправитель или используем коллекцию Получатели
        Источник.ОбменДанными.Получатели.Добавить(УзелСсылка);
    КонецЕсли;
    
КонецПроцедуры

Решение №3: Использование метода ПланыОбмена.ЗарегистрироватьИзменения()

Если вы предпочитаете использовать метод ПланыОбмена.ЗарегистрироватьИзменения(), помните, что его вызов внутри транзакции загрузки также требует предварительной очистки свойства ОбменДанными.Отправитель. Посмотрим на пример реализации в коде 1С:


// В обработчике "После загрузки" объекта
УзелСсылка = ПланыОбмена.ОбменВзаиморасчетами.НайтиПоКоду("PK");

// Обязательный шаг для снятия защиты от зацикливания
Объект.ОбменДанными.Отправитель = Неопределено;

// Теперь регистрация сработает
ПланыОбмена.ЗарегистрироватьИзменения(УзелСсылка, Объект.Ссылка);

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

Анализируем риски: бесконечные циклы

Реализуя обратную регистрацию при загрузке, мы берем на себя ответственность за возможные «кольцевые» обмены. Проанализируем ситуацию: «База 2» отправила документ обратно в «Базу 1». «База 1» приняла его, увидела изменения и (если там настроены аналогичные правила) может снова зарегистрировать его для «Базы 2».

Чтобы избежать этой ловушки, придерживайтесь следующих правил:

Подводя итог, можно сказать, что ключевым моментом в решении данной задачи является понимание роли свойства ОбменДанными.Отправитель. Его своевременная очистка в сочетании с наполнением коллекции Получатели позволяет гибко управлять процессом регистрации даже в таких сложных сценариях, как регистрация во время импорта данных.

← На главную