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