При разработке и доработке функционала в конфигурациях 1С, основанных на управляемых формах, часто возникает необходимость программно открывать формы выбора различных справочников. При этом требуется не просто открыть форму, но и установить определенные параметры отбора, чтобы пользователь мог выбирать только те элементы, которые соответствуют заданным условиям. Особые сложности могут возникнуть, когда речь идет об иерархических справочниках, где отбор должен применяться только к элементам определенного уровня иерархии, например, только к "месяцам" в справочнике "Периоды", исключая "годы" или "кварталы" из выбора.
Давайте рассмотрим подробнее, как решить эту задачу на примере справочника "Периоды" в конфигурации "1С:Бухгалтерия государственного учреждения 2.0" (БГУ 2.0), где нужно обеспечить выбор только элементов с периодичностью "Месяц".
Прежде всего, нам нужно добавить новое поле на форму нашего документа, например, "Ведомость на выплату в банк", и связать его с реквизитом объекта. Затем для этого поля необходимо назначить обработчик события НачалоВыбора, который будет отвечать за программное открытие формы выбора.
// Пример программного добавления поля на форму документа
// Этот код обычно размещается в обработчике события формы, например, ПриСозданииНаСервере
Период = Элементы.Добавить( "Период", Тип("ПолеФормы"), Элементы.ШапкаДокумента.ПодчиненныеЭлементы.ШапкаПравая);
Период.Заголовок = "Период";
Период.Вид = ВидПоляФормы.ПолеВвода;
Период.ПутьКДанным = "Объект.Период";
// Назначение обработчика события НачалоВыбора
Элементы.Период.УстановитьДействие("НачалоВыбора", "ПериодНачалоВыбора");
Здесь мы создаем новое поле Период, устанавливаем его заголовок, вид и путь к данным, связывая его с реквизитом Объект.Период. Затем мы указываем, что при начале выбора значения в этом поле будет вызываться клиентская процедура ПериодНачалоВыбора.
В процедуре ПериодНачалоВыбора мы будем перехватывать стандартную обработку выбора и открывать свою форму выбора с заданными параметрами отбора.
&НаКлиенте
Процедура ПериодНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
// Отменяем стандартную обработку, чтобы полностью контролировать процесс выбора
СтандартнаяОбработка = Ложь;
// Создаем структуру параметров для отбора
// Изначально можно было бы попытаться передать отбор сразу через параметры открытия формы
// Однако, для решения нашей задачи, мы установим отбор после получения формы.
// СтрПараметров = Новый Структура("Периодичность", ПредопределенноеЗначение("Перечисление.Периодичность.Месяц"));
// ПараметрыВыбора = Новый Структура("Отбор", СтрПараметров);
// Получаем форму выбора справочника "Периоды"
// Здесь мы пока используем стандартную форму, но далее выясним, почему это не идеально.
ФормаВыбора = ПолучитьФорму("Справочник.Периоды.ФормаВыбора");
// Устанавливаем отбор для динамического списка формы
эОтбор = ФормаВыбора.Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
эОтбор.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Периодичность");
эОтбор.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
// Важный момент: используем ПредопределенноеЗначение для сравнения с перечислением
// Неправильно было бы использовать строковое представление "ПеречислениеСсылка.Периодичность.Месяц"
эОтбор.ПравоеЗначение = ПредопределенноеЗначение("Перечисление.Периодичность.Месяц");
эОтбор.Использование = Истина;
// Делаем элемент отбора недоступным для изменения пользователем в форме выбора
эОтбор.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
// Открываем форму выбора
ФормаВыбора.Открыть();
КонецПроцедуры
В этом коде мы сначала отменяем стандартную обработку. Затем получаем форму выбора справочника Периоды. Создаем новый элемент отбора для динамического списка формы (ФормаВыбора.Список.Отбор.Элементы). Указываем, что отбор будет производиться по полю Периодичность, используя вид сравнения "Равно", и сравниваем его с предопределенным значением перечисления Месяц. Делаем отбор недоступным для пользователя и открываем форму.
После реализации кода, как показано выше, пользователи столкнулись с проблемой: форма открывается, отбор срабатывает, но отображаются только верхние элементы иерархического списка, у которых Периодичность = Месяц. Подчиненные элементы (например, месяцы, входящие в год или квартал), даже если их периодичность также "Месяц", не отображаются. Это привело к следующей ситуации:
Периодичность = Месяц установлен.В результате, при открытии формы выбора, отображались только такие "свободные" элементы, как "Июнь 2022", но не "Январь 2021", хотя у него тоже периодичность "Месяц". Почему так происходит?
Мы выясним причину такого поведения. Справочник "Периоды" в БГУ 2.0, как правило, использует иерархию элементов, а не иерархию групп и элементов. В чем разница?
Именно эта особенность иерархии элементов и была причиной проблемы. Элементы "2021 год" или "1 квартал 2021" имеют периодичность "Год" или "Квартал", а не "Месяц". Поэтому, когда мы устанавливаем отбор Периодичность = Месяц, эти родительские элементы не проходят отбор и не отображаются. Следовательно, их подчиненные элементы-месяцы также не могут быть показаны в иерархическом виде, потому что их родитель отсутствует в отфильтрованном списке.
Теперь, когда мы поняли корень проблемы, можем предложить эффективное решение. Если мы хотим, чтобы отбор применялся ко всем элементам независимо от их иерархического положения, и при этом отображались только те элементы, которые соответствуют условию (в нашем случае, Периодичность = Месяц), нам нужно отключить иерархическое отображение списка в форме выбора или использовать форму, которая по умолчанию отображает элементы списком.
Самым надежным способом в данной ситуации является создание новой формы выбора для справочника "Периоды" и настройка ее типа отображения как "Список".
Откройте конфигуратор: Запустите 1С в режиме конфигуратора.
Перейдите к справочнику "Периоды": В дереве объектов конфигурации найдите ветку "Справочники", затем "Периоды".
Создайте новую форму выбора:
Правой кнопкой мыши кликните на ветку "Формы" под справочником "Периоды" и выберите "Добавить".
В появившемся окне "Новая форма" укажите следующие параметры:
ФормаВыбораМесяцев или ФормаВыбораСписком.Нажмите "Готово".
Настройте основной реквизит формы:
После создания формы откроется ее конструктор. Убедитесь, что на форме присутствует элемент "Список" (обычно он создается по умолчанию) и что этот динамический список является основным реквизитом формы (свойство формы "Основной реквизит").
Установите тип отображения списка:
Выберите динамический список на форме. В его свойствах найдите свойство "Иерархический просмотр" и установите его значение в "Ложь". Также убедитесь, что свойство "Режим просмотра" (или "Вид списка") установлено в "Список", а не "Дерево" или "Иерархический список". Это критически важно для того, чтобы отбор работал корректно, игнорируя иерархию.
Теперь, когда у нас есть специализированная форма выбора, мы можем изменить наш клиентский код для ее вызова.
&НаКлиенте
Процедура ПериодНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Создаем параметры отбора
ПараметрыОтбораДляФормы = Новый Структура();
ПараметрыОтбораДляФормы.Вставить("Периодичность", ПредопределенноеЗначение("Перечисление.Периодичность.Месяц"));
// Создаем структуру для передачи отбора в форму
ПараметрыДляОткрытия = Новый Структура();
ПараметрыДляОткрытия.Вставить("Отбор", ПараметрыОтбораДляФормы);
// Получаем и открываем нашу новую форму выбора
// Замените "ФормаВыбораМесяцев" на имя вашей новой формы, которое вы задали на Шаге 4.
ФормаВыбора = ПолучитьФорму("Справочник.Периоды.ФормаВыбораМесяцев", ПараметрыДляОткрытия);
// Устанавливаем режим отображения отбора как недоступный,
// чтобы пользователь не мог его изменить.
// Это можно сделать, если в новой форме выбора есть доступ к динамическому списку.
Если ФормаВыбора.Список.Отбор.Элементы.Количество() > 0 Тогда
Для Каждого ЭлементОтбора Из ФормаВыбора.Список.Отбор.Элементы Цикл
Если ЭлементОтбора.ЛевоеЗначение.ИмяПоля = "Периодичность" Тогда
ЭлементОтбора.РежимОтображения = РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ФормаВыбора.Открыть();
КонецПроцедуры
В этом коде мы используем метод ПолучитьФорму(), передавая ему полное имя нашей новой формы выбора (например, "Справочник.Периоды.ФормаВыбораМесяцев") и структуру с параметрами отбора. Важно, что мы передаем отбор как параметр при открытии формы, а не устанавливаем его непосредственно через элементы отбора формы после ее получения. Такой подход является более предпочтительным, так как отбор будет применен к динамическому списку формы еще до ее инициализации и отображения.
Примечание: Если вы передаете отбор через параметры открытия формы, то при наличии на форме динамического списка с соответствующими полями, система компоновки данных формы сама создаст элементы отбора на основе переданных параметров. Если вы также хотите, чтобы эти элементы отбора были недоступны для пользователя, вы можете пройтись по элементам отбора динамического списка после получения формы и установить РежимОтображения в Недоступный.
Используя отдельную форму выбора с типом отображения "Список" (то есть, без иерархического просмотра), мы обходим проблему "невидимости" подчиненных элементов. В режиме списка все элементы справочника рассматриваются как единый плоский список, независимо от их иерархической подчиненности. Когда мы накладываем отбор по Периодичность = Месяц, система компоновки данных просто выбирает все элементы, у которых это свойство равно "Месяц", и отображает их одним списком. Родительские элементы, чья периодичность не "Месяц", не будут отображены, но это не помешает отображению их дочерних элементов, так как иерархический контекст игнорируется.
Таким образом, пользователь увидит список всех доступных "Месяцев" из справочника "Периоды", будь то "Январь 2021", "Февраль 2021" или "Июнь 2022", и сможет выбрать необходимый, не отвлекаясь на родительские элементы или проблемы с их отображением.
При решении подобных задач можно рассмотреть и другие варианты, хотя они могут быть менее универсальными или более сложными в реализации для данного сценария:
Теоретически, можно было бы попытаться получить стандартную форму выбора и программно отключить для ее динамического списка свойство ИерархическийПросмотр:
ФормаВыбора = ПолучитьФорму("Справочник.Периоды.ФормаВыбора");
Если ФормаВыбора.Элементы.Содержит("Список") Тогда
ФормаВыбора.Элементы.Список.ИерархическийПросмотр = Ложь;
КонецЕсли;
// Далее установить отбор как в Шаге 2
Однако этот подход может быть менее надежным. Если для динамического списка в конфигураторе заданы группировки, он все равно может отображаться в режиме дерева, несмотря на программную установку свойства ИерархическийПросмотр = Ложь. Кроме того, изменение свойств стандартной формы в рантайме может быть непредсказуемым или приводить к нежелательным побочным эффектам.
Условное оформление динамического списка позволяет скрывать или делать недоступными для выбора элементы, которые не соответствуют заданным критериям. Можно было бы настроить условное оформление таким образом, чтобы элементы с Периодичность <> Месяц были неактивными или скрытыми.
Однако, этот метод не решает проблему с иерархией элементов, когда родительский элемент не соответствует отбору. Если родитель скрыт или недоступен, его дочерние элементы все равно не будут отображаться в иерархическом представлении. К тому же, условное оформление может быть сложнее в настройке для полного запрета выбора родительских элементов при сохранении иерархического отображения.
ВыбратьИзСписка()Если количество элементов для выбора невелико и их можно заранее сформировать, можно использовать метод ВыбратьИзСписка(). Этот метод открывает выпадающий список из переданного СписокЗначений.
СписокДляВыбора = Новый СписокЗначений;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Периоды.Ссылка,
| Периоды.Наименование
|ИЗ
| Справочник.Периоды КАК Периоды
|ГДЕ
| Периоды.Периодичность = &Месяц";
Запрос.УстановитьПараметр("Месяц", ПредопределенноеЗначение("Перечисление.Периодичность.Месяц"));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СписокДляВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);
КонецЦикла;
ВыбранноеЗначение = ВыбратьИзСписка(СписокДляВыбора, Элемент);
Если ВыбранноеЗначение <> Неопределено Тогда
Элемент.Значение = ВыбранноеЗначение;
КонецЕсли;
Этот подход позволяет полностью контролировать содержимое списка выбора, но он менее удобен, когда количество элементов велико, требуется поиск по списку или отображение дополнительных колонок.
В нашем случае, когда необходимо было отобразить все "месяцы" без учета их родительской иерархии для целей отбора, создание специализированной формы выбора с типом отображения "Список" оказалось наиболее эффективным и гибким решением.
Таким образом, мы успешно решили проблему программного открытия формы выбора иерархического справочника с отбором, гарантируя, что пользователь увидит и сможет выбрать только необходимые элементы определенного уровня, избегая сложностей, связанных с особенностями работы иерархии элементов в динамических списках 1С.