Как программно настроить отбор в динамическом списке для отображения проведенных документов пользователя?

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

При разработке интерфейсов на управляемых формах 1С одной из самых частых задач является ограничение видимости данных в списках. Например, когда необходимо показать пользователю только «его» документы, которые к тому же должны быть уже проведены. Несмотря на кажущуюся простоту, здесь новичков подстерегают две ловушки: особенности логики SQL-запросов (приоритеты операций) и нюансы работы с объектами компоновки данных. Рассмотрим подробно, как правильно реализовать такой функционал.

Анализ проблемы: почему не работает простой запрос?

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


ВЫБРАТЬ
    Табель.Ссылка,
    Табель.Проведен,
    Табель.Ответственный
ИЗ
    Документ.Табель КАК Табель
ГДЕ
    Табель.Куратор.Наименование = &ТекущийПользователь
    ИЛИ Табель.Ответственный.Наименование = &ТекущийПользователь
    ИЛИ &ПолныеПрава

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

Если же мы добавим И Проведен = Истина в конец без скобок, система прочитает это так: «Покажи документ, если (Куратор = Пользователь) ИЛИ (Ответственный = Пользователь) ИЛИ (У пользователя полные права И документ проведен)». В итоге условие проведения применится только к администратору, а обычные пользователи по-прежнему будут видеть свои непроведенные документы.

Решение №1: Правильная группировка условий в запросе

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


ВЫБРАТЬ
    ДокументТабель.Ссылка,
    ДокументТабель.Проведен,
    ДокументТабель.Ответственный,
    ДокументТабель.Куратор
ИЗ
    Документ.Табель КАК ДокументТабель
ГДЕ
    (ДокументТабель.Куратор = &ТекущийПользователь
    ИЛИ ДокументТабель.Ответственный = &ТекущийПользователь
    ИЛИ &ПолныеПрава)
    И ДокументТабель.Проведен = ИСТИНА

Важный момент по производительности: Обратите внимание, что мы ушли от обращения к полю Наименование (через точку). Сравнение ссылочных полей Куратор = &ТекущийПользователь работает на уровне СУБД гораздо быстрее, так как используются индексы таблиц, а не неявные соединения (LEFT JOIN) со справочником пользователей.

Решение №2: Программная установка отбора через Компоновку Данных

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

Разработчик пытался сделать так:


ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Проведен");
ЭлементОтбора.ПравоеЗначение = ПолеОтбора; // ПолеОтбора - это тоже ПолеКомпоновкиДанных

Здесь кроется ошибка: в ПравоеЗначение должно передаваться конкретное значение (в нашем случае Истина), а не само поле. Исправим код и реализуем правильную процедуру установки отбора на сервере:


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

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

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

Решение №3: Использование фиксированных настроек

Если ваша задача — сделать так, чтобы пользователь принципиально не мог отключить установленный программно отбор через стандартное меню «Настроить список», используйте свойство ФиксированныеНастройки. Рассмотрим подробнее этот механизм. Отбор, установленный в Список.Отбор, виден пользователю в настройках, а фиксированный отбор применяется «под капотом» и невидим в интерфейсе.


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    ГруппаОтбора = Список.ФиксированныеНастройки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
    ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
    
    Элемент = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
    Элемент.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных("Проведен");
    Элемент.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
    Элемент.ПравоеЗначение = Истина;
    Элемент.Использование  = Истина;
    
КонецПроцедуры

Подводные камни и рекомендации

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

ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Проведен", Истина, ВидСравненияКомпоновкиДанных.Равно);

Также помните о следующих нюансах:

  1. Rls (Record Level Security): Если ограничение видимости — это вопрос безопасности, а не просто удобства интерфейса, то динамический список — это «слабое звено». Пользователь может вытащить данные через консоль запросов или другие формы. В таких случаях отбор нужно настраивать на уровне ролей (RLS) в метаданных документа.
  2. Текущий пользователь: Никогда не передавайте имя пользователя строкой. Всегда используйте ссылку. Получить её можно так: Пользователи.АвторизованныйПользователь() (для БСП) или через поиск в справочнике по текущему сеансу.
  3. Параметры запроса: Если вы все же решили использовать запрос динамического списка, не забывайте устанавливать параметры в событии ПриСозданииНаСервере:

Список.Параметры.УстановитьЗначениеПараметра("ТекущийПользователь", ПараметрыСеанса.ТекущийПользователь);
Список.Параметры.УстановитьЗначениеПараметра("ПолныеПрава", РольДоступна("ПолныеПрава"));

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

← На главную