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