Как реализовать нестандартную периодичность в 6 месяцев от даты начала в отчетах 1С:Комплексная автоматизация 2?

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

В процессе работы с отчетами в 1С:Комплексная автоматизация 2 (КА 2) часто возникает потребность в анализе данных по нестандартным временным интервалам. Один из таких запросов — построение отчета с периодичностью в 6 месяцев, начинающейся не с начала года или полугодия, а строго от даты, заданной пользователем. Например, если пользователь выбирает 01.02.2023 как дату начала, первый период должен охватывать 01.02.2023 - 31.07.2023, второй — 01.08.2023 - 31.01.2024 и так далее. Стандартные периодичности (день, месяц, полугодие, год) не позволяют гибко настроить такой интервал. В этой статье мы рассмотрим два основных подхода к решению данной задачи: через Систему Компоновки Данных (СКД) с помощью запроса и через модификацию формы отчета расширениями.

Подход 1: Формирование периодов с помощью запроса в СКД

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

  1. Создание вспомогательных таблиц чисел:

    Для начала нам потребуется способ генерировать последовательность чисел в запросе. Это удобно сделать с помощью временных таблиц, содержащих цифры от 0 до 9, а затем их комбинаций для получения более широкого диапазона чисел (например, от 0 до 99).

    
    ВЫБРАТЬ
        0 КАК Цифра
    ПОМЕСТИТЬ табЦифр
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        1
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        2
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        3
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        4
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        5
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        6
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        7
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        8
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ
        9
    ;
    ////////////////////////////////////////////////////////////////////////////////
    ВЫБРАТЬ
        таб.Цифра * 10 + табЦифр.Цифра КАК локЧисло
    ПОМЕСТИТЬ табЧисел
    ИЗ
        табЦифр КАК таб
            ЛЕВОЕ СОЕДИНЕНИЕ табЦифр КАК табЦифр
            ПО (ИСТИНА)
    ;
    

    Здесь мы сначала создаем таблицу табЦифр с числами от 0 до 9. Затем, используя декартово произведение (ЛЕВОЕ СОЕДИНЕНИЕ ... ПО (ИСТИНА)), формируем таблицу табЧисел, которая будет содержать числа от 0 до 99. Этого диапазона обычно достаточно для большинства отчетов, но при необходимости можно добавить еще одно соединение с табЦифр для получения чисел до 999 и так далее.

  2. Генерация 6-месячных периодов:

    Имея таблицу чисел, мы можем использовать их для последовательного добавления месяцев к дате начала, заданной пользователем. Для этого нам пригодится функция ДОБАВИТЬКДАТЕ().

    
    ВЫБРАТЬ
        ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) КАК НачПериода,
        ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * (табЧисел.локЧисло + 1)) КАК КонПериода
    ИЗ
        табЧисел КАК табЧисел
    

    В этом запросе &дтНачала — это параметр, который мы будем передавать из настроек отчета СКД (дата начала, указанная пользователем). Мы добавляем к этой дате 6 * локЧисло месяцев для получения начала текущего 6-месячного интервала и 6 * (локЧисло + 1) месяцев для получения начала следующего 6-месячного интервала. Полученное КонПериода будет началом следующего интервала. Важно: для корректного включения всего дня в конец периода, если мы хотим, например, чтобы период был "с 01.02.2023 по 31.07.2023 23:59:59", нам нужно будет либо вычесть одну секунду из КонПериода, либо использовать функции КОНЕЦПЕРИОДА() при связывании с данными, либо просто помнить, что КонПериода здесь обозначает начало следующего периода.

  3. Интеграция с данными регистра:

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

    
    ВЫБРАТЬ
        Периоды.НачПериода КАК ПериодНачала,
        ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1) КАК ПериодОкончания,
        ЕСТЬNULL(Регистр.ВыручкаОборот, 0) КАК ВыручкаОборот
    ИЗ
        (ВЫБРАТЬ
            ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) КАК НачПериода,
            ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * (табЧисел.локЧисло + 1)) КАК КонПериода
        ИЗ
            табЧисел КАК табЧисел
        ГДЕ
            ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) <= &КонецПериодаОтчета
        ) КАК Периоды
            ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(
                    НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ),
                    КОНЕЦПЕРИОДА(&КонецПериодаОтчета, ДЕНЬ),
                    Период,
                    ) КАК Регистр
            ПО Регистр.Период МЕЖДУ Периоды.НачПериода И ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1)
    УПОРЯДОЧИТЬ ПО
        Периоды.НачПериода
    

    В этом примере мы:

    1. Создаем вложенный запрос Периоды, который генерирует наши 6-месячные интервалы. Мы также добавляем условие ГДЕ ДОБАВИТЬКДАТЕ(&дтНачала, МЕСЯЦ, 6 * табЧисел.локЧисло) <= &КонецПериодаОтчета, чтобы генерировать периоды только до заданной пользователем даты окончания отчета.
    2. Соединяем эти периоды с виртуальной таблицей Обороты регистра ВыручкаИСебестоимостьПродаж. Важно использовать ЛЕВОЕ СОЕДИНЕНИЕ, чтобы в отчете отображались все 6-месячные интервалы, даже те, по которым не было движений.
    3. В условии соединения ПО Регистр.Период МЕЖДУ Периоды.НачПериода И ДОБАВИТЬКДАТЕ(Периоды.КонПериода, СЕКУНДА, -1) мы явно указываем, что данные регистра должны попадать в интервал от начала текущего периода до одной секунды до начала следующего периода. Это обеспечивает корректное включение всех данных за 6 месяцев.
    4. ЕСТЬNULL(Регистр.ВыручкаОборот, 0) позволяет выводить нули вместо пустых значений для периодов без движений, что делает отчет более читабельным.
    5. Параметры &дтНачала и &КонецПериодаОтчета должны быть определены в СКД и заполняться пользователем. При передаче их в виртуальную таблицу оборотов регистра рекомендуется использовать НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ) и КОНЕЦПЕРИОДА(&КонецПериодаОтчета, ДЕНЬ) для корректного охвата данных за полные дни.

    После выполнения запроса в СКД, вы сможете настроить группировки по полю ПериодНачала или ПериодОкончания и вывести необходимые агрегированные данные (например, сумму ВыручкаОборот).

    Важные моменты при работе с датами в запросах:

    • Функции НАЧАЛОПЕРИОДА(<Дата>, <ТипПериода>) и КОНЕЦПЕРИОДА(<Дата>, <ТипПериода>): Эти функции позволяют получить точные границы дня, месяца, квартала, года и т.д. Если вам нужно, например, всегда начинать период с начала дня, используйте НАЧАЛОПЕРИОДА(&дтНачала, ДЕНЬ).
    • РАЗНОСТЬДАТ(<Дата1>, <Дата2>, <ТипРазности>): Эта функция может быть полезна для определения длительности интервалов или проверки условий.

