Управление командой и роли в проекте
Как в системе организуется команда проекта, какие роли существуют, как они влияют на ответственность и затраты.
Оргструктура сотрудника
Каждый авторизованный сотрудник может получить свой организационный контекст через GET /api/v1/org/me.
Ответ включает:
- самого сотрудника с должностью, primary department и прямым руководителем;
- все отделы сотрудника из
user_departments; - руководителя каждого отдела (
departments.manager_id); - цепочку руководителей вверх по
users.manager_id; - список подчинённых текущего сотрудника.
Административные операции разделены:
- отделы и руководители отделов управляются через
/api/v1/departmentsи требуютdepartments.manage; - прямые руководители и должности управляются через
/api/v1/org/users/:id/manager,/api/v1/org/users/:id/positionи требуютorg.manage; - полное дерево компании
/api/v1/org/treeи просмотр чужих цепочек требуютorg.view.
Функциональные роли отделов
Функциональная роль — то, что сотрудник умеет в своём отделе: «Звукорежиссёр», «Монтажник», «Видеоинженер». Это справочник для подбора людей на проект, а не права: права участника в проекте по-прежнему задают роли проекта (internal/projectroles — «Техник», «Инженер», «Менеджер проекта»…). У функциональной роли есть права в проекте по умолчанию (project_role_code), которые подставляются в заявку.
- Справочник —
functional_roles(отдел, название, описание,project_role_code,is_active), владеющие ролью —user_functional_roles; миграция0161. Экран — «Администрирование → Функциональные роли». - API:
GET /api/v1/functional-roles(читает любой сотрудник — нужен формам заявок),POST,PUT /:id,DELETE /:id,PUT /:id/members. Правят те, кто ведёт оргструктуру (users_and_rightsилиorg.manage), и руководитель отдела (departments.manager_id) — роли своего отдела. Экран открыт по признакуfunctional_roles_viewв/org/me: кто ведёт оргструктуру, кто читает справочник отделов, и руководитель отдела; править всё —functional_roles_manage. Владеть ролью могут только сотрудники её отдела, основного или дополнительного. - Заявка на людей: у позиции можно выбрать функциональную роль отдела (
functional_role_id); права в проекте (role_code) тогда подставляются из неё и остаются редактируемыми. Роль должна быть активной и из того же отдела (team_requests_functional_role_not_found). - Назначение по позиции: руководитель отдела видит первыми тех, кто владеет ролью; если такой один и предпочтительного нет — он выбран сразу. Назначенный попадает в команду с функциональной ролью (
project_team_members.functional_role_id). - Участник команды: функциональную роль можно задать и при ручном добавлении или правке (
functional_role_id: 0 — снять, без поля — не менять). В списке команды она видна перед правами: «Звукорежиссёр · Техник». - Удалить роль, которая уже есть в заявках или командах, нельзя (
functional_roles_in_use) — её отключают: она пропадает из форм, но остаётся в истории.
Концепция команды проекта
Команда проекта — это набор людей, которые работают вместе на достижение целей проекта. Каждый человек может иметь:
- Роль в проекте (бизнес-роль, что он делает)
- Ставку за время (сколько стоит его час)
- Статус в проекте (активен/неактивен, временно включен/выключен)
Структура члена команды (Project Team Member)
Основные поля
Project Team Member:
- ProjectID: 123 (в каком проекте)
- UserID: 456 (какой пользователь)
- RoleInProject: "frontend_developer" (основная бизнес-роль, legacy-поле)
- RoleCodes: ["frontend_developer", "finance"] (набор ролей в проекте)
- HourlyRate: 1500 (руб/ч)
- IsLead: true (координатор проекта?)
- IsActive: true (активен ли сейчас?)
- CreatedAt: 2026-04-01 (когда добавлен)
- UpdatedAt: 2026-04-20 (последнее изменение)Что значит каждое поле
ProjectID и UserID
- Обязательны — определяют, кто именно в каком проекте
- Один пользователь может быть в разных проектах с разными ролями и ставками
RoleInProject и RoleCodes
role_in_projectсохраняется как основная/первая роль для обратной совместимости.role_codes— основной механизм, когда у участника несколько функций в одном проекте.- Это проектные бизнес-роли, отдельные от системной должности/роли пользователя.
- Канонические роли проекта:
manager,lead,engineer,technician,designer,qa,logistician,accountant,finance,lawyer,executor,observer
- Legacy-алиасы (
dev,financier,project_finance,co_executorи др.) автоматически нормализуются к каноническим кодам.
Практическое значение: один пользователь может быть одновременно, например, accountant и lead в конкретном проекте, оставаясь инженером или менеджером по основной системной роли. Глобальная роль пользователя в системе не подменяет его проектную роль при проверке проектного финансового доступа.
HourlyRate
- Опционально
- Если указана, это проектная ставка участника и основной источник для расчета трудовых cost items.
- Если не указана, backend не подставляет универсальную "базовую ставку" из пользователя: финансовый расчет должен брать ставку из отдельного правила оплаты или создавать cost item вручную.
- Позволяет иметь разные ставки в разных проектах, но сама по себе не создает запись в бюджете.
Пример:
Проект A:
HourlyRate: 1500 (стандартная ставка)
Проект B:
HourlyRate: 2000 (проект более сложный, ставка выше)
Или вообще не указана → в команде проекта ставки нет, финансовый расчет должен взять другое правило
Проект C (почасовой найм контрактора):
HourlyRate: 3000 (дорогой специалист)IsLead
- true/false
- Помечает, кто является лидом/координатором проекта
- Может быть несколько лидов в одном проекте
- Лидов может быть один или несколько (в зависимости от структуры)
Практическое значение:
- Лид явно виден в составе команды.
- Лида можно использовать как координатора в UI, фильтрах и уведомлениях.
- Финансовые и документные секции могут учитывать
leadкак проектную роль доступа. - Операционные права все равно проверяются через backend RBAC и проектный доступ, а не только через флаг
is_lead.
IsActive
- true/false
- true: человек активно работает в проекте
- false: человек больше не работает в проекте (ушел, закончился контракт)
Практическое значение:
- Неактивных людей не показываем в "Текущей команде"
- Но их можно увидеть в истории проекта
- Их Time Entries остаются в системе (для финансов)
- Новые Time Entries не могут быть добавлены неактивному члену команды
Жизненный цикл члена команды
Фаза 1: Добавление в команду
Кто может добавить:
- Создатель проекта
- Менеджер проекта
- Project Lead
- Администратор
Как добавляется:
- Открыть проект
- Перейти в раздел "Team"
- Нажать "Add Member"
- Выбрать пользователя из каталога
- Указать роль (опционально)
- Указать ставку (опционально, иначе используется базовая)
- Отметить, является ли лидом
- Сохранить
Result:
Project Team Member created
ProjectID: Event Setup
UserID: Иван (123)
RoleInProject: "Project Lead"
HourlyRate: 1500
IsLead: true
IsActive: true
CreatedAt: 2026-04-01Фаза 2: Активная работа
Человек работает в проекте:
- Видит проект в своем списке
- Может видеть свои задачи
- Может создавать задачи (если это лид)
- Заполняет Time Entries
- Участвует в чатах проекта
Система отслеживает:
- Все Time Entries этого человека в проекте
- Все задачи, назначенные этому человеку
- Все комментарии и обсуждения
- Назначения задач и упоминания в чатах создают in-app уведомления и, если включено в профиле, email delivery.
Фаза 3: Изменение ролей и ставок
В ходе проекта может измениться:
- Ставка (перезаключили контракт, изменилась система оплаты)
- Роль (был "Developer", стал "Tech Lead")
- Статус (ушел в отпуск → IsActive = false)
Как изменить:
- Открыть проект
- Перейти в Team
- Найти члена команды
- Отредактировать его данные
- Сохранить
Важно: История изменений сохраняется. Система запомнит, что ставка была 1500, потом стала 1800.
Отключённые сотрудники. Сотрудника с отключённой учётной записью в команду не добавить — ни вручную, ни назначением по заявке (409 projectteam_user_is_inactive / teamrequests_assignee_is_inactive). Правка роли или ставки уже состоящего участника остаётся доступной.
Фаза 4: Удаление из команды
Кто может удалить:
- Менеджер проекта
- Project Lead
- Администратор
Когда нельзя: сотрудник отвечает за незавершённые задачи проекта (статус не done и не cancelled) — 409 projectteam_user_has_assigned_tasks_in_project, сначала задачи передают другому. Завершённые и отменённые задачи исключению не мешают. Флаг has_assigned_tasks в списке команды считается по тому же правилу, интерфейс выключает действие заранее.
Что происходит при удалении:
- IsActive = false (человека помечают как неактивного)
- Человек больше не может добавлять новые Time Entries в этот проект
- Существующие Time Entries остаются (для финансового учета)
- Все его задачи остаются (могут быть переназначены другому)
- При включенной настройке
APP_REMOVE_FROM_CHAT_ON_TEAM_REMOVALпользователь также удаляется из проектного чата и перестает получать realtime-события этого чата.
Вариант "полного удаления" (редко используется):
- Удаляются все связи (если это было ошибка при добавлении)
- Обычно не используется, чтобы не потерять историю
Роли в проекте: примеры и ответственность
Роли в IT-проекте
| Роль | Что делает | Ставка | Lead? |
|---|---|---|---|
| Project Manager | Управляет бюджетом, сроками, коммуникациями | 2000-3000 | Обычно да |
| Tech Lead | Координирует разработку, делает архитектурные решения | 2000-2500 | Часто |
| Frontend Developer | Разрабатывает интерфейс | 1500-2000 | Нет |
| Backend Developer | Разрабатывает API и логику | 1500-2000 | Нет |
| QA Engineer | Тестирует | 1200-1500 | Нет |
| DevOps | Управляет инфраструктурой | 2000-2500 | Нет |
| Contractor | Внешний разработчик | 2500-5000+ | Нет |
Роли в event/logistics проекте
| Роль | Что делает | Ставка | Lead? |
|---|---|---|---|
| Project Manager | Координирует, взаимодействует с клиентом | 2000-3000 | Да |
| Logistics Specialist | Подбирает и доставляет оборудование | 1200-1500 | Часто |
| Installer | Физически расставляет оборудование | 800-1200 | Нет |
| Designer | Проектирует локацию, дизайн | 1500-2000 | Нет |
| QA/Inspector | Проверяет качество | 1000-1200 | Нет |
Как роли влияют на работу
Проектные роли дополняют общий RBAC:
- Передать контекст — новый сотрудник видит, кто за что отвечает.
- Организовать коммуникацию — можно фильтровать команду по функциям.
- Рассчитать затраты — ставка применяется в рамках конкретного проекта.
- Ограничить финансы — финансовые данные проекта доступны
admin, менеджеру проекта и участникам с финансовыми проектными ролями (manager,lead,accountant,finance; legacy-алиасы поддерживаются).
Как команда влияет на финансы
Пример: Event Setup проект
Команда:
Project: Event Setup
Created: 2026-04-01
Manager: Александр (1500 руб/ч)
Lead: Иван (1500 руб/ч)
Team:
- Дизайнер Анна (1200 руб/ч)
- Установщик Виктор (800 руб/ч)
- QA Мария (900 руб/ч)
- Logistics Петр (1200 руб/ч)Расчет трудовых затрат за 4 дня проекта:
Фаза подготовки (2 дня):
Александр: 16 ч × 1500 = 24 000 (встречи, планирование)
Иван: 16 ч × 1500 = 24 000 (дизайн, координация)
Анна: 16 ч × 1200 = 19 200 (дизайн локации)
→ Subtotal: 67 200
Фаза логистики и доставки (1 день):
Петр: 8 ч × 1200 = 9 600 (подготовка и координация)
→ Subtotal: 9 600
Фаза выполнения (день события):
Иван: 10 ч × 1500 = 15 000 (координация на объекте)
Виктор: 8 ч × 800 = 6 400 (установка)
Мария: 4 ч × 900 = 3 600 (инспекция качества)
→ Subtotal: 25 000
ИТОГО LABOR: 101 800 рубСмета проекта:
Labor: 101 800
Tech (оборудование): 55 000
Logistics (доставка): 2 000
─────────────
ИТОГО: 158 800Это бизнес-расчет для планирования. Чтобы сумма Labor появилась в GET /api/v1/projects/:id/budget, в finance.cost_items должна быть создана строка категории labor. Текущие time_entries и auto time entries задач сохраняют часы, но не создают денежный cost item сами по себе.
Изменение команды во время проекта
Сценарий: Иван (Project Lead, 1500 руб/ч) заболел. Его заменила Людмила (1800 руб/ч).
Что происходит:
- Иван помечается как IsActive = false
- Людмила добавляется в команду с ставкой 1800
- Все Time Entries Ивана остаются в истории
- Новые Time Entries Людмилы создаются уже на ее пользователя
- Финансовая смета изменится только после обновления соответствующих
laborcost items
Заявки в команду (Team Requests)
Когда используется
Если нужный ресурс находится в другом отделе, менеджер может создать Team Request. Заявка запрашивает не конкретное поле requested_user, а одну или несколько строк: отдел, роль и предпочтительные сотрудники.
Team Request:
ProjectID: Event Setup
Status: "draft"
Items:
- DepartmentID: 12
RoleCode: "engineer"
PreferredEmployees: [44, 45]Жизненный цикл заявки
- draft — менеджер создал черновик через
POST /api/v1/projects/:id/team-requests. - pending — менеджер отправил заявку через
POST /api/v1/team-requests/:id/submit; руководители отделов из позиций заявки (departments.manager_id) получают уведомление, ведущее в карточку проекта («Команда»). РаньшеSubmitпередавал в уведомление заявку без позиций, и оно никому не уходило. - approved — заявка согласована через
POST /api/v1/team-requests/:id/approve. - rejected — заявка отклонена (только из
pending). - cancelled — заявка отменена: из
draft,pendingилиapproved(status: "cancelled"в том жеapprove). Сотрудники, уже назначенные по согласованной заявке, остаются в команде; неназначенные строки больше не назначить. Отклонённую или отменённую заявку отменить нельзя (409 teamrequests_request_cannot_be_cancelled).
Роль строки (role_code) проверяется при создании по справочнику ролей проекта (internal/projectroles, алиасы приводятся к каноническому коду): неизвестная роль — 400 teamrequests_unknown_project_role, а не ошибка позже при назначении.
После approved руководитель отдела или менеджер проекта назначает строки:
assigned— выбранassignee_id, backend добавляет сотрудника вproject_team_members. Сотрудник должен быть активен и работать в отделе строки — основном или дополнительном (user_departments). Строка помечается назначенной условно (только изpending), затем сотрудник добавляется в команду; если добавить не удалось, строка возвращается вpendingбез исполнителя — назначение и участие в команде не расходятся;declined— строку закрыли без назначения.
Отдельного статуса accepted в текущем коде нет.
API
| Сценарий | Endpoint |
|---|---|
| Заявки проекта | GET /api/v1/projects/:id/team-requests?status= — конверт {items, meta}, новые первыми, строки заявки в items[].items |
| Создать черновик | POST /api/v1/projects/:id/team-requests |
| Получить заявку | GET /api/v1/team-requests/:id |
| Отправить | POST /api/v1/team-requests/:id/submit |
| Согласовать/отклонить/отменить | POST /api/v1/team-requests/:id/approve |
| Назначить или отклонить строку | POST /api/v1/team-request-items/:id/assign |
Кто видит и ведёт заявки
- Все заявки проекта видят и ведут: администратор, роли с записью карточки проекта «все» (
OWNER,OPS,TECH_DIR), роли с охватом «свой дивизион» (DIV_HEAD,MANAGER) для проектов своих дивизионов, менеджер проекта и руководитель основного отдела проекта. - Руководитель отдела, в который запрошен человек (
departments.manager_id— любого отдела, не только основного для него), видит только заявки со строками своего отдела и назначает по ним сотрудников. Карточку проекта чужого дивизиона он открывает, пока заявка на согласовании или согласована. - Код
projects.manageсам по себе больше не даёт доступа к заявкам чужих проектов: у менеджеров он есть, а охват по матрице — свой дивизион.
Практическое применение
Заявки помогают:
- Формально запросить ресурс из другого отдела
- Учитывать нагрузку на сотрудников
- Получить согласие руководства до того, как начать работу
- Документировать причину привлечения человека
Best Practices для управления командой
1. Добавлять людей в команду ДО начала работы
Не добавлять членов команды в процессе. Лучше спланировать состав сразу.
2. Указывать правильные ставки
- Если ставка человека в проекте отличается от базовой, указать именно эту ставку.
- Это важно для расчета затрат.
3. Отмечать лидов
- Проект должен иметь как минимум одного явного лида.
- Лид получает уведомления и может управлять командой.
4. Менять IsActive при необходимости
- Человек ушел/заболел? Сразу IsActive = false.
- Это предотвратит добавление новых Time Entries от неправильного человека.
5. Документировать изменения
- Если меняется ставка, ставить комментарий (почему, с какой даты).
- Если меняется роль, тоже документировать.
6. Просматривать команду регулярно
- Менеджер должен время от времени проверять состав команды.
- Убедиться, что все люди остаются активными, нет дублей.
Интеграция с другими частями системы
Команда → Задачи
- При создании задачи указывается основной исполнитель из пользователей-сотрудников
- Соисполнители передаются отдельным списком
co_assignee_ids - Отдел задачи не вводится вручную: backend определяет его по проекту или основному исполнителю
Команда → Time Entries
- Time Entry может быть заполнена только членом команды проекта
- Проектная ставка хранится в Project Team Member и может использоваться финансовым расчетом
- Если человека удалили из команды, новые Time Entries добавить нельзя
Команда → Budget
- Budget агрегирует
finance.cost_items; команда и ставки сами по себе не создают строки бюджета - Если меняется состав команды или ставка, нужно обновить трудовые cost items, если проект ведет денежный учет труда
Команда → Уведомления
- Уведомления о статусах задач, о незаполненном времени отправляются членам команды
- Если человек неактивен, уведомления ему не приходят
Резюме
Команда проекта — это ключевой элемент системы:
- Определяет, кто работает
- Определяет стоимость работ (через ставки)
- Определяет ответственность (через роли и IsLead)
- Определяет, кто видит что в системе (через RBAC)
Менеджер должен:
- Сформировать команду ДО начала работ
- Указать роли и ставки
- Отметить лидов
- Следить за активностью (менять IsActive)
- Документировать все изменения
Система автоматически:
- Проверяет участие в проекте при создании Time Entries
- Синхронизирует проектный чат при добавлении/удалении участника
- Отправляет уведомления
- Ограничивает доступ на основе ролей