Как правильно установить параметр "Ссылка" в запросе из расширения 1С на управляемой форме?

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

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

Выясняем причину ошибки "Переменная не определена (Ссылка)"

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


&НаСервере
Процедура РасшДЛ_ПередЗаписьюНаСервереПеред(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    // ... часть кода ...

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ЗаказПокупателя.Контрагент КАК Контрагент,
        |    ЗаказПокупателя.Контрагент.РасшДЛ_ЛимитДолга КАК ЛимитДолга,
        |    ЗаказПокупателя.Ссылка КАК Ссылка
        |ИЗ
        |    Документ.ЗаказПокупателя КАК ЗаказПокупателя
        |ГДЕ
        |    ЗаказПокупателя.Ссылка = &Ссылка";

    Запрос.УстановитьПараметр("Ссылка", Ссылка); // Здесь возникает ошибка!

    РезультатЗапроса = Запрос.Выполнить();
    // ... дальнейшая обработка ...
КонецПроцедуры

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

В модулях управляемых форм основными переменными для доступа к данным и самой форме являются:

  1. Объект: Эта переменная представляет собой сам объект базы данных (например, ДокументОбъект.ЗаказПокупателя, СправочникОбъект.Контрагенты), который в данный момент редактируется на форме. Объект является основным реквизитом формы и содержит все данные, отображаемые и изменяемые на ней.
  2. ЭтотОбъект: Эта переменная относится к самой форме как к объекту (типа ФормаКлиентскогоПриложения), а не к данным, отображаемым на ней. Чтобы получить доступ к данным, отображаемым на форме, через ЭтотОбъект, нам необходимо обратиться к его свойству Объект. То есть, путь будет ЭтотОбъект.Объект.

Таким образом, наша изначальная попытка использовать просто Ссылка была неверной, поскольку мы не указали, от какого объекта мы хотим получить эту ссылку.

Основное решение: Использование Объект.Ссылка

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

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


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

    // Корректное обращение к ссылке текущего объекта формы
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка); 

    РезультатЗапроса = Запрос.Выполнить();
    
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = Выборка.ЛимитДолга;    
        Сообщение.Сообщить();
    КонецЦикла;
КонецПроцедуры

Использование Объект.Ссылка в этом контексте гарантирует, что мы передаем в запрос ссылку именно на тот документ "ЗаказПокупателя", который сейчас открыт и обрабатывается на форме. Это решение работает как для кода, находящегося непосредственно в основной конфигурации, так и для кода, добавленного через расширение, если документ "ЗаказПокупателя" заимствован в расширение.

Более надежное решение: ЭтотОбъект.Объект.Ссылка с проверкой

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

Для таких ситуаций рекомендуется использовать ЭтотОбъект.Объект.Ссылка и добавлять проверки на заполненность ссылки и модифицированность объекта. Напомним, что ЭтотОбъект — это сама форма, а ЭтотОбъект.Объект — это основной реквизит формы, содержащий данные объекта.

Рассмотрим модифицированный пример:


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

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ЗаказПокупателя.Контрагент КАК Контрагент,
        |    ЗаказПокупателя.Контрагент.РасшДЛ_ЛимитДолга КАК ЛимитДолга,
        |    ЗаказПокупателя.Ссылка КАК Ссылка
        |ИЗ
        |    Документ.ЗаказПокупателя КАК ЗаказПокупателя
        |ГДЕ
        |    ЗаказПокупателя.Ссылка = &Ссылка";

    // Использование ЭтотОбъект.Объект.Ссылка
    Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Объект.Ссылка); 

    РезультатЗапроса = Запрос.Выполнить();
    
    Выборка = РезультатЗапроса.Выбрать();
    
    Пока Выборка.Следующий() Цикл
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = Выборка.ЛимитДолга;    
        Сообщение.Сообщить();
    КонецЦикла;
КонецПроцедуры

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

Особенности работы с реквизитами из расширений в запросах

В нашем примере мы используем реквизит РасшДЛ_ЛимитДолга, который, по словам автора, также добавлен через расширение к справочнику Контрагенты. Важно понимать, что для того чтобы такой реквизит был доступен в запросах, объект метаданных (в данном случае, Справочник.Контрагенты) должен быть заимствован в расширение. Платформа 1С автоматически управляет тем, как новые реквизиты, добавленные через расширения, становятся частью структуры метаданных и доступны для запросов.

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

Например, в нашем запросе:


"ВЫБРАТЬ
|    ЗаказПокупателя.Контрагент.РасшДЛ_ЛимитДолга КАК ЛимитДолга,
// ...
ИЗ
|    Документ.ЗаказПокупателя КАК ЗаказПокупателя
"

Мы напрямую обращаемся к реквизиту РасшДЛ_ЛимитДолга через точку от объекта Контрагент. Это возможно благодаря механизму расширений, который позволяет добавлять новую функциональность и данные к существующим объектам без модификации основной конфигурации.

Контекст выполнения &НаСервере

Наш пример кода выполняется в серверной процедуре &НаСервере. Это критически важно, поскольку выполнение запросов к базе данных в 1С всегда происходит на сервере. Если бы мы попытались выполнить этот запрос на клиенте (например, в процедуре без директивы &НаСервере или &НаКлиентеНаСервере), платформа выдала бы ошибку, поскольку объекты запроса и базы данных недоступны на клиентской стороне. Помните об этом при проектировании своего кода.

Резюме и рекомендации

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

  1. Для простых случаев, когда вы уверены, что документ уже записан и его ссылка заполнена, используйте Объект.Ссылка.
  2. Для более надежного и универсального подхода, особенно когда документ может быть новым или измененным, применяйте ЭтотОбъект.Объект.Ссылка в сочетании с проверками на ЗначениеЗаполнено(ЭтотОбъект.Объект.Ссылка) и ЭтотОбъект.Модифицированность. Это позволит вам контролировать поведение системы при различных состояниях документа и обеспечит получение актуальных данных.

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

← На главную