258 Commits

Author SHA1 Message Date
lizerk
6fa63eaab6 Add employee work schedules for attendance analysis 2026-04-22 12:47:31 +03:00
lizerk
625468090b Add persisted LogStorm attendance integration 2026-04-22 10:21:44 +03:00
lizerk
78e758aedf Add LogStorm attendance UI 2026-04-21 15:56:10 +03:00
lizerk
9719b65b58 Add custom request statistics period 2026-04-20 12:53:45 +03:00
lizerk
8a65498b56 Add request statistics panel to requests page 2026-04-20 12:40:04 +03:00
lizerk
824728019b Add employee request statistics panel 2026-04-20 12:25:13 +03:00
lizerk
78b8615871 feat: refine department calendar behavior and UI 2026-04-16 17:22:06 +03:00
lizerk
5700b53f13 feat: add department calendar bindings and cleanup dept permissions 2026-04-16 15:18:43 +03:00
lizerk
1296ec9021 Fix employee sorting and department counters 2026-04-15 02:12:32 +03:00
lizerk
9ec4c2349f Add user last activity and refine profile cards 2026-04-15 01:31:18 +03:00
lizerk
35a7250183 refactor(ui): polish chat, requests, and profile actions 2026-04-14 23:11:32 +03:00
lizerk
70d2c55348 refactor(ui): polish department feed and mobile nav placement 2026-04-14 19:11:17 +03:00
lizerk
beb4b8b86b refactor(departments): unify department feed and discussion flows 2026-04-14 16:14:52 +03:00
lizerk
7a3ebbfdff fix(api): preserve role-only department members 2026-04-14 02:24:14 +03:00
lizerk
2574b47923 fix(api): enforce single active department membership 2026-04-14 01:18:32 +03:00
lizerk
2d00699831 fix(ldap): nest department role groups into department group 2026-04-14 01:07:13 +03:00
lizerk
4ad946da8b fix(api): include approver avatars in procurement approvals 2026-04-13 16:35:34 +03:00
lizerk
5ab638892b chore: checkpoint workspace changes 2026-04-13 16:00:53 +03:00
lizerk
af423d8d0a feat(procurement): tighten approval flow and request actions 2026-04-13 02:55:26 +03:00
lizerk
44da9c46c1 feat(ui): unify action menus and procurement comments 2026-04-12 20:07:34 +03:00
lizerk
e3ba6ea925 refactor(ui): stabilize equipment deep links and qr flow 2026-04-12 16:09:38 +03:00
lizerk
1552bbe84b fix(requests): confirm destructive actions and align surfaces 2026-04-12 07:19:12 +03:00
lizerk
c56fdbb915 feat(frontend): align employee profile flows 2026-04-12 06:27:18 +03:00
lizerk
097a3015e8 fix(profile): stabilize directory login and expand ldap coverage 2026-04-12 04:13:32 +03:00
lizerk
b98a82176c test(ldap): add live integration coverage and fix sync paths 2026-04-12 03:51:52 +03:00
lizerk
f5a908e443 feat(auth): restore password reset flow and polish auth UI 2026-04-12 02:05:13 +03:00
lizerk
7dcf84dcd1 feat(auth): add session management and settings integration 2026-04-11 23:31:14 +03:00
lizerk
8ce997f062 Merge branch 'investigate/master-actual-theme' into develop 2026-04-11 20:53:39 +03:00
lizerk
db05b4fcdb refactor(frontend): align calendar and list UI 2026-04-11 20:44:16 +03:00
lizerk
81b8d29b38 Refactor department LDAP sync around canonical service flows 2026-04-10 20:54:09 +03:00
lizerk
9b23f480c2 Refine search routing and deep links 2026-04-03 20:30:29 +03:00
lizerk
6ee32111db chore: configure vscode flake8 and commit lint cleanup 2026-03-31 19:17:56 +03:00
lizerk
2815efe069 chore: finalize backend lint cleanup 2026-03-31 17:53:29 +03:00
lizerk
9edb44502a Harden backend warnings and lint policy 2026-03-31 17:19:59 +03:00
lizerk
3f59f8ebb9 Add backend OpenAPI docs and clean test suite 2026-03-31 17:05:31 +03:00
lizerk
f88d90230b refactor: clean architecture, align tests to current API contracts, all tests green [deploy-ready] 2026-03-30 04:27:18 +03:00
lizerk
825726e0f0 chore: commit remaining backend and frontend changes, remove legacy test, add missing migrations 2026-03-30 02:17:28 +03:00
lizerk
2031872461 refactor(procurement): approval logic now fully driven by ApprovalRoute table (priority+min_amount), removed hardcoded steps, all workflow is DB-driven; tests and migrations updated 2026-03-30 02:16:29 +03:00
lizerk
c61b62969a Мелкие правки: обновлены serializers.py и views.py для procurement 2026-03-27 18:14:55 +03:00
lizerk
3a2d05a64d feat(requests): улучшены фильтры по датам, исправлена логика фильтрации, добавлена инструкция по тестированию 2026-03-27 14:57:31 +03:00
lizerk
540e295ef4 feat: Fix LDAP DN bugs + add password management in admin
BREAKING CHANGES:
- Department.head автоматически очищается при увольнении руководителя
- LDAP managedBy атрибут очищается при увольнении

