Как пропустить зарегистрированный объект при выгрузке через план обмена без потери данных?

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

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

Механизм нумерации сообщений и скрытая опасность

Для понимания сути проблемы проанализируем, как платформа 1С работает с регистрацией изменений. Выгрузка изменений стандартно инициируется следующим вызовом:


Выборка = ПланыОбмена.ВыбратьИзменения(УзелПланаОбмена, ЗаписьСообщения.НомерСообщения);
Пока Выборка.Следующий() Цикл
    Об = Выборка.Получить();
    // Обработка и запись объекта в XML
КонецЦикла;

В момент выполнения метода ПланыОбмена.ВыбратьИзменения платформа на уровне СУБД выполняет массовое обновление таблиц регистрации изменений: всем записям с пустым номером (NULL) присваивается текущий номер исходящего сообщения ЗаписьСообщения.НомерСообщения.

Когда база-приемник успешно загрузит пакет, она отправит ответную квитанцию с подтверждением. При получении квитанции вызывается метод ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерПринятого), который безоговорочно удаляет из таблиц изменений все записи, номер сообщения которых меньше либо равен подтвержденному номеру.

Отсюда возникает главная опасность: если мы просто пропустим запись объекта в XML-поток, объект все равно останется привязан к текущему номеру пакета. Как только узел-получатель подтвердит прием, платформа удалит запись об изменении этого объекта из таблицы регистрации. В результате приемник объект так и не получит, а источник посчитает его успешно переданным, что приведет к рассинхронизации данных.

Ограничение свойства ОтправкаЭлементаДанных.Игнорировать

В типовых событиях модуля плана обмена (ПриОтправкеДанныхПодчиненному или ПриОтправкеДанныхГлавному) доступно управление отправкой через параметр ОтправкаЭлемента:


Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента)
    Если Не ВыгружатьОбъект(ЭлементДанных) Тогда
        ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;
    КонецЕсли;
КонецПроцедуры

Рассмотрим важный нюанс: установка значения ОтправкаЭлементаДанных.Игнорировать отменяет только сериализацию объекта в XML-сообщение. При этом платформа не сбрасывает номер сообщения в таблице регистрации изменений. Если ограничиться только этой строкой, данные будут безвозвратно удалены из очереди регистрации при подтверждении пакета. Следовательно, применение этого метода требует обязательного сброса номера сообщения.

Способ 1. Отложенная перерегистрация невыгруженных объектов

Вызов метода ПланыОбмена.ЗарегистрироватьИзменения(Узел, Объект) перезаписывает строку в таблице регистрации и сбрасывает поле НомерСообщения обратно в NULL. Благодаря этому объект не попадет под удаление при подтверждении пакета и будет выбран при следующей выгрузке.

Однако вызывать повторную регистрацию построчно непосредственно внутри цикла обхода Выборка.Следующий() крайне не рекомендуется. Изменение таблицы регистрации параллельно с ее чтением выборкой провоцирует избыточные блокировки и снижает скорость выгрузки.

Разберем по шагам наиболее эффективный алгоритм:

  1. В процессе обхода выборки проверяем условия выгрузки объекта.
  2. Если объект не должен попасть в текущий файл, собираем ссылку на него в массив и не записываем в XML (или устанавливаем ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать).
  3. После завершения цикла выборки вызываем пакетную регистрацию для всего массива пропущенных объектов.

Посмотрим на пример кода:


МассивПропущенных = Новый Массив;

Выборка = ПланыОбмена.ВыбратьИзменения(УзелПланаОбмена, ЗаписьСообщения.НомерСообщения);
Пока Выборка.Следующий() Цикл
    ОбъектДанных = Выборка.Получить();
    
    Если НеобходимоПропустить(ОбъектДанных) Тогда
        МассивПропущенных.Добавить(ОбъектДанных.Ссылка);
        Продолжить;
    КонецЕсли;
    
    // Сериализация объекта в XML
    ЗаписатьXML(ЗаписьXML, ОбъектДанных);
КонецЦикла;

// Пакетный сброс номера сообщения в таблице регистрации
Если МассивПропущенных.Количество() > 0 Тогда
    ПланыОбмена.ЗарегистрироватьИзменения(УзелПланаОбмена, МассивПропущенных);
КонецЕсли;

Способ 2. Использование третьего параметра ФильтрВыборки

Метод платформы ПланыОбмена.ВыбратьИзменения принимает третий необязательный параметр: ФильтрВыборки. В него можно передавать ссылку, массив ссылок, набор записей или объект метаданных.

В этом случае номер сообщения присваивается исключительно тем объектам, которые попали в фильтр. Все остальные изменения остаются со значением номера NULL, не блокируются и не удаляются при подтверждении.

Разберем шаги реализации:

  1. С помощью запроса к виртуальным таблицам изменений (например, Документ.РеализацияТоваровУслуг.Изменения) отбираем только те объекты, у которых НомерСообщения ЕСТЬ NULL и которые удовлетворяют всем прикладным условиям выгрузки.
  2. Формируем массив ссылок, готовых к отправке.
  3. Передаем сформированный массив в метод ВыбратьИзменения.

Посмотрим на пример выборки с фильтрацией:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ТаблицаИзменений.Ссылка КАК Ссылка
    |ИЗ
    |   Документ.РеализацияТоваровУслуг.Изменения КАК ТаблицаИзменений
    |ГДЕ
    |   ТаблицаИзменений.Узел = &Узел
    |   И ТаблицаИзменений.НомерСообщения ЕСТЬ NULL
    |   И ТаблицаИзменений.Ссылка.Проведен = ИСТИНА";

Запрос.УстановитьПараметр("Узел", УзелПланаОбмена);
МассивДляВыгрузки = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

Если МассивДляВыгрузки.Количество() > 0 Тогда
    Выборка = ПланыОбмена.ВыбратьИзменения(УзелПланаОбмена, ЗаписьСообщения.НомерСообщения, МассивДляВыгрузки);
    Пока Выборка.Следующий() Цикл
        ОбъектДанных = Выборка.Получить();
        ЗаписатьXML(ЗаписьXML, ОбъектДанных);
    КонецЦикла;
КонецЕсли;

Способ 3. Перенос фильтрации на этап регистрации изменений

Наиболее архитектурно верным решением в среде 1С является исключение попадания «лишних» объектов в очередь изменений еще на этапе их записи, а не во время формирования пакета.

Постоянная регистрация с последующим отсечением перегружает таблицы регистрации и увеличивает нагрузку на диск. Чтобы этого избежать, отключим свойство Авторегистрация для нужных объектов в составе плана обмена и настроим точечную регистрацию через подписку на событие ПередЗаписью или ПриЗаписи, проверяя все необходимые условия до вызова ПланыОбмена.ЗарегистрироватьИзменения.

← На главную