Как исправить ошибку «Предопределенный элемент не существует» в запросе 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно разберем, почему возникает эта ошибка, как устроена двойственная природа предопределенных элементов в 1С, и рассмотрим несколько безопасных способов решения этой проблемы, которые уберегут вашу программу от аварийного завершения.

Выясняем причину: почему запрос падает, а не возвращает пустой результат?

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


ВЫБРАТЬ
    Начисления.Ссылка КАК Ссылка
ИЗ
    ПланВидовРасчета.Начисления КАК Начисления
ГДЕ
    Начисления.Предопределенный 
    И Начисления.ИмяПредопределенныхДанных = "ОблагаетсяЦеликом"

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

Дело в том, что системное поле ИмяПредопределенныхДанных в языке запросов 1С обрабатывается особым образом. Это не простое строковое поле в таблице базы данных. Когда платформа компилирует запрос для передачи СУБД, она выполняет сопоставление строкового литерала (в нашем примере — "ОблагаетсяЦеликом") с реальной структурой метаданных, описанной в конфигураторе.

Если в самом Конфигураторе у объекта метаданных (в данном случае у плана видов расчета Начисления) нет предопределенного элемента с именем ОблагаетсяЦеликом, компилятор запросов платформы не может связать эту строковую константу с внутренним идентификатором. Происходит сбой на этапе подготовки запроса, и платформа генерирует исключение. Таким образом, использовать обычный запрос для проверки существования имени предопределенного элемента в базе данных нельзя, если есть вероятность, что этого имени вообще нет в конфигурации.

Двойственная природа предопределенных данных в 1С

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

  1. В конфигурации (метаданные): Это имя (идентификатор), которое разработчик создает руками в конфигураторе на вкладке «Предопределенные».
  2. В информационной базе (данные ИБ): Это реальная запись в таблице базы данных, которая создается автоматически при первом запуске системы (или обновлении конфигурации) и связывается с метаданными через уникальное имя. У такой записи свойство ИмяПредопределенныхДанных содержит строку с именем из конфигуратора.

Платформа жестко контролирует эту связь. Вы не можете программно в режиме «1С:Предприятие» просто так записать в реквизит ИмяПредопределенныхДанных произвольную строку — система выдаст ошибку, если такого имени нет в метаданных конфигурации.

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

Решение 1. Безопасная проверка через метаданные перед выполнением запроса

Самый надежный способ избежать ошибки — предварительно проверить, объявлено ли вообще такое имя в конфигураторе. Для этого мы можем использовать встроенный метод платформы ПолучитьИменаПредопределенных(). Этот метод возвращает массив строк, содержащий имена всех предопределенных элементов объекта метаданных, и никогда не вызывает исключений.

Рассмотрим пример кода, реализующий этот подход:


ИменаПредопределенных = Метаданные.ПланыВидовРасчета.Начисления.ПолучитьИменаПредопределенных();

Если ИменаПредопределенных.Найти("ОблагаетсяЦеликом") <> Неопределено Тогда
    // Имя гарантированно есть в конфигураторе. 
    // Теперь мы можем безопасно запустить запрос для проверки его физического наличия в ИБ.
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Начисления.Ссылка КАК Ссылка
        |ИЗ
        |   ПланВидовРасчета.Начисления КАК Начисления
        |ГДЕ
        |   Начисления.Предопределенный 
        |   И Начисления.ИмяПредопределенныхДанных = ""ОблагаетсяЦеликом""";
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        Выборка = РезультатЗапроса.Выбрать();
        Выборка.Следующий();
        СсылкаНаЭлемент = Выборка.Ссылка;
    Иначе
        СсылкаНаЭлемент = Неопределено; // Элемент есть в метаданных, но не создан в ИБ (например, удален пользователем)
    КонецЕсли;
Иначе
    // Такого имени предопределенного элемента вообще нет в конфигурации
    СсылкаНаЭлемент = Неопределено;
КонецЕсли;

Этот способ идеален, так как он полностью исключает возникновение непредвиденных ошибок компиляции запроса и работает очень быстро.

Решение 2. Использование функций Библиотеки стандартных подсистем (БСП)

Если вы работаете в любой типовой конфигурации 1С (например, 1С:Бухгалтерия, 1С:ЗУП, 1С:ERP), то в вашем распоряжении уже есть готовые механизмы Библиотеки стандартных подсистем (БСП). Разработчики БСП предусмотрели подобные сценарии и написали безопасные функции-обертки.

Проанализируем использование функции ПредопределенныйЭлемент() из общего модуля ОбщегоНазначенияКлиентСервер:


ИмяПредопределенного = "ПланВидовРасчета.Начисления.ОблагаетсяЦеликом";
СсылкаНаЭлемент = ОбщегоНазначенияКлиентСервер.ПредопределенныйЭлемент(ИмяПредопределенного);

Если ЗначениеЗаполнено(СсылкаНаЭлемент) Тогда
    // Элемент успешно найден в базе данных, с ним можно работать
    НайденоНачисление = СсылкаНаЭлемент;
Иначе
    // Элемент отсутствует в метаданных или не инициализирован в базе данных
    НайденоНачисление = Неопределено;
КонецЕсли;

Преимущество этого метода: Вам не нужно писать громоздкие проверки и запросы вручную. Функция БСП сама проверит метаданные, корректно обработает возможные исключения и вернет либо ссылку на элемент, либо пустую ссылку/значение Неопределено, если элемента не существует.

Решение 3. Обработка исключений через блок Попытка — Исключение

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

Посмотрим на пример реализации:


Попытка
    // Платформенный метод вызовет исключение, если имени нет в конфигураторе
    СсылкаНаЭлемент = ПредопределенноеЗначение("ПланВидовРасчета.Начисления.ОблагаетсяЦеликом");
Исключение
    // Сюда мы попадем, если предопределенного элемента не существует
    СсылкаНаЭлемент = Неопределено;
КонецПопытки;

Если СсылкаНаЭлемент <> Неопределено И Не СсылкаНаЭлемент.Пустая() Тогда
    // Элемент существует в базе данных и успешно получен
Иначе
    // Элемент не существует или не был инициализирован
КонецЕсли;

Этот способ прост в реализации, однако помните, что частое возникновение исключений (если код выполняется в цикле) может негативно сказываться на производительности системы. Используйте его обдуманно.

Подведем итоги

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

Главные правила работы с предопределенными элементами:

← На главную