Обход значений перечисления во встроенном языке и запросах 1С 8.3

· 9 мин

Содержание

Перебрать перечисление в 1С проще всего циклом Для Каждого Значение Из Перечисления.ИмяПеречисления Цикл: менеджер перечисления сам отдаёт все значения в том порядке, в каком они стоят в конфигураторе. Если нужны ещё и имена значений, обходите Метаданные.Перечисления.ИмяПеречисления.ЗначенияПеречисления.

Частое заблуждение — что перечисление нужно читать запросом или собирать значения в массив вручную. Запрос нужен, только когда значения перечисления участвуют в отборе или соединении с другими таблицами. Для простого перебора хватает трёх строк кода.

В примерах ниже используется условное перечисление СтатусыЗаказа со значениями Новый, ВРаботе, Выполнен, Отменен. Подставьте имя своего перечисления из дерева конфигурации.

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

Перечисление — объект конфигурации с фиксированным набором значений. Значения задаются в конфигураторе, пользователь в режиме «Предприятие» добавить их не может. Поэтому перебор всегда даёт один и тот же список, пока конфигурацию не изменят.

В коде вы работаете с тремя разными вещами:

  • 📦 Менеджер перечисления — Перечисления.СтатусыЗаказа. Через него получают значения и перебирают их.
  • 🔗 Значение (ссылка) — Перечисления.СтатусыЗаказа.Новый, тип ПеречислениеСсылка.СтатусыЗаказа. Именно оно хранится в реквизитах документов и справочников.
  • 🧩 Объект метаданных — Метаданные.Перечисления.СтатусыЗаказа. Описание перечисления: имена, синонимы, комментарии значений.

Базовые обращения:

// Конкретное значение
Статус = Перечисления.СтатусыЗаказа.ВРаботе;

// Пустое значение (так выглядит незаполненный реквизит)
ПустойСтатус = Перечисления.СтатусыЗаказа.ПустаяСсылка();

// Проверка заполненности
Если Не ЗначениеЗаполнено(Документ.Статус) Тогда
    Сообщить("Статус не указан");
КонецЕсли;

Глобальный контекст Перечисления доступен на сервере и в толстом клиенте. В тонком и веб-клиенте его нет, там используйте ПредопределенноеЗначение("Перечисление.СтатусыЗаказа.ВРаботе").

Перебор всех значений перечисления циклом и с отбором

Способ 1. Цикл по менеджеру

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

&НаСервере
Процедура ВывестиСтатусыНаСервере()
    Для Каждого Значение Из Перечисления.СтатусыЗаказа Цикл
        Сообщить(Строка(Значение));
    КонецЦикла;
КонецПроцедуры

Строка(Значение) возвращает представление — синоним значения, если он заполнен, иначе имя.

Способ 2. Обход через метаданные

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

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

Здесь МетаЗначение.Имя — строка вроде ВРаботе, а МетаЗначение.Синоним — то, что видит пользователь, например «В работе».

Способ 3. По индексу

Значения перечисления пронумерованы с нуля. Это удобно, если нужен номер позиции.

&НаСервере
Процедура ОбойтиПоИндексу()
    Менеджер = Перечисления.СтатусыЗаказа;
    Всего = Метаданные.Перечисления.СтатусыЗаказа.ЗначенияПеречисления.Количество();
    Для Индекс = 0 По Всего - 1 Цикл
        Значение = Менеджер.Получить(Индекс);
        Сообщить(Формат(Индекс, "ЧН=0; ЧГ=0") + ": " + Строка(Значение));
    КонецЦикла;
КонецПроцедуры

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

Перебор с отбором

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

&НаСервере
Функция АктивныеСтатусы()
    Исключить = Новый Массив;
    Исключить.Добавить(Перечисления.СтатусыЗаказа.Выполнен);
    Исключить.Добавить(Перечисления.СтатусыЗаказа.Отменен);

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

Функция вернёт Новый и ВРаботе. Такой массив можно сразу передать в параметр запроса или в список выбора поля на форме.

Заполнение списка выбора на форме

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

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

Для поля ввода с типом перечисления включите в его свойствах режим выбора из списка, иначе пользователь откроет полный список значений кнопкой выбора.

Поиск значения по имени, синониму и обратное преобразование

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

ИмяЗначения = "ВРаботе";
Статус = Перечисления.СтатусыЗаказа[ИмяЗначения];

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

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

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

Прямого свойства «Имя» у ссылки нет. Рабочих приёмов два.

Через индекс и метаданные:

Индекс = Перечисления.СтатусыЗаказа.Индекс(Статус);
ИмяЗначения = Метаданные.Перечисления.СтатусыЗаказа.ЗначенияПеречисления[Индекс].Имя;

Через XML-сериализацию: функция XMLСтрока(Статус) для значения перечисления возвращает его имя. Обратно строку превращают в ссылку функцией XMLЗначение:

ИмяЗначения = XMLСтрока(Статус); // "ВРаботе"
Статус = XMLЗначение(Тип("ПеречислениеСсылка.СтатусыЗаказа"), ИмяЗначения);

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

Поиск по синониму

По тексту, который видит пользователь, ищут перебором:

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

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

