Перечисления во встроенном языке 1С: код, запросы, СКД и подводные камни

· 9 мин

Содержание

К значению перечисления в коде обращаются так: Перечисления.ВидыОплат.Наличные, в клиентском коде — ПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные"), а в тексте запроса — ЗНАЧЕНИЕ(Перечисление.ВидыОплат.Наличные). Три формы, три контекста: перепутать их — самая частая причина ошибок у тех, кто только начал работать с перечислениями 1С.

Ниже синтаксис каждой формы с примерами, способы получить значение по строке и обратно, сравнение, работа в запросах и СКД, перебор всех элементов. Всё относится к платформе 1С:Предприятие 8.3 и одинаково работает в любой конфигурации: БП, ЗУП, УТ, ERP или собственной разработке.

Что такое перечисление и как к нему обращаться в коде

Перечисление — объект конфигурации с фиксированным списком значений. Значения задаёт разработчик в Конфигураторе, пользователь в режиме Предприятия их не добавляет и не удаляет. Типичные примеры из типовых решений: виды оплат, ставки НДС, статусы заказов, пол физического лица.

У каждого значения перечисления три характеристики:

  • 🔹 Имя — идентификатор для кода (Наличные), без пробелов;
  • 🔹 Синоним — то, что видит пользователь («Наличные деньги»);
  • 🔹 Порядок — позиция в списке, от неё зависит сортировка и индекс.

В реквизитах значение хранится как ссылка типа ПеречислениеСсылка.ВидыОплат. Это не строка и не число. Отсюда главное правило: сравнивать значение перечисления можно только с другим значением того же перечисления.

Базовая форма: менеджер перечисления

На сервере и в модулях, где доступен глобальный контекст Перечисления, обращение строится по схеме Перечисления.<ИмяПеречисления>.<ИмяЗначения>:

ДокументОбъект.ВидОплаты = Перечисления.ВидыОплат.Наличные;

Если ДокументОбъект.Статус = Перечисления.СтатусыЗаказов.Закрыт Тогда
    Возврат;
КонецЕсли;

Пустое значение получают методом менеджера:

ПустойВид = Перечисления.ВидыОплат.ПустаяСсылка();

Тип для объявлений и проверок записывается как Тип("ПеречислениеСсылка.ВидыОплат"):

Если ТипЗнч(Значение) = Тип("ПеречислениеСсылка.ВидыОплат") Тогда
    // работаем как с видом оплаты
КонецЕсли;

Составной тип: когда в реквизите не только перечисление

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

Если ТипЗнч(Параметр) = Тип("ПеречислениеСсылка.СтатусыЗаказов") Тогда
    Номер = Перечисления.СтатусыЗаказов.Индекс(Параметр);
КонецЕсли;

Описание типов для реквизитов, создаваемых программно, собирают так: Новый ОписаниеТипов("ПеречислениеСсылка.ВидыОплат"). Такой реквизит формы сразу получит выпадающий список значений перечисления.

Не знаете, к какому перечислению относится значение? У ссылки есть метод Метаданные(): Значение.Метаданные().Имя вернёт имя перечисления, например ВидыОплат.

Обращение к перечислению на клиенте: ПредопределенноеЗначение

В модуле управляемой формы часть кода выполняется на клиенте (&НаКлиенте). Там принято обращаться к значениям перечисления через функцию ПредопределенноеЗначение(). Она принимает строку с полным путём и работает в любом контексте: и на клиенте, и на сервере.

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

Обратите внимание на форму строки: «Перечисление» в единственном числе, как в запросе, а не «Перечисления», как у менеджера. Пустая ссылка тоже доступна:

Пустой = ПредопределенноеЗначение("Перечисление.ВидыОплат.ПустаяСсылка");

Сводка по формам обращения:

Где пишемФормаПример
Серверный код, общие модулиМенеджерПеречисления.ВидыОплат.Наличные
Клиентский код формыФункцияПредопределенноеЗначение("Перечисление.ВидыОплат.Наличные")
Текст запросаЗНАЧЕНИЕЗНАЧЕНИЕ(Перечисление.ВидыОплат.Наличные)
Выражения СКДЗначениеЗначение(Перечисление.ВидыОплат.Наличные)
Пустое значение в кодеМетод менеджераПеречисления.ВидыОплат.ПустаяСсылка()

Если имя значения записано в строке с опечаткой, ПредопределенноеЗначение() вызовет ошибку только во время выполнения. Синтаксический контроль модуля её не поймает — проверяйте такие строки вручную.

Как получить перечисление по строке и строку по перечислению

Задача возникает при обмене данными, загрузке из Excel, чтении настроек из JSON. На входе строка "Наличные", нужна ссылка. Способов несколько, выбирают по тому, что лежит в строке: имя значения или его синоним.

Строка содержит имя значения

Менеджер перечисления поддерживает обращение по индексу-строке:

ИмяЗначения = "Наличные";
ВидОплаты = Перечисления.ВидыОплат[ИмяЗначения];

