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

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

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

В данной статье мы подробно разберем, как решить задачу построения отчета по документам-основаниям за последние 6 месяцев с разбивкой по месяцам, не прибегая к СКД. Мы проанализируем типичные ошибки начинающих разработчиков, рассмотрим изменения в архитектуре электронного документооборота (ЭДО) в современных конфигурациях и реализуем эффективный алгоритм вывода кросс-таблицы вручную.

Анализ исходной задачи и разбор критических ошибок

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

В представленном на форуме коде содержится ряд критических ошибок, которые не только не позволят коду выполниться, но и существенно снижают производительность системы. Давайте проанализируем их, чтобы никогда не допускать в своей практике:

  1. Конфликт псевдонимов (Alias) в запросе. В секции ВЫБРАТЬ запроса указано обращение к таблице через ее полное имя: ЭлектронныйДокументВходящийДокументыОснования.Ссылка.Ссылка. При этом в секции ИЗ этой же табличной части присвоен короткий псевдоним ЭДОснования. При выполнении такого запроса СУБД выдаст ошибку компиляции, так как она «не знает» длинного имени таблицы после того, как ей был назначен псевдоним. Все обращения должны идти исключительно через ЭДОснования.
  2. Избыточное разыменование ссылок (антипаттерн .Ссылка.Ссылка). Конструкция вида ЭДОснования.Ссылка.Ссылка — это классическая ошибка, за которую разработчики получают заслуженную критику. Поле Ссылка табличной части уже само по себе является ссылкой на документ-владелец. Повторное обращение к свойству Ссылка заставляет платформу выполнять избыточное левое соединение таблицы с самой собой. Это создает лишнюю нагрузку на сервер СУБД. Всегда используйте просто ЭДОснования.Ссылка.
  3. Неверный выбор реквизита для анализа. Табличная часть ДокументыОснования содержит реквизит ДокументОснование (в котором и хранится ссылка на исходный прикладной документ, например, Поступление товаров и услуг). Поле Ссылка — это сам входящий электронный документ. Если нам нужны именно основания, мы обязаны выбирать реквизит ДокументОснование.
  4. Ошибка при обходе выборки результата запроса. В коде обхода выборки написано: Результат.ЭДОснования. Однако в запросе псевдоним для поля выборки задан как КАК Ссылка. Обращение к полям выборки должно происходить строго по их именам (псевдонимам), определенным в секции ВЫБРАТЬ, то есть Результат.Ссылка.
  5. Нелогичный вывод областей макета. Метод ТабДок.Вывести(ОбластьМакета) для области «Данные» вызывается один раз до цикла заполнения параметров. В этот момент параметры пусты, поэтому в результирующий табличный документ выведется пустая строка. Затем внутри цикла та же область выводится повторно. Это нарушает структуру отчета.

Важное замечание: Изменение архитектуры ЭДО в современных конфигурациях

Если вы работаете в актуальных конфигурациях 1С (таких как ERP 2.5, Комплексная автоматизация 2.5, Управление торговлей 11.5 или Бухгалтерия предприятия 3.0), использующих современные версии Библиотеки электронных документов (БЭД), вы столкнетесь с тем, что табличной части ДокументыОснования у документа ЭлектронныйДокументВходящий больше нет.

В целях повышения производительности системы разработчики фирмы «1С» перенесли связи между электронными документами и объектами учета в регистры сведений. В современных базах связь хранится в таких регистрах, как:

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

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

Шаг 1. Формирование правильного и оптимизированного запроса

Для реализации задачи нам необходимо решить две подзадачи в запросе:

  1. Ограничить выборку периодом «Текущий месяц - 6 месяцев». Расчет этой даты крайне нежелательно делать внутри самого запроса функциями СУБД, так как это снижает производительность и мешает использованию индексов. Правильнее рассчитать граничную дату на встроенном языке и передать ее в качестве параметра.
  2. Сгруппировать данные по месяцам с помощью функции НАЧАЛОПЕРИОДА и рассчитать количество уникальных документов-оснований через агрегатную функцию КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...).

Давайте посмотрим, как выглядит правильный, оптимизированный запрос:


// 1. Расчет параметров периода на встроенном языке
ГраницаПериода = ДобавитьКДате(НачалоПериода(ТекущаяДата(), "МЕСЯЦ"), "МЕСЯЦ", -6);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	НАЧАЛОПЕРИОДА(ЭДОснования.Ссылка.Дата, МЕСЯЦ) КАК ПериодМесяц,
    |	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЭДОснования.ДокументОснование) КАК КоличествоДокументов
    |ИЗ
    |	Документ.ЭлектронныйДокументВходящий.ДокументыОснования КАК ЭДОснования
    |ГДЕ
    |	ЭДОснования.Ссылка.Дата >= &ГраницаПериода
    |ГРУППИРОВАТЬ ПО
    |	НАЧАЛОПЕРИОДА(ЭДОснования.Ссылка.Дата, МЕСЯЦ)
    |УПОРЯДОЧИТЬ ПО
    |	ПериодМесяц УБЫВ";

