Как правильно зарегистрировать изменения регистров в РИБ при изменении реквизитов документа?

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

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

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

Суть проблемы: рассинхронизация регистрации документа и его движений

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

Но когда дело доходит до подписки ПередЗаписью для набора записей регистра, старые данные регистратора уже недоступны или сложно извлекаемы. Код регистра видит только новое «Подразделение2» и регистрирует набор только для узла ПУ2. В результате в ПУ1 документ остается со старыми движениями или возникает рассинхронизация.

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

Решение 1: Использование коллекции «ДополнительныеСвойства»

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

В подписке на событие ПередЗаписью документа мы определяем нужные узлы обмена и помещаем их в свойства:


// В обработчике ПередЗаписью документа
МассивУзлов = Новый Массив;
// ... логика определения узлов по Ссылке и Объекту ...

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

// Регистрация самого документа
Для Каждого Элемент Из МассивУзлов Цикл
    ЭтотОбъект.ОбменДанными.Получатели.Добавить(Элемент);
КонецЦикла;

Теперь в подписке ПередЗаписью набора записей любого регистра мы просто обращаемся к регистратору и забираем уже готовый массив узлов:


// В обработчике ПередЗаписью набора записей
РегистраторОбъект = ЭтотОбъект.Отбор.Регистратор.Значение;

Если РегистраторОбъект.ДополнительныеСвойства.Свойство("УзлыРегистрации") Тогда
    МассивУзлов = РегистраторОбъект.ДополнительныеСвойства.УзлыРегистрации;
    Для Каждого Узел Из МассивУзлов Цикл
        ЭтотОбъект.ОбменДанными.Получатели.Добавить(Узел);
    КонецЦикла;
КонецЕсли;

Этот метод работает быстро, не создает лишней нагрузки на СУБД и решает проблему недоступности данных старой ссылки в момент записи регистра.

Решение 2: Проблема миграции и отправка объекта удаления

Внимательно посмотрим на бизнес-логику при смене фильтрующего реквизита. Если мы поменяли «Подразделение1» на «Подразделение2», документ перестал относиться к ПУ1. Штатное добавление в коллекцию получателей просто прекратит отправку новых версий документа в ПУ1. Это приведет к тому, что в старом узле зависнет «фантомная» копия документа, которая никогда больше не обновится.

Чтобы избежать этого, при потере видимости объекта для узла (миграции из узла) мы должны явно зарегистрировать удаление объекта. Разберем код, как это можно реализовать в подписке документа:


// Проверка потери видимости для старого узла ПУ1
Если СтароеПодразделение = ПодразделениеПУ1 И НовоеПодразделение <> ПодразделениеПУ1 Тогда
    // Объект ушел из области видимости ПУ1, нужно его там удалить
    Удаление = Новый УдалениеОбъекта(ЭтотОбъект.Ссылка);
    ПланыОбмена.ЗарегистрироватьИзменения(УзелПУ1, Удаление);
    
    // Аналогично регистрируем удаление для всех подчиненных регистров
    // (потребуется передать команду на удаление движений)
КонецЕсли;

Обратите внимание: когда вы регистрируете УдалениеОбъекта, в файл обмена уходит специальный маркер, который физически удаляет объект в базе-приемнике. Это единственно правильный подход для поддержания консистентности распределенной базы.

Решение 3: Использование Библиотеки стандартных подсистем (БСП)

Написание собственной логики маршрутизации и контроля миграции объектов — трудоемкий процесс, требующий учета множества пограничных сценариев (например, порядок загрузки метаданных, когда регистр приходит раньше документа).

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

  1. Декларативная настройка: Правила фильтрации (по организации, подразделению и т.д.) настраиваются визуально (например, в конфигурации «Конвертация данных»), без необходимости писать код в подписках.
  2. Автоматический контроль миграции: БСП под капотом сама запоминает состояние объекта до записи, сравнивает его с новым и, если объект выпадает из фильтра узла, автоматически формирует запись типа УдалениеОбъекта для старого узла обмена.
  3. Связанная регистрация: БСП умеет регистрировать движения регистров строго по правилам, примененным к их документу-регистратору, исключая рассинхронизацию.

Внедрение подсистемы обмена из БСП избавит вас от необходимости поддерживать сложный и нестабильный код в обработчиках событий платформы и обеспечит надежную доставку и удаление данных во всех узлах РИБ.

← На главную