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