Запрос.УстановитьПараметр("ГраницаПериода", ГраницаПериода);
РезультатЗапроса = Запрос.Выполнить();

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

Шаг 2. Разработка структуры макета для кросс-таблицы

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

Разберем структуру макета по шагам:

Шаг 3. Реализация алгоритма вывода кросс-таблицы вручную

Теперь объединим наш запрос и подготовленный макет с помощью встроенного языка 1С. Ключевой секрет создания динамических колонок заключается в использовании метода ТабличныйДокумент.Присоединить(). Этот метод выводит указанную область макета справа от уже выведенных областей в текущей строке.

Проанализируем детальный код формирования отчета:


Процедура СформироватьОтчет(ТабДок) Экспорт

    // Получаем макет обработки/отчета
    Макет = ПолучитьМакет("Макет");
    
    // Очищаем табличный документ перед формированием
    ТабДок.Очистить();
    
    // Выводим общую шапку отчета
    ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
    ТабДок.Вывести(ОбластьШапка);
    
    // Подготавливаем данные за последние 6 месяцев
    ГраницаПериода = ДобавитьКДате(НачалоПериода(ТекущаяДата(), "МЕСЯЦ"), "МЕСЯЦ", -6);
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	НАЧАЛОПЕРИОДА(ЭДОснования.Ссылка.Дата, МЕСЯЦ) КАК ПериодМесяц,
        |	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЭДОснования.ДокументОснование) КАК КоличествоДокументов
        |ИЗ
        |	Документ.ЭлектронныйДокументВходящий.ДокументыОснования КАК ЭДОснования
        |ГДЕ
        |	ЭДОснования.Ссылка.Дата >= &ГраницаПериода
        |ГРУППИРОВАТЬ ПО
        |	НАЧАЛОПЕРИОДА(ЭДОснования.Ссылка.Дата, МЕСЯЦ)
        |УПОРЯДОЧИТЬ ПО
        |	ПериодМесяц";
        
    Запрос.УстановитьПараметр("GramicaPerioda", ГраницаПериода);
    Выборка = Запрос.Выполнить().Выбрать();
    
    // Шаг 1. Формируем шапку таблицы (Строка заголовков)
    ОбластьСтрокаЗаголовок = Макет.ПолучитьОбласть("СтрокаЗаголовок");
    ТабДок.Вывести(ОбластьСтрокаЗаголовок); // Выводим крайнюю левую ячейку шапки
    
    ОбластьКолонкаМесяц = Макет.ПолучитьОбласть("КолонкаМесяц");
    
    // Массив для временного хранения данных, чтобы не делать повторных запросов
    МассивДанных = Новый Массив;
    
    Пока Выборка.Следующий() Цикл
        
        // Заполняем параметр месяца в шапке (например, "Октябрь 2023")
        ОбластьКолонкаМесяц.Параметры.ИмяМесяца = Формат(Выборка.ПериодМесяц, "ДФ='ММММ гггг'");
        ТабДок.Присоединить(ОбластьКолонкаМесяц); // Присоединяем колонку месяца вправо
        
        // Сохраняем структуру данных во временный массив для последующего вывода строк данных
        СтруктураКолонки = Новый Структура;
        СтруктураКолонки.Вставить("Период", Выборка.PeriodMesyac);
        СтруктураКолонки.Вставить("Количество", Выборка.КоличествоДокументов);
        МассивДанных.Добавить(СтруктураКолонки);
        
    КонецЦикла;
    
    // Шаг 2. Выводим строку с данными показателей
    ОбластьДанныеЗаголовок = Макет.ПолучитьОбласть("ДанныеЗаголовок");
    ТабДок.Вывести(ОбластьДанныеЗаголовок); // Выводим левое название строки показателей
    
    ОбластьДанныеЯчейка = ... Макет.ПолучитьОбласть("ДанныеЯчейка");
    
    Для Каждого ЭлементДанных Из МассивДанных Цикл
        
        // Заполняем количество для конкретного месяца
        ОбластьДанныеЯчейка.Параметры.Количество = ЭлементДанных.Количество;
        ТабДок.Присоединить(ОбластьДанныеЯчейка); // Присоединяем ячейку с числом вправо
        
    КонецЦикла;

КонецПроцедуры

Преимущества предложенного решения

Рассмотренный нами подход позволяет полностью обойтись без СКД, решая задачу исключительно средствами встроенного языка и оптимизированных запросов 1С. Выделим основные преимущества данного метода:

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

← На главную