Fixes:
- DN не перемещался при восстановлении сотрудника (Dismissed → Users)
- DN не перемещался при увольнении сотрудника без отделов
- Ошибка при увольнении из множественных отделов (двойной modify_dn)
- Неправильные URL patterns для LDAP моделей в админке
- Двойное кодирование DN в редиректе смены пароля

Features:
- Добавлен метод _ensure_ldap_dn_location для гарантированного перемещения DN
- Добавлена проверка already_there в sync_department_member_to_ldap
- Управление паролями LDAP пользователей через админку:
  * Кнопка смены пароля в карточке пользователя
  * Массовое действие смены пароля
  * Автосинхронизация с Django password hash
- Автоочистка Department.head и LDAP managedBy при увольнении руководителя
- Добавлены методы set_password() и change_password() в LdapUser

Changed:
- Переписан сигнал sync_department_member_to_ldap с реальными методами
- Исправлены URL patterns: ldap_* → employees_*
2026-03-23 20:32:39 +03:00
lizerk
2688df46ef Fix: Wrap all unsafe int() conversions with try/except
Fixed ValueError-prone int() calls in:
- communications/api/viewsets.py (4 places: limit, before_limit, after_limit)
- notifications/api/views.py (2 places: page, page_size)
- api/v1/search/views.py (1 place: limit)
- api/v1/procurement/views.py (1 place: quantity)
Also updated audit script to exclude .venv/
2026-03-20 09:10:33 +03:00
lizerk
2943299c08 Fix: Handle both dict and QueryDict in perform_update
DRF request.data is dict for JSON but QueryDict for form data.
Use hasattr check to call .dict() only when available, otherwise
use dict() constructor for plain dicts.
2026-03-20 09:01:54 +03:00
lizerk
649bd62a2b Fix: Use QueryDict.dict() instead of dict(QueryDict)
dict(QueryDict) wraps every value in a list: {'first_name': ['Вадимин']}
QueryDict.dict() returns single values: {'first_name': 'Вадимин'}

This caused LDAP fields to be saved as "['Вадимин']" instead of 'Вадимин'
because the list was stringified by ldapdb before writing to AD.
2026-03-20 01:01:07 +03:00
lizerk
f15e8aca3f feat(ldap): add LDAP sync retry queue with Celery
- Add LdapSyncQueue model for deferred LDAP operations
- Add Celery tasks: process_ldap_queue_item, process_ldap_queue
- Refactor all LDAP signals (employee, department, group, position)
  to enqueue operations on DirectoryLdapError/DirectoryServiceError
- Add _queue.py helper for enqueuing with automatic Celery dispatch
- Add Celery Beat periodic task (every 60s) for queue processing
- Add management command: ldap_queue (status/retry/cleanup)
- Add migration 0043_ldap_sync_queue
- Add 18 tests covering model, tasks, signals, and enqueue logic
- Include LDAP services refactoring (base_service, constants,
  user_login_service, user_mapper_service, user_password_service,
  phone_utils)
2026-03-19 15:07:34 +03:00
lizerk
0539f50233 refactor: переход на строгий AD-only LDAP, регистрация + верификация email
- Удалён 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-ограничения работают независимо
- Полевые ошибки регистрации с красной подсветкой на фронте
2026-03-19 00:48:13 +03:00
lizerk
bf9bc8f341 refactor(api): полная инкапсуляция LDAP логики в миксин
ПРОБЛЕМА:
- RegisterAPIView знал о режиме LDAP (вызывал _is_ldap_enabled())
- if ldap_enabled: create_ldap_user() else: create_db_user()
- View имел LDAP-специфичную логику выбора режима
- Нарушение принципа инкапсуляции

РЕШЕНИЕ:
Создан единый метод в LdapUserCreationMixin:

  create_user(first_name, last_name, email, phone, password, ...)
    - Автоматически выбирает LDAP или БД режим
    - Инкапсулирует вызов _is_ldap_enabled()
    - View больше не знает о существовании LDAP

