867 Commits

Author SHA1 Message Date
lizerk
80cdc36d92 fix: исправлена система прав доступа к чатам и сообщениям
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 при удалении/редактировании комментариев
- Комментарии в общем списке чатов
- Гости могли отправлять сообщения
2026-03-12 14:30:25 +03:00
lizerk
d87e8ed796 фикс 2026-03-12 01:09:51 +03:00
lizerk
f58b87c9a8 фикс 2026-03-12 01:08:11 +03:00
lizerk
0e133a4633 фикс 2026-03-12 01:01:53 +03:00
lizerk
77551b5240 фикс 2026-03-12 00:59:39 +03:00
lizerk
66c243879f фикс миграций 2026-03-12 00:57:41 +03:00
lizerk
6eaadeef43 фикс миграций 2026-03-12 00:54:58 +03:00
lizerk
8649307772 фикс 2026-03-12 00:47:07 +03:00
lizerk
9462f08825 оптимизация веб сокет 2026-03-12 00:42:10 +03:00
lizerk
0217bc1a38 refactor: migrate comments to unified communications system
- 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
2026-03-11 23:56:41 +03:00
lizerk
8e2d4ad231 мини баф 2026-03-11 21:56:51 +03:00
lizerk
3aaadd64ce фильтры в чаты 2026-03-11 18:50:49 +03:00
lizerk
1fe718aefd Merge feature/communications-universal-refactoring: Универсальный переиспользуемый модуль communications 2026-03-11 17:59:59 +03:00
lizerk
359c5f7f9d fix: Обновить тесты и диагностические скрипты после удаления type='department'
- Обновлена фикстура department_chat: использует GenericFK и type='channel'
- Обновлен тест test_create_department_chat для новой модели
- Обновлен скрипт check_chat_access.py: удалена логика department
- Добавлена поддержка типов group/channel в диагностике
- Удалены все ссылки на удаленное поле department
2026-03-11 17:57:17 +03:00
lizerk
9744009d6b fix(employees): Заменить устаревший type='department' на 'channel' в сигналах
- Тип 'department' был полностью удален из communications
- Чаты отделов теперь используют type='channel'
- Исправлено 2 вхождения в create_main_department_chat()
2026-03-11 17:51:51 +03:00
lizerk
ae21c7cae7 refactor(communications): Сделать автоматические уведомления опциональными через COMMUNICATIONS_AUTO_NOTIFY
- Добавлен флаг COMMUNICATIONS_AUTO_NOTIFY (по умолчанию True)
- Оба сигнала теперь проверяют флаг перед выполнением
- Улучшена документация: объяснено что сигналы опциональны
- Для standalone использования можно отключить автоуведомления
2026-03-11 17:38:55 +03:00
lizerk
2e1f413264 refactor(communications): Сделать зависимость от notifications опциональной
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()
2026-03-11 17:25:15 +03:00
lizerk
5ebf9dd71a refactor(communications): Полное удаление проектно-специфичного типа department
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 строк
2026-03-11 17:16:10 +03:00
lizerk
6ddd3f69ba feat(communications): Добавить тип 'comments', пометить 'department' как legacy
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 типов
2026-03-11 17:06:06 +03:00
lizerk
c670246dd5 fix(communications): Удалить поле department из ChatListSerializer и ChatDetailSerializer
- Убрано поле 'department' из списка полей в ChatListSerializer
- Убрано поле 'department' из списка полей в ChatDetailSerializer
- Исправлена ошибка ImproperlyConfigured при запросе списка чатов
- Поле было удалено из модели, но осталось в сериализаторах
2026-03-11 16:51:05 +03:00
lizerk
7c8ed68a6b refactor(communications): Переименовать Employee в User для универсальности
- Заменено Employee на User во всех файлах модуля communications
- Использование нейтрального названия вместо проектно-специфичного
- Изменено 6 файлов: models.py, viewsets.py, serializers.py, handlers.py, signals.py, utils.py
- Улучшена переиспользуемость модуля communications
2026-03-11 16:48:20 +03:00
lizerk
0164c328f7 refactor(communications): Удалить зависимость от employees app
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
2026-03-11 16:39:37 +03:00
lizerk
0c00652c8e fix(communications): Исправить вызов метода get_participants()
- 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
2026-03-11 16:04:56 +03:00
lizerk
45b35b0f36 refactor(communications): Удалить классические Django views и templates
- Удалены классические 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 остались полностью функциональны
2026-03-11 15:49:23 +03:00
lizerk
d491cbdff9 refactor: Сделать communications приложение автономным
- Перенос 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)
2026-03-11 15:34:51 +03:00
lizerk
ef3c02290e docs: Add phase 2 completion report for Chat refactoring 2026-03-11 14:31:28 +03:00
lizerk
f56e256a2a feat(communications): Phase 2 - Update views, signals, rules for GenericFK
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
2026-03-11 14:30:23 +03:00
lizerk
66f6473e6e docs: Add phase 1 completion report for Chat refactoring 2026-03-11 14:24:15 +03:00
lizerk
51aecacf09 feat(communications): Refactor Chat model to universal GenericForeignKey
BREAKING CHANGES (backward compatible with data migration):
- Add context_object (GenericFK) to replace department FK
- Add flags (JSONField) to replace is_main boolean
- Add extra_data (JSONField) for extensibility
- Rename include_all_employees → include_all_users

