При разработке отчетов в платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с задачей построения двумерных отчетов (кросс-таблиц, шахматок, матричных форм). Типичный пример такой задачи — вывод списка номенклатуры или единиц измерения по вертикали и показателей за различные периоды (месяцы, декады, дни) по горизонтали.
Однако при попытке вывести сначала всю вертикальную колонку вниз, а затем пристыковать к ней данные по месяцам вправо, разработчик обнаруживает, что новые колонки начинают выводиться не с первой строки отчета, а с самого низа — сразу после последней выведенной строки. Разберем архитектурные особенности табличного документа 1С 7.7, выясним причину такого поведения и рассмотрим надежные способы построения подобных отчетов.
Для понимания логики работы с печатными формами в платформе 1С 7.7 проанализируем поведение двух базовых методов объекта Таблица:
ВывестиСекцию(): переносит текущий внутренний курсор построения таблицы на новую строку вниз. Если секция содержит несколько строк, курсор смещается вниз на соответствующую высоту.ПрисоединитьСекцию(): присоединяет колонки секции строго справа от последней выведенной строки на текущей высоте курсора.Главное ограничение платформы 1С 7.7: после того как курсор сместился вниз через вызовы Таб.ВывестиСекцию(), вернуть его обратно вверх для продолжения построения правых колонок штатными методами вывода секций невозможно. Если вывести 100 строк единиц измерения вниз, а затем вызвать Таб.ПрисоединитьСекцию(), секция месяца пристыкуется справа к 100-й строке, а не к 1-й.
Поэтому формирование кросс-таблицы в 1С 7.7 всегда должно происходить построчно: сначала выводится левая ячейка строки, затем к ней в цикле справа присоединяются все колонки этой же строки, и только после этого осуществляется переход на следующую строку.
Рассмотрим классический и наиболее надежный подход к решению задачи. Если данные по месяцам рассчитываются последовательно, их нельзя выводить в таблицу прямо в процессе расчета каждого месяца. Сначала необходимо собрать и нормализовать все данные в оперативной памяти с помощью объекта ТаблицаЗначений, а затем построчно вывести готовую матрицу в печатную форму.
Разберем по шагам реализацию этого алгоритма:
ЕдИзм) и колонок (месяцев отчета).ТаблицаЗначений с колонкой ЕдИзм и динамическими колонками для каждого месяца: Месяц_1, Месяц_2, ..., Месяц_N.Посмотрим на пример программного кода:
// 1. Формирование шапки таблицы
Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("ТаблицаОтчета");
// Выводим фиксированную левую шапку
Таб.ВывестиСекцию("Шапка|ЕдИзм");
// Присоединяем заголовки месяцев в цикле
Для НомМесяца = 1 По КоличествоМесяцев Цикл
ПечМесяц = ПолучитьИмяМесяца(НомМесяца);
Таб.ПрисоединитьСекцию("Шапка|Месяц");
КонецЦикла;
// 2. Построчный вывод тела отчета
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
// Заполняем реквизиты для левой колонки строки
ПечЕдИзм = ТЗ.ЕдИзм;
// Начинаем новую строку отчета выводом левой секции
Таб.ВывестиСекцию("Строка|ЕдИзм");
// Присоединяем к текущей строке данные по каждому месяцу
Для НомМесяца = 1 По КоличествоМесяцев Цикл
// Считываем рассчитанное значение из динамической колонки ТЗ
ИмяКолонки = "Месяц_" + СокрЛП(Строка(НомМесяца));
ПечКоличество = ТЗ.ПолучитьЗначение(ТЗ.НомерСтроки, ИмяКолонки);
// Присоединяем ячейку справа к текущей строке
Таб.ПрисоединитьСекцию("Строка|Месяц");
КонецЦикла;
КонецЦикла;
// Отображаем сформированный отчет пользователю
Таб.Показать("Отчет по месяцам");
Область()Проанализируем альтернативный вариант, который может быть полезен в случаях, когда структура строк и колонок известна заранее, но расчет показателей для каждого месяца требует значительных ресурсов и выполняется отдельными блоками.
Суть метода заключается в том, что в печатную форму сначала выводится пустая матрица отчета со всеми пересечениями секций, а затем рассчитанные значения прописываются в нужные ячейки через прямое обращение по координатам Таб.Область(R, C).Текст.
Разберем реализацию данного способа:
Посмотрим на пример кода:
// Формируем пустую сетку отчета
Таб.ВывестиСекцию("Шапка|ЕдИзм");
Для НомМ = 1 По ВсегоМесяцев Цикл
ПечМесяц = "Месяц " + НомМ;
Таб.ПрисоединитьСекцию("Шапка|Месяц");
КонецЦикла;
// Выводим строки с пустыми показателями
СписокСтрок = СоздатьОбъект("СписокЗначений");
СтрокаСтарта = Таб.ВысотаТаблицы() + 1;
СпрЕд.ВыбратьЭлементы();
Пока СпрЕд.ПолучитьЭлемент() = 1 Цикл
ПечЕдИзм = СпрЕд.ТекущийЭлемент();
Таб.ВывестиСекцию("Строка|ЕдИзм");
// Запоминаем номер строки в таблице для данной единицы измерения
СписокСтрок.ДобавитьЗначение(Таб.ВысотаТаблицы(), ПечЕдИзм);
Для НомМ = 1 По ВсегоМесяцев Цикл
ПечКоличество = "";
Таб.ПрисоединитьСекцию("Строка|Месяц");
КонецЦикла;
КонецЦикла;
// Теперь рассчитываем каждый месяц и заполняем координаты ячеек
Для НомМ = 1 По ВсегоМесяцев Цикл
НомерКолонкиВТаблице = 1 + НомМ; // С учетом левой колонки ЕдИзм
// Выполняем расчет за выбранный месяц
РассчитатьДанныеЗаМесяц(НомМ, ВременнаяТЗ);
ВременнаяТЗ.ВыбратьСтроки();
Пока ВременнаяТЗ.ПолучитьСтроку() = 1 Цикл
ПозицияВМакете = СписокСтрок.Получить(ВременнаяТЗ.ЕдИзм);
Если ПустоеЗначение(ПозицияВМакете) = 0 Тогда
Таб.Область(ПозицияВМакете, НомерКолонкиВТаблице).Текст = ВременнаяТЗ.Количество;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Важное ограничение: метод прямого заполнения через Область() корректно работает только тогда, когда полный список строк (единиц измерения) известен заранее и одинаков для всех рассчитываемых периодов.
Является универсальным стандартом разработки в 1С 7.7. Позволяет легко вычислять промежуточные итоги по строкам и колонкам, сортировать результирующие данные и корректно обрабатывать ситуации, когда в разных месяцах присутствует разный набор номенклатурных позиций.
Область():
Подходит только для жестко фиксированных бланков и макетов с постоянным количеством строк. Требует строгого контроля координат ячеек и менее удобен при отладке.
Таким образом, для надежного построения кросс-таблицы в 1С 7.7 всегда разделяйте процесс на два независимых этапа: сбор и расчет данных в объект ТаблицаЗначений, а затем построчный вывод макета с использованием связки Таб.ВывестиСекцию() для начала строки и Таб.ПрисоединитьСекцию() для колонок внутри строки.