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