- Changed resp.content to resp.text in register_view (ApiResponse has no content attribute)
- Added logging for API base URL to debug 404 errors
- This should help identify why API returns 404 for registration endpoint
- Added logging in RegisterAPIView to track incoming data and validation errors
- Added logging in register_view to track form data and API communication
- Created test_register_api.py script for testing registration API
- Logs include: POST data, Content-Type, validation errors, API payload and responses
- All logs use [REGISTER] and [REGISTER_VIEW] prefixes for easy filtering
Календарь (rightbar) :
• Цвет события по умолчанию (#0d6efd) + удобная палитра (input[type=color] + быстрые свотчи).
• Детали события в модале (центрирование, перенос модала в <body> при необходимости), кнопки Редактировать/Удалить.
• Редактирование: заполнение формы из деталки, PATCH по API, возврат к календарю, безопасные обработчики (навешиваются один раз).
• Еженедельная лента теперь собирает события со всех доступных календарей (компания + отделы пользователя), дедупликация по id.
• В ленте недели: учитывается цвет события, показ источника (Компания / Отдел: N), location и краткое описание (≤20 символов).
• Устранены дубли отделов в селекторе календарей; нормализованы id; аккуратные ошибки/403; безопасная работа без токена.
Список отделов:
• Поиск по названию и руководителю (data-haystack); счётчик сотрудников обновляется только по видимым строкам.
• Кнопка «Создать отдел» в стиле списка (вся строка — кликабельна), видна только при наличии прав/статуса.
• Чёткое центрирование текста и иконки без кастомных стилей (Bootstrap-утилиты), стрелка фиксирована справа.
• Прокрутка только у списка (#deptList): шапка, поиск и кнопка остаются на месте; высота как в employees_list.html.
Auth / Регистрация / Логин / Верификация email / Сброс пароля:
• Шаблоны приведены к единой теме: уважаем переключатель тем из base.html, устранён «мигание» темы.
• RegistrationForm: валидация контактов (хотя бы один из Telegram/WhatsApp/WeChat), пароли совпадают.
• Поля телефона (регистрация/логин): inputmode='numeric', pattern для E.164, подсказки; на бэке по-прежнему нормализуется.
• Регистрация: превью аватара с заглушкой, radio для пола; убраны поля навыков/должности из формы (они не нужны здесь).
• API регистрации: принимаем avatar через Base64ImageField и сохраняем user.avatar при создании; остальная логика без изменений.
• Сообщение об ошибке «нужно хотя бы одно контактное поле» возвращается в ключе detail (совместимо с тестом).
• Логин/верификация/сброс: единые заголовки, floating labels, иконки, соответствие теме.
Техническое:
• Аккуратные утилиты JS: извлечение токена, формирование URL с диапазонами дат, нормализация событий, один раз навешиваемые обработчики.
• Безопасные fallbacks для отсутствующих узлов/данных; кросс-браузерные мелочи (overflow-scrolling, aria-атрибуты).
Примечания:
• Миграций БД нет. Изменены шаблоны, фронтовый JS и вью/сериализатор регистрации.
• Если пути файлов в проекте отличаются, коммит всё равно включает все изменённые файлы (git add -A).
Backend / API
- DepartmentViewSet:
- добавлены экшены POST /departments/{id}/add_member/ и /remove_member/
• включают/выключают членство (is_active) без изменения роли
• идемпотентны: add_member активирует существующую связь или создаёт, remove_member не трогает роль
- set_member_role: не создаёт членство, 404 если сотрудник не состоит в отделе; роль проверяется на принадлежность отделу; возвращает {employee_id, role_id|null, is_active}
- get_permissions: исправлено пересечение прав
• add_member/remove_member → только ManagePerm (DeptPerm.MANAGE)
• set_member_role → только AssignRolePerm (DeptPerm.ASSIGN_ROLE)
• set_head → ChangeHeadPerm
• update/partial_update/destroy → ManagePerm
- queryset/поиск/сортировка: аннотации для employees_count, поиск по имени/описанию/ФИО руководителя, сортировка по name/id
- Исправления валидации head в partial_update/set_head: «ослабление» требования email_verified, если меняет текущий руководитель
Frontend / Views & Templates
- employees/views_front.py:
- department_detail: берёт агрегат /api/v1/departments/{id}/ui-context/; формирует members_exclude_ids (участники+руководитель)
- добавлены прокси-вьюхи department_add_member / department_remove_member (POST) — ходят в API, показывают messages и редиректят обратно
- employees_list: поддержан формат ?format=json для фронт-поиска (без прямого запроса в DRF → нет 401)
- set_member_role: прокси, защита от изменения собственной роли
- templates/employees/department_detail.html:
- «голая» шестерёнка в правом нижнем углу инфоблока; запоминает состояние (localStorage), аккуратные отступы
- описание отдела: white-space: pre-line + overflow-wrap:anywhere (учёт переносов и длинных слов), уменьшен верхний «воздух»
- секция «Команда отдела» переделана в стилях блока «Профиль» (как в employee_detail.html):
• строки-элементы с аватаром, ФИО, текущей ролью
• форма смены роли (если есть права)
• две отдельные кнопки: «Пригласить» (добавить) и «Удалить»
- два модальных окна:
• Пригласить (add): поиск по всем сотрудникам, исключая уже состоящих/руководителя (members_exclude_ids)
• Удалить (remove): поиск только по сотрудникам текущего отдела; исключаем самого себя
• список с аватарками, выбор подсвечивается; сабмит не активен до выбора; клавиши Enter/Escape
- JS для модалок: поиск через фронтовую employees_list (?format=json), дебаунс, «портал» меню в body, обработка ошибок
- templates/employees/_department_controls.html:
- «Новая роль»: фикс разворота/скрытия формы, сброс состояний
- выпадающее меню ролей рендерится в body (не клипается родителями)
- кнопка «Отмена» теперь полноценная кнопка (btn btn-link), работает
- чипсы прав + скрытые permission_ids синхронизируются корректно
Тесты
- tests/api/v1/employees/test_department_membership_separation.py (новые/обновлённые):
- test_add_member_requires_manage_and_does_not_assign_role
- test_remove_member_requires_manage_and_does_not_touch_role
- test_set_member_role_requires_assign_not_manage
- test_set_member_role_does_not_create_membership_and_does_not_toggle_active
- test_all_three_actions_require_auth_and_permissions
- фиксы фабрик/тест-данных: гарантированно уникальные email/phone_number (устранён IntegrityError: UNIQUE constraint failed)
- поправлены ожидания статусов (401 для неаутентифицированных, 403 без прав, 200 при корректных правах); для set_member_role — 404, если членства нет
Bugfixes / прочее
- VariableDoesNotExist (list) в department_detail: использован безопасный контекст и корректный default
- 401 при поиске сотрудников из шаблона: переведено на фронтовую прокси-вьюху (?format=json), вместо прямого запроса в DRF
- мелкие правки стилей и отступов, соответствие стилю «Профиль»
Как проверить
1) API-права:
- без аутентификации: все 3 экшена → 401
- с аутентификацией, но без прав: add/remove → 403; set_member_role → 403
- с DeptPerm.MANAGE: add/remove → 200; set_member_role → 403
- с DeptPerm.ASSIGN_ROLE: set_member_role → 200; add/remove → 403
2) set_member_role не создаёт членство и не меняет is_active; add/remove не трогают роль
3) UI:
- шестерёнка показывает/скрывает настройки; состояние помнится
- модалки «Пригласить»/«Удалить»: поиск, выбор, сабмит; аватарки; корректные фильтры (исключения и текущий отдел)
- «Отмена» работает, кнопки и выпадающие списки не клипаются
BREAKING CHANGES: нет
Migrations: нет
### Что сделано
**Position API**
* Добавлен `PositionViewSet` с CRUD и экшенами:
* `POST /positions/{id}/set_groups`
* `POST /positions/{id}/add_groups`
* `POST /positions/{id}/remove_groups`
* `GET /positions/{id}/permissions` (агрегирует permissions из связанных групп)
* Чтение: `IsAuthenticated`; запись: staff/superuser **или** наличие модельных прав (`employees.add|change|delete_position`), экшены групп — кастомное право `employees.assign_position_groups`.
* Выставление требуемого права через `view.required_perm_code` в `get_permissions()`.
* Поиск/сортировка: `?search=`, `?ordering=`.
* **Отключена пагинация** (возвращаем plain list, как ожидают тесты).
* Сериализатор включает `groups` и `groups_verbose`.
* Роутер: зарегистрирован `positions` в `api:api_v1`.
**Кастомный пермишен для Position**
* В `Position.Meta.permissions` добавлен `("assign_position_groups", "Can assign auth groups to positions")`.
* В `employees.apps.EmployeesConfig.ready()` добавлен `post_migrate`-хук для гарантированного создания этого пермишена (страхует тестовую БД).
**DepartmentRole API**
* Добавлен `DepartmentRoleViewSet` (CRUD + экшены управления правами роли):
* `GET /department-roles/{id}/perms`
* `POST /department-roles/{id}/set_perms`
* `POST /department-roles/{id}/add_perms`
* `POST /department-roles/{id}/remove_perms`
* Фильтр: `?department=<id>` (+ безопасная обработка нечислового значения → пустой список).
* Права:
* CRUD: требуется `manage_department` (через главу отдела, роль с правом, staff/superuser **или** глобальное модельное право `employees.manage_department`);
* управление правами роли: `assign_department_role` (аналогично).
* Сериализатор `DepartmentRoleSerializer` с полями `permissions` и `permissions_verbose`.
**Пермишены и проверки доступа**
* `IsDeptManagerForWrite`:
* Научили работать с объектами, у которых есть `obj.department` (например, `DepartmentRole`).
* Добавили поддержку **глобальных** модельных прав (`user.has_perm("employees.<code>")`), чтобы уполномоченные пользователи могли управлять **любым** отделом.
* `IsStaffOrHasModelPerm`:
* Перед `user.has_perm(...)` сбрасываем кэш прав (`_perm_cache`, `_user_perm_cache`, `_group_perm_cache`) — важно для тестов, где права выдают в ходе сценария.
**Department.save(): членство и права главы**
* Перевели создание/активацию линка `EmployeeDepartment` для **нового** главы на **синхронный** режим (без `on_commit`) при **смене** главы.
* **Не** создаём линк на первичном создании отдела с уже заданным `head` (чтобы не ловить `UNIQUE` в кейсах, где тесты добавляют линк вручную).
* При смене главы:
* для **нового** главы — гарантируем наличие активной ссылки (`is_active=True`, `date_from` при необходимости);
* для **старого** главы:
* если `head -> None` — гарантируем наличие ссылки и деактивируем её (`is_active=False`, проставляем `date_to`);
* если смена `A -> B` — гарантируем, что ссылка старого главы существует и остаётся активной (бизнес-логика и тесты).
* Таким образом, права «главы» корректно переходят и отражаются в ссылках членства.
**Маршрутизация**
* Зарегистрированы в `api/v1/urls.py`:
* `positions`
* `department-roles`
**Тесты**
* **positions:** полный набор на права, экшены групп, поиск/сортировку, валидацию payload’ов; фикс хелпера телефонов (уникальные номера).
* **department-roles:** основной набор + расширенный (`extra`) на:
* роли с правами внутри отдела (manage/assign),
* глобальные модельные права (доступ к любому отделу),
* кросс-доступ (роль в отделе A не даёт прав в отделе B),
* семантику `set_perms` (полная замена, дедуп), идемпотентность `add/remove`,
* валидацию payload’ов, 404 при удалении несуществующей роли.
* **department\_head\_rights:** сценарии смены/снятия главы:
* старый глава теряет права (если нет роли),
* новому создаётся активная ссылка и права работают сразу,
* снятие головы → деактивация ссылки + `date_to`,
* у старого главы права сохраняются, если назначена роль с `manage_department`,
* смена головы не деактивирует ссылку старого главы по умолчанию.
* Все тесты зелёные.
### Технические детали / файлы
* `employees/models.py` — правки `Position.Meta.permissions`, логика `Department.save()`.
* `employees/apps.py` — `post_migrate` для `assign_position_groups`.
* `employees/permissions.py` — обновлён `IsDeptManagerForWrite`, `IsStaffOrHasModelPerm`.
* `api/v1/employees/serializers.py` — `PositionSerializer`, `DepartmentRoleSerializer`.
* `api/v1/employees/views.py` — `PositionViewSet` (без пагинации), `DepartmentRoleViewSet` (CRUD + экшены), безопасный фильтр `department`.
* `api/v1/urls.py` — регистрация роутеров.
* `tests/api/v1/employees/` — новые файлы тестов и доработки существующих.
### Миграции/заметки
* Требуется миграция для добавления `assign_position_groups` в `Position.Meta.permissions`.
* `post_migrate` гарантирует наличие права в тестовой БД.
* После деплоя: `python manage.py migrate`.
---
Таким образом, добавлены полноценные API для **Position** и **DepartmentRole**, корректно выстроена модель прав (глава/роль/глобальные), а сценарии смены/снятия главы полностью покрыты тестами.