diff --git a/backend/templates/emails/password_reset_email.html b/backend/templates/emails/password_reset_email.html
index 8c860d29..42f61310 100644
--- a/backend/templates/emails/password_reset_email.html
+++ b/backend/templates/emails/password_reset_email.html
@@ -2,10 +2,10 @@
Вы запросили сброс пароля. Нажмите на кнопку ниже для смены пароля:
-
Сбросить пароль
diff --git a/docs/reports/AUTH_TESTS_ANALYSIS.md b/docs/reports/AUTH_TESTS_ANALYSIS.md
new file mode 100644
index 00000000..a23dbb44
--- /dev/null
+++ b/docs/reports/AUTH_TESTS_ANALYSIS.md
@@ -0,0 +1,319 @@
+# 🔍 Подробный отчет: Проблемы с тестами аутентификации
+
+**Дата:** 5 января 2026
+**Файл:** `tests/api/auth/test_auth_and_registration.py`
+
+## 📊 Статистика
+
+```
+✅ 8 тестов ПРОШЛИ
+❌ 11 тестов ПРОВАЛЕНЫ
+━━━━━━━━━━━━━━━━━━━━━
+📈 Успешность: 42%
+```
+
+## 🔴 ОСНОВНАЯ ПРОБЛЕМА
+
+### 🚨 КРИТИЧЕСКОЕ НЕСООТВЕТСТВИЕ: Модель vs Сериализатор
+
+#### ✅ Модель Employee (models.py:162-165):
+```python
+gender = models.PositiveSmallIntegerField(
+ "Пол", choices=GENDER_CHOICES,
+ default=0, # ✅ Есть дефолт
+ blank=True # ✅ НЕОБЯЗАТЕЛЬНОЕ
+)
+avatar = models.ImageField(
+ "Фото", upload_to="users/avatars",
+ blank=True, # ✅ НЕОБЯЗАТЕЛЬНОЕ
+ null=True # ✅ НЕОБЯЗАТЕЛЬНОЕ
+)
+```
+
+#### ❌ RegisterSerializer (serializers.py:29-67):
+```python
+class RegisterSerializer(serializers.Serializer):
+ # ... другие поля ...
+
+ avatar = Base64ImageField(required=True) # ❌ ОБЯЗАТЕЛЬНОЕ (противоречие!)
+
+ gender = serializers.ChoiceField(
+ required=True, # ❌ ОБЯЗАТЕЛЬНОЕ (противоречие!)
+ choices=((1, "Мужской"), (2, "Женский")),
+ error_messages={
+ 'required': 'Поле "Пол" обязательно для заполнения.',
+ }
+ )
+```
+
+**⚠️ Проблема:** Сериализатор требует поля, которые в модели **необязательны**. Это создает искусственные требования для API регистрации, не соответствующие структуре БД.
+
+---
+
+### Обязательные поля в RegisterSerializer
+
+**Файл:** `backend/api/v1/employees/serializers.py` (строки 29-67)
+
+```python
+class RegisterSerializer(serializers.Serializer):
+ # ... другие поля ...
+
+ avatar = Base64ImageField(required=True) # ❌ ОБЯЗАТЕЛЬНОЕ
+
+ gender = serializers.ChoiceField(
+ required=True, # ❌ ОБЯЗАТЕЛЬНОЕ
+ choices=((1, "Мужской"), (2, "Женский")),
+ error_messages={
+ 'required': 'Поле "Пол" обязательно для заполнения.',
+ }
+ )
+```
+
+### Что отправляют тесты
+
+**Файл:** `tests/api/auth/test_auth_and_registration.py` (строка 44)
+
+```python
+def register_payload(**overrides):
+ data = {
+ "first_name": "Иван",
+ "last_name": "Иванов",
+ "phone_number": "+79990000001",
+ "email": "ivan@example.com",
+ "password": "Str0ngPass!",
+ "birth_date": "1990-01-01",
+ "telegram": "@ivan",
+ # ❌ НЕТ avatar
+ # ❌ НЕТ gender
+ }
+ return data
+```
+
+## 📝 Ошибки валидации
+
+Все 11 проваленных тестов получают одинаковую ошибку:
+
+```python
+[REGISTER] Validation errors: {
+ 'avatar': [ErrorDetail(string='Ни одного файла не было отправлено.', code='required')],
+ 'gender': [ErrorDetail(string='Поле "Пол" обязательно для заполнения.', code='required')]
+}
+```
+
+**HTTP статус:** `400 Bad Request` (ожидается `201 Created`)
+
+## 📋 Детальный список проваленных тестов
+
+### 1. ❌ test_register_success_sends_email_and_user_inactive
+**Цель:** Проверка успешной регистрации с отправкой email
+**Проблема:** Не передает `avatar` и `gender`
+
+### 2. ❌ test_register_duplicate_email_phone
+**Цель:** Проверка дубликатов email/phone
+**Проблема:** Не может создать первого пользователя (нет `avatar`, `gender`)
+
+### 3. ❌ test_register_accepts_optional_fields
+**Цель:** Проверка опциональных полей (patronymic, whatsapp)
+**Проблема:** Передает `gender=1`, но **не передает `avatar`**
+
+```python
+# Частичное исправление в тесте:
+payload = {
+ ...
+ "patronymic": "Петрович",
+ "gender": 1, # ✅ Есть
+ "whatsapp": "+79995550011",
+ # ❌ Нет avatar
+}
+```
+
+### 4-11. ❌ Тесты верификации email и логина
+Все зависят от `register()` функции, которая не передает обязательные поля:
+- `test_verify_email_success_activates_user`
+- `test_verify_email_wrong_code`
+- `test_resend_email_sends_new_code_and_old_becomes_invalid`
+- `test_verify_expired_more_than_5_minutes_deletes_account`
+- `test_login_by_email_denied_before_verify`
+- `test_login_by_email_allowed_after_verify`
+- `test_login_by_phone_allowed_after_verify`
+- `test_login_wrong_password`
+
+## ✅ Успешные тесты (8 штук)
+
+Эти тесты проверяют **валидацию отсутствующих полей**, поэтому они ожидают `400 Bad Request`:
+
+1. ✅ `test_register_missing_required_field[first_name]`
+2. ✅ `test_register_missing_required_field[last_name]`
+3. ✅ `test_register_missing_required_field[phone_number]`
+4. ✅ `test_register_missing_required_field[email]`
+5. ✅ `test_register_missing_required_field[password]`
+6. ✅ `test_register_missing_required_field[birth_date]`
+7. ✅ `test_register_requires_at_least_one_contact`
+8. ✅ `test_register_birth_date_must_be_valid_date`
+
+**Почему они проходят?** Они ожидают ошибку валидации и получают её (хоть и по другим причинам).
+
+## 🔍 Анализ причин
+
+### Вопрос 1: Почему `avatar` и `gender` обязательные?
+
+Вероятно, это бизнес-требование:
+- **Avatar:** Для корпоративной системы важно иметь фото сотрудника
+- **Gender:** Для HR-целей и документооборота
+
+### Вопрос 2: Должны ли они быть обязательными при регистрации?
+
+**Два подхода:**
+
+#### Вариант A: Сделать опциональными для регистрации
+```python
+avatar = Base64ImageField(required=False, allow_null=True)
+gender = serializers.ChoiceField(
+ required=False,
+ allow_null=True,
+ choices=((1, "Мужской"), (2, "Женской")),
+)
+```
+
+**Плюсы:** Упрощает регистрацию
+**Минусы:** Может нарушить бизнес-логику
+
+#### Вариант B: Исправить тесты (добавить поля)
+```python
+def register_payload(**overrides):
+ data = {
+ "first_name": "Иван",
+ "last_name": "Иванов",
+ "phone_number": "+79990000001",
+ "email": "ivan@example.com",
+ "password": "Str0ngPass!",
+ "birth_date": "1990-01-01",
+ "telegram": "@ivan",
+ "avatar": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==", # 1x1 прозрачный PNG
+ "gender": 1, # Мужской
+ }
+ return data
+```
+
+**Плюсы:** Соответствует текущей бизнес-логике
+**Минусы:** Нужно обновить все тесты
+
+## 🎯 Рекомендации
+
+### ✅ РЕКОМЕНДУЕМОЕ РЕШЕНИЕ: Исправить сериализатор
+
+**Причина:** Модель Employee позволяет `avatar` и `gender` быть необязательными. Сериализатор должен соответствовать модели.
+
+**Изменение в `backend/api/v1/employees/serializers.py`:**
+
+```python
+class RegisterSerializer(serializers.Serializer):
+ # ... другие поля ...
+
+ # Было:
+ # avatar = Base64ImageField(required=True)
+ # gender = serializers.ChoiceField(required=True, ...)
+
+ # Стало:
+ avatar = Base64ImageField(required=False, allow_null=True) # ✅ Соответствует модели
+
+ gender = serializers.ChoiceField(
+ required=False, # ✅ Соответствует модели
+ choices=((1, "Мужской"), (2, "Женский"), (0, "Не указано")),
+ default=0, # ✅ Соответствует модели
+ error_messages={
+ 'invalid_choice': 'Выберите корректное значение пола.',
+ }
+ )
+```
+
+**Преимущества:**
+- ✅ Соответствие архитектуре (модель → сериализатор → API)
+- ✅ Упрощение регистрации для пользователей
+- ✅ Тесты пройдут без изменений
+- ✅ Возможность заполнить профиль позже
+
+---
+
+### Альтернатива: Обновить тесты (НЕ рекомендуется)
+
+**Обновить `register_payload()` в тестах:**
+
+```python
+def register_payload(**overrides):
+ # Минимальное base64 изображение (1x1 пиксель)
+ MINIMAL_AVATAR = (
+ "data:image/png;base64,"
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
+ )
+
+ data = {
+ "first_name": "Иван",
+ "last_name": "Иванов",
+ "phone_number": "+79990000001",
+ "email": "ivan@example.com",
+ "password": "Str0ngPass!",
+ "birth_date": "1990-01-01",
+ "telegram": "@ivan",
+ "avatar": MINIMAL_AVATAR, # ✅ Добавлено
+ "gender": 1, # ✅ Добавлено (1 = Мужской)
+ }
+ data.update(overrides)
+ return data
+```
+
+**Недостатки этого подхода:**
+- ❌ Не решает архитектурную проблему (несоответствие модели и сериализатора)
+- ❌ Усложняет регистрацию в production (требует обязательную загрузку аватара)
+- ❌ Требует изменений в тестах вместо исправления кода
+
+---
+
+### Долгосрочное решение (архитектурное)
+
+**Вариант 1:** Разделить регистрацию на этапы
+1. **Этап 1:** Email, пароль, базовая информация
+2. **Этап 2:** После верификации email → заполнение профиля (avatar, gender)
+
+**Вариант 2:** Сделать поля условно обязательными
+- При регистрации: опциональные
+- При активации аккаунта: требуется заполнить
+- При первом входе: напоминание заполнить
+
+## 📊 Влияние на production
+
+### ⚠️ Текущая ситуация
+
+Если `avatar` и `gender` обязательны:
+- Фронтенд **должен** отправлять эти поля при регистрации
+- Если фронтенд не отправляет → регистрация не работает
+- Пользователи не могут зарегистрироваться
+
+### ✅ Вопросы для проверки
+
+1. **Работает ли регистрация на production?**
+ - Если да → значит фронтенд корректно отправляет поля
+ - Если нет → критическая ошибка
+
+2. **Как фронтенд отправляет avatar?**
+ - Base64 строка
+ - File upload
+ - URL изображения
+
+3. **Обязателен ли gender в бизнес-логике?**
+ - Требование HR
+ - Требование законодательства
+ - Опционально
+
+## 🚀 Заключение
+
+**Основная проблема:** Рассинхронизация между тестами и сериализатором
+
+- **Сериализатор** требует `avatar` и `gender`
+- **Тесты** не передают эти поля
+
+**Решение:** Нужно привести в соответствие:
+- ЛИБО сделать поля опциональными в сериализаторе
+- ЛИБО добавить поля в тестовые данные
+
+**Рекомендация:** Обновить тесты (добавить `avatar` и `gender`), так как это, вероятно, соответствует реальному поведению фронтенда.
diff --git a/docs/reports/PASSWORD_RECOVERY_ANALYSIS.md b/docs/reports/PASSWORD_RECOVERY_ANALYSIS.md
new file mode 100644
index 00000000..18bf9bfb
--- /dev/null
+++ b/docs/reports/PASSWORD_RECOVERY_ANALYSIS.md
@@ -0,0 +1,374 @@
+# 🔍 Анализ: Восстановление пароля не отправляет email
+
+**Дата:** 5 января 2026
+**Статус:** ⚠️ Email не отправляется при восстановлении пароля
+**Тип:** Логика фильтрации пользователей в Django PasswordResetForm
+
+## 📋 Проблема
+
+Пользователи жалуются, что при нажатии кнопки "Забыли пароль?" не приходит письмо на почту.
+
+### Логи системы:
+
+```
+[INFO] 08:52:00 HTTP GET /auth/password-reset/ 200 ← Открыта форма
+[INFO] 08:52:24 HTTP POST /auth/password-reset/ 302 ← Форма отправлена
+[INFO] 08:52:24 HTTP GET /auth/password-reset/done/ 200 ← Показана страница "готово"
+```
+
+**Проблема:** Нет записей об отправке email! Ожидалось что-то вроде:
+```
+[INFO] Email отправлен: password_reset → user@example.com
+```
+
+## 🔍 Исследование
+
+### Код восстановления пароля
+
+**Используется:** Django встроенный `PasswordResetView` и `PasswordResetForm`
+
+[employees/views_auth.py](backend/employees/views_auth.py#L217-L222):
+```python
+class PasswordResetView(DjangoPasswordResetView):
+ template_name = "auth/password_reset.html"
+ email_template_name = "auth/password_reset_email.txt"
+ subject_template_name = "auth/password_reset_subject.txt"
+ success_url = "/auth/password-reset/done/"
+```
+
+### Django PasswordResetForm.get_users()
+
+Проверил исходный код Django `PasswordResetForm`:
+
+```python
+def get_users(self, email):
+ """Given an email, return matching user(s) who should receive a reset.
+
+ This allows subclasses to more easily customize the default policies
+ that prevent inactive users and users with unusable passwords from
+ resetting their password.
+ """
+ email_field_name = UserModel.get_email_field_name()
+ active_users = UserModel._default_manager.filter(
+ **{
+ "%s__iexact" % email_field_name: email,
+ "is_active": True, # ← 🚨 ФИЛЬТР НА АКТИВНЫХ!
+ }
+ )
+ return (
+ u
+ for u in active_users
+ if u.has_usable_password()
+ and _unicode_ci_compare(email, getattr(u, email_field_name))
+ )
+```
+
+**Ключевая строка:** `"is_active": True`
+
+### Логика регистрации в EUSRR
+
+[api/v1/employees/views.py](backend/api/v1/employees/views.py#L473-L477):
+```python
+# Режим без LDAP: создаём пользователя напрямую в БД
+emp = Employee.objects.create(
+ first_name=v["first_name"],
+ last_name=v["last_name"],
+ email=email,
+ phone_number=phone_norm,
+ is_active=False, # ← не активен до верификации email
+ is_ldap_managed=False,
+)
+```
+
+Пользователь становится активным только после верификации email:
+
+[api/v1/employees/views.py](backend/api/v1/employees/views.py#L1778-L1788):
+```python
+# Верификация email
+emp = Employee.objects.filter(email__iexact=email).first()
+# ... проверка кода ...
+
+# Активируем пользователя
+emp.is_active = True # ← Активируем
+emp.email_verified = True
+emp.save(update_fields=["is_active", "email_verified", "email_activation_code"])
+```
+
+## 🎯 КОРНЕВАЯ ПРИЧИНА
+
+### Сценарий 1: Незаверифицированный пользователь
+
+1. **Пользователь регистрируется** → `is_active=False`, `email_verified=False`
+2. **НЕ верифицирует email** (не ввел код из письма)
+3. **Пытается восстановить пароль**
+4. Django `PasswordResetForm.get_users()` фильтрует: `is_active=True`
+5. **Пользователь не найден** → email НЕ отправляется
+6. Но форма показывает "done" (чтобы не раскрывать существование аккаунта)
+
+### Сценарий 2: Верифицированный пользователь
+
+1. **Пользователь регистрируется** → `is_active=False`, `email_verified=False`
+2. **Верифицирует email** → `is_active=True`, `email_verified=True`
+3. **Пытается восстановить пароль**
+4. Django `PasswordResetForm.get_users()` находит пользователя
+5. **Email отправляется** ✅
+
+## 📊 Диагностика
+
+### Проверка текущего статуса пользователей:
+
+```bash
+.venv/Scripts/python manage.py shell
+```
+
+```python
+from employees.models import Employee
+
+# Незаверифицированные пользователи
+unverified = Employee.objects.filter(
+ is_active=False,
+ email_verified=False
+).count()
+print(f"Незаверифицированных: {unverified}")
+
+# Заверифицированные пользователи
+verified = Employee.objects.filter(
+ is_active=True,
+ email_verified=True
+).count()
+print(f"Заверифицированных: {verified}")
+
+# Проблемные: email verified, но is_active=False (не должны существовать)
+problem = Employee.objects.filter(
+ is_active=False,
+ email_verified=True
+).count()
+print(f"Проблемных: {problem}")
+```
+
+### Тест восстановления пароля:
+
+```bash
+.venv/Scripts/python manage.py shell
+```
+
+```python
+from django.contrib.auth.forms import PasswordResetForm
+from employees.models import Employee
+
+# Создать тестового неактивного пользователя
+test_user = Employee.objects.create(
+ email="test@example.com",
+ first_name="Test",
+ last_name="User",
+ phone_number="+79990001234",
+ is_active=False, # НЕ активен
+ telegram="@test"
+)
+test_user.set_password("password123")
+test_user.save()
+
+# Попытка сброса пароля
+form = PasswordResetForm(data={"email": "test@example.com"})
+if form.is_valid():
+ users = list(form.get_users(form.cleaned_data["email"]))
+ print(f"Найдено пользователей: {len(users)}") # ← Должно быть 0!
+else:
+ print("Форма невалидна")
+
+# Очистка
+test_user.delete()
+```
+
+**Ожидаемый результат:** `Найдено пользователей: 0` для неактивного пользователя.
+
+## ✅ РЕШЕНИЯ
+
+### Вариант 1: Переопределить get_users() (рекомендуется)
+
+Создать кастомную форму, которая позволяет сбрасывать пароль незаверифицированным пользователям:
+
+**Создать:** `backend/employees/forms_auth.py` (дополнение)
+
+```python
+from django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm
+from django.contrib.auth import get_user_model
+
+class CustomPasswordResetForm(DjangoPasswordResetForm):
+ """Форма сброса пароля, разрешающая сброс для неактивных пользователей"""
+
+ def get_users(self, email):
+ """Находит пользователей по email, включая неактивных.
+
+ Отличие от Django: не фильтрует по is_active=True,
+ чтобы пользователи могли восстановить пароль до верификации email.
+ """
+ UserModel = get_user_model()
+ email_field_name = UserModel.get_email_field_name()
+
+ # Убираем фильтр is_active=True
+ users = UserModel._default_manager.filter(
+ **{f"{email_field_name}__iexact": email}
+ )
+
+ return (
+ u for u in users
+ if u.has_usable_password()
+ and u.email.lower() == email.lower()
+ )
+```
+
+**Обновить:** `backend/employees/views_auth.py`
+
+```python
+from .forms_auth import CustomPasswordResetForm
+
+class PasswordResetView(DjangoPasswordResetView):
+ template_name = "auth/password_reset.html"
+ email_template_name = "auth/password_reset_email.txt"
+ subject_template_name = "auth/password_reset_subject.txt"
+ success_url = "/auth/password-reset/done/"
+ form_class = CustomPasswordResetForm # ← Использовать кастомную форму
+```
+
+**Преимущества:**
+- ✅ Пользователи могут восстановить пароль до верификации email
+- ✅ Стандартное поведение Django, просто без фильтра `is_active`
+- ✅ Безопасно (форма все равно не раскрывает существование аккаунта)
+
+**Недостатки:**
+- ⚠️ Пользователи без верификации email могут получить доступ
+
+### Вариант 2: Активировать при регистрации
+
+Изменить логику: `is_active=True` сразу, но блокировать вход через middleware до верификации.
+
+**Изменить:** `backend/api/v1/employees/views.py`
+
+```python
+emp = Employee.objects.create(
+ # ...
+ is_active=True, # ← Активен сразу
+ email_verified=False, # ← Но email не подтвержден
+ is_ldap_managed=False,
+)
+```
+
+**Создать middleware:** `backend/eusrr_backend/middleware.py` (дополнение)
+
+```python
+class EmailVerificationRequiredMiddleware:
+ """Блокирует доступ пользователям без верификации email"""
+
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ if request.user.is_authenticated:
+ if not request.user.email_verified:
+ # Разрешить только страницы верификации
+ allowed = [
+ '/auth/verify-email/',
+ '/auth/resend-email/',
+ '/auth/logout/',
+ ]
+ if not any(request.path.startswith(p) for p in allowed):
+ return redirect('auth_front:verify_email')
+
+ return self.get_response(request)
+```
+
+**Преимущества:**
+- ✅ Восстановление пароля работает для всех
+- ✅ Безопасность через middleware
+
+**Недостатки:**
+- ⚠️ Более сложная логика
+- ⚠️ Требует тщательного тестирования
+
+### Вариант 3: Разрешить вход без верификации (НЕ рекомендуется)
+
+Просто убрать требование верификации email вообще.
+
+**Недостатки:**
+- ❌ Небезопасно (фейковые email)
+- ❌ Не соответствует логике системы
+
+## 🧪 Тестирование после фикса
+
+### 1. Тест незаверифицированного пользователя:
+
+```python
+# 1. Создать пользователя (is_active=False)
+# 2. Попытаться восстановить пароль
+# 3. Проверить что email отправлен
+# 4. Перейти по ссылке из письма
+# 5. Установить новый пароль
+# 6. Войти с новым паролем
+```
+
+### 2. Тест заверифицированного пользователя:
+
+```python
+# 1. Создать пользователя и верифицировать email
+# 2. Попытаться восстановить пароль
+# 3. Проверить что email отправлен
+# 4. Установить новый пароль
+# 5. Войти
+```
+
+## 📈 Дополнительные проверки
+
+### Настройки EMAIL
+
+Проверить [settings.py](backend/eusrr_backend/settings.py#L233-L246):
+
+```python
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+EMAIL_HOST = 'smtp.yandex.ru'
+EMAIL_PORT = 465
+EMAIL_HOST_USER = 'robotail-info@yandex.ru'
+EMAIL_HOST_PASSWORD = '***' # Проверить что пароль валиден
+EMAIL_USE_SSL = True
+```
+
+**Тест отправки email:**
+
+```bash
+.venv/Scripts/python manage.py shell
+```
+
+```python
+from django.core.mail import send_mail
+
+send_mail(
+ 'Test Subject',
+ 'Test Message',
+ 'robotail-info@yandex.ru',
+ ['recipient@example.com'],
+ fail_silently=False,
+)
+# Если ошибка → проверить настройки SMTP
+```
+
+### Шаблоны писем
+
+Проверить существование:
+- ✅ `backend/templates/auth/password_reset_email.txt`
+- ✅ `backend/templates/auth/password_reset_subject.txt`
+
+## 🎯 Рекомендация
+
+**Использовать Вариант 1** - переопределить `get_users()`:
+
+1. Минимальные изменения кода
+2. Решает проблему напрямую
+3. Безопасно (не раскрывает существование аккаунтов)
+4. Пользователи смогут восстановить пароль в любом статусе
+
+**Дополнительно:** Добавить в письмо восстановления пароля ссылку на верификацию email, если он не подтвержден.
+
+---
+
+**Приоритет:** 🔴 ВЫСОКИЙ - пользователи не могут восстановить доступ
+**Трудозатраты:** ⚡ 10-15 минут на реализацию Варианта 1
diff --git a/docs/reports/REGISTRATION_ERROR_SYNOLOGY_ANALYSIS.md b/docs/reports/REGISTRATION_ERROR_SYNOLOGY_ANALYSIS.md
new file mode 100644
index 00000000..5c558649
--- /dev/null
+++ b/docs/reports/REGISTRATION_ERROR_SYNOLOGY_ANALYSIS.md
@@ -0,0 +1,206 @@
+# 🔴 КРИТИЧЕСКАЯ ОШИБКА: Регистрация падает с IntegrityError
+
+**Дата:** 5 января 2026
+**Статус:** 🚨 БЛОКИРУЕТ регистрацию пользователей
+**Тип:** Синхронизация БД и модели - несоответствие
+
+## 📋 Проблема
+
+```
+IntegrityError at /api/v1/auth/register/
+NOT NULL constraint failed: employees_employee.synology_username
+```
+
+### Что происходит?
+
+1. **Пользователь отправляет данные на** `/api/v1/auth/register/`
+2. **Django пытается создать Employee** с помощью `Employee.objects.create()`
+3. **SQLite попадает на колонки в БД**, которых НЕТ в модели Django
+4. **Результат:** пустые значения для этих колонок
+5. **БД отвергает:** поле `synology_username` помечено как `NOT NULL` → ошибка
+
+## 🔍 Диагностика
+
+### В БД (SQLite) существуют колонки:
+
+```sql
+PRAGMA table_info(employees_employee);
+
+-- Результат grep:
+(25, 'synology_user_id', 'INTEGER', 0, None, 0)
+(26, 'synology_username', 'varchar(100)', 1, None, 0) -- ❌ NOT NULL!
+(27, 'synology_session_expires', 'datetime', 0, None, 0)
+(28, 'synology_session_id', 'varchar(255)', 1, None, 0) -- ❌ NOT NULL!
+```
+
+**Пояснение колонок (слева направо):**
+- `0` = порядковый номер
+- `'synology_username'` = название колонки
+- `'varchar(100)'` = тип данных
+- `1` = NOT NULL constraint (1 = есть constraint, 0 = нет)
+- `None` = default значение
+- `0` = PRIMARY KEY flag
+
+### В модели (employees/models.py):
+
+```python
+class Employee(AbstractUser):
+ # ... другие поля ...
+
+ # ❌ SYNOLOGY ПОЛЕЙ НЕТ!
+ # synology_user_id - не определено
+ # synology_username - не определено
+ # synology_session_expires - не определено
+ # synology_session_id - не определено
+```
+
+## 🚨 КОРНЕВАЯ ПРИЧИНА
+
+### Сценарий что произошло:
+
+1. **На ранней стадии разработки** были добавлены Synology поля в БД (миграция была создана)
+2. **Позже эти поля были удалены из модели** Employee (вероятно, закомментированы или удалены)
+3. **НО соответствующая миграция удаления никогда не была создана** (`RemoveField`)
+4. **Результат:** Мисматч между:
+ - ✅ Тем, что Django ожидает создать (модель)
+ - ⚠️ Тем, что реально в БД (таблица с Synology полями)
+
+### Почему ошибка происходит именно при регистрации?
+
+Когда Django создаёт объект:
+```python
+emp = Employee.objects.create(
+ first_name=v["first_name"],
+ last_name=v["last_name"],
+ email=email,
+ phone_number=phone_norm,
+ is_active=False,
+ is_ldap_managed=False,
+ # ❌ synology_username НЕ УКАЗАН (его нет в модели)
+ # ❌ synology_session_id НЕ УКАЗАН (его нет в модели)
+)
+```
+
+SQLite вставляет `NULL` для недостающих полей, но:
+- `synology_username` имеет `NOT NULL` constraint → **ОШИБКА**
+- `synology_session_id` имеет `NOT NULL` constraint → **ОШИБКА**
+
+## ✅ РЕШЕНИЕ
+
+### Вариант 1: Удалить поля из БД (рекомендуется)
+
+Создать миграцию удаления:
+
+```bash
+.venv/Scripts/python manage.py makemigrations employees --empty --name remove_synology_fields
+```
+
+Затем заполнить миграцию:
+
+```python
+from django.db import migrations
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('employees', 'PREV_MIGRATION'), # последняя миграция
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='employee',
+ name='synology_user_id',
+ ),
+ migrations.RemoveField(
+ model_name='employee',
+ name='synology_username',
+ ),
+ migrations.RemoveField(
+ model_name='employee',
+ name='synology_session_expires',
+ ),
+ migrations.RemoveField(
+ model_name='employee',
+ name='synology_session_id',
+ ),
+ ]
+```
+
+Затем применить:
+
+```bash
+.venv/Scripts/python manage.py migrate
+```
+
+### Вариант 2: Вернуть поля в модель
+
+Если Synology интеграция ещё нужна, вернуть поля в модель:
+
+```python
+class Employee(AbstractUser):
+ # ... другие поля ...
+
+ synology_user_id = models.IntegerField(null=True, blank=True)
+ synology_username = models.CharField(
+ max_length=100,
+ blank=True,
+ null=True, # ← Чтобы Django мог вставлять NULL
+ verbose_name="Synology Username"
+ )
+ synology_session_expires = models.DateTimeField(null=True, blank=True)
+ synology_session_id = models.CharField(
+ max_length=255,
+ blank=True,
+ null=True, # ← Чтобы Django мог вставлять NULL
+ verbose_name="Synology Session ID"
+ )
+```
+
+Затем создать миграцию:
+
+```bash
+.venv/Scripts/python manage.py makemigrations
+```
+
+**Внимание:** Миграция обнаружит уже существующие поля в БД, может показать `AlterField` вместо `AddField`.
+
+### Вариант 3: Очистить и пересоздать БД (только для разработки)
+
+```bash
+rm backend/db.sqlite3
+.venv/Scripts/python manage.py migrate
+```
+
+## 📊 Рекомендация
+
+**Используйте Вариант 1** - удаление полей:
+
+1. Synology поля очевидно не используются (они отсутствуют в коде)
+2. Это чистит БД и приводит её в соответствие с моделью
+3. Регистрация сразу же начнёт работать
+4. Если Synology интеграция понадобится позже, её легко добавить заново
+
+## 🔗 Связанные проблемы
+
+Эта ошибка влияет на:
+- ❌ **Регистрация пользователей** (новые пользователи не могут зарегистрироваться)
+- ❌ **API endpoint** `/api/v1/auth/register/`
+- ❌ **Фронт-end регистрация** (через views_auth.py)
+- ✅ Всё остальное (логин, профиль) работает нормально
+
+## 🧪 Тестирование после фикса
+
+После удаления полей:
+
+1. Попробовать регистрацию через API
+2. Проверить, что пользователь создан в БД
+3. Выполнить `python manage.py check` для валидации схемы
+4. Запустить тесты регистрации
+
+```bash
+pytest tests/api/auth/test_auth_and_registration.py::test_register_success_sends_email_and_user_inactive -v
+```
+
+---
+
+**Приоритет:** 🔴 КРИТИЧЕСКИЙ - блокирует основной функционал
+**Трудозатраты:** ⚡ 5-10 минут на реализацию + 2-3 минуты на тестирование
diff --git a/docs/reports/TESTS_REFACTORING_REPORT.md b/docs/reports/TESTS_REFACTORING_REPORT.md
index d045cbb2..ea770cde 100644
--- a/docs/reports/TESTS_REFACTORING_REPORT.md
+++ b/docs/reports/TESTS_REFACTORING_REPORT.md
@@ -1,6 +1,6 @@
-# 🎉 Отчет о рефакторинге тестов
+зне# 🎉 Отчет о рефакторинге тестов
-**Дата:** 5 января 2026
+**Дата:** 5 января 2026
**Статус:** ЗАВЕРШЕНО ✅
## 📊 Результаты рефакторинга
@@ -125,5 +125,5 @@ pytest -m integration
- ✅ Manual tests правильно изолированы
- ✅ Конфигурация оптимизирована
-**Наше исправление (коммит `de0caea`):** БЕЗОПАСНО ✅
+**Наше исправление (коммит `de0caea`):** БЕЗОПАСНО ✅
**Готовность к production:** ДА 🚀