13 Commits

Author SHA1 Message Date
a335594932 fix: remove mocks from LDAP tests, use real LDAP integration, set gender default=0 2026-02-16 21:12:25 +03:00
4629a52834 fix: resolve test failures - add logger, LDAP_POSITIONS_BASE, make avatar/gender optional, add ensure_ldap_disabled fixtures 2026-02-16 17:38:00 +03:00
f85b5dfa08 refactor: remove DB access from LDAP layer update method
- Move department_id to department_dn resolution to ViewSet layer
- update_user_in_ldap_only now receives ready department_dn parameter
- LDAP service no longer queries Department.objects directly
- All three *_in_ldap_only methods are now pure LDAP (no DB access)
2026-02-16 17:22:38 +03:00
4b56711636 refactor(employees): minimize ViewSet overrides - let mixin do the work
Removed unnecessary method overrides:

Deleted:
- create(): removed entirely - миксин работает через perform_create
- partial_update(): removed entirely - миксин работает автоматически
- perform_update() avatar handling: moved to perform_update

Simplified:
- perform_create(): only unusable password + skills (30 lines)
- perform_update(): only email verification (15 lines)

Benefits:
- Миксин работает "из коробки" без явных переопределений
- Меньше кода для поддержки
- create/update/destroy используют стандартный механизм DRF
- Только необходимая кастомная логика в perform_*

Теперь ViewSet минимален:
- Конфигурация миксина (get_external_sync_method, prepare_external_data)
- Только критичная логика (unusable password, email verification)
- Все остальное - автоматически через миксин

manage.py check: no issues
2026-02-16 17:16:19 +03:00
518fb6e790 refactor(employees): remove _perform_user_update, simplify me() PATCH
Removed 180+ lines of complex update logic:

Deleted:
- _perform_user_update(): 180 lines of manual LDAP/DB coordination
- Complex validation and error handling
- Manual transaction management
- Direct DirectoryService.update_user() calls

Simplified me() PATCH:
- Now uses standard perform_update() mechanism
- 4 lines instead of 15
- Consistent with partial_update()
- Миксин управляет всем автоматически

Total code reduction in employees.py: ~400 lines removed
(create: -150, partial_update: -200, _perform_user_update: -180)

File now: ~800 lines (was 1200+)

Benefits:
- Single source of truth for update logic
- No code duplication
- Automatic transaction management
- Cleaner, easier to maintain

manage.py check: no issues
2026-02-16 17:10:03 +03:00
7a3402f1b6 refactor(employees): simplify partial_update using ExternalSystemSyncMixin
Replaced _perform_user_update (180+ lines) with clean mixin approach:

Before:
- _perform_user_update(): 180 lines of manual LDAP/DB coordination
- Manual transaction management
- Direct DirectoryService.update_user() calls with transaction.atomic inside
- Complex error handling
- Duplicate email verification logic

After:
- partial_update(): delegates to super() + email verification (30 lines)
- perform_update(): only avatar handling (20 lines)
- prepare_external_data(): builds LDAP changes dict with avatar_bytes
- Миксин автоматически управляет транзакциями

Changes:
1. partial_update(): simplified to super() + email verification
2. perform_update(): handles avatar file -> ContentFile
3. prepare_external_data(): extracts LDAP fields (first_name, last_name, email, etc),
   converts avatar to bytes, passes move_to_department_dn and group_cns
4. update_user_in_ldap_only(): updated signature to accept move_to_department_dn, group_cns

Code reduction: 200 lines -> 80 lines
Benefits: cleaner, automatic rollback, consistent with create()

Next: можно удалить _perform_user_update() если не используется

manage.py check: no issues
2026-02-16 17:08:45 +03:00
59b8f56c5a refactor(employees): simplify create() using ExternalSystemSyncMixin
Replaced 150+ lines of manual transaction management with mixin:

Before:
- Manual transaction.atomic()
- Manual LDAP sync calls
- Duplicate code for LDAP/non-LDAP modes
- Manual error handling and rollback
- ~150 lines of boilerplate

After:
- create() delegates to super() - миксин управляет всем
- perform_create() - только БД логика (30 lines)
- handle_external_sync_result() - обработка LDAP результата
- Миксин автоматически управляет транзакциями и откатами

Changes:
1. create(): simplified to validation + super().create()
2. perform_create(): creates Employee in DB, sets password/avatar/skills
3. prepare_external_data(): builds DTO with avatar_bytes, group_cns
4. handle_external_sync_result(): saves DN/GUID to LdapSyncState,
   sets employeeNumber, assigns position

5. ExternalSystemSyncMixin: added handle_external_sync_result() hook
   for custom post-sync actions

Code reduction: 150 lines -> 80 lines
Benefits: cleaner, DRYer, automatic error handling

manage.py check: no issues
2026-02-16 17:07:15 +03:00
df82e4f66d feat: add ExternalSystemSyncMixin for universal LDAP/DB sync
Created universal solution for ViewSet + external system synchronization:

