80 Commits

Author SHA1 Message Date
45870fba86 fix(calendar): timezone handling для datetime-local inputs
- Добавлена функция toLocalDateTimeString для корректной конвертации UTC в локальное время
- Исправлено отображение времени в модальном окне событий (было 21:00 UTC вместо 00:00 MSK)
- Упрощена логика datetime-local inputs (удалена сложная условная логика)
- Добавлен allDayAccessor в react-big-calendar для корректного отображения событий
- Унифицирован EventModal для обоих календарей (~718 строк удалено)
2026-02-25 00:38:54 +03:00
0dd3ac7cc1 Реализация системы schedule (django-scheduler) с тестовыми данными
- Добавлен CustomToolbar для BigCalendar со стрелками навигации
- Выпадающий список для выбора вида календаря
- Management command для создания тестовых пользователей и календарей
- Создано 3 тестовых пользователя (anna_ivanova, petr_petrov, maria_sidorova)
- Создано 5 календарей с 11 событиями
- Убраны подписки (они для старой системы calendar_app)
- Исправлены стили тулбара BigCalendar
- Модалы календаря рендерятся на корневом уровне AppShell
2026-02-24 20:24:39 +03:00
3e4137daa4 feat(ldap): добавлено кэширование LDAP логина с возможностью обновления
- Добавлено поле Employee.username для кэширования LDAP логина
- API endpoint обновлен: поддержка кэша и force_refresh параметра
- Новый дизайн: убран blur, добавлена иконка обновления
- Автоматическая загрузка при инициализации (из кэша или LDAP)
- Визуальная индикация источника данных (cached/fresh)
- Доступно всем пользователям без ограничений
2026-02-06 15:33:48 +03:00
17c4e506cf refactor(employees): упрощена логика прав для LDAP endpoint
- Использована встроенная система пермишенов DRF вместо ручной проверки
- Добавлен ldap_info в required_perms_by_action с правом view_ldap_info
- Добавлен ldap_info в get_permissions() для применения IsSelfOrStaff
- Удалена избыточная ручная проверка прав внутри метода ldap_info()
- Проверка прав теперь выполняется автоматически через get_object()
- Пользователь может видеть свой LDAP логин (IsSelfOrStaff)
- Админы и пользователи с view_ldap_info могут видеть любые логины
2026-02-06 14:28:21 +03:00
c6cd2ba20d feat(employees): добавлен функционал отображения LDAP логина в профиле
- Добавлено поле с LDAP логином (sAMAccountName) в профиле сотрудника
- Реализован blur-эффект с раскрытием по клику для безопасности
- Создан API endpoint /api/v1/employees/{id}/ldap-info/ для получения LDAP данных
- Добавлен новый пермишен employees.view_ldap_info
- Стили: SCSS компонент _ldap-login.scss с анимациями и состояниями
- JavaScript: класс LdapLoginField с AJAX запросами и обработкой ошибок
- Доступ только для staff или пользователей с правом view_ldap_info
- Данные запрашиваются из Active Directory on-demand (не хранятся в БД)
2026-02-06 14:24:31 +03:00
ae340f4d03 refactor: extract serialize_message to separate module and remove obsolete ChatConsumer classes
- Create communications/serialization.py with serialize_message function
- Update imports in realtime/consumers.py, api views and serializers
- Remove communications/consumers.py (ChatConsumer and ChatListConsumer now obsolete)
- UserConsumer in realtime/consumers.py handles all WebSocket connections
- Add seed_test_data management command for database population
2026-01-21 17:59:46 +03:00
e8af9245ed fix: исправлена отправка аватаров через multipart вместо base64 и ошибка photo->avatar
- Изменена отправка аватаров на multipart/form-data вместо JSON+base64
- Исправлено в employee_create, employee_create_modal, employee_edit, employee_edit_me
- Исправлена ошибка 'Employee' object has no attribute 'photo' (user.photo -> user.avatar)
- Убрано сжатие изображений в _file_to_data_uri (функция deprecated)

