169 Commits

Author SHA1 Message Date
lizerk
9cd21a1b1e procurement: add item comments 2026-05-22 14:57:45 +03:00
lizerk
92fb845b70 Fix birthday event sync lifecycle 2026-05-14 16:32:27 +03:00
lizerk
da0cadc50c Handle existing LDAP users during Django sync 2026-05-14 15:06:47 +03:00
lizerk
f0024f7a09 Add attendance aliases and weekly summary 2026-05-03 23:02:33 +03:00
lizerk
3a20c3d86a Normalize personnel events and attendance state 2026-04-29 09:54:11 +03:00
lizerk
1bf3fac8b5 fix migration 2026-04-22 14:18:58 +03:00
lizerk
49002d50a0 Prevent duplicate request employee actions 2026-04-22 14:10:55 +03:00
lizerk
625468090b Add persisted LogStorm attendance integration 2026-04-22 10:21:44 +03:00
lizerk
5700b53f13 feat: add department calendar bindings and cleanup dept permissions 2026-04-16 15:18:43 +03:00
lizerk
64eaecc06e makemigrations 2026-04-16 13:29:05 +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
8d22156b36 feat(admin): add department role ldap sync action 2026-04-14 01:49:30 +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
274e5767c9 refactor(departments): label role-only members 2026-04-14 00:35:51 +03:00
lizerk
5ab638892b chore: checkpoint workspace changes 2026-04-13 16:00:53 +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
81b8d29b38 Refactor department LDAP sync around canonical service flows 2026-04-10 20:54:09 +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
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
1a3d533c1d админка + 2026-03-22 13:55:26 +03:00
lizerk
4c5ea98743 админка+ 2026-03-20 14:44:41 +03:00
lizerk
55d0bcf906 Feature: Add validation warnings for missing fields in sync actions
Both LDAP→Django and Django→LDAP sync actions now check for missing
required fields (name, email, employeeNumber) and display warnings
before syncing. Shows up to 10 warnings per action.
2026-03-20 02:35:29 +03:00
lizerk
7a770165f9 Fix: Correct get_action_choices signature (use BLANK_CHOICE_DASH) 2026-03-20 02:31:11 +03:00
lizerk
130dafc5a2 Fix: Replace delete_selected with LDAP-safe custom action
Django's delete_selected passes queryset with dn__in filter that
ldapdb doesn't support. Custom delete_selected_ldap reads DNs from
POST and deletes objects one by one. Also hides standard delete_selected
from action choices.
2026-03-20 02:20:42 +03:00
lizerk
6050cd9d26 Fix: Handle ldapdb unsupported dn__in lookup in admin delete
- Override get_deleted_objects() in LdapUserAdmin to avoid dn__in
  queryset that ldapdb doesn't support
- Also fix phone_number UNIQUE constraint in LDAP auth backend
2026-03-20 02:18:39 +03:00
lizerk
63a96a0122 Fix: Render thumbnailPhoto as image preview in LdapUser admin
Binary thumbnail_photo data caused 500 error when Django admin tried
to render it as a text input. Now displayed as base64 inline image
preview in a readonly field.
2026-03-20 02:03:09 +03:00
lizerk
efc649aeae Feat: Implement department move and position group sync in UserService
- Department move: set ldap_user.base_dn via ModifyDnMixin (replaces DirectoryService)
- Position sync: assign/unassign POS-groups via PositionService on position change
- DB update: position_id saved to Employee in atomic block
- Best-effort: position sync errors logged but don't interrupt update
2026-03-20 01:45:25 +03:00
lizerk
0ba594d317 Style: Fix PEP8 whitespace and line-length in orm_models.py 2026-03-20 01:37:05 +03:00
lizerk
9bc4d70a27 Cleanup: Remove dead position sync code from user_service
- Removed 30 lines of commented-out DirectoryService code
- Removed unused Position import
- Removed unused old_pos/new_pos/pos_in_payload variables
- Fixed stale comment about displayName auto-update in save()
- Position sync remains as a 3-line TODO comment
2026-03-20 01:26:04 +03:00
lizerk
999ecaaadf Fix: Handle RDN rename in ModifyDnMixin.save()
AD forbids modifying RDN (CN/OU) via modify_s - only via rename.
ModifyDnMixin now:
1. Tracks _original_rdn_value (cn/ou) from __init__/from_db
2. On save(), if RDN changed: reverts to old value → save() → modify_dn
3. This fixes NOT_ALLOWED_ON_RDN for both admin and API edits

Simplified user_service: just set ldap_user.cn = new_cn and call save().
The mixin handles the rest automatically.
2026-03-20 01:21:03 +03:00
lizerk
afd66662ce Fix: Don't modify CN via save(), use modify_dn only
AD forbids modifying RDN (CN) through modify_s - must use rename.
- Removed LdapUser.save() override that synced cn→displayName
  (this caused cn to be in modlist → NOT_ALLOWED_ON_RDN error)
