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