NEW FEATURES:
- Generic context support (Department, Project, Team, etc.)
- Flexible flags system without migrations
- Settings-based participant resolver callback
- Zero data loss migrations (4 migrations)

MIGRATIONS:
- 0031: Add new fields (context_object, flags, extra_data)
- 0032: Migrate department → context_object (15 chats)
- 0033: Migrate is_main → flags['is_primary'] (15 chats)
- 0034: Rename include_all_employees → include_all_users

FILES CHANGED:
- models.py: Refactored Chat.get_participants() with callback support
- employees/utils.py: Added resolve_chat_participants() callback
- settings.py: Added COMMUNICATIONS_PARTICIPANT_RESOLVER setting
- verify_chat_migration.py: Command to verify data integrity

TESTED:
✅ All 32 chats migrated successfully
✅ Legacy department FK still works
✅ Context-based chats work (GenericFK)
✅ Global/private/announcement chats work
✅ Participants resolver callback works
2026-03-11 14:21:57 +03:00
lizerk
436dc45ff2 Merge feature/communications-universal-refactoring: fix циклический импорт 2026-03-11 13:37:52 +03:00
lizerk
ec6189b96c fix: исправлен циклический импорт в scheduling/notifications/config.py 2026-03-11 13:37:44 +03:00
lizerk
f5ec8ffc8b docs: add communications refactoring plan and migration strategy
- Add detailed comparison of current vs refactored Chat model
- Add step-by-step migration plan with zero data loss guarantee
- Document GenericForeignKey approach for universal chat contexts
- Include rollback procedures and verification commands
2026-03-11 12:55:21 +03:00
lizerk
9dc0fdb848 фикс мобильных уведомлений 2026-03-11 10:41:41 +03:00
lizerk
b6ad3c3469 fix: исправлен паттерн роутинга Celery задач уведомлений
Изменено: notifications.tasks.* → notifications.*

Проблема: задачи регистрируются как 'notifications.send_*',
а не 'notifications.tasks.send_*', поэтому старый паттерн
не совпадал и задачи шли в дефолтную очередь 'celery'.

