Skip to content

Отпуска, больничные и отгулы ​

Модуль отвечает на вопрос «кто и когда отсутствует» и денег не двигает: ни отпускных, ни вычетов (ТЗ «Отпуска, больничные и отгулы»). Сотрудник подаёт отсутствие, руководитель и генеральный директор решают, вся система видит, что человека нет.

Реализовано: этап 1 — модуль internal/modules/absences, пакет internal/workcalendar (календарь компании), миграция 0175 (company_calendar с праздниками РФ на 2026–2027, absences, absence_events, absence_settings); этап 2 — встраивание в табель, прогресс, напоминания, помощника, командировки, чаты и главную, миграция 0176 (вид сообщения auto_reply); этап 3 — остатки, переработка, корректировки, Excel, помощник, загрузка людей, миграция 0177 (absence_adjustments, users.vacation_days_per_year).


Виды ​

ВидkindРешениеДни
ОтпускvacationРуководитель, затем генеральный директорКалендарные без праздников из календаря компании
БольничныйsickДействует сразу; руководитель принимает к сведениюКалендарные; конец может быть открыт (date_to = NULL)
Отгул за свой счётunpaidРуководитель, затем генеральный директорРабочие
Отгул в счёт переработкиovertime_offРуководитель, затем генеральный директорРабочие

Половина дня (half = am | pm) — только у отсутствия в один день и не у больничного; стоит 0,5 дня. Утро и вторая половина одного дня не пересекаются. Число дней (days) — снимок на момент подачи или правки.

Статусы ​

pending ──руководитель──▶ pending_director ──генеральный директор──▶ approved ──дата начала──▶ active ──после окончания──▶ done
   │                            │                                         │
   └──── rejected (с причиной) ◀┘                     cancelled (до начала) ◀┘
больничный ──────────────────────────────────────────▶ approved / active сразу
  • Руководитель — users.manager_id, иначе руководитель основного отдела. Нет его, он же генеральный директор или сам сотрудник — шаг пропускается.
  • Генеральный директор — absence_settings.director_id, иначе первый активный владелец (OWNER). Его собственная заявка сразу «Согласовано»; заявка, поданная администратором, — тоже.
  • Руководитель, подавший заявку за подчинённого, проходит первый уровень сам. Больничный, заведённый руководителем, сразу «принят к сведению».
  • Переходы по датам делает воркер раз в час (approved → active → done); чтение «кто отсутствует сегодня» от воркера не зависит.

Правила ​

ПравилоЧто происходит
Общие дни с другим отсутствием409 absences_overlap. Больничный поверх отпуска или отгула разрешён: тот заканчивается накануне больничного (или отменяется, если начинался позже), руководитель получает уведомление
Командировка в эти дни (не отменена и не завершена)Отпуск и отгулы — 409 absences_trip_overlap; больничный — разрешён, организатор командировки получает уведомление
Начало в прошломСотрудник: отпуск и отгулы — 400 absences_past_date, больничный — до 60 дней назад. Руководитель, генеральный директор, администратор, кадры — любая дата; прошедшее сразу «Завершено»
Отпуск позже срока подачи (notice_days, 14)Предупреждение late и пометка «поздняя заявка» у согласующих
Отгулов за свой счёт больше порога за год (unpaid_warn_days, 5)Предупреждение unpaid_many
Сроки задач сотрудника в эти дниПредупреждение tasks_due со списком задач

Все проверки и предупреждения показывает POST /absences/check до подачи; blocking — причина, по которой заявку не подать.

Правка (PATCH, то же тело, что и подача). На согласовании — свободно. Согласованное сотрудник только сокращает; сдвиг или удлинение возвращают заявку на согласование. У действующего меняется только дата окончания: раньше — свободно, позже — больничный продлевает кто угодно из имеющих доступ, отпуск и отгул — руководитель. Каждая правка — в истории (absence_events) с автором и прежними датами.

Отмена — до даты начала: сотрудник без причины, решающие — с причиной. После начала — «Закрыть раньше» (POST /absences/:id/close {date}). Начавшееся или прошедшее отсутствие не удаляется: администратор и кадры помечают его ошибочным (POST /absences/:id/void {note}) — статус «Отменено», дни возвращаются в остаток, в истории — voided.

Кто что видит ​

КтоЧто
СотрудникСвои отсутствия целиком
Коллеги по отделам (основному и дополнительным)Вид, даты, кто на связи (limited = true): комментарий, решения и история скрыты
РуководительПодчинённые (рекурсивно) и отделы, которыми руководит, — целиком; решает первый уровень, принимает больничный к сведению
Генеральный директорВсе; утверждает второй уровень, отзывает согласованное
Чтение others_timesheets«Все» — все сотрудники, «свой» — свои отделы
Администратор, запись users_and_rightsВсе; заводят за любого; календарь компании и настройки

Метку «отсутствует до…» (GET /absences/today?user_ids=) видит каждый. Признаки для интерфейса в /org/me: absences_decide, absences_director, absences_manage.

Файлы отсутствия (скан больничного, entity_type = absence) — только сотрудник, его руководитель, генеральный директор и администратор; глобальное чтение их не открывает.

Календарь компании ​

company_calendar: особые дни — holiday (праздник), day_off (перенесённый выходной), workday (рабочая суббота). При запуске заполнен производственным календарём РФ на 2026–2027. Пакет internal/workcalendar — одна функция «рабочий ли день» и счёт рабочих и календарных дней; ведёт администратор на странице «Календарь и отсутствия» (GET/PUT/DELETE /calendar).

