- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
- Добавлена поддержка разных типов чатов (private, global, department, group)
- Для личных чатов проверяется участие через participants
- Для чатов отдела проверяется принадлежность к отделу
- Для глобального чата проверяется только is_active
- Для групповых/канальных чатов используется ChatMembership
Настройки CSRF:
- CSRF_COOKIE_HTTPONLY = False
- CSRF_COOKIE_SAMESITE = 'Lax'
- CSRF_USE_SESSIONS = False
- Added departments ManyToMany field to Document model
- Implemented department selection in document create/edit modals
- Added automatic notification creation for all department employees
- Fixed document filtering to include uploaded documents in 'My Documents'
- Fixed acknowledgements API to include department recipients
- Added bulk notification support with configurable threshold
- Removed acknowledge button, added visual highlighting for unread documents
- Implemented server-side acknowledgment flow with redirect to file
- Fixed mobile responsive layout for document actions
- Removed temporary test files and documentation
- ApiClient теперь обрабатывает 401 и 403 ошибки для обновления токена
- Добавлен JWTRefreshMiddleware для проактивного обновления токенов за 5 минут до истечения
- Добавлена настройка JWT_REFRESH_THRESHOLD_MINUTES (по умолчанию 5)
- Исправлена проблема с периодическими 403 ошибками после истечения access token
- сервис integrations/ldap_writeback.py
- фича-флаг и маппинг в settings (LDAP_WRITE_*)
- (рекоменд.) поля в Employee: ldap_dn, ldap_guid, last_ldap_modify_ts, last_ldap_sync_at
- write-back в PATCH /employees/{id} и /employees/me/ (409/503 на конфликты/ошибки)
- оптимизация queryset (prefetch actions только для detail/me)
- мелкие правки add/remove_skill и валидаций
Большое обновление по модулю заявок и интеграции с отделами.
## API (DRF)
* `requests_app.views.RequestViewSet`
* `create()`: валидируем write-сериализатором, **отдаём read-сериализатор** → `employee` в ответе теперь объект, а не PK (чинит падение `TypeError: 'int' object is not subscriptable`).
* `perform_create()`: для не-админов принудительно подставляем текущего пользователя.
* `perform_update()`: явные проверки финальности и владения.
* `destroy()`: подключён объектный пермишен `NotFinalOrStaff` через `get_permissions()` — **нельзя удалять финальные заявки** не-админам.
* `get_permissions()`:
* исправлены ветки `comments/approve/reject/cancel/destroy`, убран некорректный «битовый OR» классов пермишенов;
* для `comments` — специальный пермишен (владелец без прав НЕ имеет доступа), соответствует тестам.
* для `approve/reject/retrieve/comments` добавлена интеграция с `AdminOrDeptAllowed` (департаментные права) — основа для отдел-скоупа.
* `get_queryset()`: добавлен **департаментный скоуп** — «свои» + заявки отделов, где у пользователя есть право `view_request` (через `EmployeeDepartment → role.scoped_permissions`).
* `get_object()`: для `retrieve/approve/reject/comments/cancel` объект берётся из полного набора и затем прогоняется через `check_object_permissions()` → возвращаем **403, а не 404** при нехватке прав.
* `pagination_class = None` — `list` стабильно возвращает массив, без обёртки `{count, results…}` (устраняет типовую ошибку парсинга в тесте).
* `requests_app.permissions`
* `CommentsPermission`: политика «только staff/право отдела», **владелец без прав — 403**.
* `NotFinalOrStaff`: объектный пермишен для запрета удаления финальных заявок.
* `requests_app.serializers`
* `RequestWriteSerializer`:
* делаем поля `employee/status/approver/department` **необязательными**;
* в `to_internal_value()` для обычных пользователей **тихо удаляем** запрещённые поля до полевой валидации (чтобы не ловить 400 на несуществующих PK);
* в `create()` для обычных пользователей проставляется текущий пользователь; статус берётся из `default` модели.
## Фронтенд (server-rendered views)
* Добавлены FBV, работающие через наш `backend/api/client.py`:
* `my_requests`, `all_requests`, `request_create`, `request_detail`,
* `request_process` (approve/reject), `request_cancel`,
* `request_comment_add`, `request_comment_delete`.
* Проксируем аутентификацию, корректно отображаем ошибки API, поддерживаем multipart для вложений.
## Тесты
* Добавлен модуль `tests/api/v1/requests_app/test_requests_departments.py` с чек-листом интеграции заявок и отделов:
* видимость по отделам, детальный просмотр, approve/reject по отдел-правам,
* доступ к комментариям по отдел-правам,
* валидация подмены `department` при создании.
* Базовые API-тесты теперь проходят:
* `tests/api/v1/requests_app/test_requests_api.py` — **все зелёные**.
* Департаментные тесты **частично красные** (починим завтра):
```
FAILED tests/api/v1/requests_app/test_requests_departments.py::test_dept_viewer_cannot_retrieve_other_department_request - AssertionError: b'{"detail":"No Request matches the given query."}'
FAILED tests/api/v1/requests_app/test_requests_departments.py::test_dept_processor_can_approve_own_department_request - AssertionError: b'{"detail":"У вас недостаточно прав для данного действия."}'
FAILED tests/api/v1/requests_app/test_requests_departments.py::test_dept_comment_viewer_can_read_comments_in_department - AssertionError: b'{"detail":"Недостаточно прав для доступа к комментариям."}'
FAILED tests/api/v1/requests_app/test_requests_departments.py::test_dept_comment_adder_can_post_in_department - AssertionError: b'{"detail":"Недостаточно прав для доступа к комментариям."}'
```
Причины на текущем этапе:
* `retrieve` местами ещё даёт 404 вместо 403 при отсутствии отдел-прав — добьём маршрут через `get_object()`/пермишены.
* Для `approve/reject` и `comments` требуется довести проверку `AdminOrDeptAllowed(required_code=...)` до полной логики «И» по кодам и убедиться, что в тестовых фикстурах корректно создаются связки ролей/прав отдела.
## Прочее
* Мелкие чистки импортов и докстрингов; типизация (Google-style) по новым функциям/классам.
---
**Итог по прогону:**
`tests/api/v1/requests_app/` — **20 passed, 5 failed** (см. список выше). Базовые сценарии готовы, департаментные — почти готовы; добьём завтра строгую выдачу 403 и все ветки отдел-прав для `retrieve/approve/reject/comments`.
### Основное
* Политика доступа для документов:
* **CRUD** — только `is_staff` или обладатели модельных прав (`add/change/delete_document`).
* **Чтение/скачивание/ACK** — любой **аутентифицированный**, но **только** свои документы (получатель активен) или если `sent_to_all=True`.
* Для календаря: `GET /api/v1/calendar/events/` (компания) доступен авторизованным пользователям.
### permissions / views
* Введён `DocumentReadOrModelPerms` на базе `DjangoModelPermissions`:
* `GET/HEAD/OPTIONS` не требуют model-perms (только `IsAuthenticated`).
* Объектно: staff/любой model-perm → доступ; иначе только `sent_to_all` или пользователь в `recipients(is_active=True)`.
* Небезопасные методы требуют `add/change/delete_document` (или staff); **не требуем** дополнительно `view_document`.
* `DocumentViewSet.get_queryset()` фильтрует **list** для пользователей без `view_document`: только `sent_to_all` и документы, где текущий пользователь — активный получатель.
* Экшен `acknowledge` больше не переопределяет `permission_classes`; вызывается `self.get_object()` → срабатывает объектная проверка доступа.
* (dev) Раздача медиа: убедились, что подключён `urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)` — скачивание по `file_url` отдаёт `200`.
### serializers
* Добавлен `RecipientIDsField`: принимает `recipient_ids` в форматах **repeat-params / JSON-строка / CSV**, корректно читает `QueryDict.getlist()`.
* `DocumentWriteSerializer`:
* `file` теперь **необязателен** на PUT/PATCH, обязателен на POST (проверяется в `validate()`).
* Валидация `sent_to_all`/`recipient_ids` учитывает текущее состояние при апдейте; пустой список при `sent_to_all=False` запрещён.
* `_set_recipients()` назначает **только активных** пользователей и очищает список при `sent_to_all=True`.
* `validate_file()` — отказ по лимиту размера (`DATA_UPLOAD_MAX_MEMORY_SIZE`) с 400.
* `DocumentReadSerializer` — `file_url` через `FileField(source="file")`, `is_acknowledged` рассчитывается корректно.
### create response
* В `DocumentViewSet.create()` рассчитываем `recipient_count` из **нормализованных входных ID** (до `save()`), считая только активных; при `sent_to_all=True` возвращаем `0`.
### employees
* `EmployeeBriefSerializer` дополнен полем `full_name` (для тестов сериализации документов).
### Исправленные тесты/регрессии
* `TestCreate::test_create_sent_to_all_false_with_recipients_variants` — теперь `recipient_count` корректен для repeat/JSON/CSV.
* `TestUpdate::*` (PUT/PATCH) — больше не требуют `file`, возвращают `200`.
* `TestDelete::test_delete_and_cascade_acknowledgements` — `delete_document` достаточно, `403` устранён.
* `TestAcknowledge::test_ack_forbidden_if_not_recipient_and_not_sent_to_all` — теперь **403** для чужих документов с `sent_to_all=False`.
* `TestSerialization::test_file_url_downloadable_in_dev` — `200` при скачивании `file_url` в dev.
* `TestSecurityAndPolicy::test_policy_without_perms_list_is_200_detail_and_ack_require_access` — `list` даёт `200`, `detail/ack` чужого документа с `sent_to_all=False` дают `403`.
* `calendar_app`: `TestAuthAndPermissions.test_company_get_ok_regular_user` — `GET` по компании даёт `200`.
### Затронутые файлы (основные)
* `backend/api/v1/documents/permissions.py` (+ `DocumentReadOrModelPerms`)
* `backend/api/v1/documents/serializers.py` (+ `RecipientIDsField`, правки `DocumentWriteSerializer`, `DocumentReadSerializer`)
* `backend/api/v1/documents/views.py` (фильтрация `get_queryset()`, логика `create()`, `acknowledge`)
* `backend/api/v1/employees/serializers.py` (+ `full_name`)
* `eusrr_backend/urls.py` (проверка `static()` для MEDIA)
* *(календарь)* `backend/api/v1/calendar/views.py` (`get_permissions()` для company list)
---
**BREAKING**: ACK для документов с `sent_to_all=False` теперь доступен **только получателям** (или при наличии модельных прав/staff).
### Skill
* Добавлен `SkillViewSet`:
* CRUD: `/api/v1/skills/`
* Любой авторизованный может **создавать** навыки.
* Изменение/удаление — только `staff/superuser` или при наличии модельных прав:
* `employees.change_skill` (PATCH/PUT, а также `merge`)
* `employees.delete_skill` (DELETE)
* Поиск/сортировка: `?search=` по `name`, `?ordering=name|-name`, без пагинации.
* Экшены:
* `POST /api/v1/skills/bulk_create` — массовое создание (`names: string[]`, нормализация, дедуп).
* `POST /api/v1/skills/merge` — объединение навыков:
* body: `{ source_id, target_id, reassign=true, delete_source=true }`
* переносит связь сотрудников `source → target`, опционально удаляет `source`.
* идемпотентно, учитывает случаи когда у сотрудника уже есть `target`.
* Сериализатор: `SkillSerializer`.
* Роутинг: зарегистрирован `skills`.
### EmployeeAction
* Добавлен `EmployeeActionViewSet`:
* CRUD: `/api/v1/employee-actions/`
* Чтение — для авторизованных.
* Запись — `staff/superuser` или держатели модельных прав:
* `employees.add_employeeaction`
* `employees.change_employeeaction`
* `employees.delete_employeeaction`
* Фильтры: `?employee=<id>`, `?date_from=`, `?date_to=`.
* Сортировка: по умолчанию `-date`.
* Бизнес-эффекты:
* `ACTION_DISMISSED` — деактивация сотрудника (`employee.is_active=False`) и всех активных `EmployeeDepartment` с выставлением `date_to=today`.
* Любое иное действие — активирует сотрудника (`employee.is_active=True`), без автоматического восстановления отделов.
* Сериализатор: `EmployeeActionSerializer` (если `date` не передан — проставляется `now()`).
* Роутинг: зарегистрирован `employee-actions`.
### Тесты
* `tests/api/v1/employees/test_skills.py`:
* список/поиск/сортировка;
* создание обычным пользователем;
* права на обновление/удаление (включая staff bypass);
* `bulk_create` (нормализация и дедуп);
* `merge` (переназначение, параметры `reassign/delete_source`, идемпотентность).
* `tests/api/v1/employees/test_employee_actions.py`:
* доступ и фильтрация по сотруднику;
* создание с правами/без, staff bypass;
* эффекты увольнения (деактивация сотрудника и связей);
* активация при неувольняющем действии;
* эффекты при изменении существующего события на `DISMISSED`;
* удаление с правами.
* **Учёт авто-создаваемых событий**: проверки построены на изменении/baseline, а не на «пустой истории».
### Изменённые файлы (основные)
* `backend/api/v1/employees/serializers.py` — `SkillSerializer`, `EmployeeActionSerializer`
* `backend/api/v1/employees/views.py` — `SkillViewSet`, `EmployeeActionViewSet`
* `backend/api/v1/urls.py` — роуты `skills`, `employee-actions`
* `tests/api/v1/employees/test_skills.py` — новый
* `tests/api/v1/employees/test_employee_actions.py` — обновлён с учётом baseline и добавленными сценариями
### Заметки
* Миграций нет (использованы существующие модели).
* Поведение по автособытиям при создании сотрудника поддержано в тестах (baseline).
### Что сделано
**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**, корректно выстроена модель прав (глава/роль/глобальные), а сценарии смены/снятия главы полностью покрыты тестами.
**Что сделано**
* **API**
* Добавлен `POST /api/v1/auth/register/` — создание пользователя, генерация кода и письмо (повторная отправка, если email есть, но не подтверждён).
* `POST /api/v1/auth/verify-email/` возвращает `user_id` при успехе.
* `POST /api/v1/auth/resend-email/` — без изменений по контракту.
* **Фронт (views\_front)**
* `RegisterView.form_valid` теперь вызывает API `auth/register` (без прямой работы с ORM).
* `EmailVerifyView.post` переводён на API `auth/verify-email` + автологин при успехе.
* Повторная отправка кода (`resend_email`) уже через API, добавлена защита от частых запросов по сессии.
* Исправлена сериализация телефона (E.164/строка) при отправке в API.
* **Клиент API**
* Новый модуль `employees/api_client.py`: безопасная сериализация payload, удобные ошибки (`api_bad_json status=… body=…`) и абсолютные URL через `request.build_absolute_uri`.
* **Middleware**
* `AuthRequiredMiddleware` теперь **пропускает**: `/api/v1/**`, статику/медиа, страницы логина/регистрации/сброса пароля, админ-логин и `OPTIONS`. Остальное для анонима — редирект на `login`.
* **Аутентификация/выход**
* Страница подтверждения выхода (в стиле iOS bottom sheet) + `ConfirmLogoutView`. Пункт «Выйти» ведёт на GET `/auth/logout/`, далее POST подтверждения.
* Шаблон выхода принимает `next` из контекста (без обращений к `request.GET.next` в шаблоне).
* **UX/мелочи**
* Устранены 500 при отсутствии `password_reset` — добавлены/учтены URL’ы потока сброса (или безопасные ссылки).
* Уточнены сообщения об ошибках при регистрации/верификации.
**Как проверить**
1. Аноним может вызвать:
* `POST /api/v1/auth/register/` → `201 {"ok":true}` / `200 {"ok":true,"resent":true}` / `400 {"error":"email_taken"}`.
* `POST /api/v1/auth/verify-email/` с корректным кодом → `200 {"ok":true,"user_id":...}`.
* `POST /api/v1/auth/resend-email/` → `200 {"ok":true}`.
2. UI:
* Регистрация в форме → обращение к API, письмо с кодом, переход на ввод кода.
* Ввод кода → API, автологин, редирект в ленту.
* «Выйти» в меню → страница подтверждения → POST → сообщение «Вы вышли…» и редирект на логин/`next`.
3. Проверить, что `/api/v1/**` не редиректится на логин (middleware whitelist работает).
4. Телефон в форме не ломает JSON (нет `TypeError: PhoneNumber is not JSON serializable`).
**Миграции**: нет.
## Что сделано
### Модели
- **employees**
- : теперь включает отделы, где сотрудник является **руководителем**.
- Уточнены :
- → у , у .
- : добавлены удобные геттеры , ; улучшён .
- Модели , , приведены к актуальным полям (без start_date/end_date/level и т.п., чтобы не конфликтовать с формами/админкой).
- **feed**
- Вынесены константы типов постов: , , + .
- Жёсткие **CheckConstraint**:
- для департаментских постов ,
- для остальных — .
- Индексы по , , .
- **communications**
- :
- для (чаты отдела удаляются вместе с отделом).
- Метод : активные сотрудники отдела + **руководитель**; для глобального — все активные.
- Запрет удаления **основных** чатов (global/department), но обычные удаляются нормально.
### Админка
- Приведена в соответствие моделям: убраны несуществующие поля, добавлены фильтры/поиск по реальным полям, inlines для отделов (связи сотрудников).
- Исправлены ошибки system check (E033/E108/E116).
### Формы
- , : валидация аватара, корректные required/optional, PhoneNumberField для WhatsApp/WeChat.
- , (динамический queryset сотрудников, не состоящих в отделе).
- , , : соответствуют актуальным моделям, с базовой валидацией диапазонов дат (если применимо).
### Вьюхи (employees)
- Переписаны аккуратно и проще:
- Списки/детали отделов на CBV (, ), в контексте — сотрудники, новости отдела, новые сотрудники.
- Профиль и чужой профиль использует общий шаблон с подключаемыми блоками.
- : доступ только **админу/руководителю**; добавление связи и автодобавление участника (и руководителя) в **чат отдела**.
- : подготовлено к фильтрации по .
### Вьюхи (feed)
- — **универсальная** форма:
- контекст определяется автоматически из GET: | ,
- скрываем поля , чтобы пользователь не ломал контекст,
- редирект — на **красивые** страницы: главная лента / страница отдела / лента сотрудника.
- : только админ — для компании, только **руководитель отдела** — для департамента.
- : комментарии + форма добавления.
- **Добавлены** CRUD для постов и комментариев:
- , — автор/админ/руководитель отдела.
- , — только автор комментария.
### URL-ы
- : добавлены маршруты , , , .
- : все нужные эндпоинты в рабочем состоянии, на месте.
### Шаблоны
- : опрятная шапка, линк на «Все сотрудники отдела», кнопка «Пригласить» (для админа/руководителя), внизу — include **новостей отдела**.
- : приведение к общему стилю, кнопка «Новая публикация» с автоподстановкой department.
- : отображает бейдж контекста (Компания/Отдел/Личное), **одна** кнопка «Отмена», приходит из вьюхи.
- : контекстная кнопка «Назад к ленте» (определяется автоматически), кнопки **Редактировать/Удалить** (по правам), комментарии с кнопками **ред./удал.** у автора.
- Исправлены include-пути (везде , без обратных слешей).
## Почему всё ок
- Нет циклических импортов, выровнены.
- Никакой магии в шаблонах: вся логика определения контекста — во вьюхах.
- Чаты отдела удаляются вместе с отделом; основные чаты защищены от удаления.
- Учитываем **руководителя отдела** везде, где нужно (новости, чат, права).
## Что проверить локально
1) Миграции:
- python manage.py makemigrations
- python manage.py migrate
2) Создать отдел, назначить руководителя.
3) Пригласить сотрудника → автоматически попадёт в чат отдела.
4) Создать новости:
- с главной → тип company,
- со страницы отдела → тип department, поле Department скрыто,
- из ленты сотрудника → тип employee.
5) Закрепление новостей:
- компания — только staff,
- отдел — только head.
6) Редактирование/удаление постов и комментариев — по правам.
## Примечания
- Если ранее в БД были посты с типом 'personal' — вручную мигрируйте на 'employee'.
- Add birth_date, created_at/updated_at to Employee; remove username field
- Introduce EmployeeAction for tracking HR events and is_actually_active/status properties
- Create BotSubscriber model (OneToOne relation) for TG/WA binding
- Implement Aiogram-based Telegram bot:
• request contact to bind by phone or @username
• department selection keyboard from DEPARTMENTS dict
• async ORM via sync_to_async, custom filters for bound & active employees
- Update profile view & template:
• show “Bind Telegram/WhatsApp” button only when not already bound
• preview avatar on upload
- Remove registration tokens in favor of contact-based auth
- Clean up templates, forms and add avatar validation mixin