В процессе разработки конфигураций 1С часто возникает ситуация, когда для реквизита используется определяемый (составной) тип данных. Это удобно и гибко, но иногда в разных объектах или формах требуется разрешить пользователю выбор не всех типов, входящих в состав определяемого, а только некоторых из них. Рассмотрим, как решить эту задачу и запретить выбор ненужных типов данных непосредственно на форме.
Проанализируем ситуацию: изначально был создан определяемый тип, включающий, например, ссылки на справочники банковских счетов, касс, сотрудников и физических лиц. Позже выяснилось, что в одном документе нужно дать возможность выбирать только счета и кассы, а в другом — только сотрудников и физлиц. Важно, чтобы пользователь даже не видел "запрещенные" типы в списке выбора, а не получал ошибку после того, как уже сделал неверный выбор.
Существует два основных подхода к решению этой задачи: современный и простой для управляемых форм, и более универсальный, но сложный, который подходит в том числе и для обычных форм.
В управляемых формах платформа предоставляет штатный и очень удобный механизм для решения нашей задачи. У каждого элемента формы, связанного с данными составного типа, есть свойство ОграничениеТипа. Установив это свойство, мы можем "на лету" изменить набор доступных для выбора типов.
Посмотрим на пример. Допустим, у нас есть форма документа с реквизитом КонтрагентПолучатель, который имеет определяемый тип, включающий справочники "Контрагенты", "Сотрудники", "БанковскиеСчета". Мы хотим, чтобы в этой конкретной форме можно было выбрать только контрагентов и сотрудников.
Код для установки ограничения лучше всего размещать в серверном обработчике события формы ПриСозданииНаСервере.
Разберем по шагам:
ОписаниеТипов.ОграничениеТипа нашего элемента формы.Вот как это выглядит в коде:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// 1. Создаем массив разрешенных типов
МассивРазрешенныхТипов = Новый Массив;
МассивРазрешенныхТипов.Добавить(Тип("СправочникСсылка.Контрагенты"));
МассивРазрешенныхТипов.Добавить(Тип("СправочникСсылка.Сотрудники"));
// 2. Создаем объект ОписаниеТипов
НовоеОграничение = Новый ОписаниеТипов(МассивРазрешенныхТипов);
// 3. Присваиваем ограничение элементу формы
// Предполагаем, что на форме есть элемент с именем "КонтрагентПолучатель"
Элементы.КонтрагентПолучатель.ОграничениеТипа = НовоеОграничение;
КонецПроцедуры
После выполнения этого кода, когда пользователь нажмет кнопку выбора в поле "КонтрагентПолучатель", платформа автоматически покажет ему меню выбора только из двух пунктов: "Контрагенты" и "Сотрудники", хотя в метаданных реквизита типов может быть гораздо больше. Это самый правильный и простой способ для управляемых форм.
Важный момент: Если список доступных типов должен меняться динамически в зависимости от другого поля на форме (например, от вида операции), то код установки ОграничениеТипа нужно размещать в обработчике события ПриИзменении для этого другого поля, не забывая делать вызов серверной процедуры.
Этот подход основан на полной отмене стандартного поведения платформы и реализации всей логики выбора вручную. Он более трудоемкий, но дает полный контроль над процессом и работает в том числе в обычных формах, где свойство ОграничениеТипа недоступно.
Суть метода заключается в следующем:
НачалоВыбора мы отменяем стандартную обработку, устанавливая параметр СтандартнаяОбработка = Ложь.Рассмотрим подробнее код, который реализует этот алгоритм для обычной формы.
// Это обработчик события НачалоВыбора для поля ввода на форме
Процедура МоёПоле_НачалоВыбора(Элемент, СтандартнаяОбработка)
// Отменяем стандартный выбор
СтандартнаяОбработка = Ложь;
// Создаем массив с типами, которые хотим разрешить
ОграничениеТипов = Новый Массив;
ОграничениеТипов.Добавить(Тип("ДокументСсылка.ПлатежноеПоручениеИсходящее"));
ОграничениеТипов.Добавить(Тип("ДокументСсылка.РасходныйКассовыйОрдер"));
ОграничениеТипов.Добавить(Тип("ДокументСсылка.АвансовыйОтчет"));
// ... и другие разрешенные типы
// Вызываем нашу универсальную процедуру для обработки выбора
НачалоВыбораЗначенияДокументаСоставногоТипа(ЭтотОбъект, ЭтаФорма, Элемент, СтандартнаяОбработка, , , ОграничениеТипов);
КонецПроцедуры
// Универсальная процедура, которая реализует всю логику
Процедура НачалоВыбораЗначенияДокументаСоставногоТипа(ДокументОбъект, ФормаДокумента, ЭлементФормы, СтандартнаяОбработка, СтруктураОтбора, ИмяТабличнойЧасти, ОграничениеТипов = Неопределено)
СтандартнаяОбработка = Ложь;
// 1. Формируем список типов для выбора
СписокТипов = Новый СписокЗначений;
// Получаем все типы, которые есть в реквизите из метаданных
МассивТипов = ЭлементФормы.Данные.Тип.Типы(); // Упрощенный вариант для реквизита шапки
// 2. Отбираем только разрешенные
Для каждого ЭлементМассива Из МассивТипов Цикл
// Если передан массив ограничений и текущего типа в нем нет, пропускаем его
Если ОграничениеТипов <> Неопределено Тогда
Если ОграничениеТипов.Найти(ЭлементМассива) = Неопределено Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
// Добавляем разрешенный тип в список для показа пользователю
ОбъектОписанияМетаданных = Метаданные.НайтиПоТипу(ЭлементМассива);
СписокТипов.Добавить(ОбъектОписанияМетаданных.Имя, ОбъектОписанияМетаданных.Представление);
КонецЦикла;
СписокТипов.СортироватьПоПредставлению();
// 3. Показываем пользователю список типов и ждем его выбора
ВыбранныйЭлемент = ФормаДокумента.ВыбратьИзСписка(СписокТипов, ЭлементФормы);
// Если пользователь ничего не выбрал (нажал "Отмена"), выходим
Если ВыбранныйЭлемент = Неопределено Тогда
Возврат;
КонецЕсли;
ВыбранныйТип = ВыбранныйЭлемент.Значение;
// Если в поле уже было значение другого типа, очищаем его
Если ТипЗнч(ЭлементФормы.Значение) <> Тип("ДокументСсылка." + ВыбранныйТип) Тогда
ЭлементФормы.Значение = Документы[ВыбранныйТип].ПустаяСсылка();
КонецЕсли;
// 4. Открываем стандартную форму выбора для выбранного типа
ФормаВыбора = Документы[ВыбранныйТип].ПолучитьФормуВыбора(, ЭлементФормы);
Если ЗначениеЗаполнено(ЭлементФормы.Значение) Тогда
ФормаВыбора.ПараметрТекущаяСтрока = ЭлементФормы.Значение;
КонецЕсли;
ФормаВыбора.Открыть();
КонецПроцедуры
Этот код полностью имитирует и заменяет стандартное поведение. Сначала он предлагает пользователю выбрать тип документа из заранее отфильтрованного списка, а затем открывает привычную форму выбора документов уже конкретного, выбранного типа.
Важно понимать, что "Определяемый тип" — это, по сути, абстракция уровня конфигуратора. Когда вы создаете реквизит и указываете для него определяемый тип, платформа на этапе компиляции подставляет в этот реквизит все типы, входящие в состав определяемого. Во время работы программы у реквизита нет ссылки на сам "Определяемый тип", а есть свойство Тип, которое содержит объект ОписаниеТипов с коллекцией конкретных типов (СправочникСсылка.БанковскиеСчета, СправочникСсылка.Кассы и т.д.).
Именно поэтому все манипуляции мы производим с этой коллекцией реальных типов, а не пытаемся как-то изменить сам определяемый тип в режиме предприятия, что невозможно.
Итак, мы рассмотрели два способа ограничения типов в полях с составным типом данных:
ОграничениеТипа.НачалоВыбора с установкой СтандартнаяОбработка = Ложь.Возникновение такой задачи также может быть поводом задуматься об архитектуре конфигурации. Если в разных частях системы требуются совершенно разные наборы типов, возможно, исходный определяемый тип был спроектирован слишком "широким". В некоторых случаях более правильным решением будет создание нескольких более узкоспециализированных определяемых типов, что упростит разработку в дальнейшем.