Так же можно получить и сам менеджер, если имя перечисления тоже приходит строкой:

Значение = Перечисления["ВидыОплат"]["Наличные"];

Если такого имени нет, платформа вызовет исключение. Чтобы не ловить его через Попытка, сначала проверьте метаданные:

Функция ЗначениеПеречисленияПоИмени(ИмяПеречисления, ИмяЗначения)
    
    МетаПеречисление = Метаданные.Перечисления.Найти(ИмяПеречисления);
    Если МетаПеречисление = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    Если МетаПеречисление.ЗначенияПеречисления.Найти(ИмяЗначения) = Неопределено Тогда
        Возврат Перечисления[ИмяПеречисления].ПустаяСсылка();
    КонецЕсли;
    
    Возврат Перечисления[ИмяПеречисления][ИмяЗначения];
    
КонецФункции

Второй вариант — XML-сериализация. Она короче и удобна для обменов:

ВидОплаты = XMLЗначение(Тип("ПеречислениеСсылка.ВидыОплат"), "Наличные");

Обратная задача: имя значения из ссылки

Строка(Значение) вернёт синоним — то, что видит пользователь. Для кода и обменов нужен именно идентификатор. Его даёт XMLСтрока():

ИмяЗначения = XMLСтрока(Перечисления.ВидыОплат.Наличные); // "Наличные"

Для пустой ссылки XMLСтрока() вернёт пустую строку. Тот же результат через метаданные, если нужен ещё и синоним или комментарий:

Индекс = Перечисления.ВидыОплат.Индекс(ВидОплаты);
МетаЗначение = Метаданные.Перечисления.ВидыОплат.ЗначенияПеречисления[Индекс];
Сообщить(МетаЗначение.Имя + " — " + МетаЗначение.Синоним);

Строка содержит синоним

Если из внешней системы пришло «Наличные деньги», прямого метода нет. Переберите значения и сравните представление:

Функция ЗначениеПоСинониму(Синоним)
    Для Каждого Значение Из Перечисления.ВидыОплат Цикл
        Если НРег(Строка(Значение)) = НРег(СокрЛП(Синоним)) Тогда
            Возврат Значение;
        КонецЕсли;
    КонецЦикла;
    Возврат Перечисления.ВидыОплат.ПустаяСсылка();
КонецФункции

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

Сравнение значений перечисления в условиях

Сравнение выполняют операторами = и <> со значением того же перечисления. Варианты, которые встречаются в чужом коде и не работают:

  • ❌ Если ВидОплаты = "Наличные" — ссылка никогда не равна строке, условие всегда ложно;
  • ❌ Если Строка(ВидОплаты) = "Наличные деньги" — работает, пока не поменяли синоним, и зависит от языка интерфейса;
  • ✅ Если ВидОплаты = Перечисления.ВидыОплат.Наличные.

Проверку на заполненность делают функцией ЗначениеЗаполнено(). Для пустой ссылки она возвращает Ложь:

Если Не ЗначениеЗаполнено(Объект.ВидОплаты) Тогда
    ОбщегоНазначения.СообщитьПользователю("Укажите вид оплаты");
КонецЕсли;

Процедура СообщитьПользователю есть в конфигурациях на Библиотеке стандартных подсистем. В собственной конфигурации без БСП используйте объект СообщениеПользователю.

Несколько значений сразу

Когда вариантов три и больше, цепочку Или удобнее заменить массивом или фиксированным списком:

Безналичные = Новый Массив;
Безналичные.Добавить(Перечисления.ВидыОплат.Карта);
Безналичные.Добавить(Перечисления.ВидыОплат.Перевод);

Если Безналичные.Найти(ВидОплаты) <> Неопределено Тогда
    // безналичная оплата
КонецЕсли;

Сравнение «больше-меньше»

Операторы < и > к ссылкам перечисления в коде не применяют. Если нужна логика «статус не ниже Согласован», сравнивайте индексы:

Менеджер = Перечисления.СтатусыЗаказов;
Если Менеджер.Индекс(Заказ.Статус) >= Менеджер.Индекс(Менеджер.Согласован) Тогда
    // заказ уже согласован или продвинулся дальше
КонецЕсли;

Такой код опирается на порядок значений в Конфигураторе. Переставят значения — поменяется логика. Оставьте комментарий рядом, чтобы следующий разработчик это видел.

Перечисления в запросах и СКД

В тексте запроса менеджеров нет. Константное значение задают конструкцией ЗНАЧЕНИЕ():

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|   Заказ.Ссылка,
|   Заказ.Сумма
|ИЗ
|   Документ.ЗаказПокупателя КАК Заказ
|ГДЕ
|   Заказ.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт)";

Если значение выбирается во время выполнения, передайте его параметром. Это гибче, и текст запроса не меняется:

Запрос.Текст = "... ГДЕ Заказ.Статус В (&Статусы)";
Запрос.УстановитьПараметр("Статусы", МассивСтатусов);