Теперь задачи корректно роутятся в очередь 'notifications',
которую worker слушает с флагом -Q celery,notifications
2026-03-11 09:58:53 +03:00
lizerk
2bf1079280 фикс селери 2026-03-11 09:53:55 +03:00
lizerk
3431897ac4 fix: всегда показывать кнопку 'Показать все' в NotificationCenter
Кнопка теперь отображается даже при пустом списке уведомлений,
чтобы пользователь мог перейти на страницу /notifications
2026-03-11 09:16:30 +03:00
lizerk
6fe7eeb222 feat: добавлены realtime бэйджи с уведомлениями в боковую панель
- Исправлена логика подсчёта уведомлений по категориям (n.verb вместо n.category)
- Добавлена синхронизация между компонентами через CustomEvent API
- Реализована автоматическая отметка категории уведомлений при переходе в раздел
- Backend: универсальный endpoint /category/read/ принимающий список verbs
- Frontend: функция getVerbsByCategory() для маппинга категорий
- Счётчики обновляются в реальном времени через WebSocket
- Синхронизация работает между всеми вкладками браузера

Модуль уведомлений остался переиспользуемым - категории управляются на фронте
2026-03-11 08:48:26 +03:00
lizerk
6a9a846455 fix 2026-03-10 18:23:35 +03:00
lizerk
8838b09737 feat: оптимизация загрузки уведомлений и счетчиков чатов
Frontend изменения:
- Оптимизирована загрузка уведомлений: только 50 unread для badges
- Упрощены проверки is_read в AppShell и NotificationCenter
- Разделены useNotifications (для badges) и notifications page (для списка)
- Добавлены визуальные индикаторы в список чатов (badges, pin, notifications)
- Улучшена сортировка: закрепленные чаты показываются первыми

Связано с backend оптимизацией denormalized unread_count
2026-03-10 18:09:43 +03:00
lizerk
7dcc775b71 feat: денормализация unread_count для оптимизации чатов
- Добавлено поле 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
2026-03-10 18:09:06 +03:00
lizerk
96e375cd6f --------------------- 2026-03-10 17:00:51 +03:00
lizerk
b0e88e8e8b refactor(backend): migrate calendar_app to scheduling app
- Rename calendar_app → scheduling (integration app for django-scheduler)
- Move schedule_patch.py → scheduling/patch.py with improved logging
- Create scheduling.notifications system for django-scheduler events
- Add scheduling.rules for django-rules integration
- Remove unused calendar_app models and API (not registered in urls)
- Remove backend/api/v1/calendar/ (dead code)
- Remove old tests for calendar_app
- Add comprehensive documentation in scheduling/README.md
- Update settings.py: calendar_app.apps.CalendarAppConfig → scheduling.apps.SchedulingConfig
- Clean api/apps.py: remove schedule_patch import (now in scheduling.apps)

The scheduling app provides:
- Patches for django-scheduler bugs (byweekday fix)
- Notification system for calendar events
- Access rules via django-rules
- Auto-applies on Django startup

Closes integration of django-scheduler for EUSRR calendar system.
2026-03-10 15:21:28 +03:00
lizerk
4d985990f5 cleanup: удалить упоминания bots и hikcentral из settings.py
- Удалены logger для 'bots' и 'aiogram' (telegram bot библиотека)
- Почищены пустые папки bots/ и hikcentral/ оставшиеся после merge
- Все остальные упоминания уже удалены в feature ветке

Merge прошел корректно, просто git не удаляет пустые директории автоматически.
2026-03-10 14:28:01 +03:00
lizerk
7cfb899711 Merge: реструктуризация notifications во всех приложениях
Объединяет изменения из feature/optimize-notifications-usage:
- requests_app: модульная структура notifications
- procurement: удален дублирующий WebSocket модуль
- feed: добавлены уведомления о лайках
- communications: оптимизация для упоминаний и ответов
- documents: поддержка bulk режима для массовых рассылок
- calendar_app: отслеживание изменений событий

