Роли
Источники требований
Референс
YumaPOS: Роли, Добавление сотрудника
Модуль управления правами сотрудников. Владелец франшизы создаёт роли с настраиваемыми правами на разделы бэк-офиса и операции POS, а затем назначает их сотрудникам с привязкой к торговым точкам.
Единая модель ролей
(Переработано в BR 1.4.4)
В системе один слой ролевой модели — объект-роль с настраиваемыми permissions. Enum (admin_franchise и т.п.) удалён.
У сотрудника:
- Набор permissions-ролей (N штук, каждая со своим набором ТТ)
- Полные права = объединение permissions всех назначенных ролей
- Scope (какие данные видит) — определяется не ролью, а правилами из Ролевая модель:
- Владелец главного ЮЛ франшизы → вся франшиза
- Владелец ЮЛ партнёра → свои ЮЛ + их ТТ
- Обычный сотрудник → ТТ из
employee_role_stores
Сущность «Роль»
| Поле | Обязательность | Описание |
|---|---|---|
| Название | Обязательно | Уникально в рамках франшизы |
| Описание | Опционально | Короткое пояснение для чего роль |
| Разрешения Бэк-офиса | Обязательно | Набор флажков Read/Edit по разделам (см. ниже) |
| Разрешения POS | Обязательно | Набор флажков на операции кассы |
| Формула зарплаты | Опционально | Одна на роль. Применяется если у сотрудника нет индивидуальной |
| Системная | Автофлаг | true — нельзя удалить/редактировать права |
owner_legal_entity_id | Автофлаг | UUID ЮЛ-партнёра (если роль скрытая — персональная для владельца партнёра). См. раздел «Скрытые роли» |
| Статус | Обязательно | Активна / Удалена (soft delete) |
Разрешения — вкладка «Разделы Бэк-офиса»
Для каждого раздела админки — два флажка: Чтение (Read) и Редактирование (Edit).
Правило Edit implies Read
Включение Edit автоматически включает Read. Выключение Read автоматически выключает Edit.
Каталог разделов
| Раздел | Что включает |
|---|---|
| Меню | Товары, категории, модификаторы |
| Внешние меню | Конструктор меню для рекламного монитора и JSON-экспорта; список товаров каталога с override-полями (имя, описание, цена, видимость), публикация в каналы. external_menus.read — просмотр списка и редактора (read-only); external_menus.edit — CRUD + публикация + удаление + восстановление. (Добавлено в BR 4.1) |
| Прейскуранты | Настройка прейскурантов, привязка к ТТ |
| Техкарты | Рецептуры, per-size варианты |
| Ингредиенты | Справочник ингредиентов |
| Стоп-листы | Отключение позиций по ТТ |
| Склад | Операции приёма, списания, инвентаризации |
| Торговые точки | CRUD ТТ, публикация, расписание |
| Юридические лица | CRUD ЮЛ |
| Сотрудники | CRUD сотрудников, назначение ролей |
| Роли | CRUD ролей, настройка прав |
| Расписание смен | Плановое расписание работы сотрудников |
| Кухонные станции | Справочник производственных зон + цветовые пороги для KDS-карточек (yellow_threshold_minutes, red_threshold_minutes per-станция). Управляется через catalog.edit (раздел «Меню» уже даёт это право). (Расширено в BR 5.1) |
| Настройки KDS | Per-франшиза звуки KDS (мелодия нового заказа, интервал повтора, звук просрочки, громкость, авто-логаут), список зарегистрированных KDS-устройств с возможностью force-logout. kds.settings.edit — изменение настроек + удаление устройств. (Добавлено в BR 5.1) |
| Учёт рабочего времени | Фактически отработанные часы |
| Зарплата | Формулы, ведомости |
| Дашборд активности | Сводка по работе сотрудников |
| Отчёты | Отчёты по сменам, заказам, выручке |
| Заказы | Просмотр и управление заказами. Помимо orders.read / orders.edit — отдельный orders.delivery (право двигать курьерские статусы handed_over → in_delivery → delivered. Не даёт прав менять состав заказа или делать refund). Выдаётся обычно курьерам. (Добавлено в BR 2.5) |
| Настройки | Настройки франшизы |
| Клиенты | CRM: база клиентов, адреса, привязка к заказам. Помимо customers.read / customers.edit — отдельный customers.delete (выдаётся ограниченно, только для Владельца франшизы). (Добавлено в BR 3.1) |
| Группы клиентов | Сегментация клиентов: статические и динамические группы (Добавлено в BR 3.1) |
| Интеграции | Управление подключениями к агрегаторам доставки и webhook-подписками для внешних POS. integrations.read — просмотр списка подключений и логов доставки; integrations.manage — CRUD подключений и подписок. (Добавлено в BR 2.5) |
| Маркетинговая информация | Слайды для standby-карусели на POS Desktop (per-store). marketing.read — просмотр списка и превью; marketing.write — загрузка, редактирование, удаление, изменение порядка. Edit implies Read. (Добавлено в BR 6.1) |
Разрешения — вкладка «Функции POS-терминала»
Флажки на операции, доступные сотруднику на кассе.
Источник — YumaPOS, без позиций, нерелевантных нашему скоупу
Убраны настройки для вендинговых автоматов, торговых устройств, кухонных терминалов — они вне MVP.
Каталог операций
| Операция | Permission | Описание |
|---|---|---|
| Доступ к POS | pos.access | Базовая возможность входить в приложение (используется PIN-логином) |
| Открытие смены | — | Начало смены кассира |
| Закрытие смены | — | Завершение смены + Z-отчёт |
| Приём заказов | — | Создание и редактирование заказа |
| Применение скидок | — | Ручная скидка на позицию или заказ |
| Внесение наличных | — | Пополнение денежного ящика |
| Изъятие наличных | — | Изъятие из денежного ящика |
| Инкассация | — | Передача наличных в сейф магазина |
| Возврат средств | — | Возврат клиенту по закрытому заказу |
| Аннулирование заказа | — | Отмена незакрытого заказа |
| Аннулирование позиции | — | Удаление позиции из заказа |
| Изменение цены | — | Ввод цены для товаров с открытой ценой |
| Настройки кассы | — | Доступ к настройкам POS-терминала |
| Быстрое создание клиента | customers.create_quick | Поиск клиента по телефону, quick-create на POS, прикрепление клиента к заказу. (Добавлено в BR 3.1) |
Deferred: лимиты Min/Max
YumaPOS поддерживает у отдельных операций минимум/максимум (например, «Изъятие наличных — не более 5000 ₽»). В этой итерации не реализуется — только флажки on/off. См. раздел Deferred.
Разрешения — вкладка «Функции KDS-терминала»
(Добавлено в BR 5.1)
Флажки на операции, доступные сотруднику в KDS-приложении (Android-планшет на кухне).
| Операция | Permission | Описание |
|---|---|---|
| Доступ к KDS | kds.access | Базовая возможность входить в KDS-приложение по PIN и менять kitchen_status позиций. Выдаётся поварам, барменам, менеджерам. Без этого права — 403 KDS_ACCESS_DENIED при попытке логина |
| Управление настройками устройства | kds.settings.edit | Менять локальные настройки на устройстве (звуки, локальные пороги, layout), регистрировать новое устройство (выбор ТТ при первом запуске). Также используется в админке для редактирования kds_franchise_settings и удаления устройств |
Сотрудник может иметь и pos.access, и kds.access одновременно — типично для менеджера, который и за кассой работает, и кухню контролирует.
Разрешения — вкладка «Зарплата»
На вкладке роли указывается формула начисления зарплаты для сотрудников с этой ролью.
Типы формул, поля и иерархия применения описаны в спеке Зарплата — без изменений по сравнению с BR 1.4.1.
Изменение по сравнению с 1.4.1: формула привязана к конкретной роли (объекту), а не к enum-значению. Одна роль → одна формула на всю франшизу.
Deferred: формулы по ТТ
BR 1.4.1 предусматривал различие ставок по ТТ (роль × ТТ → формула). В этой итерации — одна формула на роль для всей франшизы. Per-ТТ ставки — отдельной BR когда возникнет реальная потребность. См. раздел Deferred.
Системная роль «Администратор»
| Свойство | Значение |
|---|---|
| Создание | Автоматически при bootstrap франшизы |
Флаг Системная | true |
| Название | Фиксированное: «Администратор» |
| Права | Все флажки Read/Edit по разделам, все операции POS, pos.access |
| Редактирование прав | Запрещено |
| Удаление | Запрещено |
| Кому выдаётся | Владельцу франшизы при bootstrap; владельцу партнёра — если выбран режим «Полный доступ» (см. ниже) |
Переименование, изменение описания — допустимо. Права и статус системной — нет.
Сотрудники с одной и той же системной ролью «Администратор» различаются scope (см. Ролевая модель), а не правами.
Скрытые роли владельцев партнёров
(Введено в BR 1.4.4 §5.2)
Когда главный админ создаёт ЮЛ партнёра и выбирает режим «Настроенные права» (вместо «Полный доступ»), для владельца партнёра создаётся скрытая персональная роль.
Зачем скрытые роли
- У каждого партнёра свой набор прав (админ может дать Петрову всё кроме «изменение цен», а Сидорову — всё кроме «публикации ТТ»)
- Эти роли не переиспользуются и не должны мешать в списке ролей
/admin/roles - Управление правами партнёра происходит в карточке ЮЛ партнёра, а не в общем списке ролей
Техническая характеристика
- В таблице
rolesполеowner_legal_entity_id(UUID NULL, FK →legal_entities.id) - Если задано → роль скрыта из всех списков
/admin/roles, фильтров, выпадающих списков при создании сотрудника - Редактируется только через карточку ЮЛ партнёра → вкладка «Права»
- Название в БД техническое (владелец не видит)
Управление через карточку ЮЛ партнёра
В карточке ЮЛ партнёра /admin/legal-entities/{id} есть вкладка «Права»:
ИП Петров
[Реквизиты] [Владелец] [Права] [ТТ]
─ Права владельца ──────────
○ Полный доступ (системная роль «Администратор»)
● Настроенные права
Разделы Бэк-офиса:
Меню ☑ чтение ☑ редакт.
Прейскуранты ☑ чтение ☐ редакт.
Публикация ТТ ☐
...
Функции POS:
☑ pos.access (нельзя снять)
☑ Открытие смены
...
Режимы
| Режим | Что происходит |
|---|---|
| Полный доступ | Владельцу партнёра назначается системная роль «Администратор». Scope ограничен его ЮЛ (через правила scope). Scope не смешивается с правами. |
| Настроенные права | Создаётся скрытая роль с owner_legal_entity_id = {id ЮЛ}, назначается владельцу. Админ вручную выбирает permissions. |
Минимум permissions владельца партнёра
Всегда выдаётся автоматически (нельзя снять через UI):
pos.access— чтобы владелец мог логиниться на POS по PINstores.read— видеть свои ТТemployees.read— видеть свой персонал
Это форсится как на UI (галки заблокированы), так и серверной валидацией.
Переключение режима
- «Настроенные» → «Полный доступ»: скрытая роль отсоединяется от владельца; назначается системная «Администратор»
- «Полный доступ» → «Настроенные»: создаётся новая скрытая роль (галки по умолчанию — все включены или копия системной минус настройки на усмотрение UX)
Видимость
- В
/admin/rolesскрытые роли не отображаются никогда - В списке сотрудников permissions-роли сотрудника показываются, но скрытая роль отображается как «Собственные права» или подобный лейбл (без её тех. названия)
CRUD обычных (не скрытых) ролей
Создание
- Кнопка «Добавить» на странице списка ролей
- Форма с четырьмя вкладками: Общее (название, описание), Разделы Бэк-офиса, Функции POS, Зарплата
- Минимально достаточно заполнить название; права по умолчанию — все выключены
- Сохранение: ошибка при дублировании названия в рамках франшизы
Редактирование
- Клик по строке роли → открывается та же форма
- Системную роль «Администратор» можно переименовать/изменить описание, но не права и не флаг системной
- Изменения применяются немедленно — все сотрудники с этой ролью получают новые права со следующего запроса
Удаление (soft delete)
- Кнопка «Удалить» в списке ролей или в форме редактирования
- Запрещено, если роль назначена хоть одному активному сотруднику — UI показывает: «Снимите эту роль со всех сотрудников перед удалением» + список сотрудников
- Системную роль удалить нельзя — кнопка недоступна
- Скрытые роли удалить через
/admin/rolesнельзя (их там нет). Они удаляются автоматически при удалении ЮЛ партнёра или при переключении режима на «Полный доступ» - Удалённая роль попадает в архив (статус «Удалена»)
Восстановление
- Страница «Удалённые роли» (открывается с основного списка)
- Кнопка «Восстановить» возвращает роль в статус «Активна» со всеми правами и формулой зарплаты
- Срок хранения в архиве — без ограничения (не удаляется физически)
Список ролей
Колонки
- Название
- Количество работников (число сотрудников, у кого эта роль назначена) — клик ведёт на отфильтрованный список сотрудников
- Системная (иконка/флаг для системных)
- Дата создания
Фильтры
- Системные / Пользовательские
- Активные / Удалённые
Поиск
- По названию
Действия на панели
- Добавить
- Удалить (mass delete выбранных, с учётом правил)
- Удалённые роли (переход в архив)
Скрытые роли не отображаются
Все роли с
owner_legal_entity_id != NULLисключаются из списка, фильтров, поиска и счётчика.
Ролевая матрица доступа к модулю «Роли»
(Обновлено в BR 1.4.4 — колонки scope вместо enum-ролей; permission roles.read / roles.edit для обычных сотрудников)
| Действие | Владелец франшизы | Владелец партнёра | Обычный сотрудник |
|---|---|---|---|
| Просмотр списка ролей | Все роли франшизы | Роли, назначенные его сотрудникам | roles.read permission |
| Создание роли | Да | Нет (scope) | roles.edit permission (обычно не выдаётся) |
| Редактирование роли | Да | Нет (scope) | roles.edit permission |
| Удаление роли | Да | Нет (scope) | roles.edit permission |
| Восстановление роли | Да | Нет (scope) | roles.edit permission |
| Назначение роли сотруднику | Любому сотруднику франшизы | Только своим сотрудникам | Если есть employees.edit |
| Настройка скрытой роли своего партнёра | — | Не напрямую (управляется главным админом через карточку ЮЛ) | Нет |
Бизнес-правила
- Уникальность названия роли — в рамках франшизы (case-insensitive). Скрытые роли не участвуют в проверке
- Один сотрудник → N ролей одновременно (см. Сотрудники)
- Каждая связка сотрудник × роль имеет собственный набор магазинов, где роль действует
- Permissions не кладутся в JWT — только
role_ids; полный список прав отдаётся через отдельный эндпоинт - Мгновенное применение изменений — при изменении прав роли все активные JWT продолжают работать, но следующий запрос к сервису получает обновлённые permissions
- Edit implies Read — нельзя разрешить редактирование раздела, не разрешив его чтение
- Системная роль — права и флаг фиксированы, редактируются только название и описание
- Скрытые роли — не отображаются в
/admin/roles, редактируются только через карточку ЮЛ партнёра, имеют принудительный минимумpos.access + stores.read + employees.read
Миграция
BR 1.4.3 (разовая акция при выкатке permissions-модели)
- Создаётся системная роль «Администратор» для текущей франшизы
- Сотрудник
admin@erp.local(бывший enumadmin_franchise) привязывается к этой системной роли - Удаляются все остальные сотрудники (бывшие
admin_franchisee,manager,cashier) - Для удаляемых владельцев:
legal_entities.owner_user_id → NULL— ЮЛ остаются, ТТ остаются, можно позже привязать новых владельцев
BR 1.4.4 (удаление enum)
(Введено в BR 1.4.4 §9)
ALTER TABLE employees DROP COLUMN role— колонка enum удаляетсяALTER TABLE franchises ADD COLUMN type VARCHAR(20) DEFAULT 'corporate' NOT NULL- Сервисы пересобираются и деплоятся синхронно (зависимости от enum убираются одновременно)
admin@erp.localостаётся с привязанной системной ролью и какowner_user_idглавного ЮЛ → его scope после миграции = вся франшиза, permissions — полный набор
Тестовая среда
Production-миграции не предусмотрено — платформа ещё не в проде.
UI-гейтинг по permissions
(Введено в BR 1.5)
Фронтенд скрывает UI-элементы на основании массива permissions[], полученного из /auth/me.
Принцип
- Нет
section.read→ пункт меню скрыт, маршрут недоступен - Есть
section.readбезsection.edit→ страница открывается в read-only режиме (кнопки создания/редактирования/удаления скрыты, формы заблокированы) - Есть
section.edit→ полный доступ (edit implies read)
Группы разделов
Каталог и Склад — группы с подпунктами. Группа видна если хотя бы один подпункт доступен. Например: нет menu.read, но есть price_list.read → группа «Каталог» отображается, внутри только «Прейскуранты».
Franchise owner bypass
Владелец франшизы (scope.type = all_franchise) видит все разделы и кнопки — проверки permissions пропускаются. Реализовано в usePermission hook.
POS-only сотрудники
Если у сотрудника нет ни одного backoffice *.read permission (только pos.* ключи) — вход в бэк-офис запрещён. При попытке логина отображается сообщение: «У вашей роли нет доступа к бэк-офису. Используйте POS-приложение.»
Дашборд
Дашборд доступен всегда (если сотрудник вошёл в бэк-офис). Является fallback-страницей.
Подробная спецификация
Полная таблица sidebar-гейтинга, кнопок на страницах, route guard и 403-обработки — в фронтенд-спеке UI-гейтинга.
Deferred
Отложено до отдельных BR
Зафиксировано сознательно, чтобы не раздувать скоуп текущей задачи.
| Функция | Обоснование отсрочки | Возврат |
|---|---|---|
| Per-ТТ формулы зарплаты (роль × ТТ → formula) | Нужна инфраструктура настроек per-ТТ ставок. Пока одна формула на роль покрывает 90% сценариев | Отдельная BR когда появится запрос от нескольких партнёров с разными ставками по городам |
| Min/Max лимиты на POS-операциях | UX лимитов требует отдельной проработки. Пока достаточно on/off флажков | Отдельная BR когда появится жалоба/риск |
| Шаблоны ролей владельцев партнёров | Сейчас скрытая роль создаётся с нуля для каждого партнёра. В будущем — библиотека шаблонов «Партнёр (базовый)», «Партнёр без цен» и т.п., из которых можно создавать скрытую роль по пресету | Отдельная BR когда появится реальный запрос на переиспользование |
Связи с другими модулями
- Сотрудники — присвоение ролей сотрудникам
- Юридические лица — вкладка «Права» у ЮЛ партнёра управляет скрытой ролью владельца
- Зарплата — формула на роль
- Авторизация — JWT с
role_ids, permissions отдельным эндпоинтом - Ролевая модель — общий обзор (scope-правила)