Как исправить ошибку «Переменная не определена (ДокументОбъект)» в 1С?

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

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

Почему возникает ошибка «Переменная не определена (ДокументОбъект)»?

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

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

Способ 1. Использование свойства ЭтотОбъект

Посмотрим на решение, предложенное участниками форума. Простым и быстрым способом исправления ошибки в обычной форме является замена несуществующей переменной на предопределенное свойство ЭтотОбъект. В контексте модуля обычной формы это свойство указывает непосредственно на редактируемый прикладной объект (например, документ).

Давайте перепишем исходный код автора с использованием свойства ЭтотОбъект:


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

Данный вариант полностью работоспособен, однако он перегружен лишними обращениями. Существует более лаконичный и правильный с точки зрения стандартов разработки способ.

Способ 2. Прямое обращение к реквизитам (Лучшая практика для ОФ)

Рассмотрим подробнее ключевую особенность архитектуры обычных форм в 1С:Предприятии. Модуль обычной формы тесно интегрирован с контекстом самого прикладного объекта. Это означает, что нам вообще не требуется использовать вспомогательные ключевые слова вроде ЭтотОбъект или ЭтаФорма для доступа к реквизитам и табличным частям редактируемого документа.

Мы можем обращаться к ним напрямую по их именам. Давайте проанализируем, как преобразится наш код при таком подходе:


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

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

Почему Ссылка.ПолучитьОбъект() — это опасный антипаттерн?

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

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

Как эта задача решается в Управляемых формах (УФ)?

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

Кроме того, в управляемом приложении действует строгое разделение на клиентский и серверный контексты:

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


&НаКлиенте
Процедура ГруппаПриИзменении(Элемент)
    // Вызываем серверную процедуру для работы с базой данных
    ЗаполнитьТабличнуюЧастьНаСервере();
КонецПроцедуры

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

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

← На главную