Как в отчете 1С сгруппировать задачи по ночным сменам с переходом через полночь?

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

В практике разработки на платформе 1С:Предприятие 8 часто встречается задача построения отчетов не по календарным суткам, а по рабочим сменам. Типичный пример: смена начинается в 20:00 одного дня и заканчивается в 08:00 следующего. Если использовать стандартные механизмы группировки по дате, одна такая смена будет разорвана на два разных дня, что сделает отчет бесполезным для анализа производительности сотрудников. В этой статье мы подробно разберем, как правильно реализовать фильтрацию и группировку для «переходящего» времени.

Метод 1. Простая фильтрация по часам в секции «ГДЕ»

Если наша основная задача — просто исключить из отчета дневные задачи и оставить только те, что были выполнены в ночной интервал (с 20:00 до 08:00), мы можем воспользоваться функцией ЧАС непосредственно в тексте запроса или в настройках отбора СКД. Рассмотрим, как это выглядит в коде запроса:


ГДЕ
    (ЧАС(СкладскаяЗадача.ДатаВыполнения) >= 20
    ИЛИ ЧАС(СкладскаяЗадача.ДатаВыполнения) <= 8)

Разберем логику этого условия. Мы указываем системе, что нас интересуют события, время которых либо больше или равно 20 (вечер текущего дня), либо меньше или равно 8 (утро следующего дня). Однако, чтобы сделать условие более лаконичным и исключить возможные ошибки на границах секунд, можно использовать отрицание «дневного» интервала:


ГДЕ
    НЕ (ЧАС(СкладскаяЗадача.ДатаВыполнения) МЕЖДУ 9 И 19)

Проанализируем этот вариант: мы отсекаем всё время с 09:00:00 до 19:59:59. Все остальное время (ночь и раннее утро) попадает в отчет. Этот метод хорош для фильтрации, но он не решает проблему группировки задач в одну строку за одну конкретную смену.

Метод 2. Виртуальное смещение времени (Метод 8 часов)

Рассмотрим более продвинутый и эффективный способ, который часто называют «методом виртуального смещения». Чтобы все события одной ночной смены попали на одну календарную дату, нам нужно «сдвинуть» время выполнения задачи назад. Если смена заканчивается в 08:00, то вычитание 8 часов из времени задачи приведет к тому, что событие, произошедшее в 07:59 утра, формально станет относиться к 23:59 предыдущего дня.

Для реализации в Системе компоновки данных (СКД) добавим вычисляемое поле или опишем поле в запросе следующим образом:

НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(ДатаВыполнения, ЧАС, -8), ДЕНЬ) КАК ДатаСмены

Разберем по шагам, что происходит в этой формуле:

  1. Функция ДОБАВИТЬКДАТЕ(ДатаВыполнения, ЧАС, -8) вычитает 8 часов из фактического времени.
  2. Если задача выполнена 02.05.2025 в 03:00, то после вычитания получится 01.05.2025 19:00.
  3. Если задача выполнена 01.05.2025 в 21:00, то получится 01.05.2025 13:00.
  4. Функция НАЧАЛОПЕРИОДА(..., ДЕНЬ) отсекает время и оставляет только дату. В обоих примерах выше результатом будет 01.05.2025.

Таким образом, все задачи, выполненные с 20:00 1-го числа до 08:00 2-го числа, будут иметь единую «Дату смены» — 1-е число. Именно по этому полю и следует делать группировку в отчете.

Метод 3. Использование конструкции ВЫБОР для точного определения смены

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


ВЫБОР 
    КОГДА ЧАС(ПолеСДатой) < 8 
    ТОГДА ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ПолеСДатой, ДЕНЬ), ДЕНЬ, -1) 
    ИНАЧЕ НАЧАЛОПЕРИОДА(ПолеСДатой, ДЕНЬ) 
КОНЕЦ

В данном случае мы явно проверяем: если час выполнения меньше 8, значит это еще «хвост» предыдущей смены, и мы принудительно отнимаем один день от даты. В противном случае считаем дату текущей. Это позволяет избежать проблем с дробными смещениями и делает логику отчета прозрачной для разработчика.

Проблема границ периода при формировании отчета

Выясним важную причину, по которой отчет может выдавать неверные данные при выборе периода (например, за месяц). Стандартный период 1С (с 01.05 по 31.05) включает данные строго внутри этих календарных границ. Однако для ночной смены, которая началась 30.04 в 20:00 и закончилась 01.05 в 08:00, часть данных будет потеряна, так как они находятся «до» начала периода.

Для корректного решения этой проблемы рекомендуется:

Профессиональные советы по оптимизации

Проанализируем ситуацию с точки зрения производительности SQL-сервера. Использование функций ЧАС() или ДОБАВИТЬКДАТЕ() в секции ГДЕ может замедлить работу отчета на больших объемах данных, так как сервер не сможет использовать индексы по полю даты. Если ваша база содержит миллионы записей СкладскаяЗадача, лучше поступить следующим образом:

  1. Сначала отобрать данные во временную таблицу по «широкому» диапазону дат (с запасом в 1 день).
  2. Затем во временной таблице рассчитать поле ДатаСмены.
  3. Наложить окончательный фильтр уже по рассчитанному полю.

Также стоит помнить о производственных календарях. Если в компании графики работы меняются (например, праздничные смены короче), жесткая прошивка часов (20:00 и 08:00) в коде может привести к ошибкам. В идеале следует использовать связь с регистром сведений ГрафикиРаботы, где для каждого дня указано точное время начала и окончания смен.

← На главную