Итак, в системе появилось требование: считать сроки не в календарных, а в рабочих днях. Что делать и на что обратить внимание при тестировании и разработке? Какие решения принимать с точки зрения UX?
На первый взгляд - мелочь. Добавили производственный календарь, исключили выходные, готово. На практике же это превратилось в клубок вопросов:
Когда начинается отсчёт - в момент создания заявления или с начала рабочего дня?
Что делать, если заявление пришла в 23:00?
Когда именно наступает просрочка по заявлению?
Почему пользователь видит 0/5, хотя “срок уже идёт”?
И самое неприятное - у этих вопросов нет “очевидно правильного” ответа. Любое решение - компромисс между логикой системы и ожиданиями пользователя. В этой статье я расскажу о принятых решениях и немного об их альтернативах.
P.S. Здесь не будет кода, только аналитика, примеры, и заметки по реализации и тестированию.
Суть доработки
Все сроки оказаний услуг будут производиться в рабочих днях.
Календарь один на всех. Подразумевается одинаковое рабочее расписание у каждого сотрудника. Хотя, конечно, можно подумать над усложненной вариацией календаря с личным расписанием, но в этой статье не об этом.
Актуальный календарь с рабочими днями (в нашем случае) настраивается в стороннем сервисе и подтягивается автоматически через API запросы при запуске/перезапуске системы + раз в сутки по мере работы системы проверяется, есть ли календарь на текущий год (если его все еще нет) и на следующий (аналогично, если он все еще не заполнен в системе).
Если календарь не настроен, все сроки будут рассчитываться в календарных днях.
Подразумевается, что календарь настраивается техподдержкой/админом в стороннем сервисе. Т.е. уже существует способ хранения календаря и базовые методы работы с ним.
Примеры сроков регистрации (например, заявлений)
У разных организаций словосочетание "регистрация заявления" может означать разное. Пока у одних это означает "заявление проходит авто/ручную модерацию и только потом публикуется", у других это "так, нам пришло заявление, надо ему вручную/автоматически присвоить номер", а у третьих это "что пришло - то и публикуем, причем как можно скорее".
Сроки могут быть разные. Имеет смысл это обсуждать напрямую с заказчиками. Например, у вот этих ребят (Роснедр) https://www.consultant.ru/document/cons_doc_LAW_362470/66e65fcb6db80a6ea07545b5a8770a24e22131f9/ заявление регистрируется (в том числе в электронной форме) до 12 часов рабочего дня, следующего за днем поступления заявления.
У ФНС, судя по всему, отдается на всё про всё 30 минут. Предполагаю, что также имеется ввиду электронная форма. https://www.consultant.ru/document/cons_doc_LAW_124006/577a2a95103c95741893ced43758ba0bfce44904/
У Минприроды срок 1 рабочий день с момента поступления (в том числе в электронной форме) https://www.consultant.ru/document/cons_doc_LAW_388075/3e4ca88c284c82b6f1c231dbdaa2b17a8f35343a/
И так далее. Что-то гуглится, что-то узнается только из общения. И помним: не факт, что тот закон, который мы нашли, означает именно то, что там написано, и что мы вообще все правильно п��няли, и вообще надо было все реализовывать как бабка нагадала, а не вот это вот всё.
Итоговые принятые требования по рабочим дням с учетом нюансов по рабочим часам
Будем рассуждать про некое "заявление" - на его месте может быть любая ваша сущность.
Заявление, опубликованное в системе, автоматически регистрируется в день его публикации. Это значит, что оно будет видно для работы с ним сразу и не будет “ждать” наступления ближайшего рабочего дня, чтобы отобразиться пользователям.
Как работает отсчет начала срока с момента публикации? Допустим, нам на всё про всё дано 5 дней - как их считать?
Если заявление опубликовали:
• В рабочее время (рабочий день + рабочие часы согласно расписанию), начало срока - день публикации заявления.
(даже если опубликовали в 17:59, а рабочий день до 18:00)
• В нерабочее время (нерабочий день, или рабочий день, но не рабочие часы согласно расписанию), начало срока - ближайший рабочий день (первый рабочий день, следующий за днем его получения)
Если публикация произошла в 06:00 рабочего дня, но рабочее время с 9:00, значит, начало срока = день публикации.
Если публикация произошла в 23:00 рабочего дня, а рабочий день до 18:00, то начало срока - следующий рабочий день.
Если публикация произошла в нерабочий день, то начало срока - следующий рабочий день.
Некоторые вопросы, которые мы обсуждали
Были некоторые дебаты, просто приведу ответы, к которым мы пришли.
1. В день, когда происходит публикация, какое значение счетчика? Сколько дней прошло из 5?
Ответ: 0/5 (Независимо от того, в какое время и в какой день опубликовано заявление, будет цифра 0 вплоть до 24:00 ближайшего рабочего дня)
Другие возможные варианты ответа
1 (Независимо от того, в какое время и в какой день опубликовано заявление, будет цифра 1 вплоть до 24:00 ближайшего рабочего дня).
В зависимости от того, совпало ли начало срока согласования и день публикации
Если публикация произошла в 06:00 рабочего дня, но рабочее время с 9:00, то все это время (начиная с 6:00 и до 24:00) будет цифра 1
Если публикация произошла в 23:00 рабочего дня, а рабочий день до 18:00, то с 23:00 до 24:00 - цифра 0, а с 00:00 и весь следующий день - цифра 1
Если публикация произошла в нерабочий день, то пока не настанет ближайший рабочий день, будет цифра 0. Когда настанет рабочий день - станет цифра 1
Эти варианты ответа отмели из соображения "Мы хотим показывать сколько ПРОШЛО полных рабочих дней, а не какой по счету сейчас день". Хотя, пожалуй, и то и другое видение могут сбивать с толку - когда наступает просрочка? (об этом ниже)
2. Когда именно должен увеличиваться счетчик дней?
Ответ: Сразу после того, как прошло 00:00 рабочего дня, который уже шёл в учёт срока.
Другие возможные варианты ответа
Сразу после того, как время перешагнуло через конец рабочего дня (отказались, т.к. не увидели проблемы в том, что человек задержится на работе и раскидается с задами, зачем создавать ему просрочку и какая разница, 17:59 или 18:01?)
Сразу после того, как время перешагнуло через начало рабочего дня (отказались, т.к. не слишком интуитивно выглядит для пользователя)
3. Если поздно вечером заявление (за рабочим временем) было опубликовано, то в рабочее время следующего рабочего дня счетчик должен показывать значение 1?
Ответ: Исходя из ответа на 1 вопрос, тут будет также 0. При других вариантах ответа на 1 вопрос могли бы быть другие цифры и здесь.
4. Какое время в день дедлайна должно выступать сигналом для просрочки?
Ответ: 00:00 дня следующего за дедлайном. Или 24:00 дня дедлайна, одно и то же, кому как легче читается.
Другие варианты ответа
Время окончания крайнего рабочего дня (все тот же вопрос, великая ли разница - 17:59 или 18:01?)
То же самое время, в которое заявление опубликовалось (посчитали, что не интуитивно)
Время начала следующего рабочего дня за дедлайном (посчитали, что не интуитивно)
5. При каком значении счётчика начинаем считать заявление просроченным?
Ответ: Когда прошло дней ≥ срока (Пример: “Прошло дней 5 из 5”)
Другой вариант ответа
Когда прошло дней > срока (Пример: “Прошло дней 6 из 5”. Не вписалось в остальные пункты).
6. Давайте разберем примеры. Все ли нас устраивает?
Все те же вопросы: как отображать вещи по типу “прошло N дней из M” ? Когда у нас действительно дедлайн?
Пример 1:
Заявление опубликовали 1 января, первый рабочий день 12 января, дедлайн 16 января.
Заявление обработали 13 января (неважно в какое время) - сколько дней прошло?
Ответ: 1 из 5 дней, потому что именно полных рабочих дней прошло всего 1 (12 января)
И да, технически уже шёл 2 день из 5.
Пример 2:
На обработку заявления отведено 2 дня. Заявление опубликовали в рабочий день в 06:00 или в рабочее время - 09:00, 15:00, 17:59 - неважно. Например, это понедельник.
В понедельник будет 0 из 2 дней.
Во вторник 1 из 2 дней.
В полночь со вторника на среду счетчик станет 2 из 2 и заявление будет просрочено.
То есть по факту может пройти не 2 рабочих дня, а чуть меньше. 1.5 дня. Или 1 день и 1 минута. В зависимости от времени, когда опубликовали.
Пример 3:
На обработку заявления отведено 2 дня. Заявление опубликовали в рабочий день в 20:00 (уже не рабочее время). Например, это понедельник.
В понедельник для всех будет 0 из 2 дней.
Во вторник 0 из 2 дней.
В среду 1 из 2 дней.
В полночь со среды на четверг счетчик станет 2 из 2 и заявление будет просрочено.
То есть по факту пройдёт ровно 2 рабочих дня.
Что если разные организации для обработки одной и той же штуки “живут в разном времени”? (разные часовые пояса)
Ответ: все даты вычисляются по часовому поясу сервера. Можно подумать о более изящном решении, когда в каждом браузере факт просрочки высчитывается по своему. И факт "когда стартовала обработка заявления" как будто бы тоже мог бы зависеть от локального времени. Но поскольку у нас разлёт был небольшой, остановились на времени сервера, которое совпадает с одним из охватываемых часовых поясов.
Нюансы реализации
Есть смысл кэшировать календарь на текущий год в памяти, а примерно в декабре - ещё и следующий год. Если сервис никогда не гаснет и работает годами - подумать о том, чтобы подчищать кэш старых календарей.
Есть смысл фиксировать в таблицах БД всякие вещи по типу "сколько дней прошло", чтобы не рассчитывать значения счетчиков каждый раз при обращении к серверу для снижения нагрузки и времени ответа.
Вероятно есть смысл сохранять в БД также значение "из скольких дней" - сроки работ могут иногда меняться для рассмотрения одного и того же типа заявления. Делать это на уровне таблицы, в которой отдельно хранятся конфигурации "с такого-то дня по такой-то день для такого-то типа сущности был актуален следующий срок обработки..." или сделать просто столбцом и заполнять для каждой сущности - тут уж сами решите, как быстрее, удобнее и лучше.
Как тестировать?
Проверить в обычный рабочий день
Проверить в выходной (или имитировать через БД/API)
Проверить вечером рабочего дня (или имитировать через БД/API)
Проверить поведение при всевозможных временно спящих промежуточных/нужных сервисах
Проверить поведение при отсутствующем календаре в нужном сервисе (т.е. он не сможет вовремя подгрузиться в систему). Затем добавить календарь в нужный сервис, подождать следующего дня и убедиться, что код автоматической подгрузки в 00:00 отработал.
Проверить и обычное количество рабочих дней, и просрочку.
Нагрузочное тестирование - что если начинает храниться много календарей? За предыдущий год, за текущий, за следующий?
Проверить Fallback на календарные дни
Заключение
Как мы убедились, расчёт сроков в рабочих днях — это не просто арифметика. Это баланс между правилами системы и ожиданиями пользователя. Правила нужно фиксировать и тестировать, иначе простая логика превращается в непредсказуемое поведение и вызывает вопросы у пользователей.
