Как правильно программно заполнять и присоединять области макета при формировании кросс-отчета в 1С

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

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

Проблема 1: Почему присоединенные области остаются пустыми или дублируют данные

В типичном сценарии разработчик сначала выводит левую часть строки (шапку строки), а затем в цикле присоединяет колонки. Рассмотрим типичную ошибку в логике работы с параметрами. Если вы получили область методом ПолучитьОбласть один раз до начала цикла по дням, то при заполнении параметра Параметры.Количество значение будет сохраняться для всех последующих итераций цикла Присоединить.

Проанализируем ситуацию: когда 1С присоединяет область, она берет текущее состояние параметров. Если в следующей итерации условие для заполнения не сработало, в параметре остается старое значение. Чтобы этого избежать, необходимо либо заново получать область макета в каждой итерации цикла по горизонтали, либо явно очищать параметры.

Проблема 2: Ошибки при сравнении дат

Часто данные не попадают в колонки из-за неточного сравнения дат. В запросе или в коде 1С дата может содержать время. Даже разница в одну секунду приведет к тому, что условие ДатаЗаправки = ДатаКалендаря вернет Ложь. Выясним причину: данные в регистрах часто записываются с точностью до секунды, а календарные графики — на начало дня.

Для решения этой проблемы необходимо всегда приводить сравниваемые значения к началу дня. В запросе для этого используется функция НАЧАЛОПЕРИОДА(Поле, ДЕНЬ), а в коде — функция НачалоДня(Значение).

Алгоритм правильного формирования кросс-отчета

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

  1. Подготовка данных. Лучше всего выгрузить результат запроса в ТаблицаЗначений. Это позволит нам быстро искать данные для конкретной ячейки (пересечение "Объект - Дата"), не обходя выборку каждый раз с начала.
  2. Вывод шапки. Сначала выводим статичную часть шапки методом Вывести, затем в цикле присоединяем колонки дат методом Присоединить.
  3. Цикл по строкам (объектам). Для каждой строки (например, транспортное средство) мы сначала выполняем метод Вывести(ОбластьСтрокаЛеваяЧасть).
  4. Вложенный цикл по колонкам (датам). Внутри каждой строки мы перебираем таблицу дат.

Посмотрим на пример правильной реализации вложенного цикла:


// Основной цикл по строкам (например, Машины)
Пока ВыборкаМашины.Следующий() Цикл
    
    // 1. Выводим начало строки (ФИО водителя, ГосНомер и т.д.)
    ОбластьСтрокаНачало = Макет.ПолучитьОбласть("Строка|Начало");
    ЗаполнитьЗначенияСвойств(ОбластьСтрокаНачало.Параметры, ВыборкаМашины);
    ТабДок.Вывести(ОбластьСтрокаНачало);
    
    // 2. Вложенный цикл по дням периода (Горизонталь)
    Для Каждого СтрДень Из ТаблицаДней Цикл
        
        // Получаем чистую область для каждой ячейки, чтобы параметры не "залипали"
        ОбластьЯчейка = Макет.ПолучитьОбласть("Строка|День");
        
        // Ищем данные для текущей машины и текущей даты
        // Для оптимизации лучше использовать Соответствие или поиск в ТЗ
        ПараметрыПоиска = Новый Структура("Машина, Период", ВыборкаМашины.Машина, СтрДень.Дата);
        НайденныеСтроки = ТЗ_Данные.НайтиСтроки(ПараметрыПоиска);
        
        Если НайденныеСтроки.Количество() > 0 Тогда
            ОбластьЯчейка.Параметры.КоличествоЛитров = НайденныеСтроки[0].Количество;
        Иначе
            // Явно прописываем 0 или пустую строку, если данных нет
            ОбластьЯчейка.Параметры.КоличествоЛитров = 0;
        КонецЕсли;
        
        // Присоединяем ячейку справа к текущей строке
        ТабДок.Присоединить(ОбластьЯчейка);
        
    КонецЦикла;
    
    // 3. В конце строки можно присоединить Итоговую колонку
    ОбластьИтог = Макет.ПолучитьОбласть("Строка|Итог");
    ОбластьИтог.Параметры.Всего = ВыборкаМашины.ИтогоПоСтроке;
    ТабДок.Присоединить(ОбластьИтог);
    
КонецЦикла;

Разберем нюансы использования областей

Обратите внимание на использование вертикальных и горизонтальных разделителей в именах областей. Чтобы метод Присоединить работал корректно, в макете области должны быть определены на пересечении строк и колонок. Например, область с именем Строка|День означает пересечение именованной строки Строка и именованной колонки День.

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

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

Если в отчете сотни машин и период в несколько месяцев, поиск в таблице значений внутри цикла может замедлить формирование отчета. Рассмотрим способ ускорения: использование объекта Соответствие. Ключом соответствия может быть строка вида Ключ = Строка(Машина.УникальныйИдентификатор()) + "_" + Формат(СтрДень.Дата, "ДФ=yyyyMMdd"). Поиск по такому ключу работает практически мгновенно по сравнению с методом НайтиСтроки.

Использование разных областей для выходных и будней

В вашем коде часто встречается проверка Если Стр.ДеньВключенВГрафик = Истина. Это правильный подход для визуального выделения выходных. Однако, вместо создания множества почти одинаковых областей (ОбластьСтрока21, ОбластьСтрока22 и т.д.), можно использовать одну область, но программно менять её оформление:


ОбластьЯчейка = Макет.ПолучитьОбласть("Строка|День");
Если НЕ СтрДень.Рабочий Тогда
    // Красим фон ячейки программно
    ОбластьЯчейка.Область().ЦветФона = ЦветаСтиля.ЦветФонаВыделения;
КонецЕсли;

Это делает макет более чистым и удобным для поддержки. Резюмируя: залог успеха ручного формирования кросс-отчета — это строгая иерархия циклов (Строки -> Колонки), обязательное использование НачалоДня для дат и пересоздание или очистка области ячейки перед каждым присоединением.

← На главную