1.4.2 Пересмотр ролевой модели сотрудников
Расширение BR 1.4
BR 1.4 реализовала 4 статичные роли. Эта BR решает проблему смешения структурных ролей владельцев (franchise, franchisee) с операционными ролями сотрудников (manager, cashier).
Кастомные роли — отдельная BR
Полноценный конструктор ролей с permission-based доступом (аналог Юмы) — отдельная большая задача, не в этой BR. Здесь — только разделение системных/обычных ролей и авто-создание владельца при создании ЮЛ.
Проблема
Текущая модель (BR 1.4) имеет архитектурные недочёты:
- Franchise может создать другого Franchise через “Сотрудники” — бессмысленное клонирование владельца бренда, потенциально опасно
- Franchise может создать Franchisee через “Сотрудники” — но Franchisee это структурная сущность (владелец ЮЛ-партнёра с договором), а не просто сотрудник
- Осиротевшие связи — можно создать ЮЛ Франчайзи без учётки владельца (партнёр не сможет войти), либо наоборот — несколько franchisee-учёток для одного ЮЛ (непонятно кто главный)
- Смешение уровней ответственности — в форме “Добавить сотрудника” в одном dropdown структурные роли (владелец бренда, владелец партнёра) рядом с операционными (менеджер точки, кассир)
Решение
Разделить:
- Системные роли (
admin_franchise,admin_franchisee) — владельцы уровня бизнеса, создаются автоматически - Операционные роли (
manager,cashier) — обычные сотрудники, создаются через форму “Сотрудники”
Автоматическое создание владельца Франчайзи при создании ЮЛ Франчайзи — одной транзакцией, чтобы не было осиротевших сущностей.
1. Роли после переименования
| Роль | Статус | Как создаётся | Что видит |
|---|---|---|---|
admin_franchise | Системная | При seed / регистрации бренда | Всё во всей франшизе |
admin_franchisee | Системная, автосоздание | При создании ЮЛ Франчайзи (одной транзакцией с ЮЛ) | Свой ЮЛ + свои ТТ + своих сотрудников |
manager | Обычная | Admin Franchise или Admin Franchisee через форму “Сотрудники” | Свою ТТ (read-only) |
cashier | Обычная | Admin Franchise или Admin Franchisee | Только POS |
Переименование:
franchise→admin_franchisefranchisee→admin_franchiseemanager,cashier— без изменений
Русские лейблы в UI:
admin_franchise→ “Владелец франшизы”admin_franchisee→ “Владелец партнёра” (или “Франчайзи”)manager→ “Менеджер”cashier→ “Кассир”
2. Изменения в форме создания сотрудника
Было
В /employees/new в dropdown “Роль”:
- Франшиза (для Franchise)
- Франчайзи (для Franchise)
- Менеджер
- Кассир
Стало
В dropdown “Роль” только:
- Менеджер
- Кассир
Роли admin_franchise и admin_franchisee не доступны для создания через эту форму — ни для Admin Franchise, ни для Admin Franchisee.
Почему
admin_franchise— создаётся только при регистрации бренда (seed), не через UIadmin_franchisee— создаётся только автоматически при создании ЮЛ Франчайзи
3. Создание ЮЛ Франчайзи — новая логика
Форма создания ЮЛ Франчайзи
Текущая форма /legal-entities/new (type=franchisee) дополняется блоком “Владелец”:
| Поле | Тип | Обязательность | Описание |
|---|---|---|---|
| Имя владельца | text | Обязательно | |
| Фамилия владельца | text | Обязательно | |
| Email (логин) | Обязательно | Уникален в рамках franchise_id | |
| Телефон | text | Опционально | |
| Пароль | password | Опционально | Если не задан — генерируется временный |
Транзакционная логика
При POST /legal-entities (type=franchisee), backend в одной транзакции:
-
Создаёт запись в
legal_entities:type = 'franchisee' name, inn, ogrn, ... — данные ЮЛ owner_user_id = NULL (пока) -
Создаёт запись в
employees:role = 'admin_franchisee' first_name, last_name, email, password_hash — из блока "Владелец" franchise_id = текущая франшиза status = 'active' -
Обновляет
legal_entities.owner_user_id→ ID только что созданного employee -
В ответ API возвращает:
{ "data": { "legal_entity": { ...все поля ЮЛ... }, "owner": { "id": "uuid", "email": "partner@example.com", "temporary_password": "Xy9k2P4m" } } }
UX
После сохранения UI показывает модалку “Партнёр создан”:
- Email:
partner@example.com - Временный пароль:
Xy9k2P4m(крупным шрифтом + кнопка “Скопировать”) - Инструкция: “Передайте эти данные партнёру. Пароль можно изменить после первого входа.”
4. Миграция существующих данных
БД
Migration Liquibase:
UPDATE employees SET role = 'admin_franchise' WHERE role = 'franchise';
UPDATE employees SET role = 'admin_franchisee' WHERE role = 'franchisee';JWT
После деплоя у всех пользователей старые токены с role = 'franchise' перестанут валидироваться → вынужденный relogin.
Опционально: добавить транзитный период — Auth Service принимает оба формата, при перевыдаче токена ставит новый. Упрощает деплой.
5. Затронутые сервисы
| Сервис | Что менять |
|---|---|
| User Service | Миграция role в БД (UPDATE + новый CHECK constraint). Валидация в CreateEmployeeRequest.role — отвергать admin_* роли. Новый internal endpoint POST /internal/employees/create-owner либо использование существующего с особым флагом. |
| Auth Service | JWT содержит новые значения role. Валидация принимает admin_franchise/admin_franchisee. |
| Store Service | Обновить все места где role.equals("franchise") / "franchisee" |
| Catalog Service | То же |
| Warehouse Service | То же |
| Order Service | То же |
| User Service (LegalEntity flow) | При POST /legal-entities (type=franchisee) — создание owner-employee в той же транзакции |
| Admin BFF | Проксирование обновлённого эндпоинта создания ЮЛ Франчайзи (тот же путь, но новый body + response) |
| Admin Web | Форма создания ЮЛ Франчайзи (добавить блок “Владелец”), модалка “Партнёр создан”, форма сотрудника (убрать admin_* роли из dropdown), русские лейблы ролей |
| Shared types | EmployeeRole = "admin_franchise" | "admin_franchisee" | "manager" | "cashier" |
6. Ролевая матрица (структура та же, имена новые)
| Действие | admin_franchise | admin_franchisee | manager | cashier |
|---|---|---|---|---|
| Просмотр всех ЮЛ | Все | Свой | — | — |
| Создание ЮЛ Франчайзи (+ авто-владелец) | Да | — | — | — |
| Создание сотрудников (manager/cashier) | Все ТТ | Свои ТТ | — | — |
| Просмотр каталога | Да (редактирование) | Да (read) | Да (read) | Да (в POS) |
| Работа на POS | — | — | — | Да |
Новые правила:
- Никто не может создать
admin_franchise/admin_franchiseeчерез форму “Сотрудники” - admin_franchise создаёт
admin_franchiseeтолько неявно через форму создания ЮЛ Франчайзи - admin_franchisee автоматически привязан к своему ЮЛ через
legal_entities.owner_user_id
7. Валидация на backend
CreateEmployeeRequest
- Поле
role— разрешены толькоmanagerиcashier - При попытке создать
admin_franchiseилиadmin_franchisee→400 VALIDATION_ERRORсdetails: [{ field: "role", message: "Admin roles cannot be created directly" }]
CreateLegalEntityRequest (type=franchisee)
- Блок “Владелец” становится обязательным для type=franchisee:
owner.first_name— обязательноowner.last_name— обязательноowner.email— обязательно, валидный email, уникален вfranchise_idowner.phone— опциональноowner.password— опционально (если null — генерируется)
Soft delete
- При soft-delete ЮЛ Франчайзи — владелец (
admin_franchisee) деактивируется (status=inactive) - При восстановлении ЮЛ — владелец реактивируется
8. Что НЕ входит в эту BR
| Фича | Почему | Когда |
|---|---|---|
| Кастомные роли (создание своих ролей с именами) | Требует отдельной таблицы roles, переделки авторизации | Отдельная BR |
| Permission-based доступ | Таблица permissions, M2M role_permissions, переписывание всей проверки доступа во всех сервисах | Отдельная BR (связана с кастомными ролями) |
| Multi-role сотрудника | Сейчас 1 роль, нужно M2M employee_roles с привязкой к магазинам | Отдельная BR |
| Смена владельца ЮЛ Франчайзи | ”Передать владение” — кнопка в карточке ЮЛ | Отложено, в MVP — руками в БД |
| Сброс пароля владельцу через email | Нет Auth Service с email-confirmations | Отложено до Auth Service с email |
| Аудит действий (кто когда что создал) | Отдельный модуль логирования | Будущее |
9. Риски и ограничения
Миграция
- Breaking change: старые JWT с
role = 'franchise'перестанут работать → все пользователи должны перелогиниться - Координация релизов: бэкенд + фронт + все сервисы должны обновиться синхронно (один деплой)
- Опция: транзитный период (Auth принимает оба формата) — упростит деплой, усложнит код
UX
- Модалка “Партнёр создан” показывает пароль в открытом виде — нужно учесть что пользователь может не скопировать
- Если email не уникален — показать понятную ошибку до submit
Legacy данные
- Пустые
owner_user_idу существующих ЮЛ Франчайзи — оставить как есть (old records). Новые ЮЛ — всегда с owner.
10. Принятые решения (закрытые вопросы)
| # | Вопрос | Решение |
|---|---|---|
| 1 | Наименования ролей | admin_franchise / admin_franchisee (явно видно что системные) |
| 2 | Смена владельца Франчайзи | Отложено в MVP, в будущем — кнопка “Передать владение” в карточке ЮЛ |
| 3 | Кастомные роли + permissions | Отдельная будущая BR, не в этой |
11. Связи с другими BR
- Расширяет: BR 1.4 (базовая модель сотрудников)
- Влияет на: BR 1.1 (форма создания ЮЛ Франчайзи)
- Параллельно: BR 1.4.1 (расписание/зарплата — роли не меняет, но использует их в ролевой матрице)
Соответствие Юме
Юма использует полностью пользовательские роли + одна системная “Администратор”. У нас — гибрид: системные роли для структуры бизнеса (владельцы) + фиксированные операционные (manager, cashier). Кастомные роли добавим отдельно.
Наша модель не противоречит Юме и не мешает будущему переходу к полностью пользовательским ролям — просто admin_* остаются системными, а manager/cashier превращаются в дефолтные шаблоны пользовательских ролей.