Как в 1С 7.7 разбить период продаж по месяцам и вывести данные в шахматную таблицу?

Программист 1С v7.7 1С:Комплексная автоматизация Управленческий учет Торговля и дистрибуция
← На главную

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

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

Особенности периодических группировок в запросах 1С 7.7

Для разбиения произвольного периода на равные временные отрезки встроенный язык запросов 1С 7.7 предоставляет готовые модификаторы даты. В тексте запроса можно указать группировку по периоду:

  1. День (или Day) — группировка движений по дням;
  2. Неделя (или Week) — группировка по календарным неделям;
  3. Месяц (или Month) — агрегация данных за каждый месяц;
  4. Квартал (или Quarter) — квартальные итоги;
  5. Год (или Year) — группировка по годам.

Рассмотрим важную деталь: по умолчанию платформа возвращает только те периоды, в которых были реальные движения по регистру. Если по товару продажи были только в январе и марте, то при обычном обходе группировка по месяцам пропустит февраль. В шахматной таблице это приведет к нарушению структуры колонок: мартовские цифры окажутся во второй колонке. Чтобы этого избежать, к группировке добавляют директиву Все: Группировка Месяц Все;. В этом случае запрос вернет все календарные месяцы интервала, подставив нулевые значения в периоды отсутствия движений.

Построение кросс-таблицы с помощью пересечения секций

В табличном документе 1С 7.7 нельзя динамически создавать произвольные ячейки во время выполнения программного кода. Формирование динамических колонок строится на пересечении горизонтальных и вертикальных секций макета с применением метода Таб.ПрисоединитьСекцию().

Проанализируем структуру макета таблицы:

  1. Вертикальные секции: назовем левую фиксированную часть Товар, повторяющуюся колонку периода — Месяц, а правую итоговую колонку — Итого.
  2. Горизонтальные секции: создадим строку заголовка Шапка, строку данных Строка и итоговую нижнюю строку Подвал.

В коде обращение к конкретной области макета происходит через комбинацию имен: "ИмяГоризонтальной|ИмяВертикальной".

Реализация отчета через прямой обход запроса

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


Процедура Сформировать()
    Перем Запрос, ТекстЗапроса, Таб;
    
    Запрос = СоздатьОбъект("Запрос");
    
    ТекстЗапроса = "
    |Период с ВыбНачПериода по ВыбКонПериода;
    |Номенклатура = Регистр.Продажи.Номенклатура;
    |Количество   = Регистр.Продажи.Количество;
    |Функция ПродажиВсего = Сумма(Количество);
    |Группировка Номенклатура без групп;
    |Группировка Месяц Все;
    |Условие (Номенклатура в СписокНоменклатуры);
    |";
    
    Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
        Предупреждение("Ошибка выполнения запроса!");
        Возврат;
    КонецЕсли;
    
    Таб = СоздатьОбъект("Таблица");
    Таб.ИсходнаяТаблица("Сформировать");
    
    // 1. Выводим шапку таблицы
    Таб.ВывестиСекцию("Шапка|Товар");
    
    // Чтобы получить список всех колонок-месяцев, обойдем первый уровень группировки периода
    ТекДата = НачМесяца(ВыбНачПериода);
    Пока ТекДата <= ВыбКонПериода Цикл
        ПериодКолонки = Формат(ТекДата, "Д ММММ YYYY");
        Таб.ПрисоединитьСекцию("Шапка|Месяц");
        ТекДата = ДобавитьМесяц(ТекДата, 1);
    КонецЦикла;
    
    Таб.ПрисоединитьСекцию("Шапка|Итого");
    
    // 2. Выводим строки по товарам
    Пока Запрос.Группировка("Номенклатура") = 1 Цикл
        ТоварНаименование = Запрос.Номенклатура.Наименование;
        Таб.ВывестиСекцию("Строка|Товар");
        
        Пока Запрос.Группировка("Месяц") = 1 Цикл
            КолМесяц = Запрос.ПродажиВсего;
            Таб.ПрисоединитьСекцию("Строка|Месяц");
        КонецЦикла;
        
        // Значение функции на верхнем уровне группировки уже содержит итог за весь период
        ИтогПоТовару = Запрос.ПродажиВсего;
        Таб.ПрисоединитьСекцию("Строка|Итого");
    КонецЦикла;
    
    // 3. Выводим общий подвал отчета
    Таб.ВывестиСекцию("Подвал|Товар");
    
    ТекДата = НачМесяца(ВыбНачПериода);
    Пока ТекДата <= ВыбКонПериода Цикл
        // Итог по колонке месяца за весь период
        ИтогЗаМесяц = 0;
        Таб.ПрисоединитьСекцию("Подвал|Месяц");
        ТекДата = ДобавитьМесяц(ТекДата, 1);
    КонецЦикла;
    
    ОбщийИтог = Запрос.ПродажиВсего;
    Таб.ПрисоединитьСекцию("Подвал|Итого");
    
    Таб.ТолькоПросмотр(1);
    Таб.Показать("Продажи по месяцам");
КонецПроцедуры

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

Альтернативный вариант: формирование промежуточной ТаблицыЗначений

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

Разберем по шагам, как построить плоскую структуру с динамическими колонками:

  1. Создаем пустой объект ТаблицаЗначений.
  2. Добавляем колонку "Номенклатура".
  3. В цикле от начальной даты до конечной с шагом в один месяц через метод ДобавитьМесяц() добавляем колонки для каждого периода (например, с идентификаторами вида "М_" + Формат(ТекДата, "ДГГГГММ")).
  4. Добавляем колонку "Итого".
  5. Заполняем таблицу данными из запроса или оборотов регистра методом УстановитьЗначение().
  6. Выполняем сортировку таблицы: ТЗ.Сортировать("Итого-");.

После подготовки данных готовая шахматная ТаблицаЗначений выводится в макет точно так же с помощью последовательного вызова Таб.ВывестиСекцию() для левой части строки и Таб.ПрисоединитьСекцию() для колонок месяцев и итогов.

← На главную