- Удалена устаревшая документация из backend/docs/
- Актуальные гайды сохранены в backend/docs/guides/ и других подпапках
- Удален migrate_participants_to_memberships.py (уже был создан в communications/management/commands/)
АРХИТЕКТУРНЫЙ РЕФАКТОРИНГ:
✅ Убрано дублирование логики 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 при удалении/редактировании комментариев
- Комментарии в общем списке чатов
- Гости могли отправлять сообщения
- Delete legacy Comment models (feed.Comment, documents.DocumentComment, requests_app.RequestComment)
- Create migration scripts to move data to communications.Message
- Add comments_helpers.py for unified comment operations
- Update API endpoints: /api/v1/posts/{id}/comments/, /api/v1/requests/{id}/comments/
- Fix notifications.0010_drop_old_tables migration with SeparateDatabaseAndState
- Implement real-time comments_count via GenericForeignKey annotations
- Update frontend API client to use new endpoints
- Update JavaScript files for new comment API routes
- Remove deprecated API endpoints and serializers
- Clean up signal handlers and admin registrations
Deployment tested: all migrations apply successfully on fresh database
- Обновлена фикстура department_chat: использует GenericFK и type='channel'
- Обновлен тест test_create_department_chat для новой модели
- Обновлен скрипт check_chat_access.py: удалена логика department
- Добавлена поддержка типов group/channel в диагностике
- Удалены все ссылки на удаленное поле department
- Тип 'department' был полностью удален из communications
- Чаты отделов теперь используют type='channel'
- Исправлено 2 вхождения в create_main_department_chat()
- Добавлен флаг COMMUNICATIONS_AUTO_NOTIFY (по умолчанию True)
- Оба сигнала теперь проверяют флаг перед выполнением
- Улучшена документация: объяснено что сигналы опциональны
- Для standalone использования можно отключить автоуведомления
BREAKING CHANGE: notifications теперь опциональная зависимость.
Проблема:
- communications имел жесткую зависимость от notifications модуля
- from notifications.signals import notify - обязательный импорт
- Нарушало независимость модуля
Решение:
- Импорт notifications обернут в try/except ImportError
- Создана функция _send_notification() - обертка над notify.send()
- Если notifications не установлен - уведомления тихо игнорируются
- Модуль работает БЕЗ notifications (graceful degradation)
Изменения:
- notifications/handlers.py:
* Опциональный импорт: try/except ImportError
* Флаг NOTIFICATIONS_AVAILABLE для проверки доступности
* Функция _send_notification() вместо прямых вызовов
* 4 вызова notify.send() → _send_notification()
Результат:
✅ communications работает БЕЗ notifications модуля
✅ Если notifications установлен - все работает как раньше
✅ Полная независимость для standalone использования
✅ System check: 0 errors
Теперь можно использовать communications как:
1. С notifications - полная функциональность
2. Без notifications - только чаты без уведомлений
3. С собственной системой уведомлений - переопределить _send_notification()
BREAKING CHANGE: Тип чата 'department' полностью удален из модуля.
Для миграции существующих department чатов используйте:
- type='group' вместо type='department'
- context_object (GenericFK) для привязки к объектам
- flags={'is_department': True} для пометки
- COMMUNICATIONS_PARTICIPANT_RESOLVER callback для логики участников
Удалено из:
- models.py: тип 'department' из CHAT_TYPE_CHOICES
- models.py: упоминания Department из комментариев
- api/viewsets.py: логика фильтрации по department (30 строк)
- consumers.py: WebSocket логика для department (19 строк)
- utils.py: проверки доступа для department
- rules.py: is_department_chat → deprecated_is_department_chat (disabled)
- notifications/: обработка department в handlers и config
- signals.py: комментарии обновлены на универсальные
Добавлено:
- migrations/0037: удаление department из choices
- Обновлен help_text для context_content_type (без Department)
- Поддержка type='comments' в utils.py и consumers.py
Результат:
✅ 100% независимость от проекта EUSRR
✅ Нет импортов из employees
✅ Нет упоминаний department (кроме deprecated функции)
✅ Универсальный модуль готов для PyPI
Статистика: 9 файлов, -85/+44 строк
BREAKING CHANGE: Тип 'department' помечен как deprecated.
Изменения:
- Добавлен новый тип чата 'comments' для систем комментариев
- Тип 'department' перемещен в конец списка и помечен как [Legacy]
- Обновлен help_text: рекомендуется использовать 'group' + context_object
- Обновлен __str__() для обработки типа 'comments'
- Создана миграция 0036_add_comments_type_deprecate_department
Универсальность:
- 'comments' + context_object работает с любыми моделями
- Треды, реакции, вложения доступны из коробки
- Real-time обновления через WebSocket
- Автоматические уведомления при упоминаниях и ответах
Рекомендации:
- Для проектно-специфичных чатов используй type='group' + context_object
- Department: type='group' + context_object=department + flags={'is_department': True}
- Comments: type='comments' + context_object=any_model
Документация:
- Создан docs/guides/COMMENTS_USAGE.md с примерами
- Описаны все возможности системы комментариев
- Примеры API, WebSocket, миграции с legacy типов
- Убрано поле 'department' из списка полей в ChatListSerializer
- Убрано поле 'department' из списка полей в ChatDetailSerializer
- Исправлена ошибка ImproperlyConfigured при запросе списка чатов
- Поле было удалено из модели, но осталось в сериализаторах
- Заменено Employee на User во всех файлах модуля communications
- Использование нейтрального названия вместо проектно-специфичного
- Изменено 6 файлов: models.py, viewsets.py, serializers.py, handlers.py, signals.py, utils.py
- Улучшена переиспользуемость модуля communications
BREAKING CHANGE: Полная независимость communications модуля
Удалено:
- Поле Chat.department (FK на employees.Department)
- Импорты Department из всех файлов communications
- Q(department=...) фильтры в API/WebSocket/rules
Перемещено:
- Сигнал create_main_department_chat → employees/signals.py (проектоспецифичная логика)
Обновлено:
- models.py: удалено поле department, constraint, index
- api/viewsets.py: использует GenericFK через ContentType.objects.get()
- consumers.py: использует GenericFK через ContentType.objects.get()
- rules.py: is_department_chat теперь использует get_participants()
- admin.py: удален department из list_display/search_fields
Миграция:
- 0035_remove_department_field.py: удаляет поле, constraint, index
Результат:
✅ communications НЕ зависит от employees
✅ System check: 0 errors
✅ Использует только GenericFK (context_object)
✅ Полностью переиспользуемый модуль
Для EUSRR-специфичной логики department чатов:
- Настроить COMMUNICATIONS_PARTICIPANT_RESOLVER в settings.py
- Сигнал автосоздания чатов теперь в employees/signals.py
- signals.py: chat.get_participants → chat.get_participants()
- notifications/handlers.py: chat.get_participants → chat.get_participants()
Ошибка: AttributeError: 'function' object has no attribute 'exclude'
Причина: Метод не вызывался, пытались вызвать метод на самом объекте функции
Исправлено: Добавлены () для вызова метода и получения QuerySet
- Удалены классические Django CBV views (ChatListView, ChatDetailView, ~400 строк)
- Удален MessageForm для HTML форм
- Удалены 6 HTML templates (chat_list.html, chat_detail.html, messengers/*)
- Утилиты _coerce_ts и user_can_access_chat перенесены в utils.py
- Обновлены импорты в communications/api/viewsets.py
Причина: Проект использует Next.js frontend + REST API + WebSocket.
Классические server-side rendered views больше не используются.
Результат:
- System check: 0 errors
- Код стал чище и проще для поддержки
- Нет зависимости от Django templates
- API и WebSocket остались полностью функциональны
- Перенос REST API в communications/api/ для standalone использования
* Создан communications/api/ с serializers.py, viewsets.py, urls.py
* Перенесено 1668 строк (442 serializers + 1226 viewsets)
* Удалена директория api/v1/communications/
* Обновлены импорты в api/v1/urls.py и api/v2/communications/views.py
- Извлечение WebSocket логики в ChatConsumerMixin
* Создан communications/consumers.py (709 строк, 32 метода)
* Перенесены chat event handlers, action handlers, helper methods
* realtime/consumers.py сокращен с 862 до 211 строк (-75%)
* Сохранено одно WebSocket соединение через наследование миксина
- Архитектура:
* communications теперь полностью автономно (REST API + WebSocket)
* realtime/UserConsumer наследуется от ChatConsumerMixin
* 0 breaking changes, обратная совместимость 100%
* Одно WebSocket соединение для всех real-time событий
- Созданы тесты и документация:
* test_phase3.py - проверка API serializers, filters, WebSocket
* docs/completed/API_REFACTORING_STANDALONE.md
* docs/completed/WEBSOCKET_REFACTORING_MIXIN.md
* docs/completed/CHAT_REFACTORING_PHASE3_COMPLETE.md
Результат:
✅ System check: 0 ошибок
✅ Phase 3 tests: все прошли
✅ Готово к standalone использованию (pip install django-communications)
BREAKING CHANGES (backward compatible):
- All code updated to support both old and new fields
- context_object (GenericFK) and flags now primary
- department and is_main still work (DEPRECATED)
FILES UPDATED:
1. signals.py:
- create_main_department_chat() uses context_object + flags
- Creates both old and new fields for compatibility
- Checks existence using Q() for both fields
2. views.py (4 critical updates):
- has_user_access_to_chat(): uses get_participants() (supports both)
- ChatListView.get_queryset(): checks department OR context_object
- ChatDetailView.get_queryset(): checks department OR context_object
- mark_as_read_ajax(): uses get_participants() and include_all_users
3. rules.py:
- is_department_chat(): checks both department and context_object
- Supports GenericFK for Department context
4. apps.py:
- create_main_global_chat(): checks and sets flags['is_primary']
- Backward compatible with is_main
5. admin.py:
- get_readonly_fields(): checks flags['is_primary'] OR is_main
- Prevents editing primary chats
6. notifications/config.py:
- get_chat_display_name(): uses context_object OR department
7. models.py:
- Chat.__str__(): uses context_object OR department
TESTED:
✅ Signal creates chat with context_object + flags
✅ get_participants() works with both fields
✅ __str__ displays correctly
✅ is_department_chat rule works
✅ Backward compatibility preserved
Изменено: notifications.tasks.* → notifications.*
Проблема: задачи регистрируются как 'notifications.send_*',
а не 'notifications.tasks.send_*', поэтому старый паттерн
не совпадал и задачи шли в дефолтную очередь 'celery'.
Теперь задачи корректно роутятся в очередь 'notifications',
которую worker слушает с флагом -Q celery,notifications
- Исправлена логика подсчёта уведомлений по категориям (n.verb вместо n.category)
- Добавлена синхронизация между компонентами через CustomEvent API
- Реализована автоматическая отметка категории уведомлений при переходе в раздел
- Backend: универсальный endpoint /category/read/ принимающий список verbs
- Frontend: функция getVerbsByCategory() для маппинга категорий
- Счётчики обновляются в реальном времени через WebSocket
- Синхронизация работает между всеми вкладками браузера
Модуль уведомлений остался переиспользуемым - категории управляются на фронте
- Добавлено поле unread_count в ChatReadState для кеширования счетчика
- Создана миграция 0030 с автоматическим пересчетом существующих данных
- Обновлен _auto_mark_read() для обнуления счетчика при прочтении
- Добавлен signal increment_unread_count_on_new_message для автоинкремента
- Упрощен get_queryset() - убраны подзапросы, используется Prefetch
- Обновлены сериализаторы для использования denormalized поля
Производительность: 4 SQL запроса вместо 100+ подзапросов
Решает проблему N+1 при загрузке списка чатов
Архитектура описана в backend/docs/architecture/UNREAD_COUNT_OPTIMIZATION.md
- Удалены logger для 'bots' и 'aiogram' (telegram bot библиотека)
- Почищены пустые папки bots/ и hikcentral/ оставшиеся после merge
- Все остальные упоминания уже удалены в feature ветке
Merge прошел корректно, просто git не удаляет пустые директории автоматически.
Проблема:
- Монолитный notification_signals.py (~220 строк) смешивал разные concerns
- Логика отслеживания изменений полей переплетена с signal receivers
- Вспомогательные функции без четкой структуры
Решение:
Разделен на модульную структуру calendar_app/notifications/:
- config.py - NotificationVerbs, MessageTemplates, ActionURLs, константы полей,
вспомогательные функции (format_date, format_changes, FIELD_NAMES, IMPORTANT_FIELDS)
- handlers.py - get_event_recipients(), notify_event_created(), notify_event_changed(),
notify_event_cancelled()
- signals.py - Django signal receivers (@receiver) для CalendarEvent
- __init__.py - публичный API модуля
Изменения:
- Создан: calendar_app/notifications/ модуль с 4 файлами
- Переименован: notification_signals.py → notification_signals.py.old
- Изменен: apps.py - обновлен импорт на calendar_app.notifications
Логика уведомлений:
1. Новое событие → получатели по типу (компания/отдел/личное)
2. Изменение события → участники события (если изменены важные поля)
3. Отмена события → все участники
Преимущества:
- ✅ Единообразие со всеми остальными приложениями
- ✅ Четкое разделение ответственности (SRP)
- ✅ Отслеживание изменений полей вынесено в отдельный signal
- ✅ Централизованные константы для важных полей
- ✅ Легче тестировать компоненты
- ✅ Публичный API для переиспользования
Верификация:
- python manage.py check - успешно