Как обратиться к перечислению в клиентском коде 1С

· 9 мин

Содержание

Значение перечисления на клиенте получают обращением по имени: Перечисления.ВидыКонтрагентов.Покупатель. Это работает в управляемой форме без вызова сервера, пока вы берёте конкретное предопределённое значение. Запрос к перечислению, выборку всех значений через Выбрать() и работу с данными базы клиент не выполнит: для этого нужен серверный вызов.

Главное правило простое. Клиент умеет держать, сравнивать и показывать значения перечисления. Всё, что требует чтения базы, он делегирует серверу. Ниже разберём, где проходит эта граница, как передавать перечисления между формами и как выводить их в табличный документ.

Что клиент умеет делать с перечислением, а что нет

В управляемом приложении код форм делится на клиентский и серверный контексты. Менеджер перечислений в клиентском контексте доступен в урезанном виде. Значения вы получаете, остальное остаётся на сервере.

ДействиеНа клиенте
Получить предопределённое значение по имениДа
Сравнить две ссылки на значенияДа
Получить пустое значение ПустаяСсылка()Да
Записать значение в реквизит формыДа
Показать значение как строкуДа
Выполнить запрос с перечислением в текстеНет, только на сервере
Прочитать данные из базы по ссылке на элемент справочникаНет, нужен серверный вызов

Практический вывод: если нужно просто сравнить выбранное пользователем значение с конкретным, серверный вызов не нужен. Он тратит время и нагружает канал, особенно в веб-клиенте и при работе через тонкий клиент по медленной сети.

Ссылки на перечисления лёгкие: на клиент приходит идентификатор, а не набор данных. Передавать их туда-сюда дёшево.

Как получить значение перечисления по имени

Базовый пример. В форме есть реквизит ВидКонтрагента типа ПеречислениеСсылка.ВидыКонтрагентов. Нужно показать поле «ИНН» только для покупателя:

&НаКлиенте
Процедура ВидКонтрагентаПриИзменении(Элемент)
	Элементы.ИНН.Видимость = (ВидКонтрагента = ПредопределенноеЗначение("Перечисление.ВидыКонтрагентов.Покупатель"));
КонецПроцедуры

Здесь два способа получить значение, и они не равны по надёжности:

  • 🔹 Перечисления.ВидыКонтрагентов.Покупатель — прямое обращение, быстрое, подсвечивается синтаксис-помощником;
  • 🔹 ПредопределенноеЗначение("Перечисление.ВидыКонтрагентов.Покупатель") — по строке, удобно, когда имя собирается динамически или нужно единообразие с другими типами.

Строковая запись не проверяется при компиляции. Опечатка в имени вылезет только при выполнении. Прямую запись проверит конфигуратор.

Пустое значение и проверка на заполненность

Перечисление в реквизите не заполнено, пока пользователь ничего не выбрал. Проверяйте так:

Если Не ЗначениеЗаполнено(ВидКонтрагента) Тогда
	Сообщить("Укажите вид контрагента");
КонецЕсли;

Либо сравнением с пустой ссылкой: ВидКонтрагента = Перечисления.ВидыКонтрагентов.ПустаяСсылка(). Оба варианта работают на клиенте.

Как передать перечисление в параметры формы

Когда одна форма открывает другую и нужно передать значение перечисления, кладите его в структуру параметров. Тип значения сохраняется, это ссылка:

&НаКлиенте
Процедура ОткрытьСписок(Команда)
	Параметры = Новый Структура("ВидКонтрагента", Перечисления.ВидыКонтрагентов.Поставщик);
	ОткрытьФорму("Справочник.Контрагенты.ФормаСписка", Параметры);
КонецПроцедуры

В принимающей форме параметр читается в ПриСозданииНаСервере:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	Если Параметры.Свойство("ВидКонтрагента") Тогда
		ВидКонтрагента = Параметры.ВидКонтрагента;
	КонецЕсли;
КонецПроцедуры

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

Параметры = Новый Структура("Отбор", Новый Структура("ВидКонтрагента", Перечисления.ВидыКонтрагентов.Поставщик));
ОткрытьФорму("Справочник.Контрагенты.ФормаСписка", Параметры);

Если значение перечисления положить в параметр формы как строку ("Поставщик"), отбор не сработает. Тип должен быть ссылкой на перечисление, а не текстом.

Ветвление по значениям перечисления

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

