Как получить реквизит из составного типа на клиенте в 1С 8.3

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

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

Анализ проблемы: почему данные не видны на клиенте

Рассмотрим стандартную ситуацию: у нас есть документ "Расходная накладная", в котором присутствует реквизит Заказ. Этот реквизит может принимать значения типов ДокументСсылка.ЗаказПокупателя или ДокументСсылка.ЗаказПоставщику. В обоих этих документах есть поле Договор.

Попытка получить договор на клиенте (например, в процедуре ПриОткрытии) обычно выглядит так:


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

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

Решение 1: Перенос логики на сервер

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


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПолучитьДанныеЗаказаНаСервере();
КонецПроцедуры

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

Решение 2: Оптимизация через БезКонтекстный вызов

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

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


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    Договор = ПолучитьДоговорИзЗаказа(Объект.Заказ);
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьДоговорИзЗаказа(Знач СсылкаНаЗаказ)
    // Проверяем тип и наличие реквизита "Договор"
    // Используем попытку-исключение или проверку метаданных для универсальности
    Попытка
        Возврат СсылкаНаЗаказ.Договор;
    Исключение
        Возврат ПредопределенноеЗначение("Справочник.ДоговорыКонтрагентов.ПустаяСсылка");
    КонецПопытки;
КонецФункции

Эффективность и «Правило точки»

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

Правильный подход: используйте запрос или метод ПолучитьОбъекты() (в определенных случаях). Рассмотрим пример с запросом, который является наиболее эффективным способом получения данных:


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

Работа с составными типами и метаданными

Если реквизит Заказ может содержать десятки разных типов документов, писать Если ТипЗнч(...) для каждого типа станет трудозатратно. В таких случаях мы можем проверить наличие реквизита программно через метаданные. Посмотрим, как это сделать:

  1. Получим метаданные объекта по ссылке.
  2. Проверим, есть ли у этого объекта реквизит с именем "Договор".
  3. Если есть — прочитаем значение.

&НаСервереБезКонтекста
Функция ПолучитьЗначениеРеквизитаУниверсально(Ссылка, ИмяРеквизита)
    Если Ссылка.Пустая() Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    // Проверяем наличие реквизита в метаданных документа
    МетаданныеОбъекта = Ссылка.Метаданные();
    Если МетаданныеОбъекта.Реквизиты.Найти(ИмяРеквизита) <> Неопределено Тогда
        Возврат Ссылка[ИмяРеквизита];
    КонецЕсли;
    
    Возврат Неопределено;
КонецФункции

Использование реквизитов формы для отображения данных

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

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

← На главную