Приёмы, которые пригодятся в запросах

  • 🔸 Пустое значение: ЗНАЧЕНИЕ(Перечисление.ВидыОплат.ПустаяСсылка).
  • 🔸 NULL после левого соединения: ЕСТЬNULL(Оплата.ВидОплаты, ЗНАЧЕНИЕ(Перечисление.ВидыОплат.ПустаяСсылка)) — иначе поле с NULL не совпадёт ни с одним условием.
  • 🔸 Текст для отчёта: ПРЕДСТАВЛЕНИЕ(Заказ.Статус) вернёт синоним.
  • 🔸 Ветвление: ВЫБОР КОГДА Заказ.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ.
  • 🔸 Системные перечисления: так же — ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход).

К самой таблице перечисления тоже можно обратиться. У неё есть поля Ссылка и Порядок:

"ВЫБРАТЬ
|   ВидыОплат.Ссылка,
|   ВидыОплат.Порядок
|ИЗ
|   Перечисление.ВидыОплат КАК ВидыОплат
|УПОРЯДОЧИТЬ ПО
|   ВидыОплат.Порядок"

Это полезно, когда в отчёте нужны все значения, даже те, по которым нет данных: таблицу перечисления соединяют левым соединением с документами.

Перечисления в схеме компоновки данных

В СКД есть три места, где встречается перечисление:

  1. Текст запроса набора данных — те же ЗНАЧЕНИЕ() и параметры, что в обычном запросе.
  2. Параметры схемы — на закладке «Параметры» задайте параметру тип ПеречислениеСсылка.<Имя>. Для выбора нескольких значений включите у параметра флаг «Доступен список значений».
  3. Вычисляемые поля и ресурсы — язык выражений СКД понимает Значение(Перечисление.СтатусыЗаказов.Закрыт).

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

Перебор всех элементов перечисления

Менеджер перечисления поддерживает обход циклом Для Каждого. Значения выдаются в том порядке, в котором они стоят в Конфигураторе:

Для Каждого Значение Из Перечисления.ВидыОплат Цикл
    Сообщить(XMLСтрока(Значение) + ": " + Строка(Значение));
КонецЦикла;

Пустая ссылка в обход не попадает.

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

МетаПеречисление = Метаданные.Перечисления.Найти(ИмяПеречисления);
Для Каждого МетаЗначение Из МетаПеречисление.ЗначенияПеречисления Цикл
    Ссылка = Перечисления[ИмяПеречисления][МетаЗначение.Имя];
    Сообщить(МетаЗначение.Имя + " | " + МетаЗначение.Синоним);
КонецЦикла;

Частая практическая задача — заполнить список выбора поля на форме не всеми значениями, а частью:

&НаСервере
Процедура ЗаполнитьСписокВидовОплат()
    Список = Элементы.ВидОплаты.СписокВыбора;
    Список.Очистить();
    Для Каждого Значение Из Перечисления.ВидыОплат Цикл
        Если Значение <> Перечисления.ВидыОплат.Бартер Тогда
            Список.Добавить(Значение);
        КонецЕсли;
    КонецЦикла;
КонецПроцедуры

Сравнение способов перебора:

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

Ошибки при обращении к перечислениям и как их не допускать

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

Ещё несколько правил, которые экономят время на отладке:

  • ✅ Имена значений пишите без сокращений и транслита: ЗаказЗакрыт, а не ZakZakr. Опечатку в строке ПредопределенноеЗначение() найти сложнее, чем в прямом обращении.
  • ✅ Не добавляйте новое значение в середину списка, если где-то используется Индекс(). Добавляйте в конец.
  • ✅ Не храните в реквизитах строку с именем значения, если можно хранить саму ссылку на перечисление. Ссылка переживает переименование синонима.
  • ✅ Для доработки типовой конфигурации добавляйте значения в расширение, а не правьте основную конфигурацию — так проще проходят обновления.

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

Можно ли добавить значение перечисления программно?
Нет. Состав перечисления задаётся только в Конфигураторе или расширении. Если пользователям нужно пополнять список, используйте справочник.
Что будет с данными, если переименовать значение перечисления?
Данные сохранятся: в базе хранится ссылка, а не имя. Перестанет работать код, где старое имя записано явно, — его придётся исправить.
Чем перечисление отличается от предопределённого элемента справочника?
Перечисление — закрытый список без дополнительных реквизитов. Справочник позволяет добавлять свои элементы и хранить реквизиты, а предопределённые элементы в нём — точки опоры для кода.
Как вывести синоним перечисления в печатную форму?
Достаточно присвоить значение параметру макета: платформа сама выведет представление. В запросе можно взять ПРЕДСТАВЛЕНИЕ(Поле).
Почему ПредопределенноеЗначение пишется через «Перечисление», а менеджер через «Перечисления»?
Строка в функции использует синтаксис запроса, где таблицы называются в единственном числе. Менеджер — свойство глобального контекста во множественном числе.