User Service — BR 1.4.1
Контракты
- API — 29 новых эндпоинтов (24 public + 4 internal + 1 CSV export)
- Data Model — 7 новых таблиц
- Events — 2 новых события
Фаза 1: Инфраструктура
Миграции
- Таблица
employee_legal_details(1:1 с employees) - Таблица
shift_templates - Таблица
shift_schedules - Таблица
shift_records - Таблица
shift_corrections - Таблица
salary_formulas - Таблица
payroll_records
JPA Entities + Repositories
-
EmployeeLegalDetailsentity + repository -
ShiftTemplateentity + repository -
ShiftScheduleentity + repository -
ShiftRecordentity + repository -
ShiftCorrectionentity + repository -
SalaryFormulaentity + repository -
PayrollRecordentity + repository
Фаза 2: Юридические детали + Шаблоны + Расписание
Юридические детали
-
GET /employees/{id}/legal-details— получить юр. детали -
PUT /employees/{id}/legal-details— upsert юр. деталей - Ролевой доступ: franchise — все, franchisee — свои, manager/cashier — 403
- Валидация ИНН (12 цифр), СНИЛС формат
Шаблоны смен
-
GET /shift-templates?store_id=— список шаблонов ТТ -
POST /shift-templates— создание (валидация: макс 4 на store_id) -
PATCH /shift-templates/{id}— обновление -
DELETE /shift-templates/{id}— удаление - Ролевой доступ: franchise/franchisee (свои) — CRUD, manager — read, cashier — 403
Расписание (плановые смены)
-
GET /schedules?store_id=&date_from=&date_to=— расписание за период -
POST /schedules— batch создание плановых смен (валидация: только будущие дни) -
PATCH /schedules/{id}— обновление (только будущие) -
DELETE /schedules/{id}— удаление (только будущие) - Уникальность: один сотрудник — одна смена на (employee_id, store_id, date)
Фаза 3: Фактические смены + Корректировки + Дашборд
Фактические смены (public)
-
GET /shift-records?store_id=&employee_id=&date_from=&date_to=— записи за период -
POST /shift-records— ручной ввод (source=manual) -
PATCH /shift-records/{id}— обновление ручной записи - Логика расчёта
break_duration_minutes= break_end − break_start - Логика расчёта
status— сопоставление с shift_schedules (±30 мин правило) - Бизнес-день:
date= датаclock_in(ночные смены)
Корректировки
-
POST /shift-records/{id}/corrections— добавить корректировку -
DELETE /shift-records/{id}/corrections/{correction_id}— сбросить корректировку - Валидация: нельзя корректировать смены со статусом
missed - Валидация:
commentобязателен
Дашборд активности
-
GET /dashboard/activity?store_id=&date=— агрегация за дату - Агрегация: join shift_records + (будущее: order data)
- Ролевой доступ: franchise — все, franchisee — свои, manager — своя ТТ
Фаза 4: Зарплата
Формулы
-
GET /salary-formulas?store_id=— список формул -
POST /salary-formulas— создание (по роли или индивидуальная) -
PATCH /salary-formulas/{id}— обновление -
DELETE /salary-formulas/{id}— удаление (только индивидуальные) - Логика иерархии: individual → role+store → нет формулы
- Валидация по formula_type: hourly → hourly_rate обязателен, fixed → monthly_salary, mixed → все три
Ведомости
-
GET /payroll?store_id=&period=— список ведомостей за период -
POST /payroll/calculate— расчёт ведомости (создание/пересчёт) -
POST /payroll/{id}/confirm— подтверждение (calculated → confirmed) -
POST /payroll/{id}/mark-paid— отметка о выплате (confirmed → paid) -
GET /payroll/{id}/export— CSV экспорт (UTF-8 BOM) - Логика расчёта: net_hours × hourly_rate OR monthly_salary OR mixed
- formula_snapshot — сохранение снимка формулы при расчёте
- Статус-машина: calculated → confirmed → paid (только вперёд)
Фаза 5: Internal API + Scheduled Jobs
Internal API для POS
-
POST /internal/shift-records/clock-in— POS: начало смены -
POST /internal/shift-records/clock-out— POS: конец смены -
POST /internal/shift-records/break-start— POS: начало перерыва -
POST /internal/shift-records/break-end— POS: конец перерыва - Auth: Service token (X-Service-Token)
Scheduled Jobs
- Автозакрытие смен: job каждые 15 мин, закрывает shift_records где clock_out IS NULL AND clock_in < NOW() - 24h
- Ставит auto_closed = true, clock_out = clock_in + 24h
- Публикует событие
user.shift.auto-closed(Kafka не подключён — заглушка в логах) - Статус
missed: вычисляется динамически при запросе расписания, job не нужен
Kafka Events
-
user.shift.auto-closed— при автозакрытии (отложено до подключения Kafka) -
user.payroll.confirmed— при подтверждении ведомости (отложено до подключения Kafka)