Как вывести таблицу значений на управляемую форму с группировками в 1С

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

При разработке отчетов и рабочих мест на управляемых формах 1С программисты часто сталкиваются с задачей вывода многоуровневых данных. Обычный объект типа ТаблицаЗначений отображает данные линейно и не поддерживает интерактивное сворачивание и разворачивание строк по аналогии с отчетами. Для решения этой задачи в платформе предусмотрен специальный объект — Дерево значений.

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

Постановка задачи и выбор структуры данных

Предположим, нам необходимо вывести на форму остатки по счетам учета в разрезе организаций, контрагентов (Субконто1) и договоров (Субконто2). Данные должны быть сгруппированы иерархически: на верхнем уровне находится Организация, далее идет Контрагент, а на самом нижнем уровне — Договор.

Для реализации такой иерархии на форме обычная таблица значений не подойдет. Нам потребуется использовать реквизит формы с типом ДанныеФормыДерево, который визуализируется на форме в виде таблицы с кнопками развертывания («плюсиками»). На стороне сервера этот реквизит конвертируется в объект типа ДеревоЗначений.

Метод 1. Использование запроса с итогами и ручным обходом (классический способ)

Рассмотрим детально пример, предложенный разработчиками на форуме. Сначала мы пишем запрос, в котором используем предложение ИТОГИ ПО для построения иерархии результатов. Затем мы программно обходим выборку с помощью вложенных циклов и переносим данные в дерево значений.

Давайте разберем серверную процедуру заполнения дерева значений:


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

Этот способ интуитивно понятен и наглядно показывает, как распределяются подчиненные строки. Однако ручной обход выборки с большим уровнем вложенности имеет существенный минус — он работает медленно при обработке десятков тысяч записей из-за накладных расходов на создание объектов строк на стороне сервера 1С.

Метод 2. Быстрая выгрузка в Дерево значений без использования циклов (Рекомендуемый способ)

В платформе 1С существует встроенный высокооптимизированный механизм автоматического преобразования иерархического результата запроса в дерево значений. Мы можем заменить многострочные циклы вызовом всего одного метода Выгрузить() с указанием типа обхода.

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


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

Важное правило: При использовании метода ЗначениеВРеквизитФормы() имена и типы колонок в выгруженном объекте ДеревоЗначений должны строго совпадать с реквизитами формы типа ДанныеФормыДерево. Если в запросе окажутся лишние колонки или типы данных будут отличаться от настроенных в конфигураторе, система вызовет ошибку выполнения.

Если источником иерархии служит не просто группировка по разным полям, а сам справочник (например, справочник "Номенклатура" с группами и элементами), то в качестве параметра выгрузки следует использовать значение ОбходРезультатаЗапроса.ПоГруппировкамСИерархией.

Метод 3. Динамическое построение Дерева значений на форме

Иногда структура выводимых данных заранее неизвестна. Например, количество и состав группировок определяются настройками пользователя непосредственно в режиме "1С:Предприятие". В такой ситуации мы не можем заранее настроить реквизиты дерева в конфигураторе. Нам поможет программное создание реквизитов и элементов формы.

Выполним эту операцию по шагам:

  1. Создадим массив добавляемых реквизитов, указав путь к родительскому реквизиту дерева.
  2. Вызовем метод формы ИзменитьРеквизиты() для изменения структуры данных в памяти сервера.
  3. Добавим элементы отображения (колонки) на форму через коллекцию Элементы.Добавить().
  4. Заполним созданную структуру данными.

Пример программного добавления колонок в Дерево значений:


&НаСервере
Процедура СоздатьКолонкиДереваДинамически(ИменаКолонок)
    
    ДобавляемыеРеквизиты = Новый Массив;
    СозданныеЭлементы = Новый Массив;
    
    // 1. Описываем колонки, которые хотим добавить в дерево "ДинамическоеДерево"
    Для Каждого ИмяКолонки Из ИменаКолонок Цикл
        // Указываем имя, тип и путь к родительскому реквизиту дерева
        НовыйРеквизит = Новый РеквизитФормы(ИмяКолонки, Новый ОписаниеТипов("Строка"), "ДинамическоеДерево", ИмяКолонки);
        ДобавляемыеРеквизиты.Добавить(НовыйРеквизит);
    КонецЦикла;
    
    // 2. Добавляем реквизиты на форму
    ИзменитьРеквизиты(ДобавляемыеРеквизиты);
    
    // 3. Создаем элементы формы (визуальные колонки в таблице)
    Для Каждого ИмяКолонки Из ИменаКолонок Цикл
        ИмяЭлементаКолонки = "ДинамическоеДерево" + ИмяКолонки;
        НовыйЭлемент = Элементы.Добавить(ИмяЭлементаКолонки, Тип("ПолеФормы"), Элементы.ДинамическоеДерево);
        НовыйЭлемент.Вид = ВидПоляФормы.ПолеВвода;
        НовыйЭлемент.ПутьКДанным = "ДинамическоеДерево." + ИмяКолонки;
        НовыйЭлемент.Заголовок = ИмяКолонки;
    КонецЦикла;
    
КонецПроцедуры

Программное управление свертыванием и развертыванием строк дерева

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

Рассмотрим, как реализовать функции развертывания и свертывания всех узлов программно:


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

&НаКлиенте
Процедура СвернутьВсеУзлыДерева(Команда)
    КоллекцияСтрок = ДеревоЗнач.ПолучитьЭлементы();
    Для Каждого СтрокаДерева Из КоллекцияСтрок Цикл
        ИдентификаторСтроки = СтрокаДерева.ПолучитьИдентификатор();
        // Метод Свернуть скрывает подчиненные элементы выбранного узла
        Элементы.ДеревоЗнач.Свернуть(ИдентификаторСтроки);
    КонецЦикла;
КонецПроцедуры

Альтернативный вариант. Использование Системы компоновки данных (СКД)

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

Более мощной альтернативой выступает Система компоновки данных (СКД) с выводом результата в Табличный документ на форме:

  1. Вы можете создать схему компоновки данных, где в качестве источника будет выступать запрос или внешняя таблица значений.
  2. В настройках структуры СКД настраиваются необходимые группировки строк и колонок любой сложности.
  3. С помощью процессора вывода результат формируется в элемент формы ПолеТабличногоДокумента.

Табличный документ по умолчанию поддерживает группировки ("плюсики" сбоку или сверху), автоматический расчет итоговых строк, гибкую настройку условного оформления (например, выделение цветом итогов по организациям) и нативную выгрузку в Excel без потери структуры группировок.

Различие группировки строк и группировки колонок

Важно различать понятия группировки строк и визуального объединения колонок, о котором шла речь в сообщениях форума:

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

← На главную