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

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

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

Анализ проблемы и выяснение причин ошибки

Проанализируем исходный код, вызвавший исключение. Попытка обратиться к основному реквизиту формы Объект внутри серверной функции приводит к ошибке, если процедура объявлена с директивой &НаСервереБезКонтекста:


&НаСервереБезКонтекста
Процедура ПолучитьЦенуНаСервере()
    Запрос = Новый Запрос;
    Запрос.Текст = "...";
    // Ошибка компиляции: Переменная не определена (Объект)
    Запрос.УстановитьПараметр("ВидЦены", Объект.ВидЦены);
КонецПроцедуры

Выясним причину такого поведения платформы. В управляемом приложении существует два вида серверных вызовов из модуля формы:

  1. Контекстный серверный вызов (&НаСервере) — форма сериализует все свои данные и передает их на сервер. В этом случае свойство Объект и остальные реквизиты формы доступны напрямую, но передача всего контекста создает дополнительную нагрузку на сеть и сервер.
  2. Бесконтекстный серверный вызов (&НаСервереБезКонтекста) — форма не передает свои данные на сервер. На сервере модуль формы исполняется изолированно, поэтому свойства формы (включая Объект, элементы управления и локальные реквизиты) там не существуют.

Кроме того, в исходном коде допущена типичная синтаксическая ошибка: обращение Объект.ТоварыНоменклатура некорректно. Табличная часть Товары является коллекцией (ДанныеФормыКоллекция), и у нее нет прямого реквизита с именем колонки — необходимо сначала спозиционироваться на конкретной строке.

Способ 1. Использование контекстного вызова &НаСервере

Если требуется прочитать или изменить множество реквизитов формы и производительность не является критическим фактором, можно использовать директиву &НаСервере. В контекстной процедуре переменная Объект доступна платформой по умолчанию.

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


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
    Если ТекущиеДанные <> Неопределено Тогда
        ПолучитьЦенуКонтекстно(ТекущиеДанные.Номенклатура);
    КонецЕсли;
КонецПроцедуры

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

Способ 2. Оптимальный вариант: &НаСервереБезКонтекста с передачей параметров

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

Разберем по шагам реализацию этого подхода:

  1. На клиенте получаем текущую строку табличной части через свойство Элементы.ИмяТаблицы.ТекущиеДанные.
  2. Считываем значения реквизита шапки (Объект.ВидЦены) и колонки текущей строки (ТекущиеДанные.Номенклатура).
  3. Передаем полученные значения параметров в бесконтекстную функцию на сервере.
  4. Возвращаем результат на клиент и записываем его в форму.

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


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Цена = ПолучитьЦенуНоменклатуры(Объект.Дата, Объект.ВидЦены, ТекущаяСтрока.Номенклатура);
    ТекущаяСтрока.Цена = Цена;
КонецПроцедуры

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

Ошибки в тексте запроса к регистру сведений

Обратим внимание на частые ошибки при составлении запроса к виртуальным таблицам:

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

Получение значений реквизитов ссылочных объектов

Рассмотрим вопрос получения реквизитов справочников или документов по имеющейся ссылке. В тонком клиенте прямое обращение через точку (например, СсылкаНаТовар.Артикул) запрещено платформой, так как ссылочный тип на стороне клиента хранит лишь идентификатор (UUID) и текстовое представление.

Для чтения реквизитов ссылки обязательно требуется серверный вызов. Рассмотрим варианты решения:

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

Прямое обращение через точку на сервере (Ссылка.Реквизит) также допустимо, однако чтение через запрос или функции БСП является более производительным, так как не загружает в память весь объект целиком.

← На главную