Как устранить ошибку «Пользователь не найден в справочнике Пользователи» в РИБ конфигурации 1С:Управление торговлей 10.3

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

При работе с распределенными информационными базами (РИБ) в конфигурациях на обычных формах, таких как 1С:Управление торговлей 10.3 или 1С:Комплексная автоматизация 1.1, администраторы и разработчики часто сталкиваются с неприятной циклической ошибкой. При входе пользователя в подчиненном или главном узле система выдает сообщение: «Пользователь "..." не найден в справочнике "Пользователи"». Если администратор вручную сопоставляет пользователя в одном узле, после очередного сеанса обмена данными связка ломается во втором узле, и ситуация повторяется бесконечно.

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

Причина возникновения проблемы: системные пользователи против прикладных

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

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

Связь между системным пользователем и элементом справочника устанавливается по уникальному реквизиту ИдентификаторПользователяИБ (тип данных УникальныйИдентификатор / UUID). При создании пользователя ИБ платформа генерирует для него уникальный GUID. Важно помнить, что свойство УникальныйИдентификатор у объекта ПользовательИнформационнойБазы доступно только для чтения. Задать один и тот же GUID пользователям ИБ в разных узлах базы данных технически невозможно.

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

  1. В узле «А» в справочнике записывается ИдентификаторПользователяИБ узла «А». Вход работает штатно.
  2. Происходит обмен РИБ: элемент справочника Пользователи передается в узел «Б» и перезаписывает реквизит ИдентификаторПользователяИБ значением из узла «А».
  3. Пользователь пытается войти в узле «Б». Процедура инициализации сеанса считывает текущего системного пользователя узла «Б», пытается найти в справочнике запись с соответствующим GUID, не находит ее и выдает ошибку.
  4. Администратор в узле «Б» перевыбирает пользователя ИБ и сохраняет справочник. Теперь вход работает в узле «Б», но после следующего обмена данными связка ломается в узле «А».

Почему вход срабатывает только под полными правами

Многие разработчики пробуют решить проблему автоматическим перепривязыванием пользователя при старте, однако пользователи без полных административных прав все равно не могут зайти. Выясним причину такого поведения:

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

Способ 1. Программное сопоставление по имени в привилегированном режиме (Рекомендуемый вариант)

Наиболее надежным решением является доработка процедуры сопоставления пользователя при начале работы системы. Алгоритм должен искать прикладного пользователя по имени (или коду), если сопоставление по GUID не дало результата, и автоматически обновлять ИдентификаторПользователяИБ прямо в текущем узле.

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


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

Обратите внимание на строку ОбъектПользователь.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;. Она гарантирует, что обновленный локальный GUID не будет зарегистрирован к отправке в другие узлы РИБ и не сломает их настройки при следующем обмене.

Способ 2. Исключение реквизита из состава обмена РИБ

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

Разберем, как это сделать через обработчики планов обмена в модуле объекта плана обмена (например, ПланОбмена.ПоУзлам):

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

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

Благодаря этому подходу общие реквизиты справочника (контакты, роли, подразделения) продолжат мигрировать между узлами РИБ, а системная связка с пользователем ИБ останется уникальной для каждой базы данных.

Способ 3. Полное исключение справочника «Пользователи» из плана обмена

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

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

Проанализировав все три варианта, наиболее стабильным и гибким решением для промышленных систем является Способ 1 — программный поиск по наименованию и установка локального идентификатора в привилегированном режиме.

← На главную