Как вывести отчет в виде шахматки (кросс-таблицы) с динамическими колонками в 1С?

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

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

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

Анализ причин проблемы

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

Также обратим внимание на типичные ошибки в запросе и коде формирования:

  1. Некорректная секция группировки: если в секцию СГРУППИРОВАТЬ ПО ошибочно включено числовое поле суммы, запрос не свернет дублирующиеся строки. Суммовые показатели необходимо оборачивать в агрегатные функции, например СУММА(РасходыСО.Сумма) КАК Сумма, а группировать только по измерениям.
  2. Смешивание ручного вывода и настроек построителя: свойства оформления Построитель.ВыводитьШапкуТаблицы, Построитель.Макет и Построитель.ОформитьМакет() предназначены для типового метода Построитель.Вывести(). При ручном заполнении параметров макета они не оказывают влияния на результат.
  3. Отсутствие вертикальных секций в макете: для горизонтального вывода макет должен быть разделен не только на строки, но и на колонки с именованными областями пересечений.

Способ 1. Автоматический вывод кросс-таблицы через Построитель отчета

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

Рассмотрим пример кода, реализующий автоматическое построение кросс-таблицы:


Процедура СформироватьОтчетАвто()
    
    ТабДок = ЭлементыФормы.ПолеОтчета;
    ТабДок.Очистить();
    
    Построитель = Новый ПостроительОтчета;
    Построитель.Текст = 
        "ВЫБРАТЬ
        |   РасходыСО.Ссылка.Проект КАК Проект,
        |   РасходыСО.Статья КАК Статья,
        |   РасходыСО.Сумма КАК Сумма
        |ИЗ
        |   Документ.Расходы.СписокОпераций КАК РасходыСО";
    
    Построитель.ЗаполнитьНастройки();
    
    // Настраиваем распределение измерений по строкам и колонкам
    Построитель.ИзмеренияСтроки.Очистить();
    Построитель.ИзмеренияСтроки.Добавить("Статья");
    
    Построитель.ИзмеренияКолонки.Очистить();
    Построитель.ИзмеренияКолонки.Добавить("Проект");
    
    // Указываем выводимый ресурс
    Построитель.ВыбранныеПоля.Очистить();
    Построитель.ВыбранныеПоля.Добавить("Сумма");
    
    Построитель.Выполнить();
    Построитель.Вывести(ТабДок);
    
КонецПроцедуры

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

Способ 2. Ручной вывод кросс-таблицы с помощью методов Вывести() и Присоединить()

Если макет требует нестандартного оформления, разберем по шагам алгоритм ручного формирования таблицы.

Шаг 1. Разметка макета. В табличном документе макета создаются пересечения горизонтальных (строки) и вертикальных (колонки) областей:

  1. Область Шапка|ШапкаФикс — левый верхний угол (заголовок «Статья расходов»).
  2. Область Шапка|КолонкаПроект — ячейка заголовка с параметром Проект.
  3. Область СтрокаДанные|ШапкаФикс — фиксированная ячейка строки с параметром Статья.
  4. Область СтрокаДанные|КолонкаПроект — ячейка пересечения данных с параметром Сумма.

Шаг 2. Подготовка запроса с итогами. Для получения данных по двум измерениям используем многомерные итоги в запросе:


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

Обратите внимание: обход сохраненного массива проектов гарантирует, что порядок колонок в каждой строке будет строго соответствовать шапке отчета, даже если по какому-то проекту отсутствуют обороты по определенной статье расходов.

← На главную