Как динамически создать колонки и строки в макете печатной формы (матрица товаров и размеров)?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

Задача формирования печатной формы в виде матрицы (или кросс-таблицы) — одна из классических и, в то же время, вызывающих сложности задач в разработке на платформе 1С:Предприятие. — для этого есть конструктор печатных форм без программирования для 1С. Типичный пример: необходимо вывести список товаров по вертикали, а размеры (или характеристики, склады, месяцы) — по горизонтали, проставив на пересечении количество или отметку о наличии. При этом количество размеров заранее неизвестно и может меняться от документа к документу.

В этой статье мы подробно разберем два принципиально разных подхода к решению этой задачи: «ручной» метод построения макета через присоединение областей и использование Системы Компоновки Данных (СКД) для программного формирования печатной формы.

Подход №1: Ручное построение макета (Метод Присоединить)

Этот метод является наиболее гибким, если вам нужно соблюсти строгий корпоративный стиль печатной формы, добавить сложные логотипы, подписи и специфическое оформление, которое трудно настроить в СКД. Суть метода заключается в использовании команды табличного документа Присоединить.

Давайте разберем логику построения такой таблицы по шагам.

1. Подготовка макета

В конфигураторе, в макете табличного документа, нам не нужно рисовать всю таблицу целиком. — есть готовый универсальный редактор макетов и структур документов. Нам понадобятся отдельные именованные области ("кирпичики"), из которых мы будем программно собирать нашу мозаику.

Нам потребуются следующие области:

  1. ОбластьШапкаЛево — левая верхняя часть заголовка (обычно название колонки "Товар").
  2. ОбластьШапкаКолонка — ячейка заголовка для динамической колонки (здесь будет выводиться конкретный "Размер").
  3. ОбластьСтрокаЛево — левая часть строки данных (название товара).
  4. ОбластьСтрокаЯчейка — ячейка данных на пересечении товара и размера (количество или "Х").

2. Подготовка данных (Запрос)

Критически важный момент: не делайте запросы в цикле! Это существенно замедлит печать больших документов. Нам нужно получить данные одним пакетом. Лучше всего выгрузить результат запроса в таблицу значений или дерево значений.

Примерная структура данных, которую мы должны получить запросом:


// Примерная структура плоской таблицы
// Товар | Размер | Количество
// Штаны | L      | 1
// Штаны | XL     | 2
// Майка | M      | 5

Также нам потребуется отдельный список всех уникальных размеров, которые есть в документе, чтобы построить шапку таблицы. Это можно сделать, свернув таблицу по колонке "Размер" или выбрав различные размеры отдельным пакетом запроса.

3. Алгоритм вывода (Программный код)

Теперь рассмотрим, как это реализуется в коде. Основная идея: мы выводим левую часть строки методом Вывести, а затем в цикле "прилепляем" к ней остальные ячейки справа методом Присоединить.

Разберем пример кода:


// Получаем макет
Макет = УправлениеПечатью.МакетПечатнойФормы("ПФ_MXL_Матрица");
ТабДок = Новый ТабличныйДокумент;

// 1. ФОРМИРУЕМ ШАПКУ
// Выводим левый верхний угол
ОбластьШапкаЛево = Макет.ПолучитьОбласть("ОбластьШапкаЛево");
ТабДок.Вывести(ОбластьШапкаЛево);

// Получаем список всех уникальных размеров для колонок
ТаблицаРазмеров = ДанныеДляПечати.Скопировать();
ТаблицаРазмеров.Свернуть("Размер");
ТаблицаРазмеров.Сортировать("Размер Возр");

// Цикл по колонкам шапки
ОбластьШапкаКолонка = Макет.ПолучитьОбласть("ОбластьШапкаКолонка");
Для Каждого СтрРазмер Из ТаблицаРазмеров Цикл
    ОбластьШапкаКолонка.Параметры.Размер = СтрРазмер.Размер;
    // ВАЖНО: Используем Присоединить вместо Вывести
    ТабДок.Присоединить(ОбластьШапкаКолонка);
КонецЦикла;

// 2. ФОРМИРУЕМ СТРОКИ С ДАННЫМИ
ОбластьСтрокаЛево = Макет.ПолучитьОбласть("ОбластьСтрокаЛево");
ОбластьЯчейка = Макет.ПолучитьОбласть("ОбластьСтрокаЯчейка");

