При разработке интерфейсов на управляемых формах 1С одной из самых частых задач является ограничение видимости данных в списках. Например, когда необходимо показать пользователю только «его» документы, которые к тому же должны быть уже проведены. Несмотря на кажущуюся простоту, здесь новичков подстерегают две ловушки: особенности логики SQL-запросов (приоритеты операций) и нюансы работы с объектами компоновки данных. Рассмотрим подробно, как правильно реализовать такой функционал.
Разберем ситуацию, когда разработчик пытается добавить условие в текст запроса динамического списка. Часто исходный запрос выглядит так:
ВЫБРАТЬ
Табель.Ссылка,
Табель.Проведен,
Табель.Ответственный
ИЗ
Документ.Табель КАК Табель
ГДЕ
Табель.Куратор.Наименование = &ТекущийПользователь
ИЛИ Табель.Ответственный.Наименование = &ТекущийПользователь
ИЛИ &ПолныеПрава
Когда мы пытаемся просто дописать в конец ИЛИ Проведен = Истина, система начинает выдавать вообще все документы. Почему так происходит? Проанализируем приоритеты. В языке запросов 1С (как и в стандартном SQL) логический оператор И имеет более высокий приоритет, чем ИЛИ. Однако, когда мы используем только ИЛИ, условия проверяются последовательно. Если хотя бы одно из них истинно (например, документ просто проведен), всё выражение становится истинным, и фильтр по пользователю перестает работать.
Если же мы добавим И Проведен = Истина в конец без скобок, система прочитает это так: «Покажи документ, если (Куратор = Пользователь) ИЛИ (Ответственный = Пользователь) ИЛИ (У пользователя полные права И документ проведен)». В итоге условие проведения применится только к администратору, а обычные пользователи по-прежнему будут видеть свои непроведенные документы.
Чтобы отбор работал корректно для всех категорий пользователей, необходимо использовать скобки для группировки условий по субъектам и выносить общее условие (проведение) за эти скобки через оператор И. Рассмотрим правильный вариант запроса:
ВЫБРАТЬ
ДокументТабель.Ссылка,
ДокументТабель.Проведен,
ДокументТабель.Ответственный,
ДокументТабель.Куратор
ИЗ
Документ.Табель КАК ДокументТабель
ГДЕ
(ДокументТабель.Куратор = &ТекущийПользователь
ИЛИ ДокументТабель.Ответственный = &ТекущийПользователь
ИЛИ &ПолныеПрава)
И ДокументТабель.Проведен = ИСТИНА
Важный момент по производительности: Обратите внимание, что мы ушли от обращения к полю Наименование (через точку). Сравнение ссылочных полей Куратор = &ТекущийПользователь работает на уровне СУБД гораздо быстрее, так как используются индексы таблиц, а не неявные соединения (LEFT JOIN) со справочником пользователей.
Иногда жесткое прописывание условий в тексте запроса неудобно, так как это затрудняет дальнейшую модификацию списка. В этом случае лучше воспользоваться механизмом программной установки отборов в объекте Список (который по сути является ДинамическимСписком). Выясним причину ошибки в коде из сообщения форума.
Разработчик пытался сделать так:
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Проведен");
ЭлементОтбора.ПравоеЗначение = ПолеОтбора; // ПолеОтбора - это тоже ПолеКомпоновкиДанных
Здесь кроется ошибка: в ПравоеЗначение должно передаваться конкретное значение (в нашем случае Истина), а не само поле. Исправим код и реализуем правильную процедуру установки отбора на сервере:
&НаСервере
Процедура УстановитьОбязательныеОтборыНаСервере()
// 1. Очистим существующие отборы по полю "Проведен", чтобы не дублировать их
ПолеПроведен = Новый ПолеКомпоновкиДанных("Проведен");
Для Каждого Элемент Из Список.Отбор.Элементы Цикл
Если Элемент.ЛевоеЗначение = ПолеПроведен Тогда
Список.Отбор.Элементы.Удалить(Элемент);
КонецЕсли;
КонецЦикла;
// 2. Добавляем новый элемент отбора
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = ПолеПроведен;
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ПравоеЗначение = Истина; // Указываем булево значение, а не поле!
// Блокируем изменение этого отбора пользователем (по желанию)
ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецПроцедуры
Если ваша задача — сделать так, чтобы пользователь принципиально не мог отключить установленный программно отбор через стандартное меню «Настроить список», используйте свойство ФиксированныеНастройки. Рассмотрим подробнее этот механизм. Отбор, установленный в Список.Отбор, виден пользователю в настройках, а фиксированный отбор применяется «под капотом» и невидим в интерфейсе.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
ГруппаОтбора = Список.ФиксированныеНастройки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
Элемент = ГруппаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Проведен");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = Истина;
Элемент.Использование = Истина;
КонецПроцедуры
Проанализируем ситуацию с точки зрения архитектуры. Если вы используете конфигурацию на базе БСП (Библиотека Стандартных Подсистем), ручное создание элементов компоновки — не самый оптимальный путь. Рекомендуется использовать встроенные функции:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Проведен", Истина, ВидСравненияКомпоновкиДанных.Равно);
Также помните о следующих нюансах:
Пользователи.АвторизованныйПользователь() (для БСП) или через поиск в справочнике по текущему сеансу.ПриСозданииНаСервере:
Список.Параметры.УстановитьЗначениеПараметра("ТекущийПользователь", ПараметрыСеанса.ТекущийПользователь);
Список.Параметры.УстановитьЗначениеПараметра("ПолныеПрава", РольДоступна("ПолныеПрава"));
Подведем итог: для решения задачи фильтрации списка по проведенным документам лучше всего подходит комбинированный метод. Используйте фиксированные настройки для обязательных системных фильтров и параметризированный запрос для сложной логики (права доступа, кураторство). Это обеспечит чистоту кода, высокую производительность и защиту от случайного изменения настроек пользователем.