Есть у меня один клиент. Владеет автосервисом с кучей сервисных постов. Для тех, кто не в курсе, сервисный пост - это оборудованное место, где мастер обслуживает автомобиль. Так вот, захотел однажды этот клиент узнать, сколько времени занимает обслуживание машин на каждом сервисном посту и в целом. Казалось бы, что может быть проще. Есть документ, который фиксирует номер сервисного поста, время поступления авто на сервис и время окончания работ. Бери, пиши несложный запрос, вычисляй разность дат заезда и выезда в секундах, дели на 3600 и получай результат в часах. А не тут то и было. Оказалось, что есть очень много мелких работ, на которые уходит от 2 до 15 минут, и клиенту очень неудобно видеть такие цифры, как, например, 0.1 часа, 0.25 часа и т.п. Ему нужно время в формате ЧЧ:ММ, без дней, чистые часы и минуты. "Хм...", - сказал я и полез почитать умные мысли других людей по этому поводу.
Решение усложнял тот момент, что отчет был написан не на СКД, а использовал известный многим программистам механизм отчета "Список / кросс-таблица", который работает на построителе отчетов. Итак, поиск результатов не принес. Вариантов предлагалось множество, но конкретного рабочего ответа я не получил. Поэтому "ноу-хау" пришлось придумывать самому.
Поскольку мой дорогой клиент хотел видеть время в формате Часы:Минуты, то какое-либо использование даты не подходило банально потому, что в дате не может быть больше 24 часов. Выносить всю кухню из запроса для внешних расчетов тоже очень не хотелось. И я понял, что поможет мне только число с плавающей запятой. Если примем факт, что до запятой мы будем хранить часы, а после запятой будут минуты, то задача очень даже может иметь решение.
Итак, мои "6 шагов к успеху" стали примерно такими:
- Представляем результат в виде числа с плавающей запятой.
- До запятой храним часы, после - минуты.
- Вычисляем часы.
- Вычисляем минуты.
- Складываем часы и минуты в единый результат.
- При выводе результата используем формат числа, а именно настройку "Разделитель дробной части".
Наверное понятно, что самые большие трудности были на 3 и 4 шаге. Функции языка запросов для работы с числами и их преобразования достаточно скудный в 1С. Пришлось много анализировать. Конечно, проще всего было вычислить часы:
ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
Как мы все знаем, функция запроса ВЫРАЗИТЬ() округляет число, чем мы и воспользуемся. Получаем целое число часов. Оно может быть совпасть с реальным, если системе нечего округлять, или быть больше на единицу, когда система выполнила округление. Если произошло округление, то из полученного числа вычитаем единицу, в противном случае берем неизменный результат функции ВЫРАЗИТЬ().
Логично предположить, что количество минут тоже вычисляется аналогичным способом, только уже используя часть запроса с вычислением часов:
ВЫБОР
КОГДА (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) * 60 <= СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
ТОГДА ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
Затем полученные два числа нужно привести к одному. Для этого объединяем две части путем сложения, но кроме этого, результат второго запроса нужно поделить на 100, чтобы оно перешло в дробную часть. Этот кусок должен размещаться в части вычисления итогов:
ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ +
ВЫБОР
КОГДА (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) * 60 <= СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ
ТОГДА ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ((СУММА(ДлительностьРабот) - 3600 * ВЫБОР
КОГДА (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) * 3600 <= СУММА(ДлительностьРабот)
ТОГДА ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))
ИНАЧЕ (ВЫРАЗИТЬ(СУММА(ДлительностьРабот) / 3600 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ) / 60 КАК ЧИСЛО(15, 0))) - 1
КОНЕЦ / 100
И наконец, оформляем результат в коде. Где общий отчет - отчет-объект "Список / кросс-таблица".
ОбщийОтчет.ЗаполнитьПоказатели("ДлительностьРабот", "Длительность работ, Часы:Минуты", Истина, "ЧДЦ=2; ЧРД=:");
И в нашем конечном отчете видим такой результат:
Наверняка, что-то похожее можно реализовать и для СКД. И скорее всего на СКД это будет даже проще. Моя публикация не претендует на Нобелевскую премию. Я думаю, что кому-нибудь она окажется полезной, поэтому публикую. Всем удачи.
P.S. В комментарии был задан вопрос "На последнем скрине в первом посту 1:00 и 0:01, дали в сумме 1:02. Этому отчету можно верить?". Отчету можно верить. В тексте статьи я забыл добавить, что в отчете может быть некоторая визуальная погрешность, но только из-за того, что остаток секунд, отбрасывается.
P.P.S. Более краткое и красивое решение предложил ildarovich, за что ему большой плюс и огромная благодарность:
РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1, 1, 1), ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, &ДлительностьВСекундах), ЧАС)
+ МИНУТА(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1), СЕКУНДА, &ДлительностьВСекундах)) / 100