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