Как исправить ошибку «Элемент не выбран» при установке пометки на удаление в 1С?

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

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

{Документ.ТребованиеНакладная.МодульОбъекта(2412)}: Ошибка при вызове метода контекста (ПринадлежитЭлементу) ... по причине: Элемент не выбран.

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

Выясняем причину: почему падает метод ПринадлежитЭлементу?

Метод ПринадлежитЭлементу(<Группа>) предназначен для проверки того, входит ли конкретный элемент справочника в указанную группу (с учетом всей вложенности иерархии). Этот метод вызывается у ссылки на объект справочника и принимает в качестве параметра другую ссылку — группу, принадлежность к которой мы проверяем.

Ошибка «Элемент не выбран» указывает на то, что один из участников этой операции (либо сам объект, у которого вызывается метод, либо переданный в скобках параметр) оказался пустой ссылкой. В контексте нашей проблемы сбой происходит в следующей строке кода:


Если НЕ РольДоступна("_РедактированиеПеремещенияПослеОтметкиОхраны") И (Подразделение.ПринадлежитЭлементу(Справочники.Подразделения.НайтиПоКоду("Р00000387")) Тогда

Причиной падения в данном случае могут выступать сразу три фактора:

  1. Отсутствие искомого элемента в базе данных. Функция Справочники.Подразделения.НайтиПоКоду("Р00000387") пытается найти элемент с конкретным кодом. Если в вашей базе данных (например, после свертки, переноса или ручного изменения кодов пользователями) нет подразделения с кодом "Р00000387", функция вернет пустую ссылку — Справочники.Подразделения.ПустаяСсылка(). Метод ПринадлежитЭлементу() не умеет работать с пустым параметром и мгновенно выдает ошибку.
  2. Не заполнено подразделение в самом документе. Если в документе ТребованиеНакладная реквизит Подразделение не заполнен (равен пустой ссылке), то вызов Подразделение.ПринадлежитЭлементу(...) аналогично завершится критической ошибкой. Платформа не может определить иерархическую принадлежность для «ничего».
  3. Влияние механизма RLS (Разграничение прав на уровне записей). Это неочевидный, но очень частый фактор на реальных предприятиях. Если у текущего пользователя ограничены права на справочник Подразделения, система безопасности RLS просто скроет от него запись с кодом "Р00000387". Для этого пользователя функция НайтиПоКоду() вернет пустую ссылку, даже если администратор базы данных прекрасно видит это подразделение в списке.

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

---

Вариант 1. Быстрое и безопасное исправление во встроенном языке

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

Давайте посмотрим на пример безопасного кода:


// Сначала найдем интересующее нас подразделение охраны и запишем его в переменную
ПодразделениеПроверки = Справочники.Подразделения.НайтиПоКоду("Р00000387");

// Проверяем, что заполнен реквизит документа и успешно найден проверяемый элемент
Если ЗначениеЗаполнено(Подразделение) И ЗначениеЗаполнено(ПодразделениеПроверки) Тогда
    
    // Проверяем роль и иерархическую принадлежность только при наличии корректных данных
    Если НЕ РольДоступна("_РедактированиеПеремещенияПослеОтметкиОхраны") 
        И Подразделение.ПринадлежитЭлементу(ПодразделениеПроверки) Тогда
        
        // Здесь располагается ваша логика блокировки или проверки перед пометкой на удаление
        ВызватьИсключение "Недостаточно прав для выполнения операции для данного подразделения!";
        
    КонецЕсли;
    
КонецЕсли;

Этот подход гарантирует, что система больше никогда не упадет с ошибкой «Элемент не выбран», даже если подразделение будет удалено или реквизит в документе окажется незаполненным.

---

Вариант 2. Использование запроса с конструкцией «В ИЕРАРХИИ» (Рекомендуемый стандарт 1С)

Согласно стандартам разработки фирмы «1С», любые проверки иерархии, особенно связанные с правами доступа и RLS, крайне рекомендуется выполнять с помощью механизма запросов. Запросы в 1С работают на уровне СУБД, они значительно быстрее обрабатывают иерархические структуры данных и, самое главное, они **никогда не вызывают аварийного завершения работы программы** при отсутствии элементов.

Давайте разберем по шагам, как переписать этот алгоритм правильно с использованием запроса:


ПодразделениеПроверки = Справочники.Подразделения.НайтиПоКоду("Р00000387");

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

Использование ключевого слова РАЗРЕШЕННЫЕ в тексте запроса позволяет корректно обрабатывать ситуацию с RLS: если у пользователя нет прав на чтение подразделения, запрос просто вернет пустой результат вместо аварийной остановки сеанса.

---

Вариант 3. Архитектурное исправление (Уход от «хардкода»)

Поиск элементов справочника по текстовому коду ("Р00000387") непосредственно в теле программного кода — это серьезная архитектурная ошибка (так называемый «антипаттерн»). Код элемента может легко измениться при переносе данных, перекодировании справочника, объединении баз или банально по ошибке пользователя. В результате работоспособность логики будет нарушена, а разработчикам придется снова вмешиваться в код конфигурации.

Рассмотрим два профессиональных способа решения этой проблемы:

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

ПодразделениеПроверки = Справочники.Подразделения.Охрана;

Предопределенный элемент невозможно удалить в пользовательском режиме, а его имя («Охрана») всегда будет доступно в коде независимо от того, какие коды или наименования присвоены ему в базе данных.

Б. Хранение ссылки в константе или регистре сведений. Если структура компании гибкая и подразделение охраны может периодически меняться, жестко прописывать его в коде нельзя. Создайте константу (например, ОсновноеПодразделениеОхраны) с типом ссылки на справочник Подразделения.

Тогда ваш код будет выглядеть следующим образом:


ПодразделениеПроверки = Константы.ОсновноеПодразделениеОхраны.Получить();

Если ЗначениеЗаполнено(Подразделение) И ЗначениеЗаполнено(ПодразделениеПроверки) Тогда
    // Выполнение безопасной проверки
КонецЕсли;

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

Резюме

Мы проанализировали ошибку «Элемент не выбран» и выяснили, что она возникает из-за отсутствия проверок на пустые ссылки при работе с методами иерархии. Для решения проблемы рекомендуется:

← На главную