- user_service now sets displayName explicitly instead of cn
- CN is only changed via conn.modify_dn() after save()
- Flow: set displayName + other attrs → save() → modify_dn(cn)
2026-03-20 01:11:44 +03:00
lizerk
7b7e071df1 Fix: Auto-sync displayName with cn in LdapUser.save()
- Added save() override in LdapUser to auto-update displayName from cn
- Fixed user_service to set ldap_user.cn BEFORE save() (not after)
- This ensures displayName is synced when givenName/sn change
- Previously displayName wasn't updated because cn was set after save()
- Now: set cn → save() → displayName auto-synced → modify_dn()
2026-03-20 01:09:24 +03:00
lizerk
11e0714411 Fix: Override build_rdn() to fix ldapdb 1.5.1 bug
ldapdb's build_rdn() checks field.primary_key AND field.db_column,
but the 'dn' field has primary_key=True and db_column=None,
while 'cn'/'ou' fields have db_column set but primary_key=False.
No field satisfies both conditions → save() always raises
'Could not build Distinguished Name'.

Fix: ModifyDnMixin.build_rdn() extracts RDN from existing DN
for loaded objects, and builds from rdn_attributes for new ones.
2026-03-20 00:55:56 +03:00
lizerk
c8ae3b3c7b Fix: Use get_ldap_str() in employees admin sync actions
- Added get_ldap_str() import to 3 admin actions
- Updated sync_from_django_to_ldap to safely compare LDAP fields
- Updated sync_from_ldap_to_django to safely extract LDAP values
- Updated show_sync_diff to safely compare LDAP vs Django data
- Prevents 'list' object errors in all admin LDAP sync operations
2026-03-20 00:42:15 +03:00
lizerk
a3bd9226a5 Fix: Refactor LDAP field handling to use get_ldap_str()
- Moved get_ldap_str() helper to ldap_utils.py for reuse
- Updated user_service.py to use shared helper from utils
- Updated admin.py to safely extract string values from LDAP fields
- LDAP fields can be lists, strings, or None - helper handles all cases
- Fixes sync_from_ldap_to_django and compare_ldap_django actions
- Prevents 'list' object errors when comparing or assigning LDAP fields
2026-03-20 00:40:51 +03:00
lizerk
be2e899046 Fix: Handle LDAP fields as lists in user_service.py
- Added _get_ldap_str() helper to safely extract string values from LDAP fields
- LDAP fields (given_name, sn, etc.) can be returned as lists, not strings
- Fixes AttributeError: 'list' object has no attribute 'strip'
- Used helper in CN generation code (lines 761-764)
2026-03-20 00:38:35 +03:00
lizerk
f8762894cd Fix: Remove deprecated DirectoryService imports
- DirectoryService module no longer exists
- Commented out position/department sync code that used DirectoryService
- Marked with TODO for future refactoring
- Fixes ModuleNotFoundError during LDAP sync operations
2026-03-20 00:35:00 +03:00
lizerk
36c5323e0e Fix employeeNumber update: use direct LDAP modify instead of ORM save
- Replaced ORM .save() with direct ldap3.Connection.modify()
- ORM save() tries to rebuild DN which fails without proper cn attribute
- Direct modify only changes the employeeNumber attribute
- Fixes Exception: Could not build Distinguished Name
2026-03-20 00:22:39 +03:00
lizerk
4cf989e5b5 Fix _UtcCompat: add missing normalize() method
- ldapdb calls timezone.utc.normalize() for datetime field conversion
- Added normalize() method that works the same as localize() for UTC
- Fixes AttributeError: '_UtcCompat' object has no attribute 'normalize'
2026-03-20 00:21:04 +03:00
lizerk
ca1deac82b Fix LDAP user creation: handle existing Employee with same email
- Check if Employee with same email exists before creating new one
- Update existing Employee and link it to LDAP instead of creating duplicate
- Prevents UNIQUE constraint failed error on email field
- Sets is_ldap_managed=True and updates user data from LDAP
2026-03-20 00:16:29 +03:00
lizerk
175796ff78 Fix LDAP admin actions: resolve dn__in unsupported lookup
- Added _resolve_ldap_users() helper to fetch LDAP objects by DN individually
- ldapdb does not support dn__in lookup used by Django admin queryset
- All 3 actions now use _resolve_ldap_users instead of iterating queryset directly
2026-03-20 00:12:12 +03:00
lizerk
14c396d483 Fix timezone compatibility: Properly implement _UtcCompat as tzinfo subclass
- Made _UtcCompat inherit from datetime.tzinfo instead of plain class
- Implemented required tzinfo methods: utcoffset, tzname, dst
- Fixes TypeError: tzinfo argument must be None or of a tzinfo subclass
- Resolves admin panel crashes when viewing LDAP users with timestamps
2026-03-20 00:09:57 +03:00