Как правильно выбрать документ на клиенте и перенести данные его табличной части через запрос на сервере?

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

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

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

Анализ типичных ошибок в коде

Рассмотрим фрагмент кода, в котором допущены типовые ошибки при попытке заполнить табличную часть:


&НаКлиенте
Процедура ВыбратьДок(Команда)
    перем зн;
    ВвестиЗначение(зн,,Тип("ДокументСсылка.ЗаказПоставщику"));
    ЗаполнитьНаСервере();
КонецПроцедуры

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

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

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

Вариант 1. Исправление запроса и правильная клиент-серверная передача данных

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

Для интерактивного ввода значения на клиенте используем асинхронный метод ПоказатьВводЗначения или ВвестиЗначениеАсинх, чтобы не нарушать работу конфигурации в режиме без модальных окон.

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


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

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

Разберем подробнее, почему этот подход работает стабильно:

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

Вариант 2. Быстрое заполнение без использования запроса

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

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


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

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

Рекомендации по чистке кода и оптимизации

  1. Проверка заполнения: Всегда проверяйте результат выбора на клиенте с помощью ЗначениеЗаполнено() перед вызовом серверного контекста. Это исключает лишние холостые вызовы сервера при нажатии кнопки «Отмена».
  2. Использование явных псевдонимов: В запросах всегда указывайте синонимы колонок (КАК ИмяКолонки), совпадающие с именами реквизитов целевой табличной части.
  3. Работа с реквизитами формы: Вместо метода ЗначениеВРеквизитФормы для табличных частей основного объекта формы предпочтительно использовать встроенный метод коллекции Объект.<ИмяТабличнойЧасти>.Загрузить().
← На главную