Как получить значение введенного элемента табличной части на управляемой форме 1С?

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

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

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

Выясняем причину: особенности работы ссылочных типов на клиенте

Рассмотрим детально, как работает платформа «1С:Предприятие 8» в контексте управляемого приложения. На стороне клиента (в тонком или веб-клиенте) данные физически не считываются напрямую из базы данных SQL. Ссылочные типы данных, такие как ДокументСсылка.РеализацияТоваровУслуг или СправочникСсылка.Номенклатура, на клиенте представляют собой всего лишь структуру, содержащую уникальный идентификатор (GUID) объекта базы данных и его текстовое представление.

Когда вы пытаетесь написать следующий код на клиенте:


ТекущаяСтрокаТаблицы = Элементы.ДокументыРеализации.ТекущиеДанные;
ВыбранныйДокумент = ТекущаяСтрокаТаблицы.Документ;

// Платформа выдаст ошибку "Поле объекта не обнаружено":
Сообщить(ВыбранныйДокумент.СуммаДокумента); 

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

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

Способ 1. Получение реквизита на сервере без контекста формы

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

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


&НаКлиенте
Процедура ПоказатьСуммуДокумента()
    
    ТекущаяСтрокаТаблицы = Элементы.ДокументыРеализации.ТекущиеДанные;
    
    Если ТекущаяСтрокаТаблицы <> Неопределено Тогда
        ВыбранныйДокумент = ТекущаяСтрокаТаблицы.Документ;
        
        Если ЗначениеЗаполнено(ВыбранныйДокумент) Тогда
            // Передаем ссылку на сервер и получаем результат
            Сумма = ПолучитьСуммуДокументаНаСервере(ВыбранныйДокумент);
            Сообщить("Сумма выбранного документа составляет: " + Сумма);
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьСуммуДокументаНаСервере(Знач ВыбранныйДокумент)
    // Обращение через точку на сервере разрешено платформой
    Возврат ВыбранныйДокумент.СуммаДокумента;
КонецФункции

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

Минусы обращения через точку на сервере

Когда вы пишете на сервере конструкцию ВыбранныйДокумент.СуммаДокумента, платформа выполняет неявный запрос к базе данных. При этом СУБД считывает весь объект целиком со всеми его реквизитами шапки и кэширует его в памяти сервера. Если документ «Реализация товаров и услуг» содержит сотни строк в табличных частях, платформа потратит лишние ресурсы на чтение ненужных данных из базы только ради получения одного числового значения.

Способ 2. Использование оптимизированной функции БСП (Рекомендуется)

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

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


&НаСервереБезКонтекста
Функция ПолучитьСуммуДокументаНаСервере(Знач ВыбранныйДокумент)
    // Метод БСП считывает из СУБД только указанную колонку
    Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ВыбранныйДокумент, "СуммаДокумента");
КонецФункции

Под капотом функции БСП выполняется оптимизированный SQL-запрос, который достает из базы данных строго одно поле. Это работает в разы быстрее прямого обращения через точку и практически не создает нагрузки на сервер.

Способ 3. Прямой запрос к базе данных (для самописных конфигураций)

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

Проанализируем пример кода:


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

Способ 4. Групповое получение нескольких реквизитов

Нередко возникает задача получить сразу несколько реквизитов выбранного документа (например, сумму, дату и контрагента). Выполнять для этого несколько серверных вызовов или поочередно запрашивать каждое поле — грубейшая ошибка, замедляющая работу интерфейса.

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

Рассмотрим пример:


&НаСервереБезКонтекста
Функция ПолучитьРеквизитыДокументаНаСервере(Знач ВыбранныйДокумент)
    // Возвращает структуру с ключами: СуммаДокумента, Дата, Контрагент
    Возврат ОбщегоНазначения.ЗначенияРеквизитовОбъекта(ВыбранныйДокумент, "СуммаДокумента, Дата, Контрагент");
КонецФункции

Методологические советы по проектированию табличной части

С точки зрения архитектуры базы данных 1С, вам следует задать себе важный вопрос: какова цель получения этой суммы в текущем документе?

Рассмотрим два основных сценария:

  1. Сценарий А: Сумма нужна только для визуализации на форме. Если пользователю нужно просто видеть сумму реализации при просмотре строк, не стоит делать серверные запросы при каждом перемещении курсора по таблице (в событии активизации строки). Это приведет к сильному замедлению отклика интерфейса при медленном соединении. Вместо этого настройте форму списка с использованием динамического списка или выведите реквизиты связанного объекта через настройки пути данных элементов формы.
  2. Сценарий Б: Сумму необходимо жестко зафиксировать в текущем документе. Если создаваемый вами документ должен хранить сумму на момент его проведения (например, это реестр платежей или акт сверки), то в табличной части вашего документа необходимо завести собственный реквизит (например, СуммаРеализации с типом Число).

Давайте разберем, как правильно реализовать Сценарий Б. Мы будем заполнять созданный реквизит один раз в момент интерактивного выбора документа пользователем в табличной части. Для этого привяжем наш обработчик к событию ПриИзменении поля выбора документа:


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

Такой подход дает огромные преимущества:

  1. Обращение к серверу происходит ровно один раз — только при непосредственном изменении данных пользователем.
  2. Поскольку сумма теперь хранится прямо в табличной части вашего документа, вы можете мгновенно подсчитать общий итог по колонке на клиенте без единого вызова сервера, используя стандартную функцию: Объект.ДокументыРеализации.Итог("СуммаРеализации").
  3. Сохраняется историческая точность учета. Если в будущем кто-то изменит сумму в исходной «Реализации товаров и услуг» задним числом, в вашем документе останется первоначальная сумма на момент проведения операции.

Таким образом, мы детально изучили клиент-серверное взаимодействие при работе с реквизитами связанных документов. Применяйте эти правила на практике, оптимизируйте код и создавайте быстрые решения в «1С:Предприятие»!

← На главную