ИЗМЕНЕНИЯ:
mixins.py:
  + create_user() - фасад, выбирает режим автоматически
  • create_ldap_user() - остался для внутреннего использования
  • create_db_user() - остался для внутреннего использования

auth.py:
  - Удалён import _is_ldap_enabled
  - Удалён блок if ldap_enabled: ... else: ...
  - Заменён на: emp, error = self.create_user(...)

РЕЗУЛЬТАТ:
- View НЕ знает про LDAP вообще
- Вся LDAP логика инкапсулирована в миксине
- Чистое разделение ответственности
- Упрощение: -18 строк в view

БЫЛО в RegisterAPIView:
  ldap_enabled = _is_ldap_enabled()
  if ldap_enabled:
      emp, err = self.create_ldap_user(...)
  else:
      emp = self.create_db_user(...)

СТАЛО:
  emp, err = self.create_user(...)

Django check: успешно
2026-03-18 19:33:35 +03:00
lizerk
bebe61b727 refactor(api): создание базового AnonymousAPIView для DRY
ПРОБЛЕМА:
- ResendEmailAPIView, VerifyEmailAPIView, RegisterAPIView дублировали:
  • throttle_scope = "anon"
  • permission_classes = [AllowAny]
- Нарушение DRY принципа (6 строк повторяющегося кода)

РЕШЕНИЕ:
Создан базовый класс AnonymousAPIView(APIView):
  - throttle_scope = "anon"
  - permission_classes = [AllowAny]
  - Используется для всех публичных auth endpoints

ИЗМЕНЕНИЯ:
- ResendEmailAPIView(AnonymousAPIView) - удалены дубли
- VerifyEmailAPIView(AnonymousAPIView) - удалены дубли
- RegisterAPIView(LdapUserCreationMixin, AnonymousAPIView) - удалены дубли

РЕЗУЛЬТАТ:
- Убрано 8 строк дублирования
- Более чистая иерархия наследования
- Легче добавлять новые анонимные endpoints
- Единая точка для настройки throttling/permissions

Django check: успешно
2026-03-18 19:32:15 +03:00
lizerk
aa70c23f0f refactor(api): вынос LDAP логики из RegisterAPIView в миксин
ПРОБЛЕМА:
- RegisterAPIView содержал ~30 строк LDAP-специфичной логики
- DirectoryService, DirectoryUserDTO, обработка исключений - всё в основном методе
- Нарушение принципа единственной ответственности
- Сложность тестирования и переиспользования

РЕШЕНИЕ:
Создан api/v1/employees/views/mixins.py с миксинами:

1. LdapUserCreationMixin:
   - create_ldap_user() - создание пользователя в LDAP через DirectoryService
   - create_db_user() - создание пользователя напрямую в БД (fallback без LDAP)
   - Централизованная обработка DirectoryLdapError/DirectoryDbError
   - Возвращает (Employee, None) при успехе или (None, Response) при ошибке

2. LdapPasswordMixin:
   - update_ldap_password() - обновление пароля в LDAP
   - Подготовка для использования в PasswordChangeView и других endpoints

ИЗМЕНЕНИЯ:
- auth.py: RegisterAPIView использует LdapUserCreationMixin
- Блок if ldap_enabled заменён на вызовы self.create_ldap_user() / self.create_db_user()
- _helpers.py: удалён _is_ldap_enabled (перемещён в mixins.py)
- Уменьшено количество строк в RegisterAPIView: ~190 → ~170

ПРЕИМУЩЕСТВА:
- Чёткое разделение: общая логика регистрации vs LDAP-специфика
- Переиспользование: миксины можно применить в других views
- Тестируемость: можно мокать методы миксина
- Читаемость: основной метод фокусируется на бизнес-логике

Django check: успешно
2026-03-18 19:28:01 +03:00
lizerk
5d729fa1f0 fix(views): исправить bulk update и удалить неиспользуемый код
Критические исправления:
- actions.py: заменить bulk update() на save() для EmployeeDepartment
  Причина: bulk update НЕ вызывает Django signals!
  Было: .update(is_active=False) - сигнал не срабатывает
  Стало: for loop с save() - сигнал срабатывает для каждой записи

- _helpers.py: удалить неиспользуемую функцию _ldap_try()
  Нигде не используется после рефакторинга

- _helpers.py: удалить неиспользуемые импорты
  DirectoryLdapError, DirectoryServiceError, DirectoryDbError, status

- actions.py: обновить комментарий с правильным названием сигнала
  sync_department_member_to_ldap вместо устаревшего sync_member

Функционал сохранен: при увольнении сотрудника теперь корректно
срабатывают сигналы для каждого EmployeeDepartment, что приводит
к корректной синхронизации с LDAP (перемещение из OU отделов).
2026-03-18 18:28:11 +03:00