Перечисления в запросах: отбор, вывод всех значений, соединения

Конкретное значение в тексте запроса

Значение перечисления подставляют прямо в текст через ЗНАЧЕНИЕ(), без параметра:

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

Пустое значение пишут так: ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказа.ПустаяСсылка).

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

Массив из предыдущего раздела передаётся параметром:

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

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

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

ВЫБРАТЬ
    Статусы.Ссылка КАК Статус,
    КОЛИЧЕСТВО(Заказ.Ссылка) КАК ЧислоЗаказов
ИЗ
    Перечисление.СтатусыЗаказа КАК Статусы
        ЛЕВОЕ СОЕДИНЕНИЕ Документ.Заказ КАК Заказ
        ПО Заказ.Статус = Статусы.Ссылка
СГРУППИРОВАТЬ ПО
    Статусы.Ссылка,
    Статусы.Порядок
УПОРЯДОЧИТЬ ПО
    Статусы.Порядок

Результат — четыре строки, по одной на каждый статус. Если отменённых заказов нет, в строке Отменен будет 0, а не пропуск.

Поле Порядок хранит позицию значения в конфигураторе. Сортировка по нему даёт тот же порядок, что и цикл Для Каждого.

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

Если реквизит составного типа и в нём может лежать перечисление, проверяйте тип через ССЫЛКА:

ГДЕ
    Таблица.Значение ССЫЛКА Перечисление.СтатусыЗаказа

Сравнение значений и сортировка по порядку

Равенство

Значения перечисления сравнивают операторами = и <>:

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

Значение сравнивается со значением, а не со строкой. Условие Заказ.Статус = "Отменен" всегда ложно, ошибки при этом не будет. Это одна из самых коварных ошибок: код работает, но ветка не выполняется.

Больше и меньше

Операторы < и > для ссылок на перечисление во встроенном языке не работают — платформа выдаст ошибку о том, что сравнение на больше-меньше допустимо только для примитивных типов. Если логика строится на «стадиях» (статус не ниже «В работе»), сравнивайте индексы:

&НаСервере
Функция СтатусНеНиже(Статус, Порог)
    Менеджер = Перечисления.СтатусыЗаказа;
    Возврат Менеджер.Индекс(Статус) >= Менеджер.Индекс(Порог);
КонецФункции

Учтите оговорку из раздела про индексы: такая логика держится на порядке значений в конфигураторе. Закрепите порядок в комментарии к перечислению, чтобы его не переставили при доработке.

Сортировка коллекции

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

Таблица.Колонки.Добавить("ПорядокСтатуса", Новый ОписаниеТипов("Число"));
Менеджер = Перечисления.СтатусыЗаказа;
Для Каждого Строка Из Таблица Цикл
    Строка.ПорядокСтатуса = Менеджер.Индекс(Строка.Статус);
КонецЦикла;
Таблица.Сортировать("ПорядокСтатуса");

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

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

ЗадачаСпособНа что смотреть
Просто пройти все значенияДля Каждого по менеджеруТолько сервер и толстый клиент
Нужны имена и синонимыОбход ЗначенияПеречисленияСсылку получать по имени
Нужен номер позицииИндекс() и Получить()Индекс меняется при перестановке
Выгрузка и загрузкаXMLСтрока / XMLЗначениеИмя стабильнее синонима
Отбор и отчётыТаблица Перечисление.Имя в запросеСортировка по Порядок

Универсальная функция перебора для общего модуля

Когда перечислений в доработке несколько, один и тот же цикл начинают копировать в каждую форму. Удобнее вынести перебор в серверный общий модуль и передавать имя перечисления строкой.

// Общий модуль РаботаСПеречислениями, флажок «Сервер», вызов с сервера включён
Функция ЗначенияПеречисления(ИмяПеречисления) Экспорт
    МетаПеречисление = Метаданные.Перечисления.Найти(ИмяПеречисления);
    Если МетаПеречисление = Неопределено Тогда
        ВызватьИсключение СтрШаблон("Перечисление %1 не найдено", ИмяПеречисления);
    КонецЕсли;

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

Вызов с клиента выглядит так:

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

Что даёт такой подход:

  • ✅ один серверный вызов вместо обращения к Перечисления на клиенте, где этого контекста нет;
  • ✅ понятная ошибка, если имя перечисления передали с опечаткой;
  • ✅ структура с именем и синонимом сразу годится для выгрузки в JSON или для списка выбора.

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

Отладка: где чаще всего ломается код с перечислениями

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

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

  1. Поставьте точку останова внутри цикла и добавьте в табло выражение XMLСтрока(Значение) — сразу видно имя, а не только синоним.
  2. Чтобы проверить тип значения в составном реквизите, вычислите ТипЗнч(Значение) = Тип("ПеречислениеСсылка.СтатусыЗаказа").
  3. Если запрос падает уже при разборе текста и ошибка указывает на строку с ЗНАЧЕНИЕ(), имя значения записано с ошибкой. Запрос не выполнится вообще, поэтому такую опечатку поймать проще, чем в коде.
  4. Для перечислений типовой конфигурации смотрите имена значений в конфигураторе, а не в интерфейсе: синоним «НДС 20%» и имя значения — разные строки.

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

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