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

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

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

Постановка задачи и основные ограничения

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

Шаг 1. Подготовка запроса с итогами

Для создания кросс-отчета нам недостаточно просто получить список записей. Нам нужна иерархическая структура данных. Рассмотрим пример запроса к регистру накопления (или регистру бухгалтерии). Проанализируем, почему здесь критически важно использование предложения ИТОГИ ПО.

Текст запроса должен выглядеть следующим образом:


ТекстЗапроса = 
"ВЫБРАТЬ
|   Остатки.Материал КАК Материал,
|   Остатки.Склад КАК Склад,
|   Остатки.КоличествоОстаток КАК Количество
|ИЗ
|   РегистрНакопления.ОстаткиМатериалов.Остатки(&Период, ) КАК Остатки
|ГДЕ
|   Остатки.Материал В (&Материал) И Остатки.Склад В (&Склад)
|ИТОГИ
|   СУММА(Количество)
|ПО
|   Материал,
|   Склад";

Использование ИТОГИ ПО позволяет нам в дальнейшем делать вложенные обходы выборки: сначала по материалам, а внутри каждого материала — по складам, на которых он числится.

Шаг 2. Формирование шапки отчета (динамические колонки)

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

  1. Сначала мы должны получить уникальный список всех складов, по которым в принципе есть остатки в рамках нашего запроса.
  2. Для этого мы можем выполнить отдельный обход результата запроса или использовать метод Выгрузить() и свернуть таблицу по колонке Склад.
  3. Выводим область «Угол» (левая верхняя часть отчета), а затем в цикле по списку складов присоединяем области колонок с названиями складов.

Рассмотрим пример кода для вывода шапки:


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

ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");

// Выводим левый верхний угол
ОбластьШапкаЗаголовок = Макет.ПолучитьОбласть("Шапка|Заголовок");
ТабДок.Вывести(ОбластьШапкаЗаголовок);

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

Шаг 3. Заполнение тела отчета (Обход данных)

Теперь перейдем к самой сложной части — заполнению строк. Нам нужно вывести название материала в первую колонку, а затем «пройтись» по всем складам, которые мы вывели в шапке, и проставить количество там, где оно есть.

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


ВыборкаМатериал = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

Шаг 4. Важные нюансы и оптимизация

При реализации данного алгоритма следует обратить внимание на несколько критических моментов, которые часто упускают новички:

Заключение

Мы рассмотрели «ручной» способ формирования кросс-отчета. Этот метод, описанный в фундаментальных трудах по разработке на платформе 1С (например, у Е.Ю. Хрусталевой), позволяет полностью контролировать процесс вывода каждой ячейки. Несмотря на то, что в реальных проектах предпочтительнее использовать СКД из-за скорости разработки и гибкости настроек, понимание механизмов Вывести/Присоединить и иерархических выборок запроса является обязательным для квалифицированного специалиста.

← На главную