Перечисления во встроенном языке 1С: код, запросы, СКД и подводные камни
Содержание
К значению перечисления в коде обращаются так: Перечисления.ВидыОплат.Наличные, в клиентском коде — ПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные"), а в тексте запроса — ЗНАЧЕНИЕ(Перечисление.ВидыОплат.Наличные). Три формы, три контекста: перепутать их — самая частая причина ошибок у тех, кто только начал работать с перечислениями 1С.
Ниже синтаксис каждой формы с примерами, способы получить значение по строке и обратно, сравнение, работа в запросах и СКД, перебор всех элементов. Всё относится к платформе 1С:Предприятие 8.3 и одинаково работает в любой конфигурации: БП, ЗУП, УТ, ERP или собственной разработке.
Что такое перечисление и как к нему обращаться в коде
Перечисление — объект конфигурации с фиксированным списком значений. Значения задаёт разработчик в Конфигураторе, пользователь в режиме Предприятия их не добавляет и не удаляет. Типичные примеры из типовых решений: виды оплат, ставки НДС, статусы заказов, пол физического лица.
У каждого значения перечисления три характеристики:
- 🔹 Имя — идентификатор для кода (
Наличные), без пробелов; - 🔹 Синоним — то, что видит пользователь («Наличные деньги»);
- 🔹 Порядок — позиция в списке, от неё зависит сортировка и индекс.
В реквизитах значение хранится как ссылка типа ПеречислениеСсылка.ВидыОплат. Это не строка и не число. Отсюда главное правило: сравнивать значение перечисления можно только с другим значением того же перечисления.
Базовая форма: менеджер перечисления
На сервере и в модулях, где доступен глобальный контекст Перечисления, обращение строится по схеме Перечисления.<ИмяПеречисления>.<ИмяЗначения>:
ДокументОбъект.ВидОплаты = Перечисления.ВидыОплат.Наличные;
Если ДокументОбъект.Статус = Перечисления.СтатусыЗаказов.Закрыт Тогда
Возврат;
КонецЕсли;
Пустое значение получают методом менеджера:
ПустойВид = Перечисления.ВидыОплат.ПустаяСсылка();
Тип для объявлений и проверок записывается как Тип("ПеречислениеСсылка.ВидыОплат"):
Если ТипЗнч(Значение) = Тип("ПеречислениеСсылка.ВидыОплат") Тогда
// работаем как с видом оплаты
КонецЕсли;
Составной тип: когда в реквизите не только перечисление
Если реквизит или параметр функции имеет составной тип (например, перечисление или справочник), прямое сравнение с перечислением безопасно: значение другого типа просто не будет равно. Но перед вызовом методов менеджера, например Индекс(), тип нужно проверить, иначе получите ошибку выполнения.
Если ТипЗнч(Параметр) = Тип("ПеречислениеСсылка.СтатусыЗаказов") Тогда
Номер = Перечисления.СтатусыЗаказов.Индекс(Параметр);
КонецЕсли;
Описание типов для реквизитов, создаваемых программно, собирают так: Новый ОписаниеТипов("ПеречислениеСсылка.ВидыОплат"). Такой реквизит формы сразу получит выпадающий список значений перечисления.
Не знаете, к какому перечислению относится значение? У ссылки есть метод Метаданные(): Значение.Метаданные().Имя вернёт имя перечисления, например ВидыОплат.
Обращение к перечислению на клиенте: ПредопределенноеЗначение
В модуле управляемой формы часть кода выполняется на клиенте (&НаКлиенте). Там принято обращаться к значениям перечисления через функцию ПредопределенноеЗначение(). Она принимает строку с полным путём и работает в любом контексте: и на клиенте, и на сервере.
&НаКлиенте
Процедура ВидОплатыПриИзменении(Элемент)
Наличные = ПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные");
Элементы.Касса.Видимость = (Объект.ВидОплаты = Наличные);
КонецПроцедуры
Обратите внимание на форму строки: «Перечисление» в единственном числе, как в запросе, а не «Перечисления», как у менеджера. Пустая ссылка тоже доступна:
Пустой = ПредопределенноеЗначение("Перечисление.ВидыОплат.ПустаяСсылка");
Сводка по формам обращения:
| Где пишем | Форма | Пример |
|---|---|---|
| Серверный код, общие модули | Менеджер | Перечисления.ВидыОплат.Наличные |
| Клиентский код формы | Функция | ПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные") |
| Текст запроса | ЗНАЧЕНИЕ | ЗНАЧЕНИЕ(Перечисление.ВидыОплат.Наличные) |
| Выражения СКД | Значение | Значение(Перечисление.ВидыОплат.Наличные) |
| Пустое значение в коде | Метод менеджера | Перечисления.ВидыОплат.ПустаяСсылка() |
Если имя значения записано в строке с опечаткой, ПредопределенноеЗначение() вызовет ошибку только во время выполнения. Синтаксический контроль модуля её не поймает — проверяйте такие строки вручную.
Как получить перечисление по строке и строку по перечислению
Задача возникает при обмене данными, загрузке из Excel, чтении настроек из JSON. На входе строка "Наличные", нужна ссылка. Способов несколько, выбирают по тому, что лежит в строке: имя значения или его синоним.
Строка содержит имя значения
Менеджер перечисления поддерживает обращение по индексу-строке:
ИмяЗначения = "Наличные";
ВидОплаты = Перечисления.ВидыОплат[ИмяЗначения];
Так же можно получить и сам менеджер, если имя перечисления тоже приходит строкой:
Значение = Перечисления["ВидыОплат"]["Наличные"];
Если такого имени нет, платформа вызовет исключение. Чтобы не ловить его через Попытка, сначала проверьте метаданные:
Функция ЗначениеПеречисленияПоИмени(ИмяПеречисления, ИмяЗначения)
МетаПеречисление = Метаданные.Перечисления.Найти(ИмяПеречисления);
Если МетаПеречисление = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
Если МетаПеречисление.ЗначенияПеречисления.Найти(ИмяЗначения) = Неопределено Тогда
Возврат Перечисления[ИмяПеречисления].ПустаяСсылка();
КонецЕсли;
Возврат Перечисления[ИмяПеречисления][ИмяЗначения];
КонецФункции
Второй вариант — XML-сериализация. Она короче и удобна для обменов:
ВидОплаты = XMLЗначение(Тип("ПеречислениеСсылка.ВидыОплат"), "Наличные");
Обратная задача: имя значения из ссылки
Строка(Значение) вернёт синоним — то, что видит пользователь. Для кода и обменов нужен именно идентификатор. Его даёт XMLСтрока():
ИмяЗначения = XMLСтрока(Перечисления.ВидыОплат.Наличные); // "Наличные"
Для пустой ссылки XMLСтрока() вернёт пустую строку. Тот же результат через метаданные, если нужен ещё и синоним или комментарий:
Индекс = Перечисления.ВидыОплат.Индекс(ВидОплаты);
МетаЗначение = Метаданные.Перечисления.ВидыОплат.ЗначенияПеречисления[Индекс];
Сообщить(МетаЗначение.Имя + " — " + МетаЗначение.Синоним);
Строка содержит синоним
Если из внешней системы пришло «Наличные деньги», прямого метода нет. Переберите значения и сравните представление:
Функция ЗначениеПоСинониму(Синоним)
Для Каждого Значение Из Перечисления.ВидыОплат Цикл
Если НРег(Строка(Значение)) = НРег(СокрЛП(Синоним)) Тогда
Возврат Значение;
КонецЕсли;
КонецЦикла;
Возврат Перечисления.ВидыОплат.ПустаяСсылка();
КонецФункции
Синоним — ненадёжный ключ: его переименуют при очередном обновлении конфигурации, и сопоставление молча сломается. В обменах передавайте имя значения.
Сравнение значений перечисления в условиях
Сравнение выполняют операторами = и <> со значением того же перечисления. Варианты, которые встречаются в чужом коде и не работают:
- ❌
Если ВидОплаты = "Наличные"— ссылка никогда не равна строке, условие всегда ложно; - ❌
Если Строка(ВидОплаты) = "Наличные деньги"— работает, пока не поменяли синоним, и зависит от языка интерфейса; - ✅
Если ВидОплаты = Перечисления.ВидыОплат.Наличные.
Проверку на заполненность делают функцией ЗначениеЗаполнено(). Для пустой ссылки она возвращает Ложь:
Если Не ЗначениеЗаполнено(Объект.ВидОплаты) Тогда
ОбщегоНазначения.СообщитьПользователю("Укажите вид оплаты");
КонецЕсли;
Процедура СообщитьПользователю есть в конфигурациях на Библиотеке стандартных подсистем. В собственной конфигурации без БСП используйте объект СообщениеПользователю.
Несколько значений сразу
Когда вариантов три и больше, цепочку Или удобнее заменить массивом или фиксированным списком:
Безналичные = Новый Массив;
Безналичные.Добавить(Перечисления.ВидыОплат.Карта);
Безналичные.Добавить(Перечисления.ВидыОплат.Перевод);
Если Безналичные.Найти(ВидОплаты) <> Неопределено Тогда
// безналичная оплата
КонецЕсли;
Сравнение «больше-меньше»
Операторы < и > к ссылкам перечисления в коде не применяют. Если нужна логика «статус не ниже Согласован», сравнивайте индексы:
Менеджер = Перечисления.СтатусыЗаказов;
Если Менеджер.Индекс(Заказ.Статус) >= Менеджер.Индекс(Менеджер.Согласован) Тогда
// заказ уже согласован или продвинулся дальше
КонецЕсли;
Такой код опирается на порядок значений в Конфигураторе. Переставят значения — поменяется логика. Оставьте комментарий рядом, чтобы следующий разработчик это видел.
Перечисления в запросах и СКД
В тексте запроса менеджеров нет. Константное значение задают конструкцией ЗНАЧЕНИЕ():
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Заказ.Ссылка,
| Заказ.Сумма
|ИЗ
| Документ.ЗаказПокупателя КАК Заказ
|ГДЕ
| Заказ.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт)";
Если значение выбирается во время выполнения, передайте его параметром. Это гибче, и текст запроса не меняется:
Запрос.Текст = "... ГДЕ Заказ.Статус В (&Статусы)";
Запрос.УстановитьПараметр("Статусы", МассивСтатусов);
Приёмы, которые пригодятся в запросах
- 🔸 Пустое значение:
ЗНАЧЕНИЕ(Перечисление.ВидыОплат.ПустаяСсылка). - 🔸 NULL после левого соединения:
ЕСТЬNULL(Оплата.ВидОплаты, ЗНАЧЕНИЕ(Перечисление.ВидыОплат.ПустаяСсылка))— иначе поле с NULL не совпадёт ни с одним условием. - 🔸 Текст для отчёта:
ПРЕДСТАВЛЕНИЕ(Заказ.Статус)вернёт синоним. - 🔸 Ветвление:
ВЫБОР КОГДА Заказ.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ. - 🔸 Системные перечисления: так же —
ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход).
К самой таблице перечисления тоже можно обратиться. У неё есть поля Ссылка и Порядок:
"ВЫБРАТЬ
| ВидыОплат.Ссылка,
| ВидыОплат.Порядок
|ИЗ
| Перечисление.ВидыОплат КАК ВидыОплат
|УПОРЯДОЧИТЬ ПО
| ВидыОплат.Порядок"
Это полезно, когда в отчёте нужны все значения, даже те, по которым нет данных: таблицу перечисления соединяют левым соединением с документами.
Перечисления в схеме компоновки данных
В СКД есть три места, где встречается перечисление:
- Текст запроса набора данных — те же
ЗНАЧЕНИЕ()и параметры, что в обычном запросе. - Параметры схемы — на закладке «Параметры» задайте параметру тип
ПеречислениеСсылка.<Имя>. Для выбора нескольких значений включите у параметра флаг «Доступен список значений». - Вычисляемые поля и ресурсы — язык выражений СКД понимает
Значение(Перечисление.СтатусыЗаказов.Закрыт).
В отборе варианта отчёта пользователь выбирает значение перечисления из выпадающего списка — дополнительной настройки не нужно, если поле имеет тип перечисления.
Перебор всех элементов перечисления
Менеджер перечисления поддерживает обход циклом Для Каждого. Значения выдаются в том порядке, в котором они стоят в Конфигураторе:
Для Каждого Значение Из Перечисления.ВидыОплат Цикл
Сообщить(XMLСтрока(Значение) + ": " + Строка(Значение));
КонецЦикла;
Пустая ссылка в обход не попадает.
Через метаданные удобно перебирать, когда нужны имя, синоним и комментарий одновременно, или когда имя перечисления известно только строкой:
МетаПеречисление = Метаданные.Перечисления.Найти(ИмяПеречисления);
Для Каждого МетаЗначение Из МетаПеречисление.ЗначенияПеречисления Цикл
Ссылка = Перечисления[ИмяПеречисления][МетаЗначение.Имя];
Сообщить(МетаЗначение.Имя + " | " + МетаЗначение.Синоним);
КонецЦикла;
Частая практическая задача — заполнить список выбора поля на форме не всеми значениями, а частью:
&НаСервере
Процедура ЗаполнитьСписокВидовОплат()
Список = Элементы.ВидОплаты.СписокВыбора;
Список.Очистить();
Для Каждого Значение Из Перечисления.ВидыОплат Цикл
Если Значение <> Перечисления.ВидыОплат.Бартер Тогда
Список.Добавить(Значение);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Сравнение способов перебора:
| Способ | Что даёт | Когда брать |
|---|---|---|
Для Каждого по менеджеру | Ссылки по порядку | Имя перечисления известно заранее |
Метаданные...ЗначенияПеречисления | Имя, синоним, комментарий | Универсальный код, имя перечисления строкой |
| Запрос к таблице перечисления | Ссылка, порядок, соединения | Отчёты, где нужны все значения |
Получить(Индекс) в цикле | Ссылка по номеру | Редко, когда работаете с индексами |
Ошибки при обращении к перечислениям и как их не допускать
Ещё несколько правил, которые экономят время на отладке:
- ✅ Имена значений пишите без сокращений и транслита:
ЗаказЗакрыт, а неZakZakr. Опечатку в строкеПредопределенноеЗначение()найти сложнее, чем в прямом обращении. - ✅ Не добавляйте новое значение в середину списка, если где-то используется
Индекс(). Добавляйте в конец. - ✅ Не храните в реквизитах строку с именем значения, если можно хранить саму ссылку на перечисление. Ссылка переживает переименование синонима.
- ✅ Для доработки типовой конфигурации добавляйте значения в расширение, а не правьте основную конфигурацию — так проще проходят обновления.