Как рассчитать количество дней действия документа, попавших в выбранный период отчета в 1С СКД?

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

При разработке отчетов в системе компоновки данных (СКД) программисты часто сталкиваются с задачей расчета показателей внутри определенного периода. Типичный пример: у нас есть документ (например, «Отсутствие сотрудника», «Договор» или «Аренда»), который имеет свои даты начала и окончания. Нам необходимо построить отчет за выбранный пользователем период (например, с 1 по 20 сентября) и определить, сколько дней действия документа пришлось именно на этот интервал.

Если документ целиком попадает в период, проблем не возникает. Но что делать, если документ начался раньше периода отчета или закончился позже него? Например, документ действует с 16 по 23 сентября, а отчет формируется по 20 сентября. В этом случае нам нужно «отсечь» лишние дни и получить ровно 5 дней (с 16 по 20 сентября), которые попали в границы отчета. Рассмотрим, как правильно реализовать эту логику в запросе 1С и настроить схему компоновки данных.

Математическая модель пересечения двух периодов

Прежде чем переходить к написанию кода в 1С, давайте разберем математическую суть пересечения двух интервалов во времени. У нас есть:

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

ДатаНачалаДок <= &ДатаОкончания И ДатаОкончанияДок >= &ДатаНачала

Если это условие выполняется, то мы можем рассчитать новые («срезанные») границы документа в рамках отчета:

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

В языке запросов 1С это реализуется с помощью простых конструкций ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ.

Решение задачи через временные таблицы (Best Practice)

Писать громоздкие условия ВЫБОР КОГДА непосредственно в соединениях с производственным календарем или в секции группировок — плохая практика разработки. Это снижает читаемость кода и может негативно сказаться на производительности СУБД. Правильнее всего решить задачу в два шага с использованием временных таблиц.

Шаг 1. Сначала мы отберем только те документы, которые пересекаются с периодом отчета, и сразу же вычислим для них скорректированные («срезанные») даты начала и окончания.

Шаг 2. Полученную временную таблицу мы соединим с производственным календарем для точного расчета рабочих или календарных дней.

Проанализируем пример запроса, реализующий эту схему:


ВЫБРАТЬ
    докОтсутствия.Сотрудник КАК Сотрудник,
    докОтсутствия.ПричинаОтсутствия КАК ПричинаОтсутствия,
    докОтсутствия.РабочийГрафик КАК РабочийГрафик,
    // Вычисляем скорректированную дату начала в рамках периода отчета
    ВЫБОР 
        КОГДА докОтсутствия.ДатаНачала > &ДатаНачала 
            ТОГДА докОтсутствия.ДатаНачала 
        ИНАЧЕ &ДатаНачала 
    КОНЕЦ КАК СрезДатаНачала,
    // Вычисляем скорректированную дату окончания в рамках периода отчета
    ВЫБОР 
        КОГДА докОтсутствия.ДатаОкончания < &ДатаОкончания 
            ТОГДА докОтсутствия.ДатаОкончания 
        ИНАЧЕ &ДатаОкончания 
    КОНЕЦ КАК СрезДатаОкончания
ПОМЕСТИТЬ ВТ_СрезанныеПериоды
ИЗ
    Документ.ОтсутствияСотрудников КАК докОтсутствия
ГДЕ
    // Отсекаем документы, которые вообще не пересекаются с периодом отчета
    докОтсутствия.ДатаНачала <= &ДатаОкончания
    И докОтсутствия.ДатаОкончания >= &ДатаНачала
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Сотрудник КАК Сотрудник,
    Пользователи.Подразделение КАК Подразделение,
    Сотрудники.РольСМК КАК РольСМК,
    ВТ.ПричинаОтсутствия КАК ПричинаОтсутствия,
    
    // Считаем количество рабочих дней по производственному календарю
    СУММА(ВЫБОР 
        КОГДА СостоянияСотрудников.Наименование = "Отпуск" 
              И ВТ.РабочийГрафик = ЛОЖЬ 
              И Календарь.ВидДня = ЗНАЧЕНИЕ(Перечисление.ВидыДнейПроизводственногоКалендаря.Рабочий) 
            ТОГДА 1 
        ИНАЧЕ 0 
    КОНЕЦ) КАК ОтпускРабочиеДни,
    
    // Считаем общее количество календарных дней, попавших в период
    КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Календарь.Дата) КАК КоличествоДнейВПериоде,
    
    ВТ.СрезДатаНачала КАК ДатаНачала,
    ВТ.СрезДатаОкончания КАК ДатаОкончания
ИЗ
    ВТ_СрезанныеПериоды КАК ВТ
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сотрудники КАК Сотрудники 
        ПО ВТ.Сотрудник = Сотрудники.Пользователь
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СостоянияСотрудников КАК СостоянияСотрудников 
        ПО ВТ.ПричинаОтсутствия = СостоянияСотрудников.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи 
        ПО ВТ.Сотрудник = Пользователи.Ссылка
        
    // Внутреннее соединение с производственным календарем по "срезанным" датам
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеПроизводственногоКалендаря КАК Календарь 
        ПО Календарь.Дата МЕЖДУ ВТ.СрезДатаНачала И ВТ.СрезДатаОкончания

СГРУППИРОВАТЬ ПО
    ВТ.Сотрудник,
    Пользователи.Подразделение,
    Сотрудники.РольСМК,
    ВТ.ПричинаОтсутствия,
    ВТ.СрезДатаНачала,
    ВТ.СрезДатаОкончания

Особенности настройки параметров в СКД

Для того чтобы наш отчет работал корректно при формировании пользователем, необходимо правильно настроить параметры в схеме компоновки данных. Обычно пользователи привыкли задавать единый параметр с типом СтандартныйПериод. Разберем, как связать его с нашими переменными в запросе.

Допустим, на вкладке «Параметры» в СКД у вас есть доступный пользователю параметр ПериодОтчета с типом СтандартныйПериод. Для параметров &ДатаНачала и &ДатаОкончания, которые мы использовали в запросе, нужно отключить флаг «Доступен список значений» и заполнить колонку «Выражение» следующим образом:

Также для параметров &ДатаНачала и &ДатаОкончания следует установить флаг «Ограничение доступности», чтобы пользователь на форме отчета видел только одно удобное поле выбора периода, а не три разных поля ввода дат.

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

← На главную