&НаКлиенте
Процедура ПересчитатьВидимость()
	Выбор
		Когда ВидКонтрагента = Перечисления.ВидыКонтрагентов.Покупатель Тогда
			Элементы.ГруппаПокупатель.Видимость = Истина;
			Элементы.ГруппаПоставщик.Видимость = Ложь;
		Когда ВидКонтрагента = Перечисления.ВидыКонтрагентов.Поставщик Тогда
			Элементы.ГруппаПокупатель.Видимость = Ложь;
			Элементы.ГруппаПоставщик.Видимость = Истина;
	КонецВыбора;
КонецПроцедуры

Конструкция Выбор ... Когда в 1С не поддерживает сравнение с набором значений в одной строке, как switch в других языках. Каждое условие записывается полностью.

Если значений много и логика одинаковая, заведите соответствие:

Соответствие = Новый Соответствие;
Соответствие.Вставить(Перечисления.ВидыКонтрагентов.Покупатель, "Продажи");
Соответствие.Вставить(Перечисления.ВидыКонтрагентов.Поставщик, "Закупки");
Раздел = Соответствие.Получить(ВидКонтрагента);

Ссылки на перечисления можно использовать как ключи соответствия, это штатный приём.

Как получить список всех значений перечисления

Перебор всех значений — основная причина, по которой разработчики упираются в ограничения клиента. Запрос на клиенте не выполнить. Есть два пути.

Путь 1. Серверная функция с возвратом массива или списка значений.

&НаСервереБезКонтекста
Функция ЗначенияПеречисления()
	Результат = Новый СписокЗначений;
	Для Каждого Значение Из Метаданные.Перечисления.ВидыКонтрагентов.ЗначенияПеречисления Цикл
		Результат.Добавить(Перечисления.ВидыКонтрагентов[Значение.Имя], Значение.Синоним);
	КонецЦикла;
	Возврат Результат;
КонецФункции

&НаКлиенте
Процедура ЗаполнитьСписок(Команда)
	Список = ЗначенияПеречисления();
	Элементы.ВидКонтрагента.СписокВыбора.Очистить();
	Элементы.ВидКонтрагента.СписокВыбора.ЗагрузитьЗначения(Список.ВыгрузитьЗначения());
КонецПроцедуры

Директива &НаСервереБезКонтекста не передаёт на сервер весь контекст формы. Вызов быстрее, чем у обычной &НаСервере. Для функций, которым не нужны реквизиты формы, используйте именно её.

Путь 2. Индексное обращение. Значения перечисления можно получить по порядку в метаданных: Перечисления.ВидыКонтрагентов.Получить(0). Метод удобен, когда порядок значений известен и зафиксирован. Если разработчик добавит значение в середину, индексы поплывут, поэтому для бизнес-логики такой способ ненадёжен.

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

Настройка свойств формы и поля, чтобы значения подтягивались сами

Часто код не нужен вовсе. Поле формы с реквизитом типа ПеречислениеСсылка.ИмяПеречисления само предлагает все значения при выборе. Что настраивается в свойствах поля:

  1. Откройте форму в конфигураторе, выделите поле ввода.
  2. Вид поля: Поле ввода — пользователь выбирает из выпадающего списка; Переключатель — значения отображаются кнопками; Поле выбора — тот же набор, но без ввода текста вручную.
  3. Свойство «Режим выбора из списка» ограничивает ввод только значениями списка.
  4. Свойство «Ограничить тип» не даёт вставить значение чужого перечисления.
  5. В «Параметрах выбора» задайте отбор, если нужны не все значения.

Для реквизита формы тип задаётся в палитре свойств: «Тип» → «ПеречислениеСсылка» → нужное перечисление.

Вывод в таблицу формы тоже работает без кода: колонка с таким реквизитом отображает синоним значения, а в режиме редактирования даёт выпадающий список.

Ограниченный список выбора: только нужные значения

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

&НаКлиенте
Процедура ПриОткрытии()
	СписокВыбора = Элементы.ВидКонтрагента.СписокВыбора;
	СписокВыбора.Очистить();
	СписокВыбора.Добавить(Перечисления.ВидыКонтрагентов.Покупатель, "Покупатель");
	СписокВыбора.Добавить(Перечисления.ВидыКонтрагентов.Поставщик, "Поставщик");
КонецПроцедуры

Второй параметр метода Добавить задаёт представление. Если опустить его, 1С покажет синоним значения.

Что важно учесть:

  • 🔹 пользователь всё равно может ввести другое значение вручную, если не включён режим выбора только из списка;
  • 🔹 при смене списка на лету сбросьте текущее значение реквизита, если оно выпало из набора;
  • 🔹 для постоянного ограничения надёжнее задать параметры выбора в свойствах поля, чем писать код.

