- Удалён LDAP_SERVER_TYPE и все OpenLDAP ветки кода
- user_service.py: только AD (sAMAccountName, UAC, microsoft.modify_password)
- auth_backends.py: AD фильтры и атрибуты (objectCategory, objectGUID)
- Удалён server_configs.py (больше не нужен)
- Удалён OpenLDAP Docker окружение (docker-compose.ldap.yml, ldap/, QUICKSTART_LDAP.md)
- Фронтенд: мульти-шаговая регистрация, AvatarCropper 3:4, verify-email страница
- Убран CSRF для JWT API, IP-ограничения работают независимо
- Полевые ошибки регистрации с красной подсветкой на фронте
АРХИТЕКТУРНЫЙ РЕФАКТОРИНГ:
✅ Убрано дублирование логики Chat.participants vs ChatMembership.is_active
✅ Теперь единственный источник истины - ChatMembership.is_active
✅ Исправлен баг с изменением ролей (роли не отображались после изменения)
ИЗМЕНЕНИЯ:
Backend (viewsets.py):
- get_queryset: мигрировано на memberships__is_active=True
- create: убрано participants.add, только memberships
- leave/remove_member: только is_active=False + left_at
- add_member: восстанавливает is_active=True если был False
- change_role: явно устанавливает is_active=True + left_at=None
Backend (serializers.py):
- Убрано поле participants из ChatDetailSerializer
- get_participant_details: через memberships.filter(is_active=True)
- get_participant_names: через memberships
Backend (models.py):
- get_participants(): только через ChatMembership.is_active=True
- __str__(): получаем участников через memberships
- participants помечен как устаревший (в комментариях)
Backend (rules.py):
- is_member_of_chat: только ChatMembership.is_active=True
- has_send_messages_permission: убран fallback на participants
Миграции:
- Создана 0038: убирает 'owner' из choices, очищает help_text
- Исправлены зависимости в requests_app/documents/feed 0010/0014/0006
(заменили __latest__ на конкретную версию 0037)
- Создан management command migrate_participants_to_memberships
(перенос данных participants → memberships)
Frontend:
- Добавлено оптимистичное обновление UI при изменении ролей
- Детальное логирование для диагностики
Тесты:
- test_change_role_improved.py: проверка изменения ролей с задержкой
- Обновлен test_chat_roles.py
Удаление устаревшей документации:
- Удалены старые отчеты из docs/ (73 файла)
- Сохранены актуальные руководства
ИСПРАВЛЕННЫЙ БАГ:
До: При изменении роли is_active оставался False → membership не попадал в filter(is_active=True) → исчезал с фронтенда
После: Явно устанавливаем is_active=True при любом действии с членством
Backend:
- Исправлен user_can_access_chat() для поддержки context-based чатов
- Обновлен is_chat_member() для использования get_participants()
- Добавлена проверка has_send_messages_permission (блокирует гостей)
- Исправлен MessageViewSet.get_queryset() для комментариев
- Добавлена поддержка Feed Post resolver в resolve_chat_participants()
- Чаты типа 'comments' исключены из общего списка
- Добавлены permissions для mark_read, messages, messages_around
- Добавлена команда manage_chat_permissions для управления ролями
Frontend:
- Скрыто поле ввода сообщений для гостей (role='guest')
- Добавлена проверка can_send_messages из membership
- Показывается информативная заглушка для пользователей без прав
Tests & Docs:
- Добавлены тесты для guest роли (send_message + reactions)
- Документация по ролям и правам в чатах
- Troubleshooting guide для отладки permissions
Fixes:
- 403 при доступе к чатам-комментариям
- 404 при удалении/редактировании комментариев
- Комментарии в общем списке чатов
- Гости могли отправлять сообщения
- Обновлена фикстура department_chat: использует GenericFK и type='channel'
- Обновлен тест test_create_department_chat для новой модели
- Обновлен скрипт check_chat_access.py: удалена логика department
- Добавлена поддержка типов group/channel в диагностике
- Удалены все ссылки на удаленное поле department
- Удалены поля document_type, status, published_at из модели Document
- Упрощены разрешения и правила доступа
- Удалены endpoint'ы для работы со статусами
- Удалены тесты FSM workflow
- Обновлены serializers для новой структуры модели
Added:
- is_not_document_uploader predicate
- Specific FSM action permissions (approve_document, publish_document, etc.)
- Tests for author cannot approve/publish own documents
Note: This is work in progress and will be reviewed/simplified after comparing with develop branch
- Added TestAuthorCanSubmitOwnDocument class with 3 tests
- test_author_can_submit_own_document_without_model_permission: verifies authors can submit their own documents via is_document_uploader predicate
- test_regular_user_cannot_submit_others_document: ensures users cannot submit others' documents
- test_author_can_edit_own_draft_document: confirms authors can edit their drafts
Benefits:
- Validates django-rules integration (is_document_uploader predicate)
- Ensures proper separation: authors manage own documents, not others'
- No model permissions required for basic author operations
All 91 tests passing ✅
- Modified DocumentReadOrModelPerms to allow 'create' action for all authenticated users
- Documents are created in DRAFT status by default (FSM workflow ensures control)
- Updated test_regular_user_access_according_to_policy to expect 201 instead of 403
- Added new test test_regular_user_can_create_document_in_draft
- Updated policy documentation in test file
Benefits:
- Encourages employee engagement in document management
- FSM workflow (draft → review → approve → publish) maintains quality control
- Approval/publish still requires change_document permission
- Regular users only see PUBLISHED documents (except their own)
All 88 tests passing ✅
- Changed return type from bool to Optional[Notification]
- In EAGER mode (tests), query and return created Notification
- In fallback mode, return created Notification object
- Fixes integration tests where code expected Notification object
Previously returned True/False which broke code expecting notification.id
attribute. Now properly returns the created Notification instance or None.
- Created test suite for all new ViewSets:
* DocumentTagViewSet (3 tests)
* DocumentTypeViewSet (2 tests)
* CabinetViewSet (4 tests)
* DocumentCommentViewSet (5 tests)
- Tests for related documents management (4 tests)
- Tests for django-reversion endpoints (2 tests)
- Integration test for complex document with all features
- Total: 21 test cases covering all new functionality
Tests use pytest fixtures for clean test data setup.
Tests cover CRUD operations, permissions, and edge cases.
- Создан FilerFileField для сериализации filer.File объектов
- При чтении: возвращает URL файла через filer.url
- При записи: создает filer.File из UploadedFile с валидацией размера
- Обновлены serializers для работы с FilerFileField
- Обновлены тесты API: _filer_file() helper, mak_document() использует filer
- Удален устаревший mock documents.notification
- Удален validate_file() (валидация перенесена в FilerFileField)
WIP: Некоторые тесты не проходят, требуется отладка permissions
- Добавлены 5 новых моделей в watson search:
* ProcurementRequest (заявки на закупку)
* Equipment (оборудование)
* Document (документы)
* Notification (уведомления)
* Event из django-scheduler (расписание)
- Backend API: форматтеры для новых типов моделей
- Frontend: добавлены иконки и названия для новых типов
- Тесты: исправлены фикстуры, все 13 тестов проходят
- Индексы: 3895 записей проиндексировано
Итого: 12 моделей доступны для полнотекстового поиска
- Создан frontend/src/lib/api.ts с 43 методами для всех API endpoint'ов
- Создан frontend/src/lib/permissions.ts для проверки прав доступа
- Исправлен URL в getChatMessagesAround: /messages/around/ → /messages-around/
- Исправлен параметр: message_id → around_id
- Восстановлен backend из ветки master для стабильности
- Удалены конфликтующие файлы Pages Router (src/pages, App.tsx, providers)
- Оставлена только чистая структура Next.js App Router
- Добавлена документация по миграции API v2
- Remove @pytest.fixture from make_user (it's a helper function, not a fixture)
- Update _is_ldap_enabled patch paths to api.v1.employees.views._helpers
- Fixes AttributeError and fixture calling errors
- Создан модуль calendarEventDragDrop.js с классами DragDropState и CalendarUtils
- Реализовано перетаскивание событий между календарями через модальное окно
- Добавлена кнопка 'Переместить все' для массового перемещения
- Модальное окно отображает источник и цель перемещения
- Поддержка legacy-календарей (company/department/personal)
- Добавлены стили для подсветки drop-target (папки и календари)
- Backend: исправлена обработка null значений в serializer
- Backend: добавлен endpoint list_raw для получения немодерируемых событий
- Использованы стандартные Bootstrap стили для модалов
- Исправлено всплытие событий dragover/drop (stopPropagation)
- Эмодзи иконки для типов календарей (����)
- Toast уведомления об успехе/ошибках перемещения
Реализовано:
1. Множественный выбор календарей через чекбоксы
- Заменил <select> на чекбоксы для выбора нескольких календарей
- События создаются одновременно во всех выбранных календарях
- При редактировании - обновляется только первый выбранный
- Цветовые индикаторы для каждого календаря
2. Современный дизайн событий
- Двухцветная схема: левая граница = цвет календаря, фон = цвет события
- Полупрозрачный фон события (80%) для лучшей видимости
- Тени и hover эффекты для объёма
- Закругленные углы (4px)
- Плавные анимации (transform + transition)
3. Улучшенное отображение накладывающихся событий
- dayMaxEvents: автоматически показывать '+N еще'
- dayMaxEventRows: 4 - максимум 4 строки на день
- moreLinkClick: 'popover' - красивый popover для остальных
- eventMaxStack: 3 - до 3 событий в стеке
- eventOrder: сортировка по началу, длительности, алфавиту
4. Новый CSS файл calendar-improvements.css
- Современные стили для FullCalendar
- Улучшенный popover с градиентом
- Стили для выходных дней (розоватый фон)
- Hover эффекты на события
- Анимация fadeInEvent при загрузке
- Улучшенная читаемость текущего дня
- Адаптивность для мобильных устройств
- Кастомные scrollbar для списков
5. Передача цвета календаря в события
- calendar_color добавляется в extendedProps
- Используется __calendar из интеграции
- Отдельная обработка eventColor и calendarColor
6. Визуальные улучшения
- Текст события: font-weight 500, line-height 1.3
- Отступы: paddingLeft 6px (с цветом календаря)
- Тень текста для читаемости на любом фоне
- Паттерн для ежегодных событий (диагональные полосы)
- Индикатор текущего дня: синий круг с белым текстом
Результат:
✅ События визуально различимы по календарям (левая граница)
✅ Сохранён индивидуальный цвет события (основной фон)
✅ Множество событий в день отображается компактно (+N еще)
✅ Современный Material Design стиль
✅ Плавные анимации и hover эффекты
✅ Улучшенная читаемость и UX
- Added calendar_id to CalendarEventWriteSerializer Meta.fields
- Fixed related_name from employee_departments to departments_links
- Updated make_event fixture to support calendar parameter
- Added comprehensive test suite for new Calendar and CalendarSubscription API
- Added fixtures: make_calendar, make_subscription, auth_client, user fixtures
- All legacy tests passing (34 passed)
- Исправлен PollSerializer для создания опросов с опциями
- Добавлено поле read_only_fields для корректной валидации
- Реализована отправка WebSocket уведомлений при создании опроса
- Исправлен импорт serialize_message из communications.serialization
- Добавлен обработчик poll_update в UserConsumer для WebSocket событий
- Исправлен эндпоинт /results/ для возврата user_voted_option_ids
- Добавлено поле voters в PollOptionSerializer для списка проголосовавших
- Реализована инициализация poll виджетов после рендера сообщений
- Добавлено игнорирование poll_update в notification-manager
- Исправлены стили для request list badges (height, word-break, flex)
- Восстановлены стили team-wheel (.wheel-focus, .scroll-arc) из git истории
- Реализовано отображение forwarded message info в messageRendererV2
- Множественные улучшения UI и UX для опросов
Fixes: создание опросов, голосование, отображение результатов, список проголосовавших
BREAKING CHANGES:
- Removed OU=Roles from LDAP structure, role groups now created directly in department OU
- Role groups use ROLE_<Name> naming convention (similar to DEP_, POS_)
New features:
- RoleAssignment model: allows assigning roles to ANY employee, not just department members
- New API endpoints: /assign/, /revoke/, /assignments/ on DepartmentRoleViewSet
- Permission system updated: has_dept_perm() checks RoleAssignment first
- Employee profile shows external roles with 'внешняя роль' badge
- Employee list filter includes role-only employees with department_relation field
- Role assignment modal now searches all employees via AJAX
LDAP changes:
- _ensure_role_group(): creates ROLE_<Name> group in department OU
- assign_role(), revoke_role(): manage RoleAssignment + LDAP sync
- rename_role_group(), delete_role_group(): full lifecycle support
- Removed all references to OU=Roles
Migrations:
- 0035: Create RoleAssignment model
- 0036: Data migration from EmployeeDepartment.role
Tests:
- 7 new tests for role assignment (all passing)
Docs:
- 9 detailed reports on architecture, models, API, LDAP, tests
Проблема: При увольнении сотрудника, не состоящего ни в одном отделе,
он оставался в текущей OU (например, OU=Users) вместо перемещения в OU=Dismissed.
Причина: Цикл по active_departments был пустым, remove_member() не вызывался,
перемещение в OU=Dismissed не происходило.
Решение:
- Добавлена проверка departments_processed
- Если сотрудник не состоял ни в одном отделе, выполняется явное перемещение
- Используется get_base_dn_for_employee() для определения целевой OU
- Прямой вызов _move_user_to_base() для перемещения в OU=Dismissed
Тест: test_dismissal_without_department_moves_to_dismissed
- Проверяет увольнение сотрудника без отделов
- Убеждается, что LDAP соединение создано для перемещения
- Подтверждает деактивацию в LDAP (userAccountControl=514)
Edge case покрыт: Теперь все уволенные попадают в OU=Dismissed независимо
от наличия отделов.
Implemented automatic movement of dismissed employees to dedicated OU.
Core Changes:
- Configuration: Added LDAP_DISMISSED_BASE setting for dismissed employees OU
- Utility: Created get_base_dn_for_employee() to determine target OU by is_active status
- Service Layer: Modified DepartmentService.remove_member() to use status-based OU selection
- View Logic: Updated EmployeeActionViewSet._apply_effects() to:
* Deactivate employee BEFORE removing from departments (critical for correct OU routing)
* Loop through all departments and call remove_member() for each
* Handle restoration: move from OU=Dismissed back to OU=Users on activation
Infrastructure:
- Management Command: ensure_dismissed_ou.py to create OU=Dismissed in AD
* Checks OU existence before creation
* Supports --force flag for recreation
* Configurable description
Testing:
- test_dismissal_moves_to_dismissed_ou: Verifies dismissal → OU=Dismissed movement
- test_restoration_moves_from_dismissed_to_users: Verifies activation → OU=Users movement
Edge Cases Handled:
- Repeat dismissal: Idempotent (already inactive employees stay in OU=Dismissed)
- Restoration: Automatic move from OU=Dismissed to OU=Users on any activating action
- Missing OU: ensure_container_exists() creates OU=Dismissed if missing (fallback to OU=Users on error)
- LDAP errors: Graceful logging without blocking database operations
Technical Details:
- OU=Dismissed: OU=Dismissed,OU=company,DC=robotail,DC=local
- Dismissal flow: is_active=False → deactivate EmployeeDepartment → update LDAP UAC → remove from all departments
- Restoration flow: is_active=True → update LDAP UAC → check current OU → move if in OU=Dismissed
- Backward compatible: Works with existing dismissals (they will move to OU=Dismissed on next department change)
Files Changed:
- backend/.env: Added LDAP_DISMISSED_BASE configuration
- backend/eusrr_backend/settings.py: Registered LDAP_DISMISSED_BASE setting
- backend/employees/ldap/utils/ldap_utils.py: New get_base_dn_for_employee() utility
- backend/employees/ldap/services/department_service.py: Modified remove_member() logic
- backend/api/v1/employees/views.py: Enhanced dismissal and restoration logic
- backend/employees/management/commands/ensure_dismissed_ou.py: New management command
- backend/tests/api/v1/employees/test_employee_actions.py: Added tests for new behavior
- Added LDAP synchronization in EmployeeActionViewSet._apply_effects()
- Dismissal sets userAccountControl=514 (disabled) in LDAP
- Other actions set userAccountControl=512 (enabled)
- Graceful error handling: DB succeeds even if LDAP fails
- Works correctly when LDAP is disabled
- Added 5 comprehensive tests for LDAP sync behavior
Date: Mon, 22 Sep 2025 10:30:00 +0300
Subject: [requests] Drawer «Рассмотреть», корректный чекбокс «Черновик», дружелюбные даты,
robust-обработка ошибок reject, и глобальный бейдж непрочитанных чатов
- Список заявлений:
- вместо россыпи кнопок добавлен аккуратный выезжающий блок действий (collapse drawer)
по клику «Рассмотреть»; компактные нейтральные стили, мягкая анимация.
- в модале «Изменить» добавлена галочка «Сохранить как черновик» с правильной
семантикой: hidden `_save_as=submit` по умолчанию + checkbox `_save_as=draft`.
Теперь снятие галочки действительно снимает draft.
- улучшены стили и структура разметки.
- Даты:
- на фронт-обёртке нормализуем datetime-поля (created_at/updated_at/decided_at)
в человекочитаемый вид, чтобы не светить сырые ISO 8601.
- Обработка ошибок:
- фикс падения при попытке отклонить уже одобренную заявку: ответ 400 у DRF
может приходить списком ошибок; теперь сообщение извлекается безопасно.
- Чаты:
- синхронизировали название ключа контекста с проектом (`chat_unread_total`);
- бейдж «Чаты» обновляется глобальным событием `chats:unread-total`, даже если
список чатов не открыт (fallback — суммирование счётчиков на странице).
Примечание: пути к шаблонам могут отличаться в вашем проекте — ниже даны
типовые варианты (`backend/requests_app/templates/...`). Если у вас другой
layout, внесите изменения в соответствующие файлы шаблонов.
Большое обновление по модулю заявок и интеграции с отделами.
## 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`.
- Добавлен RequestViewSet:
- list/retrieve — чтение с учётом ролей; для админов переключатель ?view=mine / ?mine=1|true|yes|on
- create/update/destroy — логика «владелец до финала, иначе — права»
- кастомные экшены: POST /{id}/approve|reject|cancel/
- подресурс: GET/POST /{id}/comments/
- фильтры: ?type=..., ?status=...
- Единый возврат read-сериализатора в экшенах (context=self.get_serializer_context()).
- Типизация: get_serializer_class -> type[BaseSerializer]; list[Any] -> list[...]; мелкая чистка импортов.
fix(serializers): корректная проверка прав и защита полей в write-сериализаторе
- RequestWriteSerializer.validate: вместо user.has_perms([...]) (AND) — явное OR:
is_staff OR has_perm(change_request) OR has_perm(can_process_requests)
- Создание обычным пользователем принудительно проставляет employee=request.user.
- Обновление запрещено для финальных заявок без прав (ValidationError).
refactor(views): контекст в сериализаторы, нормализация query-параметров, мелкая чистка
- В экшенах approve/reject/cancel/comments используется RequestReadSerializer(..., context=...).
- Нормализация параметра ?mine (поддержка 1/true/yes/on).
- Удалён неиспользуемый get_object_or_404.
test(requests_api): стартовый набор автотестов по чек-листу + правки
- Добавлены тесты видимости: админ (all/mine), менеджер с модельными правами, обычный пользователь (только свои).
- CRUD, экшены approve/reject/cancel, и блок комментариев.
- Фикс неверного URL в фильтрах (пропущенный '?'): now /requests/?view=mine&...
- Комментарии: тесты приведены к реальной политике пермишена
(см. ниже про AdminOrActionOrModelPerms).
test(fixtures): устойчивые фабрики пользователей и заявок
- grant_model_perm: удалён user.save(update_fields=[]), так как пустой update_fields валит Django 5.x; сохранение не нужно для M2M.
- make_user: уникальный email по умолчанию + send_activation_email=False, авто-телефон при наличии поля.
- _detect_phone_field: узкий catch исключений (AttributeError, FieldDoesNotExist).
- make_request: при финальном статусе автоматически проставляет approver (иначе падал CHECK request_approver_required_on_decision).
docs: докстринги (Google-style) и типизация по всему добавленному коду.
BREAKING/NOTE (perms):
- Во вью используется AdminOrActionOrModelPerms. Если для кастомного экшена нет явного кода,
то:
- SAFE методы требуют view_<model> на queryset-модель (для /requests — requests_app.view_request),
- POST падает на DjangoModelPermissions и требует add_<model> (requests_app.add_request).
- Это влияет на доступ к /requests/{id}/comments/: без явного маппинга потребуется
requests_app.view_request (GET) и requests_app.add_request (POST),
даже если выдали права на RequestComment. Чтобы использовать именно права на комментарии,
нужно задать required_perms_by_action={"comments": "requests_app.view_requestcomment"} (и/или add_requestcomment).
--------------------------------
Оставшиеся падения тестов и что с ними сделать
--------------------------------
1) ❌ `tests/api/v1/documents/test_documents_api.py::TestSerialization::test_file_url_downloadable_in_dev` → 404 (ожидали 200)
- Не затрагивали в этом MR. Проверить:
- настройки DEV для статики/медиа (MEDIA_URL/DEBUG/urls serve),
- выдачу file.url сериализатором документов (не прячется ли в DEV),
- router/namespace для скачивания файла.
- TODO (отдельный MR): включить dev-раздачу или адаптировать тест под текущую схему.
2) ❌ Серия 403 по requests_api:
- `test_list_regular_user_sees_only_own`
- `test_list_manager_with_model_perm_sees_all`
- `test_detail_regular_user_own_and_forbidden_foreign`
- `test_detail_admin_and_manager_can_see_any`
- `test_create_regular_user_forces_employee_and_default_status`
- `test_update_own_pending_ok_and_final_forbidden`
- `test_delete_own_pending_ok_final_forbidden`
- `test_actions_permissions_and_effects`
Причина: сейчас на вью стоит AdminOrActionOrModelPerms, который без явного кода требует
`view_request` даже для SAFE-методов. По продуктовой постановке нам нужно:
«любой аутентифицированный — видит и работает только со своими (без model perms)».
Варианты решения (нужно выбрать и применить в следующем MR):
- Вернуть кастомный RequestAccessPermission (владелец без view_request видит свои).
- Либо оставить AdminOrActionOrModelPerms, но добавить параллельно IsSelfOrStaff и/или
задать required_perms_by_action под нужные action так, чтобы владелец проходил без view_request.
- Для create: либо давать add_request всем аутентифицированным (нежелательно),
либо обойтись кастомной проверкой на уровне вью как сейчас и гарантировать,
что ответ формируется read-сериализатором (см. пункт 3).
3) ❌ `test_create_admin_can_set_employee` → `TypeError: 'int' object is not subscriptable`
- Причина: ответ на POST формируется write-сериализатором (поле employee как int),
а тест ожидает структуру read-сериализатора (employee {...}).
- TODO (следующий MR): переопределить create/update, чтобы в ответе всегда отдавался
RequestReadSerializer(instance, context=...).
4) ❌ `test_comments_allowed_for_admin_and_manager`
- Сейчас в тесте менеджеру выданы права на RequestComment, но per AdminOrActionOrModelPerms
без маппинга экшена это игнорируется. Нужно либо:
- выдать `requests_app.view_request` и `requests_app.add_request`, либо
- в вью задать `required_perms_by_action={"comments": "requests_app.view_requestcomment"}` и на POST — add_requestcomment.
- В этом MR тесты приведены к текущей логике (см. README к тестам), но код вью/пермишена ещё требует выбора стратегии.
--------------------------------
Дополнительно
--------------------------------
- Вверху тестового модуля добавлен `pytestmark = pytest.mark.django_db`.
- Исправлены разовые мелочи по типам и контексту сериализаторов.
- В README к тестам добавлены примечания, где поведение зависит от выбранной стратегии прав.
Refs: EUSRR-requests-api, пермишены AdminOrActionOrModelPerms, комментарии, фиксы фикстур и фабрик.