При разработке отчетов по производительности труда или анализу выполнения производственных этапов часто возникает задача рассчитать «чистое» время работы. Проблема заключается в том, что обычная разница между временем начала и окончания документа (или этапа) включает в себя обеденные перерывы, технологические паузы и время отдыха, которые не должны учитываться в фактических трудозатратах. В этой статье мы подробно разберем, как реализовать механизм исключения таких перерывов, используя возможности языка запросов 1С и программные методы.
Представим ситуацию: у нас есть документ, фиксирующий выполнение работы с 08:00 до 17:00. В течение этого периода существует регламентированный обед с 12:00 до 13:00 и пятиминутные перерывы в конце каждого часа. Если просто вычесть время начала из времени окончания, мы получим 9 часов, хотя фактически работа велась меньше. Наша цель — вычесть все интервалы перерывов, которые полностью или частично попали внутрь рабочего интервала.
Для решения мы проанализируем наиболее эффективный метод — метод пересечения интервалов в запросе. Мы будем использовать таблицу выполненных работ и таблицу (регистр) расписания перерывов.
Первым делом нам необходимо получить таблицу работ и присоединить к ней таблицу перерывов. Основная сложность здесь заключается в условии связи. Перерыв считается относящимся к работе, если он пересекается с ней по времени. Рассмотрим условие пересечения: перерыв пересекается с работой, если его начало меньше окончания работы, а его окончание больше начала работы.
Проанализируем пример запроса, где мы выбираем данные из временной таблицы ВТ_Работы и соединяем их с регистром РасписаниеПерерывов:
ВЫБРАТЬ
Работы.Этап КАК Этап,
Работы.ДатаНачала КАК ДатаН,
Работы.ДатаОкончания КАК ДатаК,
Перерывы.ДатаНачала КАК Перерыв_ДатаН,
Перерывы.ДатаОкончания КАК Перерыв_ДатаК
ПОМЕСТИТЬ ВТ_Пересечения
ИЗ
ВТ_Работы КАК Работы
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.РасписаниеПерерывов КАК Перерывы
ПО (Перерывы.ДатаНачала < Работы.ДатаОкончания)
И (Перерывы.ДатаОкончания > Работы.ДатаНачала)
Использование ЛЕВОЕ СОЕДИНЕНИЕ критически важно, так как если работа выполнялась в период, где нет перерывов, запись о работе все равно должна остаться в выборке (в этом случае поля перерыва будут содержать NULL).
Часто перерыв может начинаться раньше, чем работа, или заканчиваться позже нее. Чтобы корректно посчитать время перерыва, которое нужно вычесть, мы должны «обрезать» границы перерыва по границам работы. Для этого используем конструкцию ВЫБОР.
Рассмотрим подробнее логику:
Перерыв_ДатаН меньше ДатаН, то для расчета используем ДатаН. В противном случае оставляем Перерыв_ДатаН.Перерыв_ДатаК больше ДатаК, то используем ДатаК. В противном случае оставляем Перерыв_ДатаК.На языке запросов 1С это будет выглядеть следующим образом:
ВЫБРАТЬ
Этап,
ДатаН,
ДатаК,
ВЫБОР
КОГДА Перерыв_ДатаН < ДатаН ТОГДА ДатаН
ИНАЧЕ Перерыв_ДатаН
КОНЕЦ КАК СкорректированныйПерерывН,
ВЫБОР
КОГДА Перерыв_ДатаК > ДатаК ТОГДА ДатаК
ИНАЧЕ Перерыв_ДатаК
КОНЕЦ КАК СкорректированныйПерерывК
ПОМЕСТИТЬ ВТ_ПодготовленныеДанные
ИЗ
ВТ_Пересечения
После того как мы получили точные границы перерывов внутри каждого рабочего интервала, мы можем вычислить разницу в секундах. Выясним причину использования секунд: это самый точный способ избежать ошибок округления при последующем суммировании.
Важный нюанс: при суммировании длительности перерывов необходимо обработать значение NULL, которое возникает, если работа не попала ни на один перерыв. Используем функцию ЕСТЬNULL.
ВЫБРАТЬ
Этап,
ДатаН,
ДатаК,
РАЗНОСТЬДАТ(ДатаН, ДатаК, СЕКУНДА) КАК ОбщаяДлительностьСекунд,
СУММА(ЕСТЬNULL(РАЗНОСТЬДАТ(СкорректированныйПерерывН, СкорректированныйПерерывК, СЕКУНДА), 0)) КАК СуммаПерерывовСекунд
ПОМЕСТИТЬ ВТ_ИтоговыеСекунды
ИЗ
ВТ_ПодготовленныеДанные
СГРУППИРОВАТЬ ПО
Этап,
ДатаН,
ДатаК
Теперь остается просто вычесть сумму перерывов из общей длительности. Полученный результат можно перевести обратно в часы или минуты для вывода в отчет. Проанализируем финальное вычисление:
ВЫБРАТЬ
Этап,
ДатаН,
ДатаК,
(ОбщаяДлительностьСекунд - СуммаПерерывовСекунд) КАК ЧистоеВремяСекунд
ИЗ
ВТ_ИтоговыеСекунды
Если в вашей конфигурации (например, 1С:ERP или 1С:ЗУП) уже настроены графики работы, задачу можно решить более элегантно через встроенные функции программного интерфейса. Посмотрим на использование общего модуля КалендарныеГрафики.
Функция ПолучитьКоличествоРабочегоВремени позволяет получить количество секунд между двумя датами с учетом конкретного графика. Это избавляет от написания сложных запросов, так как платформа сама учитывает и перерывы, и праздничные дни, и сокращенные смены.
Пример программного кода:
ДлительностьСекунд = КалендарныеГрафики.ПолучитьКоличествоРабочегоВремени(ГрафикРаботы, ДатаНачала, ДатаОкончания);
Преимущества этого метода:
Проанализируем ситуацию, когда этап начинается в 22:00 одного дня и заканчивается в 06:00 следующего. Если расписание перерывов хранится просто как «с 12:00 до 13:00», запрос может отработать некорректно. В таких случаях мы рекомендуем:
ДатаН и ДатаК не только по времени, но и по полной дате.Если отчет строится по тысячам документов, левое соединение с регистром перерывов может замедлить выполнение. Разберем по шагам, как ускорить процесс:
ДатаНачала и ДатаОкончания в регистре перерывов проиндексированы.Таким образом, мы рассмотрели надежный способ исключения перерывов из временных интервалов. Использование РАЗНОСТЬДАТ в секундах и грамотная корректировка границ интервалов через ВЫБОР позволяют получить точные данные для любых управленческих отчетов.