Как правильно вывести кросс-таблицу с динамическими колонками в печатную форму 1С 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке отчетов в платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с задачей построения двумерных отчетов (кросс-таблиц, шахматок, матричных форм). Типичный пример такой задачи — вывод списка номенклатуры или единиц измерения по вертикали и показателей за различные периоды (месяцы, декады, дни) по горизонтали.

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

Архитектура формирования табличного документа в 1С 7.7

Для понимания логики работы с печатными формами в платформе 1С 7.7 проанализируем поведение двух базовых методов объекта Таблица:

  1. Метод ВывестиСекцию(): переносит текущий внутренний курсор построения таблицы на новую строку вниз. Если секция содержит несколько строк, курсор смещается вниз на соответствующую высоту.
  2. Метод ПрисоединитьСекцию(): присоединяет колонки секции строго справа от последней выведенной строки на текущей высоте курсора.

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

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

Способ 1. Предварительный расчет данных в Таблице Значений (рекомендуемый)

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

Разберем по шагам реализацию этого алгоритма:

  1. Определим список всех уникальных строк (например, единиц измерения ЕдИзм) и колонок (месяцев отчета).
  2. Создадим ТаблицаЗначений с колонкой ЕдИзм и динамическими колонками для каждого месяца: Месяц_1, Месяц_2, ..., Месяц_N.
  3. Заполним расчетные данные в строках ТЗ.
  4. Выведем шапку таблицы (левую фиксированную часть и динамические колонки месяцев).
  5. В двойном цикле выполним построчный вывод данных: внешний цикл перебирает строки ТЗ, а внутренний цикл присоединяет колонки месяцев.

Посмотрим на пример программного кода:


// 1. Формирование шапки таблицы
Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("ТаблицаОтчета");

// Выводим фиксированную левую шапку
Таб.ВывестиСекцию("Шапка|ЕдИзм");

// Присоединяем заголовки месяцев в цикле
Для НомМесяца = 1 По КоличествоМесяцев Цикл
    ПечМесяц = ПолучитьИмяМесяца(НомМесяца);
    Таб.ПрисоединитьСекцию("Шапка|Месяц");
КонецЦикла;

// 2. Построчный вывод тела отчета
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    
    // Заполняем реквизиты для левой колонки строки
    ПечЕдИзм = ТЗ.ЕдИзм;
    
    // Начинаем новую строку отчета выводом левой секции
    Таб.ВывестиСекцию("Строка|ЕдИзм");
    
    // Присоединяем к текущей строке данные по каждому месяцу
    Для НомМесяца = 1 По КоличествоМесяцев Цикл
        // Считываем рассчитанное значение из динамической колонки ТЗ
        ИмяКолонки = "Месяц_" + СокрЛП(Строка(НомМесяца));
        ПечКоличество = ТЗ.ПолучитьЗначение(ТЗ.НомерСтроки, ИмяКолонки);
        
        // Присоединяем ячейку справа к текущей строке
        Таб.ПрисоединитьСекцию("Строка|Месяц");
    КонецЦикла;
    
КонецЦикла;

// Отображаем сформированный отчет пользователю
Таб.Показать("Отчет по месяцам");

Способ 2. Заполнение готовой сетки через метод Область()

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

Суть метода заключается в том, что в печатную форму сначала выводится пустая матрица отчета со всеми пересечениями секций, а затем рассчитанные значения прописываются в нужные ячейки через прямое обращение по координатам Таб.Область(R, C).Текст.

Разберем реализацию данного способа:

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

Посмотрим на пример кода:


// Формируем пустую сетку отчета
Таб.ВывестиСекцию("Шапка|ЕдИзм");
Для НомМ = 1 По ВсегоМесяцев Цикл
    ПечМесяц = "Месяц " + НомМ;
    Таб.ПрисоединитьСекцию("Шапка|Месяц");
КонецЦикла;

// Выводим строки с пустыми показателями
СписокСтрок = СоздатьОбъект("СписокЗначений");
СтрокаСтарта = Таб.ВысотаТаблицы() + 1;

СпрЕд.ВыбратьЭлементы();
Пока СпрЕд.ПолучитьЭлемент() = 1 Цикл
    ПечЕдИзм = СпрЕд.ТекущийЭлемент();
    Таб.ВывестиСекцию("Строка|ЕдИзм");
    
    // Запоминаем номер строки в таблице для данной единицы измерения
    СписокСтрок.ДобавитьЗначение(Таб.ВысотаТаблицы(), ПечЕдИзм);
    
    Для НомМ = 1 По ВсегоМесяцев Цикл
        ПечКоличество = "";
        Таб.ПрисоединитьСекцию("Строка|Месяц");
    КонецЦикла;
КонецЦикла;

// Теперь рассчитываем каждый месяц и заполняем координаты ячеек
Для НомМ = 1 По ВсегоМесяцев Цикл
    НомерКолонкиВТаблице = 1 + НомМ; // С учетом левой колонки ЕдИзм
    
    // Выполняем расчет за выбранный месяц
    РассчитатьДанныеЗаМесяц(НомМ, ВременнаяТЗ);
    
    ВременнаяТЗ.ВыбратьСтроки();
    Пока ВременнаяТЗ.ПолучитьСтроку() = 1 Цикл
        ПозицияВМакете = СписокСтрок.Получить(ВременнаяТЗ.ЕдИзм);
        Если ПустоеЗначение(ПозицияВМакете) = 0 Тогда
            Таб.Область(ПозицияВМакете, НомерКолонкиВТаблице).Текст = ВременнаяТЗ.Количество;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Важное ограничение: метод прямого заполнения через Область() корректно работает только тогда, когда полный список строк (единиц измерения) известен заранее и одинаков для всех рассчитываемых периодов.

Сравнение подходов к формированию кросс-отчетов

  1. Вариант с предварительной агрегацией в ТЗ:

    Является универсальным стандартом разработки в 1С 7.7. Позволяет легко вычислять промежуточные итоги по строкам и колонкам, сортировать результирующие данные и корректно обрабатывать ситуации, когда в разных месяцах присутствует разный набор номенклатурных позиций.

  2. Вариант с прямой адресацией через Область():

    Подходит только для жестко фиксированных бланков и макетов с постоянным количеством строк. Требует строгого контроля координат ячеек и менее удобен при отладке.

Таким образом, для надежного построения кросс-таблицы в 1С 7.7 всегда разделяйте процесс на два независимых этапа: сбор и расчет данных в объект ТаблицаЗначений, а затем построчный вывод макета с использованием связки Таб.ВывестиСекцию() для начала строки и Таб.ПрисоединитьСекцию() для колонок внутри строки.

← На главную