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