Как заполнить макет отчета с заранее не известным числом колонок по датам

Программист 1С v8.3 (Обычные формы) Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке отчетов в 1С часто возникает задача вывести данные в виде таблицы, где количество колонок заранее не известно и зависит от выбранного пользователем периода (например, отчет по дням недели). Использование системы компоновки данных (СКД) не всегда возможно или оправдано, особенно в старых конфигурациях, таких как УПП, или при жестких требованиях к оформлению макета. В этой статье мы подробно разберем, как правильно реализовать динамический вывод колонок, используя программный метод формирования табличного документа.

Принцип построения динамического макета: Метод «Шахматки»

Основная ошибка начинающих разработчиков заключается в попытке вывести всю строку целиком, а затем как-то «втиснуть» в нее данные по датам. В 1С правильный подход основан на пересечении областей. Мы должны разделить макет не только по горизонтали (строки), но и по вертикали (колонки).

Проанализируем структуру макета. Нам необходимо выделить:

  1. Вертикальные области: фиксированная левая часть (Заказ, Номенклатура и т.д.) и динамическая часть (Колонка с датой).
  2. Горизонтальные области: Шапка таблицы и Строка данных.

В результате в макете образуются именованные области на пересечении. Например, область Строка|ДатыПериода — это именно та ячейка, которая будет повторяться для каждой даты в каждой строке отчета.

Шаг 1: Подготовка областей макета

Для начала рассмотрим, как правильно получить области из макета. Вместо того чтобы обращаться к макету внутри циклов (что существенно замедляет работу), мы получим все необходимые «кирпичики» заранее. Выясним, какие именно области нам понадобятся:


// Получаем макет
Макет = Отчеты.ртбПланОтгрузки.ПолучитьМакет("Макет");

// Области шапки
ШапкаЛево = Макет.ПолучитьОбласть("ШапкаТаблицы|ОбластьДанных"); // Фиксированные колонки слева
ШапкаДата = Макет.ПолучитьОбласть("ШапкаТаблицы|ДатыПериода"); // Колонка для даты

// Области строк
СтрокаЛево = Макет.ПолучитьОбласть("Строка|ОбластьДанных"); // Фиксированные данные строки
СтрокаДата = Макет.ПолучитьОбласть("Строка|ДатыПериода"); // Ячейка с количеством на дату

Обратите внимание на синтаксис "Строка|ДатыПериода". Символ вертикальной черты указывает 1С, что нужно взять область, находящуюся на пересечении строки Строка и колонки ДатыПериода.

Шаг 2: Формирование шапки таблицы

Разберем процесс вывода шапки. Сначала мы выводим фиксированную часть методом Вывести(), а затем в цикле присоединяем колонки с датами методом Присоединить().


ТабДок.Вывести(ШапкаЛево);

ТекДата = НачалоПериода;
Пока ТекДата <= КонецПериода Цикл
    ШапкаДата.Параметры.Дата = Формат(ТекДата, "ДФ=dd.MM.yy");
    ТабДок.Присоединить(ШапкаДата);
    ТекДата = ТекДата + 86400; // Добавляем одни сутки (60*60*24)
КонецЦикла;

Шаг 3: Вывод данных и заполнение колонок

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

Рассмотрим алгоритм формирования строки:


// Внешний цикл по группировкам запроса (Заказ, Номенклатура)
Для Каждого СтрокаДанных Из РезультатЗапроса Цикл
    
    // 1. Выводим левую фиксированную часть строки
    СтрокаЛево.Параметры.Заполнить(СтрокаДанных);
    ТабДок.Вывести(СтрокаЛево);
    
    // 2. Запускаем цикл по всем датам периода (точно такой же, как в шапке)
    ТекДата = НачалоПериода;
    Пока ТекДата <= КонецПериода Цикл
        
        // Очищаем параметр перед заполнением, чтобы данные не дублировались
        СтрокаДата.Параметры.Количество = ""; 
        
        // Проверяем, есть ли данные на эту дату в нашей выборке
        // Для оптимизации лучше использовать поиск в Таблице Значений с индексами
        Если ТекДата = СтрокаДанных.ДатаОтгрузки Тогда
            СтрокаДата.Параметры.Количество = СтрокаДанных.Количество;
        КонецЕсли;
        
        // 3. Присоединяем ячейку к текущей строке
        ТабДок.Присоединить(СтрокаДата);
        
        ТекДата = ТекДата + 86400;
    КонецЦикла;
    
КонецЦикла;

Оптимизация производительности: Использование индексированной таблицы

Если данных много, поиск внутри цикла по датам может стать «бутылочным горлышком». Рассмотрим более профессиональный способ. Выгрузим результат запроса в ТаблицуЗначений и построим индекс по ключевым полям и дате.

Проанализируем преимущества этого метода: вместо перебора всей выборки мы используем метод Найти() или НайтиСтроки(), что работает в десятки раз быстрее.


ТаблицаДанных = РезультатЗапроса.Выгрузить();
ТаблицаДанных.Индексы.Добавить("Заказ, Номенклатура, ДатаОтгрузки");

// При заполнении колонок:
СтруктураПоиска = Новый Структура("Заказ, Номенклатура, ДатаОтгрузки");
СтруктураПоиска.Заказ = ТекЗаказ;
СтруктураПоиска.Номенклатура = ТекНоменклатура;
СтруктураПоиска.ДатаОтгрузки = ТекДата;

НайденныеСтроки = ТаблицаДанных.НайтиСтроки(СтруктураПоиска);
Если НайденныеСтроки.Количество() > 0 Тогда
    СтрокаДата.Параметры.Количество = НайденныеСтроки[0].Количество;
КонецЕсли;

Важные рекомендации по оформлению

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

Резюме

Мы рассмотрели механизм создания гибких отчетов в 1С. Главный секрет успеха — правильное именование областей в макете и использование метода Присоединить() для каждой динамической колонки внутри каждой строки. Помните, что порядок вызова методов критичен: сначала Вывести() для левой части, затем последовательные Присоединить() для всех колонок вправо.

← На главную