// Группируем данные по товарам для обхода
// Предположим, у нас есть выборка товаров
Пока ВыборкаТоваров.Следующий() Цикл
    
    // Выводим название товара (начало строки)
    ОбластьСтрокаЛево.Параметры.Товар = ВыборкаТоваров.Товар;
    ТабДок.Вывести(ОбластьСтрокаЛево);
    
    // Теперь бежим по тем же колонкам размеров, что и в шапке
    Для Каждого СтрРазмер Из ТаблицаРазмеров Цикл
        
        // Ищем значение для текущего товара и текущего размера
        // В реальном коде лучше использовать заранее подготовленную структуру поиска,
        // а не фильтровать данные на каждой итерации, но для примера логика такая:
        Количество = ПолучитьКоличество(ВыборкаТоваров.Товар, СтрРазмер.Размер);
        
        ОбластьЯчейка.Параметры.Значение = Количество;
        
        // Присоединяем ячейку справа к строке товара
        ТабДок.Присоединить(ОбластьЯчейка);
        
    КонецЦикла;
    
КонецЦикла;

Важные нюансы метода Присоединить:

Подход №2: Использование СКД (Программный вывод)

Если требования к дизайну печатной формы не слишком жесткие (например, это внутренняя ведомость), использование Системы Компоновки Данных — самый эффективный путь. — в разработке поможет инструментарий для отладки кода и СКД. СКД умеет строить кросс-таблицы "из коробки" без написания сложных циклов.

Почему это удобно? Потому что СКД берет на себя самую сложную часть: сопоставление данных на пересечении строк и колонок.

Как реализовать СКД в печатной форме?

Многие разработчики привыкли, что СКД — это только для отчетов. Однако мы можем использовать мощь этого механизма и внутри модуля менеджера документа или обработки печати, а значительно упростить код поможет библиотека СКДБилдер.

Порядок действий:

  1. Создайте макет с типом Схема компоновки данных.
  2. В схеме напишите запрос, который выбирает: Товар, Размер, Количество.
  3. На вкладке "Настройки" добавьте группировку "Таблица".
    • В Строки таблицы добавьте группировку по полю "Товар".
    • В Колонки таблицы добавьте группировку по полю "Размер".
  4. В выбранных полях укажите ресурс "Количество".

Теперь нам нужно программно выполнить эту схему и вывести результат не в форму отчета, а в табличный документ, который вернет функция печати.

Пример программного вывода СКД в табличный документ:


Функция СформироватьПечатнуюФормуСКД(МассивОбъектов)
    
    ТабДок = Новый ТабличныйДокумент;
    
    // Получаем схему из макета
    СхемаКомпоновкиДанных = ПолучитьМакет("МакетСКД");
    
    // Настройки компоновки
    Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
    
    // Передаем параметр (ссылку на документ)
    // В запросе СКД должно быть условие: ГДЕ Ссылка В (&МассивСсылок)
    ПараметрСсылка = Настройки.ПараметрыДанных.Элементы.Найти("МассивСсылок");
    ПараметрСсылка.Значение = МассивОбъектов;
    ПараметрСсылка.Использование = Истина;
    
    // Компоновщик макета
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
    
    // Процессор компоновки
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
    
    // Процессор вывода (здесь мы указываем наш ТабДок как приемник)
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ТабДок);
    
    // Вывод результата
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    Возврат ТабДок;
    
КонецФункции

Сравнение методов и рекомендации

Давайте проанализируем, какой метод лучше выбрать в вашей ситуации.

Выбирайте метод Присоединить (Ручной), если:

Выбирайте метод СКД, если:

Альтернативный вариант: Скрытие колонок

Если количество возможных размеров (колонок) невелико (например, всего 5-10 вариантов XS, S, M, L, XL), можно использовать упрощенный метод. В макете рисуется таблица сразу со всеми возможными колонками. Каждой колонке присваивается имя.

При формировании печати мы анализируем, какие размеры присутствуют в документе. Для тех размеров, которых нет, мы устанавливаем видимость области в Ложь.


// Пример скрытия ненужных колонок
Если Не СписокРазмеровВДокументе.Содержит("XXL") Тогда
    ТабДок.Область("КолонкаXXL").Видимость = Ложь;
КонецЕсли;

Этот способ самый простой в реализации, но он не подходит для полностью динамических задач (например, если колонками являются даты поставок или названия складов, которых может быть сотни).

Оптимизация производительности

При использовании ручного метода вывода (через Присоединить) всегда помните о производительности. Главная ошибка — выполнение запроса к базе данных внутри двойного цикла (Товар -> Размер). Это создает огромную нагрузку на сервер.

Правильный подход:

  1. Сделать один запрос, получающий все данные (Товар, Размер, Значение).
  2. Выгрузить результат в Таблицу Значений или Дерево Значений.
  3. Индексировать эту таблицу для быстрого поиска строк методом НайтиСтроки() в памяти, а не в базе данных.

Используя эти методы, вы сможете создавать печатные формы любой сложности, адаптирующиеся под данные документа.

← На главную