В процессе работы с отчетами в 1С:Комплексная автоматизация 2 (КА 2) часто возникает потребность в анализе данных по нестандартным временным интервалам. Один из таких запросов — построение отчета с периодичностью в 6 месяцев, начинающейся не с начала года или полугодия, а строго от даты, заданной пользователем. Например, если пользователь выбирает 01.02.2023 как дату начала, первый период должен охватывать 01.02.2023 - 31.07.2023, второй — 01.08.2023 - 31.01.2024 и так далее. Стандартные периодичности (день, месяц, полугодие, год) не позволяют гибко настроить такой интервал. В этой статье мы рассмотрим два основных подхода к решению данной задачи: через Систему Компоновки Данных (СКД) с помощью запроса и через модификацию формы отчета расширениями.
Этот подход является наиболее гибким и позволяет нам генерировать последовательность 6-месячных интервалов непосредственно в запросе, который затем будет использоваться в СКД. Мы будем использовать временные таблицы для создания последовательности чисел, а затем на их основе формировать даты начала и окончания каждого периода.
Создание вспомогательных таблиц чисел:
Для начала нам потребуется способ генерировать последовательность чисел в запросе. Это удобно сделать с помощью временных таблиц, содержащих цифры от 0 до 9, а затем их комбинаций для получения более широкого диапазона чисел (например, от 0 до 99).
ВЫБРАТЬ
0 КАК Цифра
ПОМЕСТИТЬ табЦифр
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
3
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
4
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
5
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
6
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
7
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
8
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
9
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
таб.Цифра * 10 + табЦифр.Цифра КАК локЧисло
ПОМЕСТИТЬ табЧисел
ИЗ
табЦифр КАК таб
ЛЕВОЕ СОЕДИНЕНИЕ табЦифр КАК табЦифр
ПО (ИСТИНА)
;
Здесь мы сначала создаем таблицу табЦифр с числами от 0 до 9. Затем, используя декартово произведение (ЛЕВОЕ СОЕДИНЕНИЕ ... ПО (ИСТИНА)), формируем таблицу табЧисел, которая будет содержать числа от 0 до 99. Этого диапазона обычно достаточно для большинства отчетов, но при необходимости можно добавить еще одно соединение с табЦифр для получения чисел до 999 и так далее.
Генерация 6-месячных периодов:
Имея таблицу чисел, мы можем использовать их для последовательного добавления месяцев к дате начала, заданной пользователем. Для этого нам пригодится функция ДОБАВИТЬКДАТЕ().
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) КАК НачПериода,
ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * (табЧисел.локЧисло + 1)) КАК КонПериода
ИЗ
табЧисел КАК табЧисел
В этом запросе &дтНачала — это параметр, который мы будем передавать из настроек отчета СКД (дата начала, указанная пользователем). Мы добавляем к этой дате 6 * локЧисло месяцев для получения начала текущего 6-месячного интервала и 6 * (локЧисло + 1) месяцев для получения начала следующего 6-месячного интервала. Полученное КонПериода будет началом следующего интервала. Важно: для корректного включения всего дня в конец периода, если мы хотим, например, чтобы период был "с 01.02.2023 по 31.07.2023 23:59:59", нам нужно будет либо вычесть одну секунду из КонПериода, либо использовать функции КОНЕЦПЕРИОДА() при связывании с данными, либо просто помнить, что КонПериода здесь обозначает начало следующего периода.
Интеграция с данными регистра:
Теперь, когда у нас есть таблица с желаемыми периодами, мы можем объединить ее с данными нашего регистра (например, РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты). Для этого мы можем использовать левое соединение или вложенный запрос.
ВЫБРАТЬ
Периоды.НачПериода КАК ПериодНачала,
ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1) КАК ПериодОкончания,
ЕСТЬNULL(Регистр.ВыручкаОборот, 0) КАК ВыручкаОборот
ИЗ
(ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) КАК НачПериода,
ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * (табЧисел.локЧисло + 1)) КАК КонПериода
ИЗ
табЧисел КАК табЧисел
ГДЕ
ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) <= &КонецПериодаОтчета
) КАК Периоды
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(
НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ),
КОНЕЦПЕРИОДА(&КонецПериодаОтчета, ДЕНЬ),
Период,
) КАК Регистр
ПО Регистр.Период МЕЖДУ Периоды.НачПериода И ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1)
УПОРЯДОЧИТЬ ПО
Периоды.НачПериода
В этом примере мы:
Периоды, который генерирует наши 6-месячные интервалы. Мы также добавляем условие ГДЕ ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) <= &КонецПериодаОтчета, чтобы генерировать периоды только до заданной пользователем даты окончания отчета.Обороты регистра ВыручкаИСебестоимостьПродаж. Важно использовать ЛЕВОЕ СОЕДИНЕНИЕ, чтобы в отчете отображались все 6-месячные интервалы, даже те, по которым не было движений.ПО Регистр.Период МЕЖДУ Периоды.НачПериода И ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1) мы явно указываем, что данные регистра должны попадать в интервал от начала текущего периода до одной секунды до начала следующего периода. Это обеспечивает корректное включение всех данных за 6 месяцев.ЕСТЬNULL(Регистр.ВыручкаОборот, 0) позволяет выводить нули вместо пустых значений для периодов без движений, что делает отчет более читабельным.&дтНачала и &КонецПериодаОтчета должны быть определены в СКД и заполняться пользователем. При передаче их в виртуальную таблицу оборотов регистра рекомендуется использовать НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ) и КОНЕЦПЕРИОДА(&КонецПериодаОтчета, ДЕНЬ) для корректного охвата данных за полные дни.После выполнения запроса в СКД, вы сможете настроить группировки по полю ПериодНачала или ПериодОкончания и вывести необходимые агрегированные данные (например, сумму ВыручкаОборот).
Важные моменты при работе с датами в запросах:
НАЧАЛОПЕРИОДА(<Дата>, <ТипПериода>) и КОНЕЦПЕРИОДА(<Дата>, <ТипПериода>): Эти функции позволяют получить точные границы дня, месяца, квартала, года и т.д. Если вам нужно, например, всегда начинать период с начала дня, используйте НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ).РАЗНОСТЬДАТ(<Дата1>, <Дата2>, <ТипРазности>): Эта функция может быть полезна для определения длительности интервалов или проверки условий.Если вы работаете с типовым отчетом (например, из подсистемы БСП) и не хотите изменять его основную схему СКД или запрос, вы можете рассмотреть вариант модификации поведения стандартного элемента управления периодом на форме отчета с использованием расширений.
Использование расширений:
Для сохранения возможности легкого обновления конфигурации, мы настоятельно рекомендуем использовать расширения. В расширении можно добавить новую команду или изменить поведение существующих обработчиков.
Объект "СтандартныйПериод":
В 1С существует объект СтандартныйПериод, который часто используется в типовых отчетах для представления выбираемого пользователем интервала. Он имеет свойства ДатаНачала и ДатаОкончания. В СКД обычно присутствуют параметры НачалоПериода и КонецПериода, которые могут быть связаны со свойствами этого объекта.
Изменение логики выбора периода:
В типовых отчетах за логику работы с периодом отвечают подключаемые команды и процедуры в общих модулях, например, в ОтчетыКлиент (для управляемых форм). Это такие процедуры, как:
Подключаемый_Период_ПриИзмененииПодключаемый_ВыбратьПериодПодключаемый_СдвинутьПериодНазадПодключаемый_СдвинутьПериодВпередВы можете перехватить (или переопределить в расширении) эти процедуры для формы вашего отчета. Например, при выборе пользователем какой-либо опции или при инициализации формы, вы можете программно установить значения ДатаНачала и ДатаОкончания для объекта СтандартныйПериод, реализуя вашу 6-месячную логику.
Рассмотрим пример, как можно модифицировать установку периода:
// Пример кода в модуле формы отчета, добавленного или измененного через расширение
&НаКлиенте
Процедура УстановитьПериодШестьМесяцев(Команда)
// Предполагаем, что у нас есть реквизит формы "КомпоновщикНастроек.Настройки.ПараметрыДанных.Период"
// Или отдельный реквизит формы типа СтандартныйПериод
// Получаем текущую дату начала, если она уже установлена, или используем текущую дату
// Если параметр НачалоПериода существует и имеет значение
Перем НачалоПериода;
ПараметрНачало = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиПараметр("НачалоПериода");
Если ПараметрНачало <> Неопределено И ПараметрНачало.Использование Тогда
НачалоПериода = ПараметрНачало.Значение;
Иначе
НачалоПериода = НачалоДня(ТекущаяДата()); // Дата начала по умолчанию
КонецЕсли;
// Вычисляем конец 6-месячного периода
КонецПериода = ДОБАВИТЬКДАТЕ(НачалоПериода, МЕСЯЦ, 6);
// Для корректного включения последнего дня периода, вычитаем одну секунду
КонецПериода = ДОБАВИТЬКДАТЕ(КонецПериода, СЕКУНДА, -1);
// Устанавливаем параметры в компоновщик настроек
Если ПараметрНачало <> Неопределено Тогда
ПараметрНачало.Значение = НачалоПериода;
ПараметрНачало.Использование = Истина;
КонецЕсли;
ПараметрКонецПериода = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиПараметр("КонецПериода");
Если ПараметрКонецПериода <> Неопределено Тогда
ПараметрКонецПериода.Значение = КонецПериода;
ПараметрКонецПериода.Использование = Истина;
КонецЕсли;
// Обновляем отображение периода на форме, если есть элемент управления "Период"
Если Элементы.Найти("Период") <> Неопределено Тогда
Элементы.Период.Значение.ДатаНачала = НачалоПериода;
Элементы.Период.Значение.ДатаОкончания = КонецПериода;
КонецЕсли;
КонецПроцедуры
// Пример добавления возможности сдвига периода
&НаКлиенте
Процедура СдвинутьПериодВпередНаШестьМесяцев(Команда)
ПараметрНачало = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиПараметр("НачалоПериода");
Если ПараметрНачало <> Неопределено Тогда
НачалоПериода = ДОБАВИТЬКДАТЕ(ПараметрНачало.Значение, МЕСЯЦ, 6); // Сдвигаем на 6 месяцев вперед
Иначе
НачалоПериода = НачалоДня(ТекущаяДата()); // Если нет, берем текущую
КонецЕсли;
КонецПериода = ДОБАВИТЬКДАТЕ(НачалоПериода, МЕСЯЦ, 6);
КонецПериода = ДОБАВИТЬКДАТЕ(КонецПериода, СЕКУНДА, -1);
Если ПараметрНачало <> Неопределено Тогда
ПараметрНачало.Значение = НачалоПериода;
ПараметрНачало.Использование = Истина;
КонецЕсли;
ПараметрКонецПериода = КомпоновщикНастроек.Настройки.ПараметрыДанных.НайтиПараметр("КонецПериода");
Если ПараметрКонецПериода <> Неопределено Тогда
ПараметрКонецПериода.Значение = КонецПериода;
ПараметрКонецПериода.Использование = Истина;
КонецЕсли;
Если Элементы.Найти("Период") <> Неопределено Тогда
Элементы.Период.Значение.ДатаНачала = НачалоПериода;
Элементы.Период.Значение.ДатаОкончания = КонецПериода;
КонецЕсли;
КонецПроцедуры
Для реализации такой логики вам нужно будет:
УстановитьПериодШестьМесяцев или СдвинутьПериодВпередНаШестьМесяцев.НачалоПериода и КонецПериода существуют в схеме СКД вашего отчета и доступны для использования. Обычно они называются так же, как и в типовых отчетах на БСП.Регистр.Период МЕЖДУ &НачалоПериода И &КонецПериода) и иметь установленное свойство "Ограничение доступности" для пользователя, чтобы он не мог их менять напрямую, если вы хотите контролировать период только через свои кнопки, или "Включать в пользовательские настройки", если вы хотите предоставить ему возможность изменять их вручную.Выбор между этими двумя подходами зависит от ваших конкретных требований:
ЛЕВОЕ СОЕДИНЕНИЕ.В обоих случаях критически важно корректно обрабатывать границы дат, чтобы избежать неточностей в отчетности. Всегда проверяйте, включаются ли в период начало и конец дня, особенно если в регистре время играет роль (например, движения в течение дня).
Мы рассмотрели подробные шаги для реализации нестандартной периодичности в 6 месяцев. Надеемся, эти рекомендации помогут вам в решении задачи и позволят настроить отчетность в 1С КА 2 максимально гибко и эффективно.