В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью проверки существования предопределенных данных. Традиционный подход «обратиться напрямую» может привести к аварийному завершению работы кода или возникновению исключений, если элемент был удален в конфигураторе или еще не создан в базе данных. Рассмотрим подробно, как элегантно решить эту задачу, избегая конструкции Попытка...Исключение, и разберем наиболее эффективные методы проверки.
Разберем типичную ситуацию: вы пытаетесь получить предопределенный элемент справочника, используя стандартный метод или запрос к системному полю ИмяПредопределенныхДанных. Проблема заключается в том, что начиная с версии платформы 8.3.3, механизм работы с такими данными существенно изменился. Теперь наличие записи в конфигураторе не гарантирует наличие соответствующей ссылки в таблицах базы данных. Если свойство «Обновление предопределенных данных» установлено в значение «Не обновлять», элемент может просто отсутствовать в БД, а обращение к нему через точку в запросе вызовет ошибку.
Проанализируем ситуацию: использование метода ПредопределенноеЗначение() при передаче несуществующего имени вернет пустую ссылку, но в некоторых контекстах (например, при обращении к метаданным) может возникнуть ошибка выполнения. Наша задача — создать универсальный и «безопасный» механизм проверки.
Если нам нужно выяснить, определен ли идентификатор элемента в самом конфигураторе (т.е. в структуре метаданных), мы можем воспользоваться встроенным методом менеджера метаданных. Рассмотрим пример функции, которая проверяет наличие имени в списке предопределенных:
Функция ПредопределенныйСуществует(ИмяСправочника, ИмяПредопределенногоЭлемента) Экспорт
// Получаем массив имен всех предопределенных элементов данного справочника
ИменаПредопределенных = Метаданные.Справочники[ИмяСправочника].ПолучитьИменаПредопределенных();
// Ищем наше имя в полученном массиве
Возврат ИменаПредопределенных.Найти(ИмяПредопределенногоЭлемента) <> Неопределено;
КонецФункции
Этот метод хорош тем, что он работает очень быстро и не обращается к таблицам базы данных. Однако помните: он подтверждает только то, что такой элемент задуман разработчиком в конфигурации. Он не дает гарантии, что в базе данных уже создана ссылка с этим именем.
Для более крупных проектов удобно иметь универсальный инструмент, который позволяет проверять не только справочники, но и другие классы объектов (планы видов характеристик, планы счетов и т.д.). Разберем более сложный пример кода:
Функция ЕстьПредопределенноеЗначениеКласса(КлассМетаданных, ВидОбъекта, ИмяПредопределенного) Экспорт
// Проверяем существование через коллекцию метаданных
Попытка
СписокИмен = Метаданные[КлассМетаданных][ВидОбъекта].ПолучитьИменаПредопределенных();
Возврат СписокИмен.Найти(ИмяПредопределенного) <> Неопределено;
Исключение
// Если произошла ошибка доступа к метаданным, считаем, что элемента нет
Возврат Ложь;
КонецПопытки;
КонецФункции
При использовании этой функции вызов будет выглядеть так: ЕстьПредопределенноеЗначениеКласса("Справочники", "Номенклатура", "УслугаДоставки"). Это позволяет писать гибкий код, который легко переносить между различными конфигурациями.
Рассмотрим ситуацию, когда нам важно знать, есть ли элемент именно в базе данных (физическая запись в таблице). Прямой запрос к ИмяПредопределенныхДанных может выдать исключение, если в справочнике вообще нет ни одного предопределенного элемента. Чтобы обойти это ограничение и избежать пустых выборок, проанализируем профессиональный прием с использованием объединения (ОБЪЕДИНИТЬ).
Посмотрим на пример запроса:
Функция ПолучитьСсылкуНаПредопределенныйБезопасно(ИмяСправочника, ИмяЭлемента)
Запрос = Новый Запрос;
// Используем Максимум(Ссылка), чтобы всегда получать одну строку, даже если данных нет
Запрос.Текст =
"ВЫБРАТЬ
| МАКСИМУМ(Т.Ссылка) КАК Ссылка
|ИЗ
| (ВЫБРАТЬ
| Спр.Ссылка КАК Ссылка
| ИЗ
| Справочник." + ИмяСправочника + " КАК Спр
| ГДЕ
| Спр.ИмяПредопределенныхДанных = &ИмяЭлемента
|
| ОБЪЕДИНИТЬ ВСЕ
|
| ВЫБРАТЬ
| ЗНАЧЕНИЕ(Справочник." + ИмяСправочника + ".ПустаяСсылка)) КАК Т";
Запрос.УстановитьПараметр("ИмяЭлемента", ИмяЭлемента);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка;
КонецЕсли;
Возврат Неопределено;
КонецФункции
Почему это работает лучше? Использование ОБЪЕДИНИТЬ ВСЕ с пустой ссылкой гарантирует, что запрос всегда вернет хотя бы одну строку. Если предопределенный элемент найден, функция МАКСИМУМ вернет его ссылку. Если не найден — вернется пустая ссылка. Это избавляет нас от необходимости проверять РезультатЗапроса.Пустой() и защищает от ошибок СУБД при отсутствии колонки ИмяПредопределенныхДанных.
Если ваша конфигурация построена на базе БСП (что справедливо для большинства современных типовых решений, таких как ERP, УТ 11, БП 3.0), вам не нужно изобретать велосипед. Программисты фирмы «1С» уже подготовили надежные методы. Выясним, как ими пользоваться. Для быстрого поиска и изучения типовых подсистем пригодится справочник по методам БСП с примерами использования.
Наиболее подходящий метод находится в общем модуле ОбщегоНазначения. Рассмотрим вызов:
Ссылка = ОбщегоНазначения.ПредопределенныйЭлемент("Справочник.Валюты.Рубль");
Преимущества этого метода:
Неопределено (в зависимости от версии БСП), не вызывая падения системы.При выборе метода проверки следует учитывать несколько критических факторов:
1. Режим обновления данных. Как мы упоминали выше, если в настройках справочника стоит «Не обновлять предопределенные данные», то метод Метаданные.ПолучитьИменаПредопределенных() покажет, что имя существует, но запрос к базе данных вернет NULL или пустую ссылку. Всегда учитывайте этот разрыв между конфигурацией и реальностью БД.
2. Производительность запросов. Обращение к полю ИмяПредопределенныхДанных через точку (например, Выборка.Ссылка.ИмяПредопределенныхДанных) заставляет платформу делать дополнительное соединение с таблицей. Для оптимальной работы всегда используйте прямой отбор в секции ГДЕ или методы менеджера справочника.
3. Расширения конфигурации. Если вы работаете с расширениями, помните, что предопределенные элементы, добавленные в расширении, могут быть недоступны через стандартные коллекции метаданных основной конфигурации. В этом случае проверка через ПредопределенноеЗначение() является более предпочтительной.
4. Имя "Константы". Избегайте называть свои справочники именами, совпадающими с зарезервированными словами платформы (например, "Константы" или "ПланыСчетов"). Это может приводить к неоднозначности в синтаксисе запросов и трудночитаемому коду.
Мы рассмотрели несколько подходов к определению существования предопределенного элемента. Подведем итоги выбора инструмента:
ПредопределенноеЗначение().Использование предложенных методов позволит вам писать более отказоустойчивый код, который корректно поведет себя в любых сценариях обновления конфигурации и изменения данных пользователями.