Как ограничить выбор типов в реквизите с определяемым типом данных?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

В процессе разработки конфигураций 1С часто возникает ситуация, когда для реквизита используется определяемый (составной) тип данных. Это удобно и гибко, но иногда в разных объектах или формах требуется разрешить пользователю выбор не всех типов, входящих в состав определяемого, а только некоторых из них. Рассмотрим, как решить эту задачу и запретить выбор ненужных типов данных непосредственно на форме.

Проанализируем ситуацию: изначально был создан определяемый тип, включающий, например, ссылки на справочники банковских счетов, касс, сотрудников и физических лиц. Позже выяснилось, что в одном документе нужно дать возможность выбирать только счета и кассы, а в другом — только сотрудников и физлиц. Важно, чтобы пользователь даже не видел "запрещенные" типы в списке выбора, а не получал ошибку после того, как уже сделал неверный выбор.

Существует два основных подхода к решению этой задачи: современный и простой для управляемых форм, и более универсальный, но сложный, который подходит в том числе и для обычных форм.

Решение 1: Стандартный подход для управляемых форм через свойство `ОграничениеТипа`

В управляемых формах платформа предоставляет штатный и очень удобный механизм для решения нашей задачи. У каждого элемента формы, связанного с данными составного типа, есть свойство ОграничениеТипа. Установив это свойство, мы можем "на лету" изменить набор доступных для выбора типов.

Посмотрим на пример. Допустим, у нас есть форма документа с реквизитом КонтрагентПолучатель, который имеет определяемый тип, включающий справочники "Контрагенты", "Сотрудники", "БанковскиеСчета". Мы хотим, чтобы в этой конкретной форме можно было выбрать только контрагентов и сотрудников.

Код для установки ограничения лучше всего размещать в серверном обработчике события формы ПриСозданииНаСервере.

Разберем по шагам:

  1. Создадим массив, в который поместим только те типы, которые хотим оставить доступными.
  2. На основе этого массива создадим специальный объект ОписаниеТипов.
  3. Присвоим этот объект свойству ОграничениеТипа нашего элемента формы.

Вот как это выглядит в коде:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

	// 1. Создаем массив разрешенных типов
	МассивРазрешенныхТипов = Новый Массив;
	МассивРазрешенныхТипов.Добавить(Тип("СправочникСсылка.Контрагенты"));
	МассивРазрешенныхТипов.Добавить(Тип("СправочникСсылка.Сотрудники"));
	
	// 2. Создаем объект ОписаниеТипов
	НовоеОграничение = Новый ОписаниеТипов(МассивРазрешенныхТипов);
	
	// 3. Присваиваем ограничение элементу формы
	// Предполагаем, что на форме есть элемент с именем "КонтрагентПолучатель"
	Элементы.КонтрагентПолучатель.ОграничениеТипа = НовоеОграничение;
	
КонецПроцедуры

После выполнения этого кода, когда пользователь нажмет кнопку выбора в поле "КонтрагентПолучатель", платформа автоматически покажет ему меню выбора только из двух пунктов: "Контрагенты" и "Сотрудники", хотя в метаданных реквизита типов может быть гораздо больше. Это самый правильный и простой способ для управляемых форм.

Важный момент: Если список доступных типов должен меняться динамически в зависимости от другого поля на форме (например, от вида операции), то код установки ОграничениеТипа нужно размещать в обработчике события ПриИзменении для этого другого поля, не забывая делать вызов серверной процедуры.

Решение 2: Полная замена механизма выбора (для обычных форм и сложных сценариев)

Этот подход основан на полной отмене стандартного поведения платформы и реализации всей логики выбора вручную. Он более трудоемкий, но дает полный контроль над процессом и работает в том числе в обычных формах, где свойство ОграничениеТипа недоступно.

Суть метода заключается в следующем:

  1. В событии элемента формы НачалоВыбора мы отменяем стандартную обработку, устанавливая параметр СтандартнаяОбработка = Ложь.
  2. Программно формируем список значений из тех типов, которые разрешены в данном контексте.
  3. Показываем этот список пользователю для выбора типа.
  4. После того как пользователь выбрал нужный тип (например, "Документ РасходныйКассовыйОрдер"), мы программно открываем стандартную форму выбора для этого конкретного типа документов.

Рассмотрим подробнее код, который реализует этот алгоритм для обычной формы.


// Это обработчик события НачалоВыбора для поля ввода на форме
Процедура МоёПоле_НачалоВыбора(Элемент, СтандартнаяОбработка)

	// Отменяем стандартный выбор
	СтандартнаяОбработка = Ложь;
	
	// Создаем массив с типами, которые хотим разрешить
	ОграничениеТипов = Новый Массив;
	ОграничениеТипов.Добавить(Тип("ДокументСсылка.ПлатежноеПоручениеИсходящее"));
	ОграничениеТипов.Добавить(Тип("ДокументСсылка.РасходныйКассовыйОрдер"));
	ОграничениеТипов.Добавить(Тип("ДокументСсылка.АвансовыйОтчет"));
	// ... и другие разрешенные типы
	
	// Вызываем нашу универсальную процедуру для обработки выбора
	НачалоВыбораЗначенияДокументаСоставногоТипа(ЭтотОбъект, ЭтаФорма, Элемент, СтандартнаяОбработка, , , ОграничениеТипов);

КонецПроцедуры

// Универсальная процедура, которая реализует всю логику
Процедура НачалоВыбораЗначенияДокументаСоставногоТипа(ДокументОбъект, ФормаДокумента, ЭлементФормы, СтандартнаяОбработка, СтруктураОтбора, ИмяТабличнойЧасти, ОграничениеТипов = Неопределено)

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

КонецПроцедуры

Этот код полностью имитирует и заменяет стандартное поведение. Сначала он предлагает пользователю выбрать тип документа из заранее отфильтрованного списка, а затем открывает привычную форму выбора документов уже конкретного, выбранного типа.

Почему это работает? Немного теории

Важно понимать, что "Определяемый тип" — это, по сути, абстракция уровня конфигуратора. Когда вы создаете реквизит и указываете для него определяемый тип, платформа на этапе компиляции подставляет в этот реквизит все типы, входящие в состав определяемого. Во время работы программы у реквизита нет ссылки на сам "Определяемый тип", а есть свойство Тип, которое содержит объект ОписаниеТипов с коллекцией конкретных типов (СправочникСсылка.БанковскиеСчета, СправочникСсылка.Кассы и т.д.).

Именно поэтому все манипуляции мы производим с этой коллекцией реальных типов, а не пытаемся как-то изменить сам определяемый тип в режиме предприятия, что невозможно.

Выводы и рекомендации

Итак, мы рассмотрели два способа ограничения типов в полях с составным типом данных:

  1. Для управляемых форм предпочтительным, стандартным и самым простым методом является использование свойства элемента формы ОграничениеТипа.
  2. Для обычных форм или для реализации очень сложной логики выбора в управляемых формах используется метод полной замены стандартного обработчика через событие НачалоВыбора с установкой СтандартнаяОбработка = Ложь.

Возникновение такой задачи также может быть поводом задуматься об архитектуре конфигурации. Если в разных частях системы требуются совершенно разные наборы типов, возможно, исходный определяемый тип был спроектирован слишком "широким". В некоторых случаях более правильным решением будет создание нескольких более узкоспециализированных определяемых типов, что упростит разработку в дальнейшем.

← На главную