BR 1.4.3: Пользовательские роли с правами (Yuma-стиль)
Зависит от:
Референс
YumaPOS:
_reference/yumapos/roles.md,staff-list.md,how-to-add-new-staff-member.md
Контекст
Сейчас роль сотрудника — жёсткий enum из 4 значений (admin_franchise, admin_franchisee, manager, cashier), зашитый в код каждого сервиса через switch (user.getRole()). Набор прав для manager и cashier единый для всей сети — владелец франшизы и партнёр не могут настроить индивидуальный набор прав для своих сотрудников.
Это создаёт проблемы:
- Нельзя разрешить кассиру применять скидки, но запретить возвраты
- Нельзя создать роль «Официант» или «Курьер» с отдельным набором операций
- Нельзя запретить менеджеру одной ТТ редактировать склад, разрешив той же роли в другой ТТ
- Добавление новых ролей требует миграции БД и кода во всех сервисах
YumaPOS решает это через роль как сущность: владелец создаёт произвольные роли, выставляет чекбоксы разрешений на разделы Бэк-офиса и операции POS, сотрудник получает одну или несколько ролей с привязкой к конкретным ТТ.
Эта BR переносит Yuma-модель ролей на нашу платформу. Логика franchise/franchisee остаётся как multi-tenancy слой поверх Yuma-модели: permissions определяют что делает пользователь, а franchise_id / store_ids — над какими данными.
Требования
1. Роль как сущность с CRUD
Роль — объект с полями:
| Поле | Тип | Обязательность | Описание |
|---|---|---|---|
| Название | string | Обязательно | Название роли («Кассир», «Официант», «Су-шеф»); уникально в рамках франшизы |
| Описание | string | Опционально | Короткое пояснение для чего роль |
| Разрешения Бэк-офиса | см. раздел 2 | Обязательно | Набор прав Read/Edit по разделам |
| Разрешения POS | см. раздел 3 | Обязательно | Набор прав на операции кассы |
| Формула зарплаты | см. раздел 4 | Опционально | Как считается ЗП для сотрудников с этой ролью |
| Системная | boolean | Автофлаг | true — нельзя удалить/редактировать права |
| Статус | enum | Активна / Удалена (soft delete) | Удалённые попадают в архив с возможностью восстановления |
Операции над ролями:
- Создание роли с нуля
- Редактирование (название, описание, любые permissions, формула)
- Удаление (soft delete) — нельзя удалить роль, назначенную хоть одному активному сотруднику
- Восстановление из архива «Удалённые роли»
- Просмотр списка ролей с колонкой «Количество работников» (клик ведёт на отфильтрованный список сотрудников)
2. Разрешения: Разделы Бэк-офиса
Для каждого раздела админки — два флажка: Чтение и Редактирование.
Базовый каталог разделов (уточняется при спеке):
- Меню (товары, категории, модификаторы)
- Прейскуранты
- Техкарты и ингредиенты
- Склад (операции приёма/списания, инвентаризации)
- Торговые точки
- Юридические лица
- Сотрудники и роли
- Расписание и учёт рабочего времени
- Зарплата и ведомости
- Отчёты и аналитика
- Настройки франшизы
Правило: Редактирование без Чтения не имеет смысла; при выставлении Edit=✅ — Read=✅ автоматически.
3. Разрешения: Функции POS-терминала
Флажки на операции, которые сотрудник может выполнять на кассе. У некоторых операций — лимиты Минимум / Максимум (в рублях или штуках).
Базовый каталог операций (уточняется при спеке):
- Работа с POS-приложением (базовый доступ)
- Открытие / закрытие смены
- Приём заказов
- Применение скидок (лимит % или ₽)
- Внесение наличных в кассу (лимит ₽)
- Изъятие наличных из кассы (лимит ₽)
- Возврат средств клиенту
- Аннулирование заказа / позиции
- Изменение цены в чеке (для товаров с открытой ценой)
- Управление настройками кассы
- Инкассация
4. Разрешения: Зарплата
Переиспользуется без изменений из BR 1.4.1:
- Три типа формулы: hourly (ставка × часы), fixed (оклад за месяц), mixed (оклад + ставка сверх нормы)
- Иерархия применения: индивидуальная формула сотрудника → формула роли → нет формулы
Изменение по сравнению с 1.4.1: формула привязывается к role_id, а не к enum-значению роли. Логика расчёта остаётся той же.
5. Присвоение ролей сотруднику
Один сотрудник → N ролей одновременно. Для каждой роли сотрудника отдельно задаётся набор магазинов, где эта роль действует.
Пример:
Иван Петров (employee):
├── Роль «Менеджер» → магазины: ТТ-1, ТТ-2
├── Роль «Курьер» → магазины: ТТ-3
└── Роль «Бариста» → магазины: ТТ-2
В карточке сотрудника:
- Вкладка «Роли» со списком назначенных ролей
- Для каждой роли — поле «Магазины» (мультивыбор)
- Кнопки «Добавить роль» / «Удалить роль»
6. Системная роль «Администратор»
- Создаётся автоматически при seed франшизы
- Неудаляемая, права не редактируются
- Полный доступ ко всем разделам Бэк-офиса и всем операциям POS
- Все магазины франшизы — автоматически
- Не путать с текущим enum-значением
admin_franchise— судьба этого слоя мульти-тенантности (franchise/franchisee) решается отдельной BR
7. Soft delete и архив ролей
- Удаление роли = установка флага «Удалена», скрытие из основного списка
- Страница «Удалённые роли» — архив с возможностью восстановления
- Нельзя удалить роль, назначенную хоть одному активному сотруднику: UI показывает ошибку «Снимите эту роль со всех сотрудников перед удалением»
- При восстановлении роль возвращается со всеми правами и формулой зарплаты (переживает период нахождения в архиве)
8. Список ролей
Страница «Сотрудники → Роли»:
- Колонки: Название, Количество работников, Системная (флаг), Дата создания
- Клик по числу работников → фильтрованный список сотрудников с этой ролью
- Фильтры: системные / пользовательские, активные / удалённые
- Кнопки: Добавить, Удалить, Удалённые роли
9. Миграция существующих сотрудников
При выкатке новой модели на тестовую среду:
- Удалить всех сотрудников с ролями
manager,cashier,admin_franchisee - Сохранить только сотрудников с ролью
admin_franchise— они получают системную роль «Администратор» - Для удаляемых
admin_franchisee— их ЮЛ либо удаляются каскадно, либоlegal_entities.owner_user_id→NULL(см. открытый вопрос)
Тестовая среда — реальных данных нет, ручная миграция приемлема. Production-миграции в этой BR не предусматривается (ERP ещё не в проде).
Бизнес-правила
- Один сотрудник — N ролей, каждая со своим scope по магазинам
- Уникальность названия роли — в рамках одной франшизы
- Системные роли (флаг
is_system=true) — нельзя удалить, нельзя изменить права, название фиксированное - Каскад при удалении роли — запрещён, пока есть активные сотрудники с этой ролью
- Permissions НЕ кладутся в JWT — JWT несёт только
user_id,franchise_id,store_ids,role_ids. Полный список permissions — отдельный источник:- Фронт получает через
GET /auth/me(один раз при логине, опционально re-fetch при изменении) - Сервисы получают через
POST /internal/auth/validate(existing endpoint расширяется полемpermissions[])
- Фронт получает через
- Мгновенное применение изменений прав — при редактировании permissions роли новые права действуют со следующего запроса, без форс-релогина активных JWT
- Соответствие Edit/Read — включение Edit автоматически включает Read (нельзя редактировать то, что не видишь)
Ролевой доступ к модулю ролей
| Действие | admin_franchise | admin_franchisee | manager | cashier |
|---|---|---|---|---|
| Просмотр списка ролей | ✅ все роли франшизы | ✅ только роли, назначенные сотрудникам в своих ТТ | ❌ | ❌ |
| Создание роли | ✅ | ❌ | ❌ | ❌ |
| Редактирование роли | ✅ | ❌ | ❌ | ❌ |
| Удаление роли | ✅ | ❌ | ❌ | ❌ |
| Назначение роли сотруднику | ✅ | ✅ только своим сотрудникам | ❌ | ❌ |
(В этой таблице используются текущие enum-роли как обозначения слоя мульти-тенантности, в соответствии с решением в BR 1.4.2).
Затронутые сервисы
| Сервис | Что меняется |
|---|---|
| User Service | Новые таблицы: roles, role_permissions, employee_roles, employee_role_stores. CRUD-эндпоинты для ролей. Замена employees.role (enum) на связь many-to-many через employee_roles. Миграция: удаление всех manager/cashier/admin_franchisee |
| Auth Service | /internal/auth/validate дополняется списком permissions[] (агрегат из всех ролей пользователя). /auth/me возвращает roles[] + permissions[] для фронта. JWT payload меняется: role → role_ids[] |
| Store / Catalog / Warehouse / Order Services | Проверки прав переводятся с switch(user.getRole()) на permissions.contains("stores.edit") и подобные. В скоупе отдельной BR/миграции, не в этой |
| Admin BFF | Новый роут /api/v1/admin/roles/* (проксирование к User Service) |
| Admin Franchise (web) | Новый раздел «Сотрудники → Роли»: список, форма создания/редактирования с тремя вкладками (Разделы Бэк-офиса, Функции POS, Зарплата). Во вкладке «Роли» карточки сотрудника — мультивыбор ролей + магазинов |
Открытые вопросы
- Каталог permission-ключей — составить точный список ключей для Back-office разделов (
menu.read,menu.write,payroll.read, …) и POS-операций (pos.login,pos.discount.apply,pos.cash.withdraw, …). Взять 1:1 из Yuma или составить свой? - Судьба enum-ролей
admin_franchise/admin_franchisee— отдельная BR. Они остаются как multi-tenancy layer, но механика их создания (сейчас автосоздание при регистрации ЮЛ) требует пересмотра в контексте новой ролевой модели. - ЮЛ при удалении
admin_franchisee-владельцев — каскадное удаление ЮЛ илиowner_user_id→ NULL? - ТТ-зависимые формулы зарплаты — BR 1.4.1 говорит: «формулы по ролям настраиваются по ТТ — ставки могут отличаться по точкам». В новой модели это сохраняется как
role × store → formulaили упрощается доrole → formula(одна на роль, без per-store)? - Лимиты min/max на POS-операциях — нужны ли они сразу, или начать без лимитов (только флажки) и добавить в отдельной итерации?
- Системная роль «Администратор» — должна ли быть одна на франшизу (как в Yuma) или отдельная на Франшизу и на каждого Франчайзи?