Как исправить ошибку Значение не является значением объектного типа (ФизическоеЛицо) при открытии заказов

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

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

Разбор причины возникновения ошибки

Ошибка «Значение не является значением объектного типа (ИмяРеквизита)» в языке 1С означает, что программный код пытается обратиться «через точку» к свойству объекта, который на самом деле не является объектом или ссылкой. В нашем случае система ожидает получить ссылку на справочник Пользователи, чтобы прочитать его реквизит ФизическоеЛицо, но вместо этого получает значение Неопределено или пустую ссылку.

Рассмотрим проблемный участок кода:


запрос.УстановитьПараметр("Менеджер", ОбщегоНазначенияПовтИсп.ПолучитьМенеджера().ФизическоеЛицо);

Если функция ПолучитьМенеджера() не находит подходящего сотрудника и возвращает «пустоту», попытка обращения к .ФизическоеЛицо мгновенно приводит к аварийному завершению работы формы. Выясним, почему функция может возвращать некорректный результат.

Анализ логических ошибок в запросе

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


ВЫБРАТЬ ПЕРВЫЕ 1
    Пользователи.Ссылка
ИЗ
    Справочник.Пользователи КАК Пользователи
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ФизическиеЛица КАК ФизическиеЛица
        ПО Пользователи.ФизическоеЛицо = ФизическиеЛица.Ссылка
ГДЕ
    ФизическиеЛица.Должность = ЗНАЧЕНИЕ(Справочник.ДолжностиСотрудников.РозничныйМенеджерПоПродажеРасходныхМатериалов)

Здесь кроется первая и самая важная архитектурная ошибка. Несмотря на использование ЛЕВОЕ СОЕДИНЕНИЕ, условие в секции ГДЕ по правой таблице (ФизическиеЛица) фактически превращает соединение во Внутреннее (INNER JOIN). Если у пользователя не заполнено физическое лицо или у этого лица не указана в точности та должность, которая зашита в коде через ЗНАЧЕНИЕ(), запрос вернет пустую выборку.

Рассмотрим основные причины, по которым запрос внезапно «перестал работать»:

  1. Изменение данных в справочнике: Администратор мог изменить должность сотрудника в справочнике, и она перестала соответствовать жестко заданному значению в коде.
  2. Права доступа (RLS): Если у текущего пользователя настроены ограничения на уровне записей для справочника ФизическиеЛица, запрос не увидит нужную запись, даже если она физически существует в базе.
  3. Кэширование (ПовтИсп): Поскольку функция находится в модуле с повторным использованием значений, система может хранить старый (пустой или ошибочный) результат до перезапуска сеанса.

Решение №1: Исправление логики получения данных

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


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

Обратите внимание: мы убрали соединение таблиц и использовали обращение через точку в условии запроса (Пользователи.ФизическоеЛицо.Должность). В языке запросов 1С это безопасно и работает как неявное левое соединение.

Решение №2: Безопасное обращение к реквизитам

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

Рассмотрим, как сделать вызов более защищенным:


МенеджерСсылка = ОбщегоНазначенияПовтИсп.ПолучитьМенеджера();

// Проверяем, что ссылка не пустая, прежде чем лезть в её реквизиты
Если ЗначениеЗаполнено(МенеджерСсылка) Тогда
    ФизЛицо = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(МенеджерСсылка, "ФизическоеЛицо");
    Запрос.УстановитьПараметр("Менеджер", ФизЛицо);
Иначе
    // Обработка ситуации, когда менеджер не найден
    Запрос.УстановитьПараметр("Менеджер", Справочники.ФизическиеЛица.ПустаяСсылка());
КонецЕсли;

Использование метода ОбщегоНазначения.ЗначениеРеквизитаОбъекта() предпочтительнее, так как оно работает быстрее (не читает весь объект целиком) и более устойчиво к ошибкам структуры данных.

Рекомендации по диагностике

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

  1. Проверьте данные консолью запросов: Скопируйте текст запроса из кода и выполните его в консоли. Если результат пустой — ищите изменения в справочниках Пользователи, ФизическиеЛица или Должности.
  2. Проверьте RLS: Запустите 1С под пользователем с полными правами. Если ошибка исчезла — проблема в ограничениях доступа к данным.
  3. Отладка: Установите точку останова в функции ПолучитьМенеджера() и посмотрите, что возвращает выборка.Следующий(). В оригинальном коде автора отсутствовала проверка этого условия, что и привело к обращению к пустому результату.

Таким образом, для устранения ошибки необходимо обеспечить «защитное программирование»: всегда проверять успешность выполнения запроса, избегать жесткой привязки к конкретным элементам справочников через ЗНАЧЕНИЕ() (лучше использовать параметры или настройки программы) и корректно обрабатывать ситуации, когда данные в базе отсутствуют.

← На главную