Как исправить ошибку «Поле объекта не обнаружено» при заполнении реквизита на клиенте в 1С:УНФ?

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

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

В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем архитектурные особенности платформы 1С, выясним причины неудачных попыток решения и рассмотрим наиболее эффективные и современные способы реализации этой задачи в конфигурации «1С:Управление нашей фирмой» (УНФ).

Выясняем причину: почему нельзя писать «через точку» на клиенте?

Рассмотрим исходную ситуацию. В форме документа или справочника УНФ у нас есть реквизит Объект.Автомобиль (ссылка на справочник «Автомобили») и текстовый реквизит Объект.НомерАвто. Мы пишем простой обработчик события изменения автомобиля на клиенте:


&НаКлиенте
Процедура АвтомобильПриИзменении(Элемент)
    Объект.НомерАвто = Объект.Автомобиль.ГосНомер; // Ошибка!
КонецПроцедуры

При выполнении этого кода платформа незамедлительно выдаст ошибку: «Поле объекта не обнаружено (ГосНомер)». Почему так происходит?

Все дело в клиент-серверной архитектуре управляемого приложения. На стороне клиента (в веб-клиенте или тонком клиенте) данные базы данных не хранятся физически. Переменная Объект.Автомобиль на клиенте содержит лишь облегченную ссылку — объект типа СправочникСсылка.Автомобили. Этот объект хранит в себе только уникальный идентификатор (UUID) и текстовое представление ссылки для отображения на форме. Физических реквизитов (таких как ГосНомер, Марка, Модель и т.д.) на клиенте просто нет. Соответственно, попытка обратиться к ним «через точку» вызывает ошибку обращения к несуществующему свойству клиентского объекта.

Анализируем первую ошибку: почему не сработал контекстный вызов на сервере?

Пытаясь исправить ситуацию, разработчики часто переносят код на сервер напрямую:


&НаСервере
Процедура АвтомобильПриИзменении(Элемент)
    Объект.НомерАвто = Объект.Автомобиль.ГосНомер;
КонецПроцедуры

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

  1. Отсутствие связи процедуры с событием формы. Это самая банальная, но частая ошибка. Процедура написана в модуле, но в палитре свойств элемента формы «Автомобиль» в строке события «ПриИзменении» не выбрана эта процедура. Напротив события обязательно должна стоять лупа, указывающая на обработчик.
  2. Неверный «ПутьКДанным» у элемента формы. Если поле ввода на форме, предназначенное для отображения госномера, не связано жестко с реквизитом Объект.НомерАвто, то визуально на форме мы ничего не увидим, даже если код успешно отработал.
  3. Пустая ссылка. Если пользователь очистил поле «Автомобиль» на форме, то на сервере обращение Объект.Автомобиль.ГосНомер вернет значение Неопределено, и поле госномера очистится, что может выглядеть как отсутствие работы алгоритма.

Почему контекстный серверный вызов (&НаСервере) — это плохой тон?

Даже если контекстный вызов &НаСервере сработал, использовать его для такой простой задачи крайне не рекомендуется по соображениям производительности:

Решение №1: Классический внеконтекстный вызов (&НаСервереБезКонтекста)

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


&НаКлиенте
Процедура АвтомобильПриИзменении(Элемент)
    
    Если ЗначениеЗаполнено(Объект.Автомобиль) Тогда
        Объект.НомерАвто = ПолучитьГосНомерНаСервере(Объект.Автомобиль);
    Иначе
        Объект.НомерАвто = "";
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьГосНомерНаСервере(СсылкаНаАвто)
    Возврат СсылкаНаАвто.ГосНомер;
КонецФункции

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

Решение №2: Идеальный вариант с использованием БСП (для УНФ)

Поскольку конфигурация «1С:Управление нашей фирмой» построена на базе Библиотеки Стандартных Подсистем (БСП), нам вообще нет необходимости писать собственные функции &НаСервереБезКонтекста. В БСП уже встроены готовые и максимально оптимизированные инструменты.

Вариант А: Получение одного реквизита

Для быстрого получения одного значения мы можем использовать стандартную функцию общего модуля ОбщегоНазначенияВызовСервера.ЗначениеРеквизитаОбъекта прямо на клиенте:


&НаКлиенте
Процедура АвтомобильПриИзменении(Элемент)
    
    Если ЗначениеЗаполнено(Объект.Автомобиль) Тогда
        // Легковесный запрос к базе данных без написания лишнего серверного кода
        Объект.НомерАвто = ОбщегоНазначенияВызовСервера.ЗначениеРеквизитаОбъекта(Объект.Автомобиль, "ГосНомер");
    Иначе
        Объект.НомерАвто = "";
    КонецЕсли;
    
КонецПроцедуры

Этот метод выполняет точечный SQL-запрос к СУБД, вытаскивая исключительно поле госномера, что гарантирует максимальную производительность.

Вариант Б: Получение нескольких реквизитов (групповое заполнение)

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

В этом случае мы запрашиваем структуру реквизитов за один вызов сервера:


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

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

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

← На главную