Как в параметры запроса передать текущую активную строку из формы списка и проверить права по иерархии

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

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

Различие между ТекущейСтрокой и ТекущимиДанными

Для начала проанализируем, какие инструменты предоставляет нам управляемая форма для получения данных о выбранном элементе. Начинающие разработчики часто путают два ключевых свойства динамического списка. Разберем их отличия:

  1. Элементы.Список.ТекущаяСтрока — возвращает ссылку на выбранный элемент (уникальный идентификатор). Это свойство доступно как на клиенте, так и на сервере. Именно его следует использовать для передачи в параметры запроса или в модуль объекта.
  2. Элементы.Список.ТекущиеДанные — возвращает структуру, содержащую данные полей, которые в данный момент отображены в строке списка. Это свойство доступно только на клиенте. Если вам нужно получить значение реквизита, которого нет в колонках списка, ТекущиеДанные вам не помогут — придется обращаться к базе данных на сервере.

Рассмотрим пример кода на клиенте для получения ссылки:


&НаКлиенте
Процедура КомандаДействие(Команда)
    СсылкаНаЭлемент = Элементы.Список.ТекущаяСтрока;
    Если ЗначениеЗаполнено(СсылкаНаЭлемент) Тогда
        ВыполнитьДействиеНаСервере(СсылкаНаЭлемент);
    КонецЕсли;
КонецПроцедуры

Передача текущей строки в модуль объекта

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

Проанализируем пример серверной процедуры в модуле формы:


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

В модуле объекта мы объявляем экспортную функцию, которая будет принимать нашу «текущую строку» (ссылку) и выполнять запрос.

Реализация проверки прав по иерархии через запрос

Теперь разберем самую сложную часть: как найти «самую верхнюю» группу в иерархическом справочнике и проверить ответственного. Использование цикла Пока Ссылка.Родитель.Выбран() крайне неэффективно, так как каждое обращение через точку порождает отдельный запрос к СУБД. Правильнее использовать запрос с получением всей цепочки родителей.

Рассмотрим пример функции в модуле объекта:


Функция ПроверитьДоступПоИерархии(СсылкаНаЭлемент) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Спр.Ссылка КАК Ссылка,
        |   Спр.Ответственный КАК Ответственный
        |ИЗ
        |   Справочник.ВашСправочник КАК Спр
        |ГДЕ
        |   Спр.Ссылка В ИЕРАРХИИ(&ТекущийЭлемент)";
    
    Запрос.УстановитьПараметр("ТекущийЭлемент", СсылкаНаЭлемент);
    
    // В данном случае нам нужно найти корень. 
    // Один из способов — перебор результата запроса сверху вниз
    // или получение ПолногоПути/ПолногоКода.
    
    // Но проще получить всех родителей и найти того, у которого Родитель пустой.
    // Рассмотрим логику поиска корня через запрос к вышестоящим узлам:
    
    ТекстЗапроса = 
    "ВЫБРАТЬ
    |   Т.Ссылка КАК Ссылка,
    |   Т.Ответственный КАК Ответственный
    |ИЗ
    |   Справочник.ВашСправочник КАК Т
    |ГДЕ
    |   Т.Ссылка В (ВЫБРАТЬ ИЕРАРХИЮ (&Ссылка)) И Т.Родитель = ЗНАЧЕНИЕ(Справочник.ВашСправочник.ПустаяСсылка)";
    
    Запрос.Текст = ТекстЗапроса;
    Запрос.УстановитьПараметр("Ссылка", СсылкаНаЭлемент);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Если Выборка.Ответственный = ПараметрыСеанса.ТекущийПользователь Тогда
            Возврат Истина;
        КонецЕсли;
    КонецЕсли;
    
    Возврат Ложь;
КонецФункции

Использование ДополнительныхСвойств для передачи контекста

Часто возникает вопрос: что передавать в стандартную процедуру ПриЗаписи(Отказ) в модуле объекта? В 1С существует механизм ДополнительныеСвойства, который позволяет передать произвольные данные из формы в модуль объекта «в обход» реквизитов.

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

В модуле формы (перед записью):


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    ТекущийОбъект.ДополнительныеСвойства.Вставить("ПроверкаПройдена", Истина);
КонецПроцедуры

В модуле объекта:


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

Нюансы работы с автоматическими формами

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

Проанализируем важный момент: при создании нового элемента Ссылка может быть еще не заполнена (в обработчике ПередЗаписью). В этом случае для запроса «В ИЕРАРХИИ» нужно использовать ссылку на Родителя, так как сам элемент еще не записан в базу.

Оптимизация производительности в форме списка

Если вы решите вызывать запрос для получения данных по активной строке прямо в событии ПриАктивизацииСтроки, интерфейс может начать «тормозить» при быстром пролистывании списка. Для решения этой проблемы рекомендуется использовать ОбработчикОжидания.

Схема работы:

  1. В событии ПриАктивизацииСтроки вызываем ПодключитьОбработчикОжидания("ВыполнитьЗапросПоСтроке", 0.5, Истина).
  2. Если пользователь переключил строку быстрее, чем через 0.5 секунды, обработчик переподключается, и запрос не выполняется.
  3. Только при остановке курсора на строке срабатывает процедура ВыполнитьЗапросПоСтроке, где мы получаем Элементы.Список.ТекущаяСтрока и выполняем серверный вызов.

Такой подход значительно повышает отзывчивость программы и снижает нагрузку на сервер 1С и базу данных.

Использование RLS как альтернатива

Если ваша задача — глобально ограничить доступ к «чужим» веткам справочника, рассмотрите использование механизма RLS (Record Level Security). Настроив один раз шаблон ограничения доступа в роли, вы избавитесь от необходимости писать код проверки в модулях каждого справочника. Система будет автоматически добавлять условия к SQL-запросам, отсекая элементы, к которым у пользователя нет доступа по иерархии ответственных.

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

← На главную