Задача формирования печатной формы в виде матрицы (или кросс-таблицы) — одна из классических и, в то же время, вызывающих сложности задач в разработке на платформе 1С:Предприятие. — для этого есть конструктор печатных форм без программирования для 1С. Типичный пример: необходимо вывести список товаров по вертикали, а размеры (или характеристики, склады, месяцы) — по горизонтали, проставив на пересечении количество или отметку о наличии. При этом количество размеров заранее неизвестно и может меняться от документа к документу.
В этой статье мы подробно разберем два принципиально разных подхода к решению этой задачи: «ручной» метод построения макета через присоединение областей и использование Системы Компоновки Данных (СКД) для программного формирования печатной формы.
Этот метод является наиболее гибким, если вам нужно соблюсти строгий корпоративный стиль печатной формы, добавить сложные логотипы, подписи и специфическое оформление, которое трудно настроить в СКД. Суть метода заключается в использовании команды табличного документа Присоединить.
Давайте разберем логику построения такой таблицы по шагам.
В конфигураторе, в макете табличного документа, нам не нужно рисовать всю таблицу целиком. — есть готовый универсальный редактор макетов и структур документов. Нам понадобятся отдельные именованные области ("кирпичики"), из которых мы будем программно собирать нашу мозаику.
Нам потребуются следующие области:
Критически важный момент: не делайте запросы в цикле! Это существенно замедлит печать больших документов. Нам нужно получить данные одним пакетом. Лучше всего выгрузить результат запроса в таблицу значений или дерево значений.
Примерная структура данных, которую мы должны получить запросом:
// Примерная структура плоской таблицы
// Товар | Размер | Количество
// Штаны | L | 1
// Штаны | XL | 2
// Майка | M | 5
Также нам потребуется отдельный список всех уникальных размеров, которые есть в документе, чтобы построить шапку таблицы. Это можно сделать, свернув таблицу по колонке "Размер" или выбрав различные размеры отдельным пакетом запроса.
Теперь рассмотрим, как это реализуется в коде. Основная идея: мы выводим левую часть строки методом Вывести, а затем в цикле "прилепляем" к ней остальные ячейки справа методом Присоединить.
Разберем пример кода:
// Получаем макет
Макет = УправлениеПечатью.МакетПечатнойФормы("ПФ_MXL_Матрица");
ТабДок = Новый ТабличныйДокумент;
// 1. ФОРМИРУЕМ ШАПКУ
// Выводим левый верхний угол
ОбластьШапкаЛево = Макет.ПолучитьОбласть("ОбластьШапкаЛево");
ТабДок.Вывести(ОбластьШапкаЛево);
// Получаем список всех уникальных размеров для колонок
ТаблицаРазмеров = ДанныеДляПечати.Скопировать();
ТаблицаРазмеров.Свернуть("Размер");
ТаблицаРазмеров.Сортировать("Размер Возр");
// Цикл по колонкам шапки
ОбластьШапкаКолонка = Макет.ПолучитьОбласть("ОбластьШапкаКолонка");
Для Каждого СтрРазмер Из ТаблицаРазмеров Цикл
ОбластьШапкаКолонка.Параметры.Размер = СтрРазмер.Размер;
// ВАЖНО: Используем Присоединить вместо Вывести
ТабДок.Присоединить(ОбластьШапкаКолонка);
КонецЦикла;
// 2. ФОРМИРУЕМ СТРОКИ С ДАННЫМИ
ОбластьСтрокаЛево = Макет.ПолучитьОбласть("ОбластьСтрокаЛево");
ОбластьЯчейка = Макет.ПолучитьОбласть("ОбластьСтрокаЯчейка");
// Группируем данные по товарам для обхода
// Предположим, у нас есть выборка товаров
Пока ВыборкаТоваров.Следующий() Цикл
// Выводим название товара (начало строки)
ОбластьСтрокаЛево.Параметры.Товар = ВыборкаТоваров.Товар;
ТабДок.Вывести(ОбластьСтрокаЛево);
// Теперь бежим по тем же колонкам размеров, что и в шапке
Для Каждого СтрРазмер Из ТаблицаРазмеров Цикл
// Ищем значение для текущего товара и текущего размера
// В реальном коде лучше использовать заранее подготовленную структуру поиска,
// а не фильтровать данные на каждой итерации, но для примера логика такая:
Количество = ПолучитьКоличество(ВыборкаТоваров.Товар, СтрРазмер.Размер);
ОбластьЯчейка.Параметры.Значение = Количество;
// Присоединяем ячейку справа к строке товара
ТабДок.Присоединить(ОбластьЯчейка);
КонецЦикла;
КонецЦикла;
Важные нюансы метода Присоединить:
АвтоМасштаб, либо программно контролировать ширину, вставлять разрывы страниц или использовать печать в несколько колонок.Если требования к дизайну печатной формы не слишком жесткие (например, это внутренняя ведомость), использование Системы Компоновки Данных — самый эффективный путь. — в разработке поможет инструментарий для отладки кода и СКД. СКД умеет строить кросс-таблицы "из коробки" без написания сложных циклов.
Почему это удобно? Потому что СКД берет на себя самую сложную часть: сопоставление данных на пересечении строк и колонок.
Многие разработчики привыкли, что СКД — это только для отчетов. Однако мы можем использовать мощь этого механизма и внутри модуля менеджера документа или обработки печати, а значительно упростить код поможет библиотека СКДБилдер.
Порядок действий:
Теперь нам нужно программно выполнить эту схему и вывести результат не в форму отчета, а в табличный документ, который вернет функция печати.
Пример программного вывода СКД в табличный документ:
Функция СформироватьПечатнуюФормуСКД(МассивОбъектов)
ТабДок = Новый ТабличныйДокумент;
// Получаем схему из макета
СхемаКомпоновкиДанных = ПолучитьМакет("МакетСКД");
// Настройки компоновки
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
// Передаем параметр (ссылку на документ)
// В запросе СКД должно быть условие: ГДЕ Ссылка В (&МассивСсылок)
ПараметрСсылка = Настройки.ПараметрыДанных.Элементы.Найти("МассивСсылок");
ПараметрСсылка.Значение = МассивОбъектов;
ПараметрСсылка.Использование = Истина;
// Компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
// Процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
// Процессор вывода (здесь мы указываем наш ТабДок как приемник)
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабДок);
// Вывод результата
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Возврат ТабДок;
КонецФункции
Давайте проанализируем, какой метод лучше выбрать в вашей ситуации.
Выбирайте метод Присоединить (Ручной), если:
Выбирайте метод СКД, если:
Если количество возможных размеров (колонок) невелико (например, всего 5-10 вариантов XS, S, M, L, XL), можно использовать упрощенный метод. В макете рисуется таблица сразу со всеми возможными колонками. Каждой колонке присваивается имя.
При формировании печати мы анализируем, какие размеры присутствуют в документе. Для тех размеров, которых нет, мы устанавливаем видимость области в Ложь.
// Пример скрытия ненужных колонок
Если Не СписокРазмеровВДокументе.Содержит("XXL") Тогда
ТабДок.Область("КолонкаXXL").Видимость = Ложь;
КонецЕсли;
Этот способ самый простой в реализации, но он не подходит для полностью динамических задач (например, если колонками являются даты поставок или названия складов, которых может быть сотни).
При использовании ручного метода вывода (через Присоединить) всегда помните о производительности. Главная ошибка — выполнение запроса к базе данных внутри двойного цикла (Товар -> Размер). Это создает огромную нагрузку на сервер.
Правильный подход:
НайтиСтроки() в памяти, а не в базе данных.Используя эти методы, вы сможете создавать печатные формы любой сложности, адаптирующиеся под данные документа.