При разработке на управляемых формах 1С мы часто сталкиваемся с необходимостью работы с данными документов, которые не открыты пользователем визуально, а получены программно (например, через запрос или выборку). Возникает соблазн привести табличную часть такого документа к типу ДанныеФормыКоллекция, чтобы использовать привычные методы работы с формой. Однако здесь нас могут поджидать неочевидные ошибки и ограничения платформы.
В этой статье мы подробно разберем, почему метод ЗначениеВДанныеФормы может не работать так, как вы ожидаете, в чем разница между выборкой и объектом, и какой подход является наиболее правильным с точки зрения архитектуры 1С.
Прежде чем мы перейдем к коду, давайте проясним, с чем именно мы пытаемся работать. В платформе 1С существует четкое разделение типов данных:
ДокументОбъект). С ним мы работаем на сервере, когда объект создан или получен из базы.Многие разработчики пытаются создать ДанныеФормыКоллекция "в воздухе", чтобы передать их в какую-то процедуру. Давайте посмотрим, почему это не работает напрямую.
Рассмотрим типичную ошибку. Допустим, мы получили объект документа и пытаемся превратить его табличную часть в коллекцию формы:
// ОШИБОЧНЫЙ ПОДХОД
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// Попытка присвоить результат функции переменной
МояКоллекция = ЗначениеВДанныеФормы(ДокОбъект.Товары, ???);
Здесь мы сталкиваемся с двумя проблемами:
Во-первых, метод ЗначениеВДанныеФормы() — это процедура, а не функция. Она ничего не возвращает. Если вы попытаетесь присвоить её результат переменной, вы получите Неопределено.
Во-вторых, у этой процедуры есть два обязательных параметра: Источник и Приемник.
ЗначениеВДанныеФормы(Значение, ОбъектДанныхФормы).
Важно: Приемником должен быть уже существующий реквизит управляемой формы (или часть реквизита типа ДанныеФормыСтруктура). Вы не можете сконвертировать данные "в никуда". Платформе нужен конкретный контейнер на форме, куда она положит данные.
Если ваша цель — просто обработать данные табличной части (перебрать строки, посчитать итоги, передать в другую процедуру) и при этом документ визуально не открыт, вам вообще не нужен тип ДанныеФормыКоллекция.
Давайте воспользуемся методом Выгрузить(). Это самый быстрый и надежный способ получить данные табличной части в универсальную коллекцию, с которой удобно работать на сервере.
// Получаем объект документа
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
// Выгружаем табличную часть в Таблицу Значений
ТЗ_Товары = ДокОбъект.Товары.Выгрузить();
// Теперь мы можем работать с ТЗ стандартными методами
Для Каждого СтрокаТЗ Из ТЗ_Товары Цикл
// Ваша логика обработки
Сообщить("Товар: " + СтрокаТЗ.Номенклатура);
КонецЦикла;
Тип ТаблицаЗначений является "родным" для серверного кода 1С. Большинство процедур в типовых конфигурациях (включая БСП) умеют работать именно с таблицей значений. Если функция требует на вход коллекцию, чаще всего она примет и ТЗ.
Иногда мы сталкиваемся с ситуацией, когда нам нужно передать данные в процедуру общего модуля (например, из старой версии БСП или специфической доработки), которая в параметрах жестко ожидает тип ДанныеФормыКоллекция и падает с ошибкой, если передать ТаблицаЗначений.
В этом случае нам придется "обмануть" систему, создав временный реквизит формы. Давайте посмотрим, как это сделать программно:
&НаСервере
Процедура ОбработатьТабличнуюЧастьКакКоллекциюФормы(ДокОбъект)
// 1. Создаем описание типа для нашей временной таблицы
// (можно взять типы из колонок ТЗ или задать вручную)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ТаблицаЗначений"));
ОписаниеТипаТЗ = Новый ОписаниеТипов(МассивТипов);
// 2. Добавляем временный реквизит на форму программно
МассивРеквизитов = Новый Массив;
МассивРеквизитов.Добавить(Новый РеквизитФормы("ВременнаяТЗ", ОписаниеТипаТЗ, "", "Временная таблица"));
ИзменитьРеквизиты(МассивРеквизитов);
// 3. Теперь у нас есть контейнер на форме. Используем ЗначениеВДанныеФормы
// ДокОбъект.Товары - Источник (ТабличнаяЧасть)
// ЭтаФорма.ВременнаяТЗ - Приемник (ДанныеФормыКоллекция)
ЗначениеВДанныеФормы(ДокОбъект.Товары, ЭтаФорма.ВременнаяТЗ);
// Теперь ЭтаФорма.ВременнаяТЗ имеет тип ДанныеФормыКоллекция
// и заполнена данными из документа
ВнешняяПроцедураТребующаяКоллекцию(ЭтаФорма.ВременнаяТЗ);
КонецПроцедуры
Обратите внимание: этот способ более ресурсоемкий. Мы изменяем структуру формы на лету. Используйте его только в крайних случаях.
В обсуждении на форуме был поднят важный вопрос терминологии: "табличная часть документа, получаемого выборкой". Давайте разберем этот момент, чтобы избежать путаницы.
Объект ВыборкаИзРезультатаЗапроса не содержит табличных частей. Выборка — это способ последовательного чтения записей. Вы не можете обратиться к Выборка.Товары и ожидать там увидеть табличную часть.
Чтобы добраться до табличной части, необходимо получить сам объект документа. Рассмотрим правильную последовательность действий:
// 1. Выполняем запрос
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Документ.РеализацияТоваровУслуг ГДЕ ...");
Выборка = Запрос.Выполнить().Выбрать();
// 2. Итерируемся по выборке
Пока Выборка.Следующий() Цикл
// 3. Получаем объект из ссылки
// Важно: Табличные части доступны только у Объекта, а не у Ссылки или Выборки
ТекОбъект = Выборка.Ссылка.ПолучитьОбъект();
// 4. Работаем с табличной частью
// Лучший вариант - выгрузить в ТЗ
ТаблицаТоваров = ТекОбъект.Товары.Выгрузить();
// Работаем с данными...
КонецЦикла;
ЗначениеВДанныеФормы работает только если у вас есть заранее подготовленный реквизит на форме-приемнике. Он не создает данные "из воздуха".Выгрузить(), получая ТаблицаЗначений. Это наиболее производительный и простой способ.ПолучитьОбъект().