Влияние на систему (этап 2) ​

Отсутствие ничего не запрещает и денег не считает. Оно меняет то, что видно, кого не дёргать и какие дни рабочие.

ГдеЧто
Рабочий деньworkcalendar.Personal — календарь компании без полных дней отсутствия сотрудника. Им считаются серия дней в «Прогрессе» (отпуск и праздник серию не рвут), срок выплаты табеля, черновик часов помощника, пятничное напоминание руководителю (в последний рабочий день недели)
Напоминания о часахВ праздник компании не приходят никому; за день отсутствия — ни сотруднику, ни его руководителю (timeentries.ReminderCalendar)
Кольцо дняGET /progress/me → away: в день отсутствия кольцо показывает вид отсутствия вместо «0 из 8 ч»; половина дня — норма 4 ч
ТабельПодсказки absences_in_period, метка в «Команде», дни отсутствия в «Моих часах» — см. Табели
Метки у имениGET /absences/today?user_ids=&date= — один запрос на экран; фронт показывает «В отпуске до 15.10, на связи Петров» через общий AppUser (задачи, «Команда», команда проекта, сотрудники, чаты)
Задачи и часыСрок задачи в отсутствие ответственного и запись часов в свой день отсутствия — предупреждения, не запрет
КомандировкиКомандировку на дни отпуска или отгула (не больничного) не создать: 409 trips_absence_overlap
ЧатыСтатус собеседника в списке и шапке личного чата. Автоответ: на первое за день личное сообщение каждого собеседника отсутствующий «отвечает» текстом из заявки, сообщение вида auto_reply; в групповых чатах автоответа нет
ГлавнаяВ «Расписании» — группа «Отсутствия»; руководителю и генеральному директору — виджет «Кто отсутствует» (сегодня, неделя, заявки на решение)
ПомощникУтренняя сводка: «Сегодня отсутствуют: …» и число заявок на решение; в праздник сводки нет

Остатки и переработка (этап 3) ​

GET /absences/balance?user_id=&year= — свои остатки каждому, чужие — тем, кто видит сотрудника подробно.

  • Отпуск, дни. Остаток года = норма + перенос с прошлого года + корректировки года − использовано. Норма — личная (users.vacation_days_per_year, PATCH /absences/users/:id/norm) или компании (absence_settings.vacation_days_per_year, 28). Использовано — согласованные, идущие и прошедшие отпуска по году начала; на согласовании — отдельно, в остаток не входят. Неиспользованное переносится целиком, минус тоже. Первый год расчёта — самый ранний из отпусков и корректировок.
  • Переработка, часы. С даты overtime_since: в рабочий день — часы сверх 8 (в день половины отсутствия — сверх 4), в выходной, праздник и день отсутствия — все часы. Минус отгулы в счёт переработки: 8 ч за день, 4 ч за половину. Правка часов задним числом сразу меняет остаток.
  • Корректировки (POST /absences/users/:id/adjustments, администратор и кадры): {balance: vacation|overtime, year, amount, reason}; стартовый остаток вводится так же с причиной «остаток на 01.01.2026».
  • Предупреждения в POST /absences/check: over_balance (отпуск длиннее свободного остатка), over_overtime (не хватает переработки); vacation_left / overtime_left — свободный остаток до заявки. Согласовать можно, остаток уходит в минус.

Выгрузка GET /absences/export?year&department_id — «График отсутствий <год>.xlsx»: лист «Отсутствия» (кто согласовал — только по сотрудникам, видимым подробно) и лист «Остатки».

«Загрузка людей» на главной считает норму месяца каждого сотрудника: общая минус 8 ч за каждый рабочий день отсутствия (4 ч за половину) — поля norm_hours, absent_hours.

Помощник: инструменты absences (кто отсутствует в период), my_absences (свой остаток, переработка, отсутствия года) и propose_absence — предложение подать заявку: в чате появляется карточка с расчётом дней, согласующими и предупреждениями (POST /absences/check от имени сотрудника), подаёт сам сотрудник кнопкой «Подать».

Табель: в карточке черновика с отгулом за свой счёт в периоде — кнопка «Вычет за отгул»: подставляет причину и дни, сумму вводит руководитель. Быстрая отметка часов в день полного отсутствия не открывается. На карточках доски задач — значок у аватара отсутствующего ответственного.

Интерфейс: плитки «Остаток отпуска» и «Переработка» в «Табель → Отсутствия», кнопка «Выгрузить в Excel» решающим и кадрам, вкладка «Отсутствия» в карточке сотрудника (норма, остатки по годам, корректировки), карточка «Отпуск» в профиле.

Уведомления ​

ТипКому
absence.requestedТому, чьё решение сейчас нужно
absence.sickРуководителю (больничный заведён, продлён, закрыт; отпуск укорочен); организатору пересекающейся командировки
absence.decidedСотруднику: согласовано, утверждено, отклонено, отозвано, оформлено за него
absence.pending_reminderРешающему: заявка без решения два рабочих дня, больничный не принят к сведению за рабочий день (раз в рабочий день после 9:00)
absence.sick_openРуководителю и сотруднику: открытый больничный дольше open_sick_reminder_days, затем раз в неделю
absence.starts_tomorrowРуководителю одним списком: в следующий рабочий день начинаются отсутствия подчинённых

Живые обновления: любая запись в /absences и /calendar рассылает entity_changed с сущностью absence (кроме POST /absences/check).

Загружаем документацию…