Fixes: #400 Bad Request при загрузке больших аватаров
2026-01-09 14:14:01 +03:00
8b391cb388 - 2026-01-09 14:12:54 +03:00
6da39e5120 feat(ldap): добавлена команда sync_employee_ids_to_ldap
Команда синхронизирует Django pk существующих пользователей в LDAP employeeNumber:
- По умолчанию dry-run режим
- Опция --user-id для теста на одном пользователе
- Опция --force для перезаписи существующих значений
- Использует LdapSyncState для поиска DN пользователей
2026-01-07 15:55:28 +03:00
dbdf608051 feat(ldap): записываем Django pk в LDAP employeeNumber при создании/обновлении пользователей
- Добавлена настройка LDAP_EMPLOYEE_ID_ATTR (по умолчанию 'employeeNumber')
- UserService.create_user() теперь записывает pk в LDAP после создания Employee
- SyncService.import_users() записывает pk при создании новых пользователей
- SyncService.import_users() проверяет и заполняет пустой employeeNumber при update
- Добавлен диагностический скрипт check_employee_id_attrs.py для проверки AD атрибутов
2026-01-07 15:50:07 +03:00
5f8bd721d1 feat: add logging to send_mail method to trace password reset emails 2026-01-05 12:01:33 +03:00
c49e5cac10 fix: remove incorrect get_users override, keep logging in save method only 2026-01-05 11:20:56 +03:00
d5b5437c60 fix: password reset emails now use HTML template for Gmail compatibility 2026-01-05 10:47:56 +03:00
eb69007aa2 fix: ignore entryAlreadyExists when adding member to LDAP group 2025-12-30 12:27:57 +03:00
5d0572d17a fix(roles): enforce LDAP-first pattern for role operations
When LDAP is enabled:
- create_role: creates LDAP group before DB record
- update_role: renames LDAP group before DB update
- delete_role: deletes LDAP group before DB deletion
- assign_role: adds to LDAP group before creating RoleAssignment
- revoke_role: removes from LDAP group before deactivating assignment

On LDAP error: operation fails, no DB changes
On DB error after LDAP success: rollback LDAP changes

Fixes: roles could bypass LDAP when it was enabled
2025-12-30 11:04:39 +03:00
026bc46816 feat(department-roles): complete LDAP architecture redesign with RoleAssignment model
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
2025-12-30 10:35:34 +03:00
b3ee4f1960 feat(ldap): separate dismissed employees to 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
2025-12-18 11:56:51 +03:00
9d47fc0c5c feat: add employee dismissal UI and filter inactive employees
- Added dismissal form on employee profile page with permissions
- Shows warning when dismissing (LDAP deactivation notice)
- Inactive employees filtered by default in employee list (active=true)
- Team wheel now shows only active employees
- Auto-submit filter form on status change
- Added redirect to set active=true by default on first visit
2025-12-17 09:14:34 +03:00
992dc6a13f feat: maximize LDAP avatar quality (384px, progressive JPEG, 4:4:4)
- Увеличено разрешение: 120px -> 384px (+900% пикселей)
- Повышено качество JPEG: 50-95 -> 75-98
- Добавлен progressive JPEG для лучшего сжатия
- Включён subsampling 4:4:4 (без потери цветности)
- Добавлена обработка EXIF ориентации
- Добавлено сохранение пропорций (thumbnail вместо resize)
- Обработка прозрачности PNG

Улучшения в:
- employees/ldap/utils/image_utils.py
- employees/ldap/services/user_service.py
- employees/ldap/sync_service.py

Добавлены тестовые скрипты:
- test_ldap_simple.py (быстрый тест)
- test_avatar_simple.py (тест системного сжатия)
- TEST_IMAGE_SCRIPTS.md (документация)
2025-12-16 14:55:58 +03:00
298524ecc6 fix: use PositionService instead of DirectoryService for position groups
Problem:
- When assigning/changing position, got 502 error
- Error: 'DirectoryService' object has no attribute '_ensure_position_group'
- Method was called on wrong service class

Root Cause:
DirectoryService doesn't have _ensure_position_group method.
This method exists in PositionService (line 190 of position_service.py).

Solution:
- Import and use PositionService for position-specific operations
- Keep DirectoryService for general group operations (add/remove members)
- Use pos_svc._ensure_position_group() to create/get position group DN
- Use dir_svc for add_group_members/remove_group_members

Now position assignment/changes work correctly through LDAP sync.
2025-12-08 13:29:53 +03:00
aa9fcbe964 fix: handle empty position_id and unwillingToPerform LDAP error
Problem:
- When clearing employee position (position_id: ''), LDAP sync failed
- Error: LDAP modify failed: 'unwillingToPerform' (code 53)
- System tried to remove user from position group but LDAP refused

