При разработке и настройке прав доступа в современных конфигурациях «1С:Предприятие 8.3» (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация»), использующих ограничения на уровне записей (RLS), разработчики часто сталкиваются со специфической ошибкой. При попытке пользователя с ограниченными правами сформировать самый простой отчет, созданный на схеме компоновки данных (СКД), система аварийно завершает работу и выдает сообщение: «Попытка получения неинициализированного значения параметра сеанса». При этом под полными правами этот же отчет работает абсолютно корректно, а сам параметр сеанса при интерактивном входе пользователя инициализируется без ошибок.
В данной статье мы подробно проанализируем, почему возникает эта проблема, как связаны между собой СКД, фоновые задания и механизм RLS, а также рассмотрим несколько эффективных практических способов решения этой задачи.
---
Для того чтобы найти наиболее изящное решение, нам необходимо детально разобраться в физике происходящих на платформе процессов. Выясним основные причины сбоя по шагам:
УстановкаПараметровСеанса в модуле сеанса.ПараметрЗапуска (передаваемая через ключ запуска /C при старте RDP-сессии), то в фоновом задании эта строка будет пустой.ПриобретениеТоваровУслуг ГДЕ ПриобретениеТоваровУслуг.Подразделение = &G_ПодразделениеПользователя), СУБД физически не сможет выполнить запрос без передачи значения параметра &G_ПодразделениеПользователя. Платформа делает запрос к параметрам сеанса, видит, что в текущем серверном сеансе фонового задания данный параметр не инициализирован, пытается вызвать обработчик его установки, но из-за отсутствия контекста запуска параметр остается пустым. Это и приводит к возникновению ошибки на этапе выполнения метода СкомпоноватьРезультат.РАЗРЕШЕННЫЕ в запросе СКД указывает платформе на необходимость отфильтровать записи, к которым у пользователя нет доступа, вместо вызова ошибки ограничения прав. Однако это условие работает только тогда, когда сам механизм RLS может быть успешно скомпилирован на уровне СУБД. Если для построения SQL-запроса не хватает параметров (в нашем случае — параметра сеанса), платформа падает с ошибкой инициализации еще до того, как начнется фильтрация выборки.---
Рассмотрим несколько архитектурных подходов к решению этой проблемы — от простых быстрых правок до комплексного проектирования надежной архитектуры прав доступа.
---
Если ваша конфигурация разработана на базе БСП, разработчики фирмы «1С» уже предусмотрели штатный механизм для трансляции клиентского контекста (включая параметры командной строки запуска) в фоновые задания на сервере.
При запуске клиентского приложения БСП считывает интерактивный ПараметрЗапуска и сериализует его в специальный параметр сеанса ПараметрыКлиентаНаСервере, который представляет собой объект типа ФиксированноеСоответствие. При запуске фонового формирования отчетов БСП автоматически передает данное соответствие в новый серверный сеанс фонового задания.
Проанализируем пример кода, который необходимо внедрить в процедуру инициализации параметров сеанса (обычно она вызывается из обработчика события УстановкаПараметровСеанса в модуле сеанса или через переопределяемые модули БСП):
// Вместо прямого обращения к глобальной переменной ПараметрЗапуска:
ПараметрЗапускаКлиента = "";
Если Найти(СтрокаСоединенияИнформационнойБазы(), "Srvr=") > 0 Тогда
// Мы находимся в клиент-серверном варианте работы
Попытка
ПараметрыКлиента = ПараметрыСеанса.ПараметрыКлиентаНаСервере;
Если ТипЗнч(ПараметрыКлиента) = Тип("ФиксированноеСоответствие") Тогда
ПараметрЗапускаКлиента = ПараметрыКлиента.Получить("ПараметрЗапуска");
КонецЕсли;
Исключение
// ПараметрыКлиентаНаСервере могут быть еще не инициализированы при первичном входе
ПараметрЗапускаКлиента = "";
КонецПопытки;
Иначе
// Для файлового варианта базы или прямого вызова кода на клиенте
ПараметрЗапускаКлиента = ПараметрЗапуска;
КонецЕсли;
// Если параметр запуска определен, используем его для поиска подразделения
Если ЗначениеЗаполнено(ПараметрЗапускаКлиента) Тогда
// Здесь располагается ваш алгоритм поиска подразделения по параметру запуска, например:
// ПодразделениеСсылка = НайтиПодразделениеПоКоду(ПараметрЗапускаКлиента);
// ПараметрыСеанса.G_ПодразделениеПользователя = ПодразделениеСсылка;
КонецЕсли;
Этот способ позволяет фоновому заданию СКД гарантированно получить тот же параметр запуска /C, с которым изначально вошел пользователь, и корректно установить параметры сеанса для RLS.
---
Согласно стандартам разработки фирмы «1С», обработчик события УстановкаПараметровСеанса никогда не должен завершаться ошибкой или оставлять запрашиваемый параметр неинициализированным. Это правило актуально для любых видов сеансов, включая регламентные задания, фоновые системные процессы или сеансы интеграции.
Если контекст запуска не определен и выяснить конкретное подразделение пользователя невозможно, параметр сеанса обязательно должен быть заполнен пустым значением соответствующего типа (в нашем примере — пустой ссылкой на справочник подразделений).
Рассмотрим, как правильно реализовать обработчик инициализации параметра:
Процедура УстановитьПараметрыСеанса(ИменаПараметровСеанса) Экспорт
// Проверим, инициализирован ли наш параметр
НужноИнициализировать = ИменаПараметровСеанса = Неопределено
Или ИменаПараметровСеанса.Найти("G_ПодразделениеПользователя") <> Неопределено;
Если НужноИнициализировать Тогда
// Пытаемся получить подразделение из доступного контекста
ТекущееПодразделение = ПолучитьПодразделениеПоПараметрамЗапуска();
Если НЕ ЗначениеЗаполнено(ТекущееПодразделение) Тогда
// Если контекст пуст (например, это фоновое задание),
// обязательно устанавливаем пустую ссылку, избегая ошибки инициализации
ПараметрыСеанса.G_ПодразделениеПользователя = Справочники.СтруктураПредприятия.ПустаяСсылка();
Else
ПараметрыСеанса.G_ПодразделениеПользователя = ТекущееПодразделение;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При использовании этого подхода в шаблонах RLS на чтение документов нужно обязательно предусмотреть ситуацию, когда параметр сеанса инициализирован пустой ссылкой. Это необходимо, чтобы избежать случайного сокрытия абсолютно всех данных от пользователя в отчетах:
// Пример условия в шаблоне RLS:
ГДЕ ПриобретениеТоваровУслуг.Подразделение = &G_ПодразделениеПользователя
ИЛИ &G_ПодразделениеПользователя = ЗНАЧЕНИЕ(Справочник.СтруктураПредприятия.ПустаяСсылка)
В этом случае, если отчет формируется в контексте фонового процесса с пустой ссылкой в параметре сеанса, RLS пропустит все документы (или наоборот заблокирует их, в зависимости от заложенной вами бизнес-логики безопасности), но главное — система выполнит запрос стабильно и без падения в ошибку.
---
Использование ключей командной строки (параметра /C) при работе в терминальных RDP-сессиях часто приводит к непредвиденным ошибкам, поскольку параметры командной строки могут легко теряться при сложных серверных вызовах и фоновых процессах.
Более надежным и устойчивым архитектурным решением является хранение сведений об активных подразделениях пользователей непосредственно в таблицах базы данных. Давайте разберем этот алгоритм по шагам:
АктивныеПодразделенияСеансов. В качестве измерений укажем Пользователь (тип СправочникСсылка.Пользователи) и НомерСеанса (тип Число). В качестве ресурса добавим Подразделение (тип СправочникСсылка.СтруктураПредприятия).ПриНачалеРаботыСистемы (когда интерактивный контекст клиента полностью доступен) считаем значение ключа запуска и запишем соответствие номера текущего сеанса и подразделения в наш регистр сведений.ПередЗавершениемРаботыСистемы реализуем удаление записи по текущему сеансу из регистра сведений, чтобы не захламлять базу данных устаревшей информацией.УстановкаПараметровСеанса выполним быстрый и легкий запрос к регистру сведений АктивныеПодразделенияСеансов по текущему пользователю и текущему номеру сеанса (который можно получить с помощью метода ПолучитьТекущийСеансИнформационнойБазы().НомерСеанса).Этот подход гарантирует 100% стабильность работы как основного интерфейса пользователя, так и любых дочерних фоновых заданий СКД, поскольку сеансовые данные хранятся физически в СУБД и привязаны к конкретному идентификатору запущенного сеанса 1С.
---
Если отчет разработан для узкого круга пользователей и объем обрабатываемых им данных невелик, мы можем принудительно отключить фоновое выполнение для этой формы отчета. В таком случае компоновка данных и вывод результата будут происходить в основном (клиентском) потоке приложения, где контекст параметров запуска гарантированно определен.
Для реализации этого в форме отчета перед вызовом компоновки или при инициализации настроек отчета в БСП можно передать свойство ВыполнятьНаСервереБезПереходаВФоновыйРежим = Истина в структуру параметров вывода отчета.
Важно: Данный способ является обходным и не рекомендуется для использования на тяжелых отчетах, так как он блокирует интерфейс пользователя на время расчета данных.
---
Мы проанализировали неочевидную, но распространенную ошибку совместной работы СКД, фоновых заданий и механизма ограничений RLS. Наиболее надежным и архитектурно правильным решением является обеспечение гарантированной инициализации всех параметров сеанса по умолчанию (Способ 2) в сочетании с трансляцией клиентских параметров на сервер средствами БСП (Способ 1). Это позволит вашим отчетам формироваться быстро, безопасно и без ошибок в любых режимах работы платформы.