При разработке интерфейсов на управляемых формах в конфигурациях «1С:Предприятие 8.3» (таких как «Управление торговлей 11», «ERP Управление предприятием» или «Комплексная автоматизация») программисты часто сталкиваются с задачей динамического управления отображением данных. Классический пример: на управляемой форме списка документов расположен динамический список с произвольным запросом. В этом запросе изначально прописано жесткое условие в секции ГДЕ, которое ограничивает выборку документов по определенным критериям (например, отсекает устаревшие виды заказов или пустые значения реквизитов).
Однако бизнес-требования часто меняются. Руководству или аналитикам периодически требуется видеть полную картину — все документы без ограничений, независимо от их статуса или вида. Для этого на форму выводится удобный интерактивный флажок (галочка), при изменении состояния которого список должен мгновенно перестраиваться. В этой статье мы подробно разберем три практических способа реализации этой задачи, проанализируем их плюсы и минусы с точки зрения производительности и приведем готовые примеры кода для каждого варианта.
Этот подход является наиболее лаконичным, производительным и простым в реализации. Суть метода заключается в том, что мы не меняем сам текст SQL-запроса «на лету» и не строим сложные конструкции в коде. Вместо этого мы один раз прописываем в запросе динамического списка условие, которое гибко реагирует на значение специального управляющего параметра-булева.
Многие разработчики ошибочно полагают, что параметры динамического списка инициализируются исключительно один раз при запуске формы в обработчике ПриСозданииНаСервере. На самом деле платформа 1С позволяет программно менять значения параметров динамического списка в любой момент времени во время работы пользователя.
Выполним реализацию этого метода по шагам:
ГДЕ в его произвольном запросе, добавив управляющий параметр &ОтбиратьЗаказы:
ГДЕ НЕ &ОтбиратьЗаказы
ИЛИ (ДокументЗаказКлиента.ВидДокумента = ЗНАЧЕНИЕ(Перечисление.ВидЗаказа.ЗаказКлиента)
И ДокументЗаказКлиента.СервисныйЦентр = ЗНАЧЕНИЕ(Документ.СервисныйЦентр.ПустаяСсылка)
ИЛИ ДокументЗаказКлиента.ВидДокумента = ЗНАЧЕНИЕ(Перечисление.ВидЗаказа.ПустаяСсылка)
ИЛИ ДокументЗаказКлиента.ВидДокумента ЕСТЬ NULL)
Проанализируем это условие. Если параметр &ОтбиратьЗаказы принимает значение Ложь (когда пользователь хочет видеть абсолютно все документы), выражение НЕ &ОтбиратьЗаказы превращается в Истина. Благодаря логическому оператору ИЛИ, вся остальная (тяжелая для базы данных) часть условия отбора полностью игнорируется, и СУБД возвращает полный список записей. Если же параметр установлен в Истина, система начинает строго применять отбор по видам документов и сервисным центрам.
ПоказатьТолькоЗаказы и типом данных Булево. Выведем его на элемент формы в виде флажка.ПриИзменении. В обработчике этого события мы будем вызывать серверный контекст для безопасной передачи параметра в динамический список:
&НаКлиенте
Процедура ПоказатьТолькоЗаказыПриИзменении(Элемент)
УстановитьПараметрыСпискаНаСервере();
КонецПроцедуры
&НаСервере
Процедура УстановитьПараметрыСпискаНаСервере()
// Прямая установка значения параметра в коллекцию динамического списка
Список.Параметры.УстановитьЗначениеПараметра("ОтбиратьЗаказы", ПоказатьТолькоЗаказы);
КонецПроцедуры
Полезный совет: Если вы ведете разработку в типовой конфигурации на базе Библиотеки Стандартных Подсистем (БСП), то вместо прямого обращения к коллекции параметров списка лучше использовать стандартный программный интерфейс БСП. Это гарантирует корректное поведение интерфейса и стандартизацию вашего кода:
&НаСервере
Процедура УстановитьПараметрыСпискаНаСервере()
ОбщегоНазначенияКлиентСервер.УстановитьПараметрДинамическогоСписка(
Список,
"ОтбиратьЗаказы",
ПоказатьТолькоЗаказы
);
КонецПроцедуры
Главный плюс этого метода — высокая скорость работы. Текст запроса стабилен, СУБД кэширует план его выполнения, отсутствуют накладные расходы на перестроение схемы компоновки данных платформой 1С.
Бывают ситуации, когда логика отборов настолько вариативна, что простым параметром обойтись невозможно. Например, при включении определенной опции требуется не просто отфильтровать записи, а полностью перестроить архитектуру запроса — добавить левые соединения (ЛЕВОЕ СОЕДИНЕНИЕ) с другими таблицами или исключить ресурсоемкие подзапросы. Для изучения различных подходов к изменению состава полей и структуры выборки подойдут практические методы доработки динамических списков.
В этом случае мы можем полностью перезаписать свойство ТекстЗапроса нашего динамического списка прямо во время работы формы.
Рассмотрим, как это выглядит на практике:
&НаСервере
Процедура ПереопределитьТекстЗапросаНаСервере(ПоказатьВсе)
// Шаблон базового запроса без наложенных условий фильтрации
БазовыйТекст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Дата КАК Дата,
| Док.Номер КАК Номер,
| Док.ВидДокумента КАК ВидДокумента,
| Док.СервисныйЦентр КАК СервисныйЦентр
|ИЗ
| Документ.ЗаказКлиента КАК Док";
Если НЕ ПоказатьВсе Тогда
// Динамически приплюсовываем конструкцию ГДЕ
СтрокаФильтра =
"
|ГДЕ
| (Док.ВидДокумента = ЗНАЧЕНИЕ(Перечисление.ВидЗаказа.ЗаказКлиента)
| И Док.СервисныйЦентр = ЗНАЧЕНИЕ(Документ.СервисныйЦентр.Push)
| ИЛИ Док.ВидДокумента = ЗНАЧЕНИЕ(Перечисление.ВидЗаказа.ПустаяСсылка)
| ИЛИ Док.ВидДокумента ЕСТЬ NULL)";
Список.ТекстЗапроса = БазовыйТекст + СтрокаФильтра;
Иначе
// Применяем чистый текст запроса
Список.ТекстЗапроса = БазовыйТекст;
КонецЕсли;
КонецПроцедуры
Проанализируем критически важные нюансы и скрытые проблемы, с которыми можно столкнуться при использовании данного метода:
Список.ТекстЗапроса, платформа «1С:Предприятие» полностью переформирует внутреннюю схему компоновки данных. Если в новом тексте запроса изменятся типы полей, изменятся их псевдонимы (КАК ИмяПоля) или пропадут какие-то реквизиты, все пользовательские настройки отображения списка (группировки, условное оформление колонок, пользовательские сортировки) будут полностью стерты или заблокированы. Поэтому всегда следите за тем, чтобы выходные поля запроса были на 100% идентичны по именам и типам.Стандарты разработки фирмы «1С» диктуют следующее правило: любые условия фильтрации данных в динамических списках должны накладываться не через жесткие конструкции ГДЕ внутри sql-запроса, а через настройки системы компоновки данных (СКД). Это позволяет СУБД оптимально использовать существующие индексы таблиц и делать выборку данных порциями. Для понимания возможностей управления структурой и фильтрами пригодится руководство по системе настроек динамического списка.
Сложность нашей задачи заключается в том, что условие отбора содержит вложенные логические группы «И» / «ИЛИ» и проверку на пустое значение (ЕСТЬ NULL в запросе). Чтобы реализовать такую структуру без написания sql-кода, нам потребуется программно построить дерево элементов отбора в компоновщике настроек динамического списка.
Давайте детально разберем структуру, которую нам необходимо программно воссоздать в объекте КомпоновщикНастроек:
ВидДокумента Равно Перечисление.ВидЗаказа.ЗаказКлиентаСервисныйЦентр Равно Документ.СервисныйЦентр.ПустаяСсылкаВидДокумента Равно Перечисление.ВидЗаказа.ПустаяСсылкаВидДокумента Не Заполнено (в СКД этот вид сравнения полностью эквивалентен проверке на NULL)Посмотрим на практическую реализацию этого алгоритма на встроенном языке 1С. Мы напишем универсальную процедуру, которая будет вызываться при изменении состояния нашего интерактивного флажка на форме:
&НаСервере
Процедура ОбновитьСложныйОтборНаСервере(ПрименитьФильтр)
ОтборыСписка = Список.КомпоновщикНастроек.Настройки.Отбор;
УникальноеИмяФильтра = "СложныйФильтрДиректора";
// Шаг 1. Очищаем старые программно добавленные элементы отбора, чтобы избежать дублирования
ЭлементыДляУдаления = Новый Массив;
Для Каждого Элемент Из ОтборыСписка.Элементы Цикл
Если Элемент.Представление = УникальноеИмяФильтра Тогда
ЭлементыДляУдаления.Добавить(Элемент);
КонецЕсли;
КонецЦикла;
Для Каждого Элемент Из ЭлементыДляУдаления Цикл
ОтборыСписка.Элементы.Удалить(Элемент);
КонецЦикла;
// Если галочка снята — мы просто очистили старые отборы и выходим, список покажет все записи
Если НЕ ПрименитьФильтр Тогда
Возврат;
КонецЕсли;
// Шаг 2. Создаем корневую группу ИЛИ
ГруппаИЛИ = ОтборыСписка.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
ГруппаИЛИ.Представление = УникальноеИмяФильтра;
ГруппаИЛИ.Использование = Истина;
// Шаг 3. Ветка №1: Группа И (ВидДокумента = ЗаказКлиента И СервисныйЦентр = ПустаяСсылка)
ГруппаИ = ГруппаИЛИ.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
ГруппаИ.Использование = Истина;
ОтборВидЗаказа = ГруппаИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборВидЗаказа.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидДокумента");
ОтборВидЗаказа.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ОтборВидЗаказа.ПравоеЗначение = Перечисления.ВидЗаказа.ЗаказКлиента;
ОтборВидЗаказа.Использование = Истина;
ОтборСервисныйЦентр = ГруппаИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборСервисныйЦентр.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("СервисныйЦентр");
ОтборСервисныйЦентр.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ОтборСервисныйЦентр.ПравоеЗначение = Документы.СервисныйЦентр.ПустаяСсылка();
ОтборСервисныйЦентр.Использование = Истина;
// Шаг 4. Ветка №2: ВидДокумента = ПустаяСсылка
ОтборПустойВид = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборПустойВид.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидДокумента");
ОтборПустойВид.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ОтборПустойВид.ПравоеЗначение = Перечисления.ВидЗаказа.ПустаяСсылка;
ОтборПустойВид.Использование = Истина;
// Шаг 5. Ветка №3: ВидДокумента ЕСТЬ NULL (в СКД используем вид сравнения НеЗаполнено)
ОтборNull = ГруппаИЛИ.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ОтборNull.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидДокумента");
ОтборNull.ВидСравнения = ВидСравненияКомпоновкиДанных.НеЗаполнено;
ОтборNull.Использование = Истина;
КонецПроцедуры
Важный практический нюанс: Чтобы платформа могла успешно применить отбор по полям ВидДокумента и СервисныйЦентр, эти поля обязательно должны присутствовать в списке выбора результирующего запроса вашего динамического списка. В противном случае система сгенерирует исключение «Поле не найдено». Если вы не хотите выводить эти поля на форму пользователю, просто снимите с них флажки видимости в свойствах колонок таблицы формы, но обязательно оставьте их в самом запросе списка.
Проанализируем все рассмотренные нами варианты решений и выберем оптимальный подход для практической разработки:
ГДЕ. Его стоит выбирать тогда, когда вы хотите скрыть сложные фильтры от глаз пользователя (поместив их, например, в КомпоновщикНастроек.ФиксированныеНастройки), либо когда вам нужно предоставить пользователю возможность комбинировать эти отборы со своими собственными в окне «Настройка списка».