Root Causes:
1. Empty string '' not explicitly converted to None
2. unwillingToPerform error not handled (user may not be in group)

Solution:
1. Convert position_id: '' to None (no position) at parsing stage
2. Wrap remove_group_members in try/except
3. Ignore unwillingToPerform errors with warning log
4. Add detailed logging for position group operations

This allows users to clear positions without LDAP sync failures.
2025-12-08 13:14:40 +03:00
cbdb888168 fix: avatar upload with Base64ImageField and ForeignKey handling
- Changed EmployeeSerializer.avatar from ImageField to Base64ImageField
  to properly handle base64-encoded images from employee_edit_me view
- Added conversion of empty strings to None for ForeignKey fields (_id)
  to prevent 'Field expected a number but got empty string' errors
- Improved logging for debugging multipart form processing
2025-12-08 10:32:31 +03:00
7d15369df4 fix: handle empty avatar field in profile update (complete fix)
- Skip empty avatar field in views_front.py before sending to API
- Add validate_avatar() in serializer to convert empty strings to None
- Prevent avatar update when value is None in serializer.update()
- Fix ValidationError when updating profile without changing avatar
2025-12-08 10:15:10 +03:00
9ef3c53a46 fix: убрана двойная загрузка данных на странице списка сотрудников
- Упрощен employee_list view - НЕ загружает данные сотрудников
- Сервер отдает только справочники для фильтров
- Убран серверный рендеринг карточек из шаблона
- Добавлен spinner во время загрузки
- Данные загружаются один раз через JavaScript
- Производительность улучшена - нет двойного запроса
2025-12-05 12:35:10 +03:00
d370ef678d Валидация ФИО: запрет цифр, обязательность аватара, улучшения JS live validation, фиксы паттернов для отчества 2025-12-02 14:02:10 +03:00
2fbf0e29fc fix: синхронизация аватара LDAP <-> БД при обновлении пользователя (avatar теперь сохраняется и в employee.avatar) 2025-12-02 12:49:29 +03:00
e69de73814 fix: синхронизируем avatar из LDAP обратно в БД после обновления 2025-12-02 12:27:01 +03:00
1b3076fb02 Улучшена обработка ошибок валидации: парсинг detail с извлечением имени поля и сообщения, обработка API ошибок в try-except 2025-12-02 11:07:47 +03:00
94ff4c8fbe Разрешено создание записей LDAP синхронизации через админку, убраны readonly поля для редактирования 2025-12-02 10:53:24 +03:00
3f19f92e0b - 2025-12-02 10:50:47 +03:00
4f94e7533a Добавлена кликабельная ссылка для быстрого создания записи синхронизации:
- При отсутствии записи LdapSyncState теперь показывается кликабельная ссылка
- Клик по "❌ Нет записи" ведет на форму создания записи с предзаполненными полями model и object_pk
- Упрощено создание отсутствующих записей синхронизации одним кликом
2025-12-02 10:43:57 +03:00
2384faa6b3 Добавлено детальное логирование для диагностики проблем с LDAP синхронизацией:
- Добавлены логи при поиске записи синхронизации
- При отсутствии записи выводится список всех object_pk в таблице синхронизации
- Добавлен ID пользователя в сообщение об ошибке для упрощения отладки
- Улучшена обработка исключений с полным логированием стека
2025-12-02 10:42:48 +03:00
677feaa111 Добавлены админ-действия для управления LDAP синхронизацией:
1. Улучшен статус ldap_sync_status с детальной информацией:
   - Локальные пользователи отмечены как "Локальный" (серый)
   - LDAP пользователи с полными данными: ✅ с GUID и DN в подсказке
   - LDAP пользователи с неполными данными: ⚠️ с указанием отсутствующих полей
   - LDAP пользователи без записи: ❌ с пояснением
