При разработке управляемых форм в 1С:Предприятии часто возникает задача открыть форму списка справочника или документа с предопределенным, но гибким набором отборов. Например, нам требуется показать пользователю только подразделения из определенного списка, но при этом дать ему возможность самостоятельно отфильтровать эти подразделения по конкретной организации. Разберем, как правильно организовать такой сценарий, избегая типичных ошибок, связанных с конфликтом настроек компоновщика данных.
Прежде чем погрузиться в решение, давайте вспомним, как работает система компоновки данных (СКД) в 1С:Предприятии и какие типы настроек влияют на отображение динамических списков:
Понимание этой иерархии помогает нам избежать пересечений и правильно распределить отборы.
Рассмотрим ситуацию, описанную в теме. Изначально стояла задача открыть форму выбора подразделений с двумя видами отбора:
Наш коллега начал с реализации фиксированного отбора по списку подразделений в процедуре ПодразделениеНачалоВыбора:
&НаКлиенте
Процедура ПодразделениеНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
НастройкиКомпоновки = Новый НастройкиКомпоновкиДанных;
НастройкаОтбора = НастройкиКомпоновки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
СтрокаОтбора = НастройкаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
СтрокаОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
СтрокаОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
СтрокаОтбора.Использование = Истина;
СтрокаОтбора.ПравоеЗначение = ПолучитьСписокПодразделений();
ПараметрыВыбора = Новый Структура;
ПараметрыВыбора.Вставить("ФиксированныеНастройки", НастройкиКомпоновки);
ПараметрыВыбора.Вставить("РежимВыбора",Истина);
ОткрытьФорму("Справочник.ПодразделенияОрганизаций.ФормаСписка",ПараметрыВыбора,Элемент);
КонецПроцедуры
Этот код корректно устанавливает фиксированный отбор по списку подразделений. Однако при попытке добавить отбор по организации через стандартные механизмы формы списка, список очищался. Это произошло потому, что пользовательский отбор по организации (который мог быть уже сохранен в настройках пользователя или устанавливался по умолчанию) вступал в конфликт с попыткой фильтрации. Система не могла однозначно применить оба условия к одному и тому же набору данных, особенно если в фиксированных настройках уже была бы попытка фильтрации по организации.
Когда была предпринята попытка добавить отбор по организации непосредственно в ФиксированныеНастройки:
СтрокаОтбора = НастройкаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
СтрокаОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Владелец");
СтрокаОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
СтрокаОтбора.Использование = Истина;
СтрокаОтбора.ПравоеЗначение = Организация;
Возникла ошибка "Невозможно применить фиксированные настройки. Пересекаются элементы отбора.". Эта ошибка возникает, когда компоновщик настроек обнаруживает, что вы пытаетесь установить отбор по одному и тому же полю (в данном случае, Владелец, который соответствует организации) одновременно в фиксированных настройках и, например, в пользовательских настройках, которые могли быть сохранены для этой формы. Фиксированные настройки имеют высший приоритет, но система безопасности 1С предотвращает неявное перезатирание пользовательских настроек, выдавая такое предупреждение.
Как выяснилось через отладчик, причиной конфликта действительно были пользовательские настройки. Попытка очистить их в процедуре ПриСозданииНаСервере и добавить отборы выглядела так:
ПользовательскийОтбор = Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(Список.КомпоновщикНастроек.Настройки.Отбор.ИдентификаторПользовательскойНастройки);
ПользовательскийОтбор.Элементы.Очистить();
ЭлементОтбора = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ПравоеЗначение = ПолучитьСписокПодразделений();
ЭлементОтбора = ПользовательскийОтбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Владелец");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.Использование = Истина;
ЭлементОтбора.ПравоеЗначение = Параметры.Организация;
Эта попытка была ближе к истине, так как переносила работу с отбором по организации в обработчик ПриСозданииНаСервере открываемой формы, что является правильным подходом для отборов, которые могут быть изменены пользователем. Однако, несмотря на очистку пользовательских настроек, отбор по организации все равно работал некорректно, и список оставался пустым. Это может быть связано с тем, что отбор по Владельцу был добавлен как элемент пользовательских настроек, но без должного механизма интеграции с основными настройками, или из-за особенностей взаимодействия с уже примененными ФиксированнымиНастройками.
Для корректного решения данной задачи нам необходимо четко разделить, какой отбор является жестким и неизменяемым, а какой должен быть доступен для редактирования пользователем. Применяем следующий алгоритм:
ФиксированныеНастройки при открытии формы. Он будет применяться первым и не должен конфликтовать с остальными.ПриСозданииНаСервере открываемой формы, добавляя в основные настройки компоновщика с возможностью пользовательского изменения.В процедуре ПодразделениеНачалоВыбора (или аналогичной, где происходит вызов формы выбора) мы формируем только фиксированный отбор по списку подразделений. Отбор по организации мы передаем отдельным параметром.
&НаКлиенте
Процедура ПодразделениеНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Отменяем стандартную обработку выбора
СтандартнаяОбработка = Ложь;
// Создаем объект для фиксированных настроек компоновки данных
НастройкиКомпоновки = Новый НастройкиКомпоновкиДанных;
// Добавляем группу элементов отбора для списка подразделений
НастройкаОтбора = НастройкиКомпоновки.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
НастройкаОтбора.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ИЛИ; // Или можно использовать И, если все подразделения должны быть из списка И соответствовать другим условиям.
// Если нужно, чтобы элементы группы работали по И, то используйте ТипГруппыЭлементовОтбораКомпоновкиДанных.И
// Для отбора "ВСписке" обычно не нужна группа, если только не комбинируются другие сложные условия.
// Для простоты, если Отбор.Элементы пуст, то можно просто добавить элемент без группы, но группа дает гибкость.
// Добавляем элемент отбора по полю "Ссылка" (сами подразделения)
ЭлементОтбораСсылка = НастройкаОтбора.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораСсылка.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Ссылка");
ЭлементОтбораСсылка.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
ЭлементОтбораСсылка.Использование = Истина;
ЭлементОтбораСсылка.ПравоеЗначение = ПолучитьСписокПодразделений(); // Функция, возвращающая массив или список значений подразделений
// Подготавливаем структуру параметров для открытия формы
ПараметрыВыбора = Новый Структура;
ПараметрыВыбора.Вставить("ФиксированныеНастройки", НастройкиКомпоновки); // Передаем фиксированный отбор
ПараметрыВыбора.Вставить("РежимВыбора", Истина); // Указываем, что форма открывается в режиме выбора
// Получаем текущую организацию (предположим, она доступна в контексте формы)
// Например, если в форме есть реквизит Организация
Если ЗначениеЗаполнено(Организация) Тогда
ПараметрыВыбора.Вставить("ОрганизацияДляОтбора", Организация); // Передаем организацию как обычный параметр
КонецЕсли;
// Открываем форму списка справочника "ПодразделенияОрганизаций"
ОткрытьФорму("Справочник.ПодразделенияОрганизаций.ФормаСписка", ПараметрыВыбора, Элемент);
КонецПроцедуры
// Пример вспомогательной функции для получения списка подразделений
// (Реализация может быть на клиенте, если список небольшой и данные нечувствительны,
// или на сервере, если требуется обращение к базе данных)
&НаСервере
Функция ПолучитьСписокПодразделений()
СписокПодразделений = Новый СписокЗначений;
// Пример заполнения списка
// СписокПодразделений.Добавить(Справочники.ПодразделенияОрганизаций.НайтиПоКоду("001"));
// СписокПодразделений.Добавить(Справочники.ПодразделенияОрганизаций.НайтиПоКоду("002"));
Возврат СписокПодразделений;
КонецФункции
Обратите внимание, что отбор по организации теперь передается в виде обычного параметра ОрганизацияДляОтбора. Это позволяет избежать конфликта с ФиксированнымиНастройками, так как компоновщик данных не будет обрабатывать этот параметр как часть фиксированного отбора.
Далее, нам необходимо модифицировать форму списка Справочник.ПодразделенияОрганизаций.ФормаСписка. Если это типовая форма, и у нас нет возможности ее менять, придется создавать свою. В контексте задачи мы предполагаем, что у нас есть возможность изменить модуль формы списка или создать свою форму списка на основе типовой.
В обработчике события ПриСозданииНаСервере открываемой формы списка мы перехватываем переданный параметр ОрганизацияДляОтбора и добавляем его в основные настройки компоновщика данных нашего динамического списка (Список.КомпоновщикНастроек.Настройки.Отбор). Это позволяет сделать данный отбор видимым и изменяемым для пользователя.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, был ли передан параметр "ОрганизацияДляОтбора"
Если Параметры.Свойство("ОрганизацияДляОтбора") Тогда
ПереданнаяОрганизация = Параметры.ОрганизацияДляОтбора;
Если ЗначениеЗаполнено(ПереданнаяОрганизация) Тогда
// Получаем доступ к основным настройкам компоновщика данных списка
// Важно: работаем с Список.КомпоновщикНастроек.Настройки.Отбор,
// а не с ПользовательскиеНастройки, чтобы обеспечить управляемость отбором
// и избежать конфликтов с ФиксированнымиНастройками.
// Создаем новый элемент отбора по организации
ЭлементОтбораОрганизация = Список.КомпоновщикНастроек.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбораОрганизация.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Владелец"); // Имя поля для организации в справочнике ПодразделенияОрганизаций
ЭлементОтбораОрганизация.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбораОрганизация.Использование = Истина;
ЭлементОтбораОрганизация.ПравоеЗначение = ПереданнаяОрганизация;
ЭлементОтбораОрганизация.Представление = "Организация"; // Отображаемое имя для отбора
// Ключевой момент: устанавливаем идентификатор пользовательской настройки.
// Это делает отбор доступным для пользователя в окне "Настроить список"
// и позволяет ему изменять или отключать его.
// Используем GUID для уникальности, чтобы не конфликтовать с другими отборами
// или сохраненными пользовательскими настройками.
ЭлементОтбораОрганизация.ИдентификаторПользовательскойНастройки = Строка(Новый УникальныйИдентификатор);
// Определяем, как отбор будет отображаться в пользовательском интерфейсе.
// Обычный - будет виден в окне "Настроить список".
// БыстрыйДоступ - будет виден в быстрых отборах формы.
ЭлементОтбораОрганизация.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ;
// Или РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Обычный, если не нужен быстрый доступ.
// Обновляем настройки компоновщика
Список.КомпоновщикНастроек.ЗагрузитьНастройки(Список.КомпоновщикНастроек.Настройки);
// Очистка пользовательских настроек, как это было в сообщении 4,
// является радикальным решением. В большинстве случаев это не требуется,
// если мы корректно работаем с основными настройками и ИдентификаторПользовательскойНастройки.
// Однако, если у вас есть подозрения на конфликты с ранее сохраненными
// пользовательскими настройками, можно рассмотреть более точечную очистку
// или сброс настроек для конкретного элемента.
// Например:
// Если Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ЭлементОтбораОрганизация.ИдентификаторПользовательскойНастройки) Тогда
// Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Удалить(Список.КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ЭлементОтбораОрганизация.ИдентификаторПользовательскойНастройки));
// КонецЕсли;
// Но в большинстве случаев при правильной установке ИдентификаторПользовательскойНастройки
// это не требуется, поскольку система будет корректно управлять элементом.
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Таким образом, мы добиваемся того, что фиксированный отбор по списку подразделений всегда применяется, а отбор по организации устанавливается изначально, но пользователь может его изменить или сбросить. Этот подход является гибким и устойчивым к конфликтам.
Если в вашей конфигурации используется Библиотека стандартных подсистем (БСП), для установки отборов в динамических списках рекомендуется использовать специализированные функции из общего модуля ОбщегоНазначенияКлиентСервер. Например, метод УстановитьЭлементОтбораДинамическогоСписка() предназначен именно для таких целей. Он корректно управляет пользовательскими настройками и помогает избежать многих ошибок, связанных с ручным добавлением элементов отбора.
Пример использования (псевдокод):
// В процедуре ПриСозданииНаСервере
Если Параметры.Свойство("ОрганизацияДляОтбора") Тогда
ПереданнаяОрганизация = Параметры.ОрганизацияДляОтбора;
Если ЗначениеЗаполнено(ПереданнаяОрганизация) Тогда
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
Список,
"Владелец",
ПереданнаяОрганизация,
ВидСравненияКомпоновкиДанных.Равно,
Истина,
"Организация",
РежимОтображенияЭлементаНастройкиКомпоновкиДанных.БыстрыйДоступ
);
КонецЕсли;
КонецЕсли;
Этот метод инкапсулирует логику по правильному добавлению отбора в компоновщик настроек, автоматически присваивает уникальные идентификаторы и управляет режимом отображения. Это предпочтительный способ, если БСП доступна.
В процессе отладки сложных отборов и настроек компоновщика данных может быть очень полезен метод КомпоновщикНастроек.ПолучитьНастройки(). Он возвращает результирующий объект настроек компоновки данных, который содержит все примененные настройки: основные, пользовательские и фиксированные. Изучая структуру этого объекта в отладчике, мы можем точно увидеть, какие отборы и по каким полям были применены, и выявить возможные конфликты или некорректно установленные параметры.
// В отладчике можно добавить точку останова и просмотреть:
ТекущиеНастройки = Список.КомпоновщикНастроек.ПолучитьНастройки();
// Затем в окне переменных развернуть ТекущиеНастройки.Отбор.Элементы
Это помогает понять, как компоновщик данных интерпретировал все ваши инструкции и почему он мог прийти к тому или иному результату (например, пустому списку или ошибке).
Открытие формы списка с одновременным использованием фиксированного и пользовательского отбора требует глубокого понимания принципов работы компоновщика настроек 1С. Важно четко разделять отборы по их назначению: жестко заданные, не подлежащие изменению пользователем, должны передаваться через ФиксированныеНастройки, а те, что должны быть доступны для интерактивного изменения, должны добавляться в Настройки компоновщика данных самой формы списка в обработчике ПриСозданииНаСервере, с присвоением им ИдентификаторПользовательскойНастройки и определением РежимаОтображения. Такой подход гарантирует стабильность работы, предотвращает ошибки пересечения отборов и обеспечивает необходимую гибкость для пользователя.