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