Перечисление в динамическом списке и отчёте

В динамическом списке колонка с перечислением отбирается стандартными средствами: через пользовательские настройки списка пользователь выбирает «Равно» и нужное значение из выпадающего списка. Код при этом не нужен.

Чтобы задать отбор программно, используйте элемент отбора списка. Значение передаётся ссылкой, как и в параметрах формы:

&НаКлиенте
Процедура УстановитьОтбор(Значение)
	Список.Отбор.Элементы.Очистить();
	Элемент = Список.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидКонтрагента");
	Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
	Элемент.ПравоеЗначение = Значение;
	Элемент.Использование = Истина;
КонецПроцедуры

Если параметров отбора много, удобнее передавать их одной структурой в серверную процедуру и применять к списку там.

Какой способ выбрать

Небольшая памятка, как выбрать способ получения значения в конкретной ситуации:

Что вам нужно сделать с перечислением на клиенте?

Перечисления в табличном документе и отчётах

Табличный документ можно сформировать целиком на клиенте. Значение перечисления превращается в строку функцией Строка() и показывает синоним, а не внутреннее имя:

&НаКлиенте
Процедура ВывестиВДокумент(Команда)
	Документ = Новый ТабличныйДокумент;
	Область = Документ.Область(1, 1);
	Область.Текст = "Вид контрагента: " + Строка(ВидКонтрагента);
	Документ.ПоказатьПользователю = Истина;
	Документ.Показать("Результат");
КонецПроцедуры

Если нужно вывести список значений, сначала получите его серверной функцией из предыдущего раздела, затем выведите в цикле:

Строка = 1;
Для Каждого Элемент Из Список Цикл
	Документ.Область(Строка, 1).Текст = Элемент.Представление;
	Строка = Строка + 1;
КонецЦикла;

Для больших отчётов (сотни строк и сложное оформление) лучше формировать документ целиком на сервере и возвращать готовый результат через реквизит формы типа ТабличныйДокумент. Клиентский цикл по строкам при тысячах значений будет заметно тормозить.

Константы и перечисления

Константа типа «Перечисление» хранит значение в базе. Прочитать её на клиенте напрямую нельзя: менеджер констант клиенту не доступен. Схема всегда такая:

&НаСервереБезКонтекста
Функция ВидУчетаПоУмолчанию()
	Возврат Константы.ВидУчетаПоУмолчанию.Получить();
КонецФункции

Клиент вызывает функцию и получает ссылку на значение перечисления. Дальше она сравнивается с Перечисления... как обычно.

Если константа нужна часто, прочитайте её один раз в ПриСозданииНаСервере и сохраните в реквизите формы. Лишние обращения к серверу уберёте.

Ошибки и как их разобрать

Диагностика: выберите симптом

Две вещи ловятся особенно часто:

  • 🔸 Смешение типов. Сравнение ссылки на перечисление и строки не выдаёт ошибки, но возвращает Ложь. Баг проявляется молча.
  • 🔸 Работа с массивом значений как с перечислением. Массив значений, полученный с сервера, нужно сначала сохранить в реквизит формы или передать в функцию клиента. Через клиент между разными вызовами он не живёт.

Не вызывайте серверную функцию в цикле по каждому значению. Получите все данные одним вызовом и обрабатывайте на клиенте. Десять вызовов подряд по каналу с задержкой 100 мс дают секунду ожидания.

Отладка помогает, когда непонятно, что именно приходит. Поставьте точку останова в клиентской процедуре и посмотрите значение переменной во вкладке «Вычислить выражение»: у ссылки на перечисление там видны тип и представление.

Частые вопросы

Можно ли на клиенте выполнить запрос к перечислению?
Нет. Запросы выполняются только на сервере. Вынесите запрос в серверную функцию и верните результат на клиент массивом или списком значений.
Чем `Перечисления.Имя.Значение` отличается от `ПредопределенноеЗначение()`?
Результат одинаковый. Прямая запись проверяется при компиляции, строковая только во время выполнения. Строковая удобна при динамическом формировании имени.
Как получить синоним значения перечисления?
Функцией Строка(Значение). Она вернёт синоним, заданный в конфигураторе. Внутреннее имя доступно через метаданные на сервере.
Можно ли передать перечисление в параметры открываемой формы?
Да, ссылка на значение кладётся в структуру параметров и сохраняет тип. Для отбора списка используйте ключ Отбор.
Нужно ли отдельное обращение к серверу, чтобы сравнить значение с конкретным элементом?
Нет. Сравнение ссылок выполняется на клиенте без серверного вызова.