Все приложения теперь используют единую архитектуру notifications.
2026-03-10 14:21:49 +03:00
lizerk
8773cbe7cd фикс 2026-03-10 14:20:56 +03:00
lizerk
fc9b9de50a refactor(calendar_app): реструктуризация notifications в модульную архитектуру
Проблема:
- Монолитный 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 - успешно
2026-03-10 14:20:05 +03:00
lizerk
d9a8e71e3a refactor(notifications): сделать приложение полностью самодостаточным
Проблема:
- Service Worker файл (sw.js) находился в корне backend/
- Hardcoded пути к иконкам в sw.js и push.py (/static/img/logo-192.png)
- Функция serve_service_worker() в главном urls.py проекта
- Зависимость от project-specific структуры
- Невозможность переиспользовать приложение без изменений

Решение:
Service Worker на месте:
- backend/notifications/static/notifications/sw.js
- backend/notifications/views.py - serve_service_worker()
- backend/notifications/urls.py - добавлен endpoint sw.js
- backend/eusrr_backend/urls.py - импорт view из notifications

Удаление hardcoded путей:
- sw.js: убрана константа icon='/static/img/logo-192.png'
- senders/push.py: убран hardcoded 'icon': '/static/img/logo.png'
- config.py: добавлены PUSH_DEFAULT_ICON, PUSH_DEFAULT_BADGE (None = browser default)
- README.md: документация по настройке через NOTIFICATIONS_CONFIG

Изменения:
- Создан: notifications/views.py с serve_service_worker()
- Создан: notifications/static/notifications/sw.js
- Изменен: notifications/urls.py - добавлен path('sw.js')
- Изменен: notifications/config.py - настройки иконок
- Изменен: notifications/senders/push.py - использование config
- Изменен: notifications/README.md - документация
- Упрощен: eusrr_backend/urls.py - удалена функция, добавлен импорт
- Удалены: backend/sw.js, backend/static/sw.js, backend/communications/sw.js

Преимущества:
- ✅ Полностью автономное приложение
- ✅ Нет зависимостей от структуры проекта
- ✅ Конфигурируемые иконки через settings.py
- ✅ Browser defaults если иконки не указаны
- ✅ Легко переносить между проектами
- ✅ Единообразие архитектуры (SRP)

Верификация:
- python manage.py check - успешно
2026-03-10 13:58:53 +03:00
lizerk
0be01476fa очень много лишнего удалил в частности ботов и хикцентрал 2026-03-10 13:42:45 +03:00
lizerk
0f8dcff4ad лишнее из документов удалить 2026-03-10 13:37:59 +03:00
lizerk
cc24a073f4 refactor(documents): реструктуризация notifications в модульную архитектуру
Проблема:
- Монолитный notification_signals.py (~520 строк) смешивал разные concerns
- Сложная логика с множественными сигналами и обработчиками в одном файле
- Логирование и бизнес-логика переплетены с signal receivers

Решение:
Разделен на модульную структуру documents/notifications/:
- config.py - NotificationVerbs, MessageTemplates, ActionURLs, get_uploader_name(),
  get_bulk_threshold()
- handlers.py - notify_all_employees(), notify_specific_users(),
  notify_department_employees(), notify_all_acknowledged(), notify_document_comment(),
  notify_comment_reply()
- signals.py - Django signal receivers (@receiver) для Document, DocumentAcknowledgement,
  DocumentComment
- __init__.py - публичный API модуля

Изменения:
- Создан: documents/notifications/ модуль с 4 файлами
- Переименован: notification_signals.py → notification_signals.py.old
- Изменен: apps.py - обновлен импорт на documents.notifications

Логика уведомлений:
1. Новый документ → все сотрудники/отделы/конкретные получатели
2. Все ознакомились → уведомление загрузившему
3. Комментарий → автор документа
4. Ответ на комментарий → автор родительского комментария

Преимущества:
- ✅ Единообразие с requests_app, procurement, feed, communications
- ✅ Четкое разделение ответственности (SRP)
- ✅ Централизованное логирование с контекстом
- ✅ Поддержка bulk режима для массовых рассылок
- ✅ Легче тестировать компоненты
- ✅ Публичный API для переиспользования

Верификация:
- python manage.py check - успешно
2026-03-10 13:36:04 +03:00