Назначение эпика: HR-контур back-office: реестр сотрудников, фиксация и агрегация посещаемости (день/месяц/год), рабочие расписания и смены, выходные/праздники, управление отпусками с проверкой баланса.
Участвует в use-cases: UC-8 (check-in/out → агрегация D/M/Y; Leave Request → проверка баланса → approve → списание).
Сущности (owns/touches): owns — Employee, Attendance (daily record), Schedule (weekly per role), Shift (per-day assignment), LeaveRequest, DaysOff/PublicHoliday. touches — check-in/out события приходят из mobile (E04/E11 Profile), Department/Role из общей справочной модели.
Экраны: S34 (Employees), S03 (Attendance Daily), S14 (Attendance Monthly), S19 (Attendance Yearly), S32 (Schedules), S38 (Shifts), S42 (Days Off / Public Holidays), S22 (Leave Request + Requests list).
Открытые findings: F-checkin-history-tasks-per-employee (нужна ли история чек-инов и разрез «задачи на сотрудника» в HR/Schedule); F-placeholder-copy (лейблы-заглушки «John Wick», «Operations», «no»).
Общий каркас HR-модуля. Все десктоп-экраны эпика делят один layout: левое меню HR-модуля + контентная область (header-фильтр + data-table). Левое меню (дословно из сайдбаров S03/S14/S19/S32/S38/S42): Employees · Attendance Daily · Attendance Monthly · Attendance Yearly · Schedules · Shifts · Leave Management · Requests · Public Holidays. На каждом экране верх —
input_text(поиск/строка) +input_dropdown(фильтр) +input_button(действия). Дальше описывается только специфика экрана.
Статус реализации (2026-06-22): E09 крупный → режется на ~4 WP.
- ✅ WP-1 DONE (ветка
feat/e09-hr-wp1, PR #26): HR-шелл (вложенный layouthr/layout.tsx+HrNavподменю 9 пунктов: 2 активны, 7 disabled «coming soon») + S34 Employees (/hr/employees, 7 колонок, search+Department, StatusChip Active/Inactive, inert Add 🟡) + S03 Attendance Daily (/hr/attendance/daily, 17 колонок горизонт-скролл, производный Attendance Status Present/Late/ Absent/Day Off/On Leave/Holiday — приоритет Holiday>Day Off>On Leave>Absent>Late>Present, цветные chip
- новый Badge
warning/amber). OwnsEmployee/AttendanceRecord/AttendanceDailyRow. pure-utilscomputeAttendanceStatus/filterEmployees/toAttendanceDailyRow/filterAttendanceRows. Read-only. PDD blind: pre-review 3 CRIT+5 IMP resolved-in-plan → validation 791/791 первый прогон → post-review clean (0/0, 2 NIT fix-now) → verify-feature SHIP 8/8 (EN/ID). План:docs/superpowers/plans/2026-06-22-e09-hr-wp1.md.- ✅ WP-2 DONE (ветка
feat/e09-hr-wp2): S14 Attendance Monthly (/hr/attendance/monthly, 11 колонок, Balance = quota − taken, overdrawn → red Badge, Period-фильтр) + S19 Attendance Yearly (/hr/attendance/yearly, 10 колонок, Working Days/Public Holiday/Annual Leave/Leave, Year-фильтр) + S42 Public Holidays (/hr/public-holidays, 5 колонок, Year-фильтр + "all", inert Add/edit/delete 🟡). HrNav: AttendanceMonthly/AttendanceYearly/PublicHolidays → enabled (теперь 5 активных, 4 disabled). OwnsMonthlyAttendanceRecord/YearlyAttendanceRecord/PublicHoliday+LeaveQuota. pure-utilstoMonthlyAttendanceRow(Balance)/filterMonthlyRows/toYearlyAttendanceRow/filterYearlyRows/filterPublicHolidays. Mocked pre-aggregated summary (Q-18). Read-only. PDD blind: pre-review 3 CRIT+3 IMP resolved-in-plan → validation 1036/1036 первый прогон (+168) → post-review clean (0/0, 1 NIT) → verify-feature (DEV). Open 🟡: Q-17 (квоты/Balance), Q-18 (источник агрегата/таксономия Leave), Q-19 (Days Off count-vs-dates). План:docs/superpowers/plans/2026-06-22-e09-hr-wp2.md.- ✅ WP-3 DONE (ветка
feat/e09-hr-wp3, PR #29, SHA8b2de738): S32 Schedules (/hr/schedules, 11 колонок ID/Name/Role/Monday…Sunday/Actions, день-ячейка интервал/«Day off» viaformatScheduleDay, Role-фильтр, structuralScheduleDay) + S38 Shifts (/hr/shifts, 7 колонок, Day+Assignment фильтры) + pure-utilresolveExpectedTimes(приоритет Holiday/DayOff>Shift>Schedule, 5 веток, tested, НЕ wired в S03 — backend-эталон, Q-22). Общий inertRowActions(Edit/Delete). HrNav: Schedules/Shifts → enabled (7 активных, 2 disabled). OwnsSchedule/Shift+ Row/Filters. Read-only (всё inert 🟡). PDD blind: pre-review 2 CRIT+2 IMP resolved-in-plan → validation 1221/1221 первый прогон (+185) → post-review clean (0/0, 1 NIT fix + 1 wontfix) → verify-feature SHIP 16/16 DEV + 23/23 PROD (EN/ID). Open 🟡: Q-20/Q-21/Q-22. План:docs/superpowers/plans/2026-06-22-e09-hr-wp3.md.- ✅ WP-4 DONE (ветка
feat/e09-hr-wp4, PR #30, SHAfb2a2ff9) — ЗАВЕРШАЕТ E09 desktop (8/8): S22 Leave Request (/hr/leave, 6 колонок Request/Date/Type/Employee/Role/Status[Badge], type fast-filter чипы All/Annual/Sick/Holiday/Other, row-click → drawer [From/To/Days/Current Balance/Comment/ Attachments + inert Approve/Reject], reusecore/ui/drawer+useUiStoreпаттерн E08). pure-utilscomputeLeaveDays(inclusive, DST-safe)/computeLeaveBalance(Annual/Holiday=quota−taken, Sick/Other→null «—»)/toLeaveRequestRow/filterLeaveRequests/leaveStatusVariant. HrNav: Leave Management → enabled (8 активных, 1 disabled). OwnsLeaveRequest/LeaveRequestRow/LeaveType/LeaveStatus. Read-only (всё inert 🟡). PDD blind: pre-review 0 CRIT+3 IMP+2 NIT resolved-in-plan → validation 1328/1328 (+107, 1 механический дедуп импортов) → post-review clean (0/0, 2 NIT) → verify-feature SHIP 22/22 DEV + 23/23 PROD (EN/ID). Open 🟡: Q-23 (attendance-chip dropped), Q-24 (Sick/Other balance null). План:docs/superpowers/plans/2026-06-22-e09-hr-wp4.md. 🎉 ЭПИК E09 desktop ЗАВЕРШЁН.
3458764671657205745 (2× = точные дубли).ID, Name, Department, Role, Lark ID, Employee Status, User Type. Данные-заглушки: John Wick, Operations/Operations (department/role), статусы повторяются.input_text → фильтр по Name/ID (filter).input_dropdown → фильтр по Department (dropdown).input_button → предположительно + Add Employee (create), export/bulk-действие 🟡, и переход в строку → карточка сотрудника 🟡.ID). Lark ID — связка с внешней Lark (мессенджер/SSO). Пишет: создание/правку профиля сотрудника.+ Add — в каталоге видны только лейблы 3 кнопок без подписей; реестр без create/edit бессмыслен, поэтому минимально достроено.Lark ID (сотрудник без интеграции); длинное Name; неактивный Employee Status (показывать приглушённо); нет прав → меню HR скрыто.3458764671658286045 (2× = дубли).ID, Name, Department, Role, Lark ID, Employee Status, Attendance Status, Day Off, Check In, Check Out, Exp Check In, Exp Check Out, Leave, CI Location, CO Location, Holiday, Annual Leave. Значения: время 09:00/19:00, --:-- (нет отметки), флаги no.input_text → по Name/ID (filter).input_dropdown → выбор даты/департамента (dropdown) 🟡 точное назначение dropdown не подписано.input_button → навигация по дню (prev/next 🟡), export, ручная корректировка отметки 🟡.--:--) / day-off / on-leave / holiday.Check In/Out + CI/CO Location приходят из mobile check-in/out (E04/E11 Profile, UC-8). Exp Check In/Out берутся из Schedule (S32) или Shift (S38). Day Off/Holiday — из S32/S42. Leave/Annual Leave — из одобренного LeaveRequest (S22). Является источником для агрегатов S14 (Monthly) и S19 (Yearly).--:-- (нет check-out — смена не закрыта / забыл отметиться); check-in без геолокации (CI Location пустой); день, попавший и на holiday, и на day-off (приоритет отображения); сотрудник на leave (отметки не ожидаются).3458764673083859834 (2× = дубли). В сайдбаре пункт — Leave Management (под этим разделом живёт месячная сводка).ID, Name, Department, Role, Lark ID, Employee Status, Annual Leave Taken, Annual Leave Balance, Public Holiday Taken, Public Holiday Balance, Leave. Значения — счётчики (1).input_text → по сотруднику (filter).input_dropdown → выбор месяца/года (dropdown).input_button → export, period-навигация 🟡.Leave/Annual Leave/Holiday за месяц → *_Taken; *_Balance = квота − taken (с учётом списаний по одобренным LeaveRequest, S22). Read-down от Daily.Balance = annual_quota − Taken — квота сотрудника в прототипе не показана; нужна для смысла «Balance». Связать с S22 «Current Balance».3458764671768989422 (4× = дубли — самый частый дубль HR-набора).ID, Name, Department, Role, Lark ID, Employee Status, Working Days, Public Holiday, Annual Leave, Leave. Значения — счётчики (1).input_text (filter).input_dropdown → выбор года (dropdown).input_button → export, year-навигация 🟡.Working Days = дни с present-отметкой; Public Holiday/Annual Leave/Leave — суммы соответствующих дней. Roll-up над S14 (Monthly).Annual Leave vs общий Leave (последний — все типы из S22: Sick/Holiday/Other) — в прототипе обе колонки рядом без определения; вытекает из типов LeaveRequest.3458764674667384346 (2× = дубли).Role/группе), колонки = дни недели.ID, Name, колонки Monday…Sunday со значениями 09:00 - 18:00 или day off; per-строка edit / delete. Привязки: Office, Handyman (роли/группы).input_text (filter).input_dropdown → фильтр по роли (dropdown).input_button → + Add Schedule (create).edit (button-в-строке) → редактировать недельную сетку (update).delete (button-в-строке) → удалить расписание (delete).Exp Check In/Exp Check Out и Day Off для S03 (Daily). Конкретные смены S38 (Shifts) наследуют/переопределяют расписание.day off.per role vs per employee) — в данных видны роли (Office/Handyman), считаем привязку к роли/группе; индивидуальные отклонения — через S38. Обоснование: иначе неясно, кому применяется сетка.3458764674668376446 (2× = дубли).ID, Name, Day, Check In, Check Out, Assignment; значения 09:00/19:00, дата 25/06/2026, привязки Office/Handyman; per-строка edit / delete.input_text (filter).input_dropdown → фильтр по дню/assignment (dropdown).input_button → + Add Shift (create).edit → update смены; delete → удалить смену.Assignment связывает смену с сотрудником/группой/локацией.Exp Check In/Out в Daily — прототип не определяет разрешение конфликта; разумный дефолт: конкретная смена побеждает шаблон.3458764674668785386 (2× = дубли). Пункт меню — Public Holidays.ID, Name, Year, Days Off, Assignment; значения Office/Handyman, 2026; per-строка edit / delete.input_text (filter).input_dropdown → фильтр по году (dropdown).input_button → + Add (create запись нерабочих дней).edit → update; delete → удалить.Holiday/Public Holiday в S03 (Daily) и S14/S19 агрегатах; Public Holiday Balance в S14 опирается на эти дни. Assignment определяет, к какой группе/локации применяется набор выходных.Days Off как счётчик vs перечень конкретных дат — в прототипе колонка одна; для расчёта Daily нужны конкретные даты, поэтому редактирование подразумевает список дат внутри записи года.Assignment); праздник, совпавший с day-off расписания (без двойного списания); прошлый год (архив, read-only).Requests).3458764673084340037 (2× = дубли). Содержит ОБА состояния: форму заявки (detail) и список заявок (Requests-таб/таблица).All, Annual Leave, Sick Leave, Holiday Leave, Other, Attendance.Request, дата (22/05/2026), тип (Annual Leave), сотрудник (Agus Putri), роль (Housekeeping Coordinator).From: (22/05/2026), To: (24/05/2026), Days: (2), Current Balance: (10), Comment (textarea), Attachements (вложения).From/To (date input) → пересчёт Days (вычисляемое поле).Current Balance.Comment (textarea input); Attachements (file input).Current Balance читается из S14 (Monthly) / квоты сотрудника. Одобренная заявка списывает баланс и проставляет Leave/Annual Leave дни в S03 (Daily) на диапазон From..To → отражается в S14/S19. Days = рабочие дни между From и To (учёт расписания/праздников) 🟡.Days > Current Balance → блокировать submit или пометить overdraw; To < From (валидация диапазона); пересечение с уже одобренным leave; заявка на прошедшие даты; вложение превышает лимит.S34 Employees ──(identity: ID/Lark ID)──► все HR-экраны
S32 Schedules ─┐
S38 Shifts ────┤(Exp Check In/Out, Day Off; Shift override Schedule)
S42 Days Off ──┘──► S03 Attendance Daily ◄── mobile check-in/out (E04/E11, CI/CO Location)
│ агрегация
├──► S14 Attendance Monthly (Taken/Balance)
└──► S19 Attendance Yearly (Working Days, roll-up)
S22 Leave Request ──[Approve]──► баланс− + leave-дни в S03 Daily ──► отражение в S14/S19
└─ Current Balance ◄── S14 / квота сотрудника
Закрывают открытые 🟡 эпика (Q-17..Q-24) + сквозное K-1 (реестр
00-client-questions.md). Перенос в вёрстку — Tier B/C отдельными PDD.
Assignment оперирует конкретными людьми, НЕ ролью/локацией как абстракцией. Где назначается набор (Schedules, Public Holidays, Annual Leave) — это настраиваемая композиция: группа/роль/кластер, ИЛИ отдельные люди, ИЛИ комбинация («Office + Иван Петров», «две группы», «группа + 2 чел.») — как в Lark. В UI сохраняется идентичность группы (показывается её название, не плоский разворот); на бэке композиция резолвится в список людей (membership-expansion). Единичная замена (Shift, Q-21) — частный случай: один человек.Schedule.role: string → привязка людей.Name = свободный лейбл записи (ни к чему не привязан, можно с пресетами). Assignment = ОДИН конкретный сотрудник (выбор из базы), НЕ роль и НЕ локация — shift = разовое изменение графика конкретного человека на день. Shift.assignment свободная строка Office/Handyman → employee-picker (1 чел.). → Tier B U2.resolveExpectedTimes (5 веток). expected популяризируется в daily-БД заранее.expected check-in/out, holiday/выходной да-нет, leave-тип. Сотрудник чекинится в мобильном → факт check-in/out + фото в ту же запись. Месячные/годовые агрегаты роллапятся из daily на бэке. Прототип-снимок (pre-aggregated) — ОК. Таксономия: Annual / Sick / Holiday / Day-off / Attendance(коррекция); общий Leave = прочий (non-annual).leaveTaken ≤ monthsWorkedThisYear). Управление — через Annual Leave settings/schedules (по образцу attendance schedules): лист графиков (обычно один, «Annual Leave Bali Office»), у каждого дней/год + правило начисления, к графику подписываются сотрудники (K-1). Настройка — не отдельная админ-страница отпусков, а секция в единой «Странице настроек отделов» (Departments Settings) (общее место для ВСЕХ конфигурируемых переменных). LEAVE_QUOTA-константу не зашивать числом до ответа HR. Открыто: точная квота; пропорция при найме среди года (накрывается accrual-правилом).Current Balance для них = «—» (подтверждает WP-4 computeLeaveBalance). Квота только у Annual / Public Holiday.Assignment = люди (K-1). Public Holidays имеют свои графики (как attendance/leave), к которым подписываются сотрудники (графики праздников разные). Day-off (неоплачиваемый отгул) — через Request: без баланса (не лимитирован), HR аппрувит → пишется в attendance как day-off. Открыто: «Days Off = счётчик vs перечень дат» внутри записи года явно НЕ закрыто (для дневных holiday-колонок логично нужны даты) — уточнить.Check In с Exp Check In и наличием отметок; в каталоге только колонки.annual_quota − Taken) и пропорция квоты при найме в середине периода — отмечены, но не реализованы как UI; квота сотрудника в прототипе не показана.