В практике разработки на платформе 1С часто возникает задача вывода данных в виде кросс-таблицы, где количество колонок заранее неизвестно и зависит от содержимого базы данных. Типичный пример: необходимо вывести список логистов в заголовках колонок, а под каждым из них расположить список закрепленных водителей с их показателями. Рассмотрим подробно, как решить эту задачу с помощью Системы компоновки данных (СКД) и программного метода формирования отчета через макет.
Прежде чем переходить к сложному программному коду, проанализируем возможности СКД. Система компоновки данных позволяет создавать динамические колонки без написания кода. Для этого используется структура типа Таблица.
Разберем настройки по шагам:
Таблица.Период или Показатель).Логист. Вложенной группировкой к логисту добавим поле Водитель.Расположение группировок в значение Горизонтально.Если нам нужно, чтобы свойства (например, «Цвет кузова», «Модель») выводились не отдельными строками, а колонками под водителем, воспользуемся советом из обсуждения: свойства должны стать ресурсами. Это позволит СКД агрегировать их и выводить в ячейках на пересечении строк и колонок.
Иногда требования заказчика к оформлению (объединение ячеек, специфические цвета, сложная иерархия в шапке) невозможно выполнить стандартными средствами СКД. В таких случаях мы прибегаем к программному формированию ТабличногоДокумента. Рассмотрим ситуацию, когда у нас есть готовая ТаблицаЗначений с данными, и нам нужно развернуть её горизонтально.
Проанализируем алгоритм вывода:
// Создаем соответствие для группировки по логистам
СоответствиеЛогистов = Новый Соответствие; // Логист -> Количество водителей
МассивЛогистов = Новый Массив; // Для сохранения порядка логистов
// Сначала собираем структуру данных
Для каждого Строка Из Таблица Цикл
Логист = Строка.Логист;
Если СоответствиеЛогистов[Логист] = Неопределено Тогда
СоответствиеЛогистов.Вставить(Логист, 1);
МассивЛогистов.Добавить(Логист);
Иначе
СоответствиеЛогистов[Логист] = СоответствиеЛогистов[Логист] + 1;
КонецЕсли;
КонецЦикла;
После подготовки структуры данных мы используем метод Присоединить() объекта ТабличныйДокумент. Важно понимать разницу: Вывести() добавляет область вниз, а Присоединить() — справа от последней выведенной области.
ТекущаяКолонка = 1;
Для каждого Строка Из Таблица Цикл
ОбластьВодители.Параметры.Заполнить(Строка);
// Выводим данные по горизонтали
ТабДок.Присоединить(ОбластьВодители);
ТекущаяКолонка = ТекущаяКолонка + 1;
КонецЦикла;
Одной из главных сложностей является создание «шапки» над динамическими колонками. Если у одного логиста пять водителей, его имя должно быть написано в одной объединенной ячейке над всеми пятью колонками водителей. Выясним, как это реализовать.
Используем метод Область(НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки).Объединить(). Проанализируем пример кода:
ТекущаяКолонка = НачальнаяКолонкаДанных; // Номер колонки, с которой начинается вывод данных
Для каждого Логист Из МассивЛогистов Цикл
КоличествоВодителей = СоответствиеЛогистов[Логист];
Если КоличествоВодителей > 1 Тогда
// Определяем границы области для объединения
// Предположим, заголовок логиста находится в 3-й строке
Начало = ТекущаяКолонка;
Конец = ТекущаяКолонка + КоличествоВодителей - 1;
ТабДок.Область(3, Начало, 3, Конец).Объединить();
// Устанавливаем текст и форматирование
Ячейка = ТабДок.Область(3, Начало);
Ячейка.Текст = СокрЛП(Логист);
Ячейка.ГоризонтальноеПоложение = ГоризонтальноеПоложение.Центр;
Иначе
ТабДок.Область(3, ТекущаяКолонка).Текст = СокрЛП(Логист);
КонецЕсли;
ТекущаяКолонка = ТекущаяКолонка + КоличествоВодителей;
КонецЦикла;
Рассмотрим продвинутый способ обхода результата запроса, который часто применяется при создании кросс-отчетов вручную. В методе Выбрать() объекта РезультатЗапроса существует третий параметр. Если установить его в значение "ВСЕ", система позволит получить все значения группировки, даже если для конкретной вышестоящей группировки данных нет.
Посмотрим на пример логики:
// Получаем выборку по строкам (например, Номенклатура или Период)
ВыборкаСтроки = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период");
Пока ВыборкаСтроки.Следующий() Цикл
ТабДок.Вывести(ОбластьСтрока);
// Получаем выборку по колонкам (Логисты) для текущей строки
// Параметр "ВСЕ" гарантирует, что мы пройдем по всем логистам,
// обеспечивая одинаковую ширину таблицы во всех строках
ВыборкаКолонки = ВыборкаСтроки.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Логист", "ВСЕ");
Пока ВыборкаКолонки.Следующий() Цикл
ОбластьКолонка.Параметры.Заполнить(ВыборкаКолонки);
ТабДок.Присоединить(ОбластьКолонка);
КонецЦикла;
КонецЦикла;
Использование этого метода позволяет избежать «рваных» краев в отчете, когда в одной строке данных больше, чем в другой. Однако помните, что при большом количестве данных параметр "ВСЕ" может существенно замедлить формирование отчета, так как системе приходится перебирать пустые комбинации.
Для корректного вывода данных важно правильно настроить макет в конфигураторе. Создадим несколько именованных областей:
Проанализируем ситуацию с цветами: чтобы отчет был читаемым, программно меняйте ЦветФона ячеек. Например, ТабДок.Область(...).ЦветФона = WebЦвета.Золотой; поможет визуально отделить одного логиста от другого. Также не забывайте устанавливать свойство АвтоМасштаб = Истина для табличного документа, так как горизонтальные отчеты часто получаются очень широкими и требуют масштабирования при печати.