1. ExternalSystemSyncMixin (common/external_sync_mixin.py):
   - Automatically wraps CRUD operations in transactions
   - Coordinates DB + LDAP in single atomic transaction
   - Rolls back DB if LDAP fails
   - Configurable error handling
   - Works with any external system (LDAP, APIs, etc)

2. Applied to EmployeeViewSet:
   - Added mixin to base classes
   - Configured LDAP sync mapping (create/update/destroy)
   - prepare_external_data() for DTO creation
   - handle_external_sync_error() for LDAP errors
   - No need to override create/update/destroy methods

3. Created pure LDAP methods in DirectoryService/UserService:
   - create_user_in_ldap_only() - LDAP create, returns dn/guid
   - update_user_in_ldap_only() - LDAP update, no DB access
   - delete_user_in_ldap_only() - LDAP delete, no DB access
   - All methods removed transaction.atomic (managed by ViewSet)

4. Documentation:
   - Complete guide in docs/guides/EXTERNAL_SYNC_MIXIN_GUIDE.md
   - Usage examples for all patterns
   - Migration guide from old architecture
   - Comparison with alternatives (Signals, Celery, Saga)

Benefits:
- Minimal code (3 methods vs overriding entire ViewSet)
- Clear separation: View owns DB, LDAP service owns LDAP
- Reusable for all ViewSets (departments, positions, etc)
- Easy to test (external_sync_enabled = False)
- Universal (works with any external system)

manage.py check: System check identified no issues
2026-02-16 17:04:08 +03:00
1f0858b085 refactor(ldap): fix architectural issues in views
DirectoryService:
- Added public methods: assign_role, revoke_role, create_role, update_role, delete_role
- Added public method: move_user_to_base (replaces private _user_service._move_user_to_base)
- Fixed method signatures to match DepartmentService API

roles.py:
- Replaced direct DepartmentService usage with DirectoryService facade (5 methods)
- Removed manual GroupService/UserService instantiation
- All CRUD operations now go through facade layer

actions.py:
- Replaced private API svc._user_service._move_user_to_base() with public svc.move_user_to_base()
- Simplified LDAP connection handling (2 occurrences)
- Removed manual _ldap() and ensure_container_exists calls

manage.py check: System check identified no issues
2026-02-16 16:38:01 +03:00
19464a38cf refactor(employees): DRY duplicate logic in employees and departments
employees.py:
- Extracted _perform_user_update() method (180 lines)
- Simplified me PATCH: removed ~150 lines of duplicate code
- Simplified partial_update: removed ~155 lines of duplicate code
- Total reduction: ~255 lines (930 → 675)

departments.py:
- Extracted _perform_set_head() method (70 lines)
- Simplified set_head action: removed ~50 lines of duplicate code
- Simplified partial_update head branch: removed ~70 lines of duplicate code
- Total reduction: ~140 lines (752 → 612)

manage.py check: System check identified no issues
2026-02-16 16:32:12 +03:00
09dfb7598e refactor(employees): DRY duplicate logic in employees and departments
employees.py:
- Extracted _perform_user_update() method (180 lines)
- Simplified me PATCH: removed ~150 lines of duplicate code
- Simplified partial_update: removed ~155 lines of duplicate code
- Total reduction: ~255 lines (930 → 675)

departments.py:
- Extracted _perform_set_head() method (70 lines)
- Simplified set_head action: removed ~50 lines of duplicate code
- Simplified partial_update head branch: removed ~70 lines of duplicate code
- Total reduction: ~140 lines (752 → 612)

manage.py check: System check identified no issues
2026-02-16 16:27:08 +03:00
eaedea1403 refactor(employees): remove permissions.py shim, fix relative imports
- Replaced ..permissions with ...permissions in all 7 view modules
- Deleted backend/api/v1/employees/permissions.py (duplicate re-export)
- Views now import directly from api.v1.permissions (3 dots up)

manage.py check: System check identified no issues
2026-02-16 16:21:45 +03:00
8efcabf530 refactor(employees): split monolithic views.py (4593 lines) into views/ package
- Split into 9 modules by ViewSet: departments, employees, positions,
  roles, skills, actions, groups, auth, _helpers
- Created views/__init__.py with re-exports for backward compatibility
- Created employees/permissions.py re-export shim (views/ is now a
  sub-package, so ..permissions resolves to employees.permissions)
- Fixed api/auth/serializers.py import: PHONE_FIELD from _helpers,
  _normalize_phone from employees.utils
- Removed ~250+ debug print()/logger.info() statements from
  departments.py and employees.py
- Removed dead _with_ldap_service() decorator from helpers
- Removed unreachable code at end of GroupViewSet.replace_members

manage.py check: System check identified no issues (0 silenced)
2026-02-16 16:18:03 +03:00