2. Добавлен action "Создать записи LDAP синхронизации" для массового создания записей LdapSyncState
3. Добавлен action "Проверить статус LDAP синхронизации" с детальной статистикой по выбранным сотрудникам
2025-12-02 10:35:41 +03:00
a142d0ccbc Улучшена админка для мониторинга LDAP синхронизации:
1. Добавлена колонка ldap_sync_status в список сотрудников с иконками статуса (✅/⚠️/❌)
2. В детальной информации ldap_sync_info добавлена кликабельная ссылка на запись LdapSyncState
3. Создан кастомный фильтр LdapSyncedFilter для быстрой фильтрации сотрудников по наличию записи синхронизации
4. Улучшена читаемость статусов синхронизации с подсказками при наведении
2025-12-02 10:32:56 +03:00
1384ef1d20 Расширена админка для полного управления моделями и LDAP:
1. Добавлено поле ldap_sync_info в админку Employee для отображения состояния LDAP синхронизации (DN, GUID, направление, timestamps)
2. Добавлены недостающие поля Employee в админку: is_ldap_managed, email_activation_code, sms_activation_code
3. Добавлен is_ldap_managed в list_display и list_filter для быстрой фильтрации
4. Добавлены LDAP поля (ldap_group_dn) в админки Department, DepartmentRole и Position
5. Улучшены fieldsets для всех моделей с группировкой LDAP полей
2025-12-02 10:24:57 +03:00
7950c40636 Улучшена обработка ошибок профиля и добавлена админка для LDAP:
1. Убраны редиректы из employee_edit и employee_edit_me - теперь всегда возвращается JSON
2. Добавлена проверка наличия ldap_dn перед обновлением через LDAP в обоих методах (me и partial_update)
3. Зарегистрирована модель LdapSyncState в админке для просмотра состояния синхронизации
2025-12-02 10:13:17 +03:00
8edc856636 Исправлены ошибки редактирования профиля:
1. Изменен редирект при ошибках валидации: теперь возврат на страницу профиля вместо несуществующего GET endpoint
2. Добавлена проверка наличия ldap_dn перед обновлением через LDAP: локальные пользователи без ldap_dn теперь обновляются напрямую в БД
2025-12-02 10:03:47 +03:00
5291a921f4 Исправлена обработка ошибок валидации: теперь при ошибках валидации в employee_edit и employee_edit_me происходит возврат на форму редактирования с сообщениями об ошибках, вместо редиректа на профиль. 2025-12-02 09:58:25 +03:00
34b04d4c7f закомитил обработчик который обрабатывал сигнал смены аватара 2025-12-02 09:23:51 +03:00
a7aa6cc15b fix: handle missing avatar files gracefully in signals, improve registration phone error, PATCH avatar empty handling 2025-12-01 17:09:44 +03:00
68352580e8 Фикс: корректный placeholder для фото сотрудника при пустом avatar, форма отправляет POST на фронтовую вьюху, убрана JS-валидация и кастомный submit. 2025-12-01 15:44:02 +03:00
bfb87d796c debug: добавлено логирование вызова функции employee_edit_me 2025-12-01 15:16:00 +03:00
87fe9d5c2c debug: добавлено подробное логирование в employee_edit и employee_edit_me для отладки загрузки аватаров 2025-12-01 15:14:50 +03:00
2730b0e019 fix: добавлена поддержка multipart/form-data в employee_edit и employee_edit_me для загрузки аватаров 2025-12-01 15:12:19 +03:00
1be74f407f fix: исправлен путь к аватарам в команде optimize_avatars (используется MEDIA_ROOT) 2025-12-01 14:43:14 +03:00
46ac00255e Добавлена автоматическая оптимизация аватаров: сжатие до 512x512 (до 500KB) + удаление старых файлов 2025-11-30 10:56:52 +03:00
43fa0a2053 Добавлена загрузка/замена/удаление фото сотрудника с автоматическим удалением старых файлов 2025-11-30 10:50:09 +03:00
8fe844780e Fix ApiResponse.content error and add API base URL logging
- Changed resp.content to resp.text in register_view (ApiResponse has no content attribute)
- Added logging for API base URL to debug 404 errors
- This should help identify why API returns 404 for registration endpoint
2025-11-21 13:39:06 +03:00
2637e9ed65 Add detailed logging for registration debugging
- Added logging in RegisterAPIView to track incoming data and validation errors
- Added logging in register_view to track form data and API communication
- Created test_register_api.py script for testing registration API
- Logs include: POST data, Content-Type, validation errors, API payload and responses
- All logs use [REGISTER] and [REGISTER_VIEW] prefixes for easy filtering
2025-11-21 13:35:38 +03:00
35f365a1b0 feat: Add department-based document distribution with department picker in UI
- 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
2025-11-21 12:46:43 +03:00