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