Как исключить конкретный реквизит из обмена в РИБ на платформе 1С?

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

При работе с распределенными информационными базами (РИБ) на платформе 1С:Предприятие 8 часто возникает необходимость ограничить передачу определенных конфиденциальных или специфических для конкретного узла данных. Классический пример — справочник Пользователи и его реквизит ИдентификаторПользователяИБ. Этот реквизит связывает элемент справочника с пользователем информационной базы в конфигураторе. Если передавать его "как есть" между узлами, это может привести к нарушению авторизации в подчиненных базах, так как GUID пользователей ИБ в разных базах различаются.

В этой статье мы подробно разберем, почему в РИБ нельзя просто отключить регистрацию одного реквизита, и рассмотрим практические программные способы решения этой задачи.

Особенности регистрации изменений в РИБ

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

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

Рассмотрим основные способы решения этой задачи.

Способ 1. Использование обработчика ПриОтправкеДанныхПодчиненному

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

Разберем по шагам, как реализовать этот подход:

  1. Откроем конфигуратор и найдем нужный план обмена (например, Полный или другой используемый для РИБ).
  2. Перейдем в модуль плана обмена.
  3. Найдем или создадим предопределенную процедуру ПриОтправкеДанныхПодчиненному (для отправки из центра в филиал) или ПриОтправкеДанныхГлавному (для отправки из филиала в центр).

Проанализируем пример кода, который очищает реквизит ИдентификаторПользователяИБ перед отправкой:


Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента)
    
    // Проверяем тип отправляемого объекта
    Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи") Тогда
        
        // Модифицируем реквизит непосредственно в выгружаемом объекте.
        // Это изменение затронет только xml-файл обмена, в текущей базе значение останется прежним.
        ЭлементДанных.ИдентификаторПользователяИБ = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
        
    КонецЕсли;

КонецПроцедуры

Важный нюанс этого решения: РИБ-обмен перезаписывает объекты "намертво". Если мы отправим пустой или дефолтный УникальныйИдентификатор, то при загрузке в подчиненном узле этот реквизит также затрется и примет пустое значение. Если в подчиненном узле пользователи авторизуются в системе и их сопоставление завязано на данный реквизит, они потеряют доступ к базе данных!

Поэтому данный способ подходит только тогда, когда реквизит действительно должен быть пустым во всех принимающих базах.

Способ 2. Восстановление реквизита при приеме (Рекомендуемый)

Чтобы избежать затирания уникальных данных в принимающей базе, гораздо безопаснее использовать стратегию сохранения и восстановления реквизитов в момент загрузки данных. Для этого используется обработчик ПриПриемеДанныхОтПодчиненного (или ПриПриемеДанныхОтГлавного соответственно).

Давайте выясним причину эффективности этого метода. При получении файла обмена система считывает объект. Перед тем как записать его в базу данных, мы перехватываем этот объект, считываем старое значение реквизита, которое уже хранилось в нашей базе, и подставляем его обратно в записываемый объект. Таким образом, вся остальная информация (ФИО, контактные данные, права) обновится, а критически важный реквизит останется нетронутым.

Посмотрим на пример реализации в модуле плана обмена:


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

КонецПроцедуры

Этот способ является наиболее стабильным и безопасным при работе со справочником Пользователи в распределенных базах, так как предотвращает рассинхронизацию учетных записей 1С с пользователями операционной системы или Active Directory на местах.

Способ 3. Программный контроль регистрации через подписки

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

В коде подписки мы можем сравнить текущие данные объекта с тем, что записано в базе. Если изменился исключительно реквизит ИдентификаторПользователяИБ, а все остальные поля остались прежними, мы можем программно удалить регистрацию изменений для узлов обмена.

Рассмотрим пример реализации логики проверки:


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

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

Резюме

Для надежного исключения перезаписи реквизита ИдентификаторПользователяИБ в РИБ наиболее предпочтительным является Способ №2 (восстановление значения при приеме данных). Он гарантирует сохранность настроек авторизации во всех периферийных узлах системы, обеспечивая при этом корректную синхронизацию всех остальных реквизитов справочника пользователей.

← На главную