В процессе разработки отчетов в 1С, особенно в таких конфигурациях, как Альфа-Авто, часто возникает задача подсчета чистого рабочего времени между двумя событиями. Например, нам нужно узнать, сколько минут прошло с момента создания заказ-наряда до его закрытия, учитывая, что мастера работают только с 09:00 до 21:00. Если документ был создан ночью, отсчет должен начаться только с начала рабочей смены. Разберем несколько способов решения этой задачи: от математических формул до сложных запросов.
Если нам необходимо выполнить расчет в коде на языке 1С, мы можем воспользоваться формулой, которая разбивает интервал на три части: полные рабочие сутки между датами, остаток времени в первый день и прошедшее время в последний день. Рассмотрим логику, которую предложили участники обсуждения. Предположим, рабочая смена длится 12 часов (с 9 до 21), что составляет 720 минут.
Проанализируем алгоритм расчета:
Посмотрим на пример реализации такой функции:
ДельтаМинутЦелыхСуток = Макс(0, Окр((НачалоДня(ДатаКон) - НачалоДня(ДатаНач))/(24*60*60),0)-1) * ((21-9)*60);
МинутНачДатыДоКонСуток = Макс(0, (КонецДня(ДатаНач) - (24-21)*60*60) - Макс(ДатаНач, НачалоДня(ДатаНач) + 9*60*60)) / 60;
МинутКонДатыОтНачСуток = Макс(0, Мин(ДатаКон, НачалоДня(ДатаКон) + 21*60*60) - (НачалоДня(ДатаКон) + 9*60*60)) / 60;
ДельтаРабМинут = ДельтаМинутЦелыхСуток + МинутНачДатыДоКонСуток + МинутКонДатыОтНачСуток;
В данном примере используется функция Макс, чтобы избежать отрицательных значений, если дата создания заказа выходит за рамки рабочего интервала. Обратите внимание, что расчет ведется в секундах, поэтому результат делится на 60 для получения минут.
Реализация подобной логики в запросе гораздо сложнее, так как в языке запросов 1С отсутствуют гибкие математические функции, доступные во встроенном языке. Однако это возможно с использованием конструкции ВЫБОР и функции РАЗНОСТЬДАТ. Основная сложность заключается в «обрезании» времени начала и окончания, если они выпадают на нерабочие часы.
Рассмотрим структуру запроса, который позволяет вычислить разницу:
ВЫБРАТЬ
РАЗНОСТЬДАТ(&НачалоРВ, &КонецРВ, МИНУТА) *
ВЫБОР
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаНачала, ДЕНЬ, 1), ДЕНЬ), НАЧАЛОПЕРИОДА(&ДатаОкончания, ДЕНЬ), ДЕНЬ) > 0
ТОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаНачала, ДЕНЬ, 1), ДЕНЬ), НАЧАЛОПЕРИОДА(&ДатаОкончания, ДЕНЬ), ДЕНЬ)
ИНАЧЕ 0
КОНЕЦ +
ВЫБОР
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ), &ДатаНачала, МИНУТА) < РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&НачалоРВ, ДЕНЬ), &НачалоРВ, МИНУТА)
ТОГДА РАЗНОСТЬДАТ(&НачалоРВ, &КонецРВ, МИНУТА)
КОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ), &ДатаНачала, МИНУТА) < РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&КонецРВ, ДЕНЬ), &КонецРВ, МИНУТА)
ТОГДА РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&КонецРВ, ДЕНЬ), &КонецРВ, МИНУТА) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ), &ДатаНачала, МИНУТА)
ИНАЧЕ 0
КОНЕЦ КАК Минуты
В этом подходе &НачалоРВ и &КонецРВ — это границы рабочего времени (например, 09:00 и 21:00 одного дня). Выясним причину такой сложности: запрос должен отдельно обрабатывать ситуацию, когда начало и конец периода приходятся на один и тот же день, и ситуацию, когда они разделены несколькими сутками. Для точного отчета рекомендуется предварительно подготавливать данные (нормализовать даты) перед передачей их в запрос.
Для пользователей специализированных конфигураций, таких как Альфа-Авто, существует более правильный архитектурный путь. В системе уже есть регистр сведений ГрафикРаботыРесурсов. Проанализируем, почему использование регистра предпочтительнее ручных расчетов:
Начало — Конец.Алгоритм работы с регистром в запросе будет следующим: мы выбираем записи из регистра ГрафикРаботыРесурсов, которые попадают в период между ДатаСоздания и ДатаЗакрытия. Для каждой записи определяем фактическое начало (максимум из начала смены и начала документа) и фактический конец (минимум из конца смены и конца документа). После чего применяем функцию РАЗНОСТЬДАТ(..., МИНУТА) и суммируем результат по всему документу.
Если вы решили использовать программный расчет или простые формулы в запросе, важно выполнить процедуру нормализации. Выясним, что это такое. Если документ создан в 05:00 утра, а работа начинается в 09:00, то для всех расчетов ДатаНачала должна быть приравнена к 09:00 того же дня. Если же документ закрыт в 23:00, время окончания для расчета должно стать 21:00.
Посмотрим, как это сделать во встроенном языке:
Если Час(ДатаСоздания) < 9 Тогда
РабочаяДатаНачала = НачалоДня(ДатаСоздания) + 9 * 3600;
ИначеЕсли Час(ДатаСоздания) >= 21 Тогда
РабочаяДатаНачала = НачалоДня(ДатаСоздания + 86400) + 9 * 3600;
Иначе
РабочаяДатаНачала = ДатаСоздания;
КонецЕсли;
Такая предварительная обработка данных значительно упростит последующие формулы в запросе и сделает ваш отчет более надежным и понятным для пользователя.