Как разбить произвольный период на месяцы в 1С?

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

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

Постановка задачи и обработка граничных условий

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

  1. Для первого месяца: дата начала интервала определяется как наибольшее из двух значений — введенной пользователем даты начала и первого числа этого месяца.
  2. Для последнего месяца: дата окончания определятся как наименьшее из двух значений — введенной даты конца и последнего числа месяца.
  3. Для промежуточных месяцев: интервал рассчитывается строго с первого по последнее число месяца.

Выясним, какие механизмы платформы позволяют автоматизировать или описать этот процесс.

Способ 1. Использование конструкции ИТОГИ ПО ... ПЕРИОДАМИ в запросе

Платформа 1С содержит встроенный инструмент генерации временных интервалов. В секции итогов запроса можно применить конструкцию ПЕРИОДАМИ.

Рассмотрим пример построения запроса:


ВЫБРАТЬ
    Продажи.Период КАК Период,
    Продажи.Сумма КАК Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи
ГДЕ
    Продажи.Период МЕЖДУ &ДатаНач И &ДатаКон
ИТОГИ
    СУММА(Сумма)
ПО
    Период ПЕРИОДАМИ(МЕСЯЦ, &ДатаНач, &ДатаКон)

При использовании конструкции ПЕРИОДАМИ(МЕСЯЦ, &ДатаНач, &ДатаКон) система автоматически сформирует помесячные интервалы от начальной до конечной даты, даже если за отдельные месяцы в базе данных отсутствуют движения.

Важное ограничение: метод Запрос.Выполнить().Выгрузить() не сохраняет дополненные пустые периоды при прямой выгрузке в таблицу значений. Чтобы получить все месяцы (включая периоды без данных), необходимо использовать иерархический обход выборки через Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Период", "ВСЕ").

Способ 2. Разбиение периода с помощью производственного календаря или генерации последовательности

Если требуется объединить данные с календарем без использования группировок итогов, разберем два варианта:

  1. Соединение с регистром сведений: Если в конфигурации есть производственный календарь или регистр сведений с календарными датами, выполняем соединение с этой таблицей, применяя функцию НАЧАЛОПЕРИОДА(ТаблицаКалендаря.Дата, "МЕСЯЦ") и отбирая уникальные значения за указанный диапазон.
  2. Динамическая генерация дат в запросе: Если специальной таблицы нет, последовательность чисел и месяцев можно сгенерировать во временной таблице прямо в запросе через декартово произведение (с помощью ОБЪЕДИНИТЬ ВСЕ) и прибавить полученное количество месяцев к начальной дате функцией ДОБАВИТЬКДАТЕ(&ДатаНач, МЕСЯЦ, Индекс). Количество итераций рассчитывается с помощью функции РАЗНОСТЬДАТ(&ДатаНач, &ДатаКон, МЕСЯЦ).

Способ 3. Разбиение и дополнение периодов в СКД

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

Разберем настройки СКД по шагам:

  1. В наборе данных для поля даты задаем роль Период и выражение НАЧАЛОПЕРИОДА(Дата, "МЕСЯЦ").
  2. В настройках группировки отчета открываем вкладку параметров группировки по периоду.
  3. Включаем механизм «Дополнение периодов», указываем периодичность «Месяц», а также задаем начальную и конечную дату (параметры &ДатаНач и &ДатаКон).

СКД автоматически сформирует за каждый месяц строки или колонки, даже если реальных записей в этот период не было. Для практической реализации подобной структуры группировки подойдёт пример настройки шапки отчета по месяцам и неделям в СКД.

Способ 4. Программное разбиение периода в цикле на языке 1С

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

Посмотрим на пример алгоритма:


МассивПериодов = Новый Массив;

ТекущаяДата = НачалоДня(ДатаНач);
ДатаОкончания = КонецДня(ДатаКон);

Пока ТекущаяДата <= ДатаОкончания Цикл
    
    // Определяем начало и конец текущего месяца в интервале
    НачалоМесяцаИнтервала = Макс(ТекущаяДата, НачалоМесяца(ТекущаяДата));
    КонецМесяцаИнтервала = Мин(ДатаОкончания, КонецМесяца(ТекущаяДата));
    
    // Фиксируем полученный интервал
    СтруктураПериода = Новый Структура;
    СтруктураПериода.Вставить("ДатаНачала", НачалоМесяцаИнтервала);
    СтруктураПериода.Вставить("ДатаОкончания", КонецМесяцаИнтервала);
    МассивПериодов.Добавить(СтруктураПериода);
    
    // Переходим к следующему месяцу
    ТекущаяДата = НачалоМесяца(ДобавитьМесяц(ТекущаяДата, 1));
    
КонецЦикла;

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

Особенности работы с виртуальными таблицами

При работе с виртуальными таблицами регистров (например, РегистрНакопления.Продажи.Обороты) передача параметра периодичности Месяц возвращает данные только по тем периодам, в которых совершались реальные движения. Месяцы с нулевыми оборотами не попадут в выборку. Для получения полной картины без пропусков рекомендуем использовать механизмы дополнения периодов СКД или объединение результатов с таблицей календаря. Для генерации последовательности периодов в запросе можно использовать варианты построения выборки диапазона дат и чисел.

← На главную