Подход 2: Модификация формы отчета через расширения (для типовых отчетов)

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

  1. Использование расширений:

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

  2. Объект "СтандартныйПериод":

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

  3. Изменение логики выбора периода:

    В типовых отчетах за логику работы с периодом отвечают подключаемые команды и процедуры в общих модулях, например, в ОтчетыКлиент (для управляемых форм). Это такие процедуры, как:

    • Подключаемый_Период_ПриИзменении
    • Подключаемый_ВыбратьПериод
    • Подключаемый_СдвинутьПериодНазад
    • Подключаемый_СдвинутьПериодВперед

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

    Рассмотрим пример, как можно модифицировать установку периода:

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

    Для реализации такой логики вам нужно будет:

    1. Создать в расширении новый элемент управления (например, кнопку) на форме отчета, которая будет вызывать процедуру УстановитьПериодШестьМесяцев или СдвинутьПериодВпередНаШестьМесяцев.
    2. Убедиться, что параметры НачалоПериода и КонецПериода существуют в схеме СКД вашего отчета и доступны для использования. Обычно они называются так же, как и в типовых отчетах на БСП.
    3. В СКД эти параметры должны быть связаны с полями регистра в запросе (например, Регистр.Период МЕЖДУ &НачалоПериода И &КонецПериода) и иметь установленное свойство "Ограничение доступности" для пользователя, чтобы он не мог их менять напрямую, если вы хотите контролировать период только через свои кнопки, или "Включать в пользовательские настройки", если вы хотите предоставить ему возможность изменять их вручную.

Общие рекомендации и выбор подхода

Выбор между этими двумя подходами зависит от ваших конкретных требований:

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

Мы рассмотрели подробные шаги для реализации нестандартной периодичности в 6 месяцев. Надеемся, эти рекомендации помогут вам в решении задачи и позволят настроить отчетность в 1С КА 2 максимально гибко и эффективно.

← На главную