docs: добавление документации по рефакторингу

- Добавлен отчет о рефакторинге хранилища медиафайлов
- Добавлено сравнение веток feature/django-filer-documents и develop
- Добавлен аудит frontend документов
- Добавлен отчет об очистке types, cabinets и dashboard
This commit is contained in:
lizerk 2026-03-06 12:33:18 +03:00
parent e689ee7e5a
commit a662642864
4 changed files with 2046 additions and 0 deletions

View File

@ -0,0 +1,245 @@
# Рефакторинг структуры хранения медиа-файлов
**Дата**: 5 марта 2026
**Статус**: ✅ Завершено
## 🎯 Цели
Переход от хеш-структуры django-filer к читаемой структуре с организацией по датам.
## 📁 Было (deprecated)
```
media/filer_public/
├── 07/28/07286222-01c2-415c-ad2f-fc0ee3eb13f0/ ← Нечитаемая хеш-структура
├── 2a/cd/2acdaf8f-b428-42d6-aaf6-b9b63353e836/
└── ...
```
**Проблемы старой структуры:**
- ❌ Невозможно найти файл вручную
- ❌ Сложно организовать архивацию
- ❌ Нет ясности по периодам хранения
- ❌ Усложняет бэкапы по датам
## 📁 Стало (new)
```
media/
├── documents/
│ ├── public/2026/03/05/ ← Публичные документы по дате
│ ├── private/2026/03/05/ ← Приватные документы по дате
│ ├── public_thumbnails/ ← Превью публичных документов
│ └── private_thumbnails/ ← Превью приватных документов
├── avatars/ ← Аватарки пользователей
├── chat_attachments/2026/03/05/ ← Вложения мессенджера
└── temp/ ← Временные файлы
```
**Преимущества новой структуры:**
- ✅ Читаемые пути: `documents/public/2026/03/05/contract.pdf`
- ✅ Простая архивация: перемещение старых папок (например, `2024/`)
- ✅ Удобные бэкапы: легко выбрать период
- ✅ Естественная организация: по дате загрузки
## 🔧 Изменения в коде
### 1. Django settings.py
**Было:**
```python
FILER_STORAGES = {
'public': {
'UPLOAD_TO': 'filer.utils.generate_filename.randomized',
'UPLOAD_TO_PREFIX': '%Y/%m', # Не работало правильно
}
}
```
**Стало:**
```python
FILER_STORAGES = {
'public': {
'main': {
'ENGINE': 'filer.storage.PublicFileSystemStorage',
'OPTIONS': {
'location': os.path.join(MEDIA_ROOT, 'documents/public/'),
'base_url': '/media/documents/public/',
},
'UPLOAD_TO': 'filer.utils.generate_filename.by_date', # ← Ключевое изменение
},
'thumbnails': {
'ENGINE': 'filer.storage.PublicFileSystemStorage',
'OPTIONS': {
'location': os.path.join(MEDIA_ROOT, 'documents/public_thumbnails/'),
'base_url': '/media/documents/public_thumbnails/',
},
},
},
'private': {
'main': {
'ENGINE': 'filer.storage.PrivateFileSystemStorage',
'OPTIONS': {
'location': os.path.join(MEDIA_ROOT, 'documents/private/'),
'base_url': '/smedia/documents/private/',
},
'UPLOAD_TO': 'filer.utils.generate_filename.by_date', # ← Ключевое изменение
},
'thumbnails': {
'ENGINE': 'filer.storage.PrivateFileSystemStorage',
'OPTIONS': {
'location': os.path.join(MEDIA_ROOT, 'documents/private_thumbnails/'),
'base_url': '/smedia/documents/private_thumbnails/',
},
},
},
}
```
**Ключевые изменения:**
- `randomized` → `by_date` - функция генерации путей
- Организация: `YYYY/MM/DD/filename.ext`
- Раздельные пути для public/private
### 2. URL конфигурация (urls.py)
**Было:**
```python
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```
**Стало:**
```python
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```
**Причина:** В production статика раздается через Nginx, не Django.
### 3. .gitignore
**Добавлено:**
```gitignore
backend/media/*
!backend/media/.gitkeep
!backend/media/*/
!backend/media/*/.gitkeep
!backend/media/*/*/
!backend/media/*/*/.gitkeep
```
**Результат:** Git отслеживает структуру папок (через .gitkeep), но игнорирует загруженные файлы.
## 🧪 Тестирование
### Тест создания файла
```python
from django.core.files.base import ContentFile
from filer.models import File
test_content = ContentFile(b"Test document content", name="test_document.txt")
test_file = File.objects.create(
original_filename="test_document.txt",
file=test_content
)
print(test_file.file.path)
# Результат: /backend/media/documents/private/2026/03/05/test_document.txt
print(test_file.file.url)
# Результат: /smedia/documents/private/2026/03/05/test_document.txt
```
### Результаты
✅ **Путь корректный**: `documents/private/2026/03/05/test_document.txt`
✅ **Дата в пути**: `2026/03/05` (YYYY/MM/DD)
✅ **URL правильный**: `/smedia/documents/private/2026/03/05/test_document.txt`
✅ **Django check**: 0 ошибок
## 📋 Management команда
Создана команда для миграции старых файлов:
```bash
python manage.py migrate_documents_storage --dry-run # Предпросмотр
python manage.py migrate_documents_storage # Выполнить миграцию
```
**Статус миграции:**
- Проверено: 34 записи в БД
- Физически файлов: 0 (старые файлы уже удалены)
- Миграция не требуется
**Файл:** `backend/documents/management/commands/migrate_documents_storage.py`
## 📚 Документация
Создана документация со структурой и инструкциями:
- **Файл:** `backend/media/README.md`
- **Разделы:**
- Структура директорий
- Настройка FILER_STORAGES
- Инструкции по миграции
- Безопасность (public vs private)
- Конфигурация Nginx для production
## 🧹 Очистка
Удалены устаревшие структуры:
```bash
rm -rf backend/media/filer_public/ # Старая хеш-структура
rm -rf backend/media/documents/private/%Y/ # Тестовые папки
```
**До очистки:**
- 47 директорий
- 10 тестовых файлов в filer_public/
**После очистки:**
- 14 директорий (чистая структура)
- .gitkeep файлы во всех пустых папках
## 🎯 Итоги
### ✅ Выполнено
1. ✅ Настроена структура `documents/public/` и `documents/private/`
2. ✅ Изменен генератор путей на `by_date` (YYYY/MM/DD)
3. ✅ Обновлена конфигурация Django (settings.py)
4. ✅ Исправлен URL routing для production
5. ✅ Настроен .gitignore для media/
6. ✅ Создана management команда миграции
7. ✅ Написана документация (README.md)
8. ✅ Протестировано создание файлов
9. ✅ Удалены старые структуры
10. ✅ Созданы .gitkeep файлы
### 📊 Метрики
- **Файлов изменено:** 4 (`settings.py`, `urls.py`, `.gitignore`, `README.md`)
- **Файлов создано:** 2 (management команда + отчет)
- **Директорий очищено:** 33 (из 47 осталось 14)
- **Ошибок Django check:** 0
- **TypeScript ошибок:** 0 (не затронуто)
### 🚀 Готовность к production
- ✅ Nginx конфигурация описана в README.md
- ✅ URL routing работает корректно
- ✅ Безопасность (public/private разделение)
- ✅ Структура масштабируемая
- ✅ Бэкапы легко организовать по датам
## 📌 Следующие шаги (опционально)
1. **Настройка Nginx** - для раздачи static/media в production
2. **Автоочистка** - удаление старых файлов (например, старше 1 года)
3. **S3 интеграция** - для облачного хранения (если требуется масштабирование)
4. **Мониторинг** - отслеживание размера media/ директории
## 🔗 Связанные документы
- [backend/media/README.md](../../backend/media/README.md) - Руководство по структуре
- `backend/documents/management/commands/migrate_documents_storage.py` - Команда миграции
- `backend/eusrr_backend/settings.py` - Конфигурация FILER_STORAGES

View File

@ -0,0 +1,862 @@
# Сравнение реализаций документов: develop vs feature/django-filer-documents
## Краткое резюме
**TL;DR:** Feature ветка в **3 раза сложнее** develop по всем метрикам:
- 8 моделей против 2
- 2613 строк тестов против 888
- FSM workflow с 7 состояниями против простого флага
- django-filer + django-fsm + сложные django-rules против базового DRF
**Вопрос:** Нужна ли эта сложность для use case "публикация регламентов + отслеживание ознакомлений"?
---
## Сравнительная таблица
| Аспект | **develop** (базовая) | **feature** (сложная) |
|--------|----------------------|----------------------|
| **Модели** | 2 | 8 |
| **Строк кода моделей** | ~100 | ~600+ |
| **FSM workflow** | ❌ НЕТ | ✅ 7 состояний |
| **django-filer** | ❌ НЕТ | ✅ Полная интеграция |
| **django-rules** | ⚠️ Шаблон (не работает) | ✅ 6+ predicates |
| **Файлов тестов** | 1 | 5 |
| **Тестовых методов** | 37 | 86 |
| **Строк тестов** | 888 | 2613 |
| **Complexity ratio** | **1x** | **3x** |
---
## 1. Модели данных
### develop: 2 простые модели
```python
# backend/documents/models.py (develop)
class Document(models.Model):
"""Документ для рассылки сотрудникам"""
title = models.CharField(max_length=255)
file = models.FileField(upload_to='documents/%Y/%m/%d/')
description = models.TextField(blank=True)
uploaded_by = models.ForeignKey(User, ...)
uploaded_at = models.DateTimeField(auto_now_add=True)
# Targeting
sent_to_all = models.BooleanField(default=True)
departments = models.ManyToManyField('employees.Department', ...)
recipients = models.ManyToManyField(User, ...)
class DocumentAcknowledgement(models.Model):
"""Факт ознакомления с документом"""
document = models.ForeignKey(Document, ...)
user = models.ForeignKey(User, ...)
acknowledged_at = models.DateTimeField(auto_now_add=True)
```
**Философия:** Простота. Загрузил → разослал → отследил ознакомление.
---
### feature: 8 сложных моделей
```python
# backend/documents/models.py (feature)
1. Document (FSM-модель)
- FilerFileField (django-filer интеграция)
- FSMField: status (7 состояний)
- 6 FSM переходов с декораторами
- Cabinet (папки), DocumentType, tags
- related_documents (M2M к самой себе)
- cabinet_path property + методы navigation
2. DocumentAcknowledgement
- document, user, acknowledged_at
3. DocumentType
- name, description, icon
- is_active, requires_approval, max_file_size, allowed_extensions
- Templates support (title_template, description_template)
4. DocumentMetadata
- document, created_by
- metadata_fields (JSONField)
- Customizable per-document metadata
5. DocumentTag
- name, color, description
- Tagging system
6. Cabinet
- name, description, parent (self-referential)
- icon, color, can_manage_documents predicate
- Tree structure (папки в папках)
7. DocumentAuditLog
- document, user, action, timestamp
- old_values, new_values (JSONField)
- Audit trail для всех изменений
8. DocumentComment
- document, user, comment, created_at
- parent (nested comments)
- is_internal flag
```
**Философия:** Полнофункциональная система документооборота с workflow.
---
## 2. FSM Workflow
### develop: Нет workflow
Документ либо создан, либо нет. Всё.
```python
# Document создан → сразу доступен получателям
document = Document.objects.create(
title="Регламент",
file=file,
sent_to_all=True
)
# Готово! Пользователи могут read + acknowledge
```
---
### feature: 7 состояний FSM
```python
# backend/documents/models.py (feature)
class DocumentStatus(models.TextChoices):
DRAFT = "draft", "Черновик"
IN_REVIEW = "in_review", "На проверке"
APPROVED = "approved", "Одобрен"
PUBLISHED = "published", "Опубликован"
REJECTED = "rejected", "Отклонён"
ARCHIVED = "archived", "В архиве"
UNARCHIVED = "unarchived", "Восстановлен из архива"
# 6 FSM transitions:
@fsm_log_by
@transition(field=status, source='draft', target='in_review',
permission=lambda instance, user: user.has_perm(...))
def submit_for_review(self, by=None):
"""Черновик → На проверке"""
...
@transition(field=status, source='in_review', target='approved', ...)
def approve(self, by=None):
"""На проверке → Одобрен"""
...
@transition(field=status, source='in_review', target='rejected', ...)
def reject(self, by=None):
"""На проверке → Отклонён"""
...
@transition(field=status, source=['approved', 'rejected'], target='published', ...)
def publish(self, by=None):
"""Одобрен/Отклонён → Опубликован"""
...
@transition(field=status, source=['published', 'approved'], target='archived', ...)
def archive(self, by=None):
"""Опубликован/Одобрен → В архиве"""
...
@transition(field=status, source='archived', target='draft', ...)
def unarchive(self, by=None):
"""Архив → Черновик"""
...
```
**Проблема:** Для use case "публикация регламентов + ознакомление" это избыточно.
**Вопросы:**
- Зачем состояние IN_REVIEW для регламента?
- Кто должен approve регламенты?
- Почему rejected документ можно publish?
- Зачем unarchive возвращает в draft, а не в published?
---
## 3. django-rules Permissions
### develop: Шаблонный код (НЕ РАБОТАЕТ)
```python
# backend/documents/rules.py (develop) - TEMPLATE ONLY!
@rules.predicate
def is_document_owner(user, document):
"""ПРОБЛЕМА: document.created_by не существует в модели!"""
return document.created_by == user or document.owner == user
@rules.predicate
def is_document_author(user, document):
"""ПРОБЛЕМА: document.author не существует!"""
return document.author == user
# Правила определены, но НЕ используются в коде
rules.add_rule('documents.view_document', ...)
rules.add_rule('documents.approve_document', ...)
# ... но нет rules.add_perm(), поэтому Django не видит эти permissions
```
**Вердикт:** Это просто пример/шаблон. Реальные permissions в `api/v1/documents/permissions.py`:
```python
# backend/api/v1/documents/permissions.py (develop)
class DocumentReadOrModelPerms(AdminOrActionOrModelPerms):
"""Простая логика:
- READ/acknowledge: любой authenticated
- Фильтрация в queryset: sent_to_all или recipients или departments
- WRITE: staff или model permissions
"""
def has_permission(self, request, view):
if request.method in SAFE_METHODS or action in ('acknowledge', 'download'):
return request.user.is_authenticated
return request.user.is_staff or super().has_permission(...)
```
---
### feature: Сложная система django-rules
```python
# backend/documents/rules.py (feature) - РЕАЛЬНО РАБОТАЕТ
# 6+ predicates с object-level permissions:
@rules.predicate
def is_document_uploader(user, document):
"""Автор документа"""
return document.uploaded_by == user
@rules.predicate
def is_not_document_uploader(user, document):
"""НЕ автор (separation of duties)"""
return document.uploaded_by != user
@rules.predicate
def can_manage_documents(user):
"""Позиционные права через employee.can_manage_documents"""
return user.employee.can_manage_documents
@rules.predicate
def can_view_document_in_status(user, document):
"""Видимость зависит от статуса FSM"""
if document.status == 'published':
return True
if document.status == 'draft':
return document.uploaded_by == user
# ... etc
# Permissions для каждого FSM action:
rules.add_perm('documents.submit_for_review_document',
is_superuser | is_document_uploader | can_manage_documents)
rules.add_perm('documents.approve_document',
is_superuser | (can_manage_documents & is_not_document_uploader))
rules.add_perm('documents.publish_document',
is_superuser | (can_manage_documents & is_not_document_uploader))
rules.add_perm('documents.archive_document', ...)
rules.add_perm('documents.unarchive_document', ...)
```
**Два уровня проверки:**
1. **DRF permission class:**
```python
# backend/api/v1/documents/permissions.py (feature)
class DocumentFSMPermission(DjangoModelPermissions):
def has_object_permission(self, request, view, obj):
action = view.action
if action == "submit_for_review":
return user.has_perm("documents.submit_for_review_document", obj)
elif action == "approve":
return user.has_perm("documents.approve_document", obj)
# ... для каждого FSM action
```
2. **FSM transition decorators:**
```python
@transition(..., permission=lambda instance, user:
user.has_perm('documents.approve_document', instance))
def approve(self, by=None):
...
```
**Проблема:** Двойная проверка (DRF + FSM) = больше мест для ошибок.
---
## 4. Тестовое покрытие
### develop: 1 файл, 37 тестов, 888 строк
```
backend/tests/api/v1/documents/test_documents_api.py (888 lines)
Структура тестов:
- TestAuthAndPermissions (4 теста)
- anonymous → 401
- regular user access policy
- users with individual perms
- staff/superuser full access
- TestCreate (5 тестов)
- sent_to_all=true
- sent_to_all=false with recipients (JSON/CSV/list)
- validations
- skip nonexistent/inactive recipients
- TestRead (2 теста)
- list with pagination
- detail fields + file_url
- TestUpdate (4 теста)
- update title/description
- replace file multipart
- toggle sent_to_all
- patch recipient_ids
- TestDelete (1 тест)
- delete + cascade acknowledgements
- TestAcknowledge (3 теста)
- first acknowledge + repeat
- detail reflects is_acknowledged
- forbidden if not recipient
- TestSerialization (1 тест)
- uploaded_by + recipients shape
- TestErrorsAndEdgeCases (4 теста)
- wrong content type
- put/patch/delete without perms
- get without view_perm
- big file over limit
- TestPerformance (2 теста)
- no N+1 in list
- pagination navigation
- TestSecurityAndPolicy (2+ теста)
- JWT/session auth
- policy without perms
Итого: ~37 тестов
```
**Покрытие:** CRUD + permissions + acknowledgement + edge cases
---
### feature: 5 файлов, 86 тестов, 2613 строк
```
backend/tests/api/v1/documents/ (4 файла):
1. test_documents_api.py (~1100 lines)
- Базовый CRUD (как в develop)
- + Cabinet management
- + DocumentType endpoints
- + Tags, metadata
2. test_fsm_workflow.py (~700 lines, 30+ тестов)
- TestFSMTransitions
- submit_for_review
- approve/reject
- publish
- archive/unarchive
- TestFSMPermissions
- who can transition
- FSM + django-rules integration
- TestAuthorCanSubmitOwnDocument (новые 3 теста)
- TestSeparationOfDuties (WIP, 2 теста не запускались)
3. test_status_visibility.py (~400 lines)
- Видимость документов зависит от статуса
- draft → только автор
- in_review → автор + managers
- published → все получатели
- archived → никто (или только managers)
4. test_new_features.py (~400 lines)
- Related documents
- Comments (nested)
- Audit log
- Metadata fields
backend/tests/integration/test_related_documents_notifications.py (~13 lines)
- Integration test для уведомлений
Итого: 86 тестов, 2613 строк
```
**Покрытие:** Всё из develop + FSM + Cabinet + Tags + Comments + Audit + Metadata + Related docs
---
## 5. django-filer Integration
### develop: Plain FileField
```python
file = models.FileField(upload_to='documents/%Y/%m/%d/')
```
- Простое хранение в `/media/documents/YYYY/MM/DD/`
- Нет папок, нет организации
---
### feature: Full django-filer
```python
from filer.fields.file import FilerFileField
class Document(models.Model):
file = FilerFileField(
verbose_name=_('Файл'),
related_name='documents',
on_delete=models.PROTECT,
null=True,
blank=True
)
cabinet = models.ForeignKey(Cabinet, ...) # Folder structure
```
**Возможности:**
- Централизованное хранилище файлов
- Папочная структура (Cabinet → вложенные папки)
- Переиспользование файлов между документами
- Thumbnails для изображений
- Permissions на уровне файлов
**Проблема:** Для use case "регламенты" это избыточно.
---
## 6. API Endpoints
### develop: Простой REST
```
GET /api/v1/documents/ - список (scope=mine для фильтра)
POST /api/v1/documents/ - создать
GET /api/v1/documents/{id}/ - детали
PUT /api/v1/documents/{id}/ - обновить
PATCH /api/v1/documents/{id}/ - частичное обновление
DELETE /api/v1/documents/{id}/ - удалить
POST /api/v1/documents/{id}/acknowledge/ - ознакомиться
GET /api/v1/documents/{id}/acknowledgements/ - ведомость (staff only)
```
**8 endpoints**, стандартный REST.
---
### feature: Extended REST + FSM actions
```
# Base CRUD (как в develop)
GET /api/v1/documents/
POST /api/v1/documents/
GET /api/v1/documents/{id}/
PUT /api/v1/documents/{id}/
PATCH /api/v1/documents/{id}/
DELETE /api/v1/documents/{id}/
# FSM actions (новые)
POST /api/v1/documents/{id}/submit_for_review/
POST /api/v1/documents/{id}/approve/
POST /api/v1/documents/{id}/reject/
POST /api/v1/documents/{id}/publish/
POST /api/v1/documents/{id}/archive/
POST /api/v1/documents/{id}/unarchive/
# Acknowledgement
POST /api/v1/documents/{id}/acknowledge/
GET /api/v1/documents/{id}/acknowledgements/
# Cabinet management (новые)
GET /api/v1/cabinets/
POST /api/v1/cabinets/
GET /api/v1/cabinets/{id}/
PUT /api/v1/cabinets/{id}/
DELETE /api/v1/cabinets/{id}/
GET /api/v1/cabinets/{id}/documents/
# Tags (новые)
GET /api/v1/documents/tags/
POST /api/v1/documents/tags/
# Types (новые)
GET /api/v1/documents/types/
POST /api/v1/documents/types/
# Comments (новые)
GET /api/v1/documents/{id}/comments/
POST /api/v1/documents/{id}/comments/
DELETE /api/v1/documents/{id}/comments/{comment_id}/
# Audit log (новые)
GET /api/v1/documents/{id}/audit_log/
# Related documents (новые)
POST /api/v1/documents/{id}/add_related/
POST /api/v1/documents/{id}/remove_related/
```
**~25+ endpoints** (в 3 раза больше).
---
## 7. Use Case Analysis
### Исходное требование
> "Система для публикации регламентов с отслеживанием ознакомлений"
### Что нужно для этого use case?
**Минимум (MVP):**
1. ✅ Загрузить документ (файл + название + описание)
2. ✅ Выбрать получателей (всем / отделам / конкретным)
3. ✅ Опубликовать (сделать доступным)
4. ✅ Пользователи видят документы
5. ✅ Пользователи нажимают "Ознакомлен"
6. ✅ Ведомость ознакомлений (кто прочитал, кто нет)
**Есть в develop:** ✅ ВСЁ!
### Что добавлено в feature, но НЕ нужно для use case?
| Фича | Нужна? | Почему нет |
|------|--------|------------|
| FSM workflow (7 states) | ❌ | Регламенты не требуют approval workflow |
| draft → in_review → approved | ❌ | Кто будет approving? Зачем? |
| django-filer | ❌ | Файлы и так хранятся, папки не критичны |
| Cabinet (folders) | 🤔 | Nice to have, но не критично |
| DocumentType | ❌ | Overengineering, достаточно тегов |
| DocumentMetadata (JSONField) | ❌ | Custom fields не требуются |
| DocumentTag | 🤔 | Может быть полезно, но не критично |
| DocumentComment | ❌ | Комментарии к регламентам? Зачем? |
| DocumentAuditLog | 🤔 | Audit trail полезен, но не для MVP |
| Related documents | ❌ | Усложняет UI, не критично |
| Separation of duties | ❌ | Если нет approval, то не нужно |
**Вердикт:** 80% feature branch не нужно для use case "регламенты".
---
## 8. Complexity Metrics
### Code Complexity
```bash
# develop branch
backend/documents/models.py: ~100 lines (2 модели)
backend/documents/rules.py: ~290 lines (шаблон, не используется)
backend/api/v1/documents/views.py: ~213 lines (1 ViewSet)
backend/api/v1/documents/serializers.py: ~393 lines (2 serializers + 1 Field)
backend/api/v1/documents/permissions.py: ~50 lines (1 permission class)
TOTAL: ~1046 lines
# feature branch
backend/documents/models.py: ~600+ lines (8 моделей + FSM)
backend/documents/rules.py: ~200+ lines (6+ predicates, работающие)
backend/api/v1/documents/views.py: ~500+ lines (3+ ViewSets)
backend/api/v1/documents/serializers.py: ~800+ lines (10+ serializers)
backend/api/v1/documents/permissions.py: ~150+ lines (2+ permission classes)
+ admin.py, signals.py, etc.
TOTAL: ~2500+ lines (2.5x сложнее)
```
### Test Complexity
```bash
# develop: 1 file, 37 tests, 888 lines
# feature: 5 files, 86 tests, 2613 lines (3x сложнее)
```
### Maintenance Cost
| Аспект | develop | feature | Разница |
|--------|---------|---------|---------|
| Новый разработчик onboarding | 1 день | 3-5 дней | **3-5x** |
| Время на bug fix | 30 мин | 1-2 часа | **2-4x** |
| Regression risk | Низкий | Средний-Высокий | FSM + много связей |
| Documentation need | Минимально | Критично | Без доки не разобраться |
---
## 9. Проблемы feature branch
### 1. Неясный Use Case
- FSM workflow для чего? Кто approves регламенты?
- Зачем draft, если регламент разрабатывается в Word?
- Separation of duties - для каких процессов?
### 2. Over-engineering
- 8 моделей вместо 2
- 3x больше кода
- 3x больше тестов
- 3x сложнее поддержка
### 3. Неполная реализация
- Separation of duties в WIP состоянии
- 2 теста не запускались: `test_author_cannot_approve_own_document`, `test_author_cannot_publish_own_document`
- FSM permissions не до конца продуманы (почему reject → publish?)
### 4. UX вопросы
- Пользователь создаёт документ → статус draft
- Что дальше? Кнопка "Submit for review"?
- Кто получит уведомление о review?
- Где UI для approve/reject?
- Как пользователь понимает, что документ в архиве?
---
## 10. Рекомендации
### Вариант A: Использовать develop (простой)
**Преимущества:**
- ✅ Работает сейчас
- ✅ Покрывает 100% use case "регламенты + ознакомление"
- ✅ Простой, понятный код
- ✅ 37 тестов уже есть
- ✅ Быстрый onboarding
**Недостатки:**
- ❌ Нет папок (Cabinet)
- ❌ Нет тегов
- ❌ Нет audit trail
**Recommendation:** Добавить к develop минимум фич из feature:
1. Cabinet (папки) - полезно для организации
2. Tags - полезно для категоризации
3. Audit log - полезно для compliance
**Estimated effort:** 1-2 дня (vs 2-3 недели доработки feature)
---
### Вариант B: Упростить feature branch
**Что оставить:**
- ✅ Cabinet (папки) - полезно
- ✅ DocumentTag - полезно
- ✅ DocumentAcknowledgement - критично
- 🤔 DocumentType - можно оставить, но упростить
**Что убрать:**
- ❌ FSM workflow (все 7 состояний)
- ❌ Separation of duties
- ❌ DocumentComment
- ❌ DocumentMetadata (JSONField)
- ❌ Related documents
- ❌ DocumentAuditLog (можно добавить позже)
**Заменить FSM на:**
```python
class Document(models.Model):
is_published = models.BooleanField(default=True)
# Если нужен draft: is_published=False
```
**Estimated effort:** 3-5 дней удаления кода + рефакторинг тестов
---
### Вариант C: Оставить feature, но закончить separation of duties
**Преимущества:**
- ✅ Полнофункциональная система
- ✅ FSM workflow готов
- ✅ Cabinet, Tags, Comments
**Недостатки:**
- ❌ Нужно дописать separation of duties (1-2 дня)
- ❌ Нужно продумать UX для FSM (кнопки, уведомления)
- ❌ Нужна документация (1 день)
- ❌ Нужно обучать пользователей (сложный workflow)
**Estimated effort:** 1 неделя до production-ready
---
## 11. Финальный вопрос
**Перед тем как выбрать вариант, ответьте на вопросы:**
1. **Кто будет approving регламенты?**
- Если никто → FSM не нужен
- Если юристы/руководители → FSM нужен
2. **Где создаются регламенты?**
- Если в Word/Google Docs → draft в системе не нужен
- Если в системе → draft может быть полезен
3. **Нужны ли комментарии к регламентам?**
- Обычно нет (discussion в другом месте)
4. **Нужны ли связанные документы (related)?**
- Для регламентов обычно нет
5. **Нужен ли audit trail каждого изменения?**
- Для compliance - да
- Для внутренних регламентов - может быть избыточно
6. **Есть ли у пользователей файловые шары?**
- Если да → зачем дублировать хранение?
- Может лучше ссылки на файловые шары?
---
## 12. Сравнение по чек-листу "Публикация регламентов"
| Задача | develop | feature | Нужно? |
|--------|---------|---------|--------|
| Загрузить файл | ✅ FileField | ✅ FilerFile | ✅ |
| Навание/описание | ✅ | ✅ | ✅ |
| Организация (папки) | ❌ | ✅ Cabinet | 🤔 Nice to have |
| Выбрать получателей | ✅ sent_to_all/departments/recipients | ✅ Same | ✅ |
| Опубликовать | ✅ Сразу после create | ⚠️ draft → ... → publish | ✅ Simpler! |
| Видят получатели | ✅ Сразу | ⚠️ Только после publish | ✅ Simpler! |
| Кнопка "Ознакомлен" | ✅ | ✅ | ✅ |
| Ведомость ознакомлений | ✅ | ✅ | ✅ |
| Версионирование | ❌ | ❌ (но есть audit) | 🤔 Позже |
| Уведомления | ❌ (но легко добавить) | ❌ (тоже не готово) | ✅ Нужно! |
**Вывод:** develop проще и покрывает базовый use case. feature добавляет сложность без явной пользы.
---
## 13. Conclusion
### Текущая ситуация
- develop работает, проста, покрывает 100% базового use case
- feature в 3 раза сложнее, но не даёт критичных преимуществ для use case "регламенты"
- feature в WIP состоянии (separation of duties не закончена)
### Вопрос к пользователю
> "Зачем нам FSM workflow draft → in_review → approved → published для публикации регламентов?"
Если ответ "не нужен" → **Вариант A: develop + минимум из feature**
Если ответ "нужен approval process" → **Вариант C: доделать feature**
### Рекомендация агента: **Вариант A**
**Почему:**
1. Use case "регламенты" не требует сложного workflow
2. develop работает и тестируется
3. Можно добавить Cabinet + Tags за 1-2 дня
4. Быстрее time-to-market
5. Проще поддержка
6. Проще обучение пользователей
**Что добавить к develop:**
```python
# backend/documents/models.py (develop + improvements)
class Cabinet(models.Model):
"""Папка для организации документов"""
name = models.CharField(max_length=255)
parent = models.ForeignKey('self', null=True, blank=True)
class DocumentTag(models.Model):
"""Тег для категоризации"""
name = models.CharField(max_length=100, unique=True)
color = models.CharField(max_length=7, default='#007bff')
class Document(models.Model):
# ... existing fields ...
cabinet = models.ForeignKey(Cabinet, null=True, blank=True)
tags = models.ManyToManyField(DocumentTag, blank=True)
```
**Estimated effort:** 1-2 дня → production ready
vs
**feature branch completion:** 1 неделя → production ready (но сложнее)
---
## Приложение: Метрики по файлам
### develop branch
```
backend/documents/
├── models.py (100 lines, 2 models)
├── rules.py (290 lines, template/example)
├── admin.py
├── views.py
└── ...
backend/api/v1/documents/
├── views.py (213 lines, 1 ViewSet)
├── serializers.py (393 lines, 2 serializers)
├── permissions.py (50 lines, 1 class)
└── urls.py
backend/tests/api/v1/documents/
└── test_documents_api.py (888 lines, 37 tests)
TOTAL FILES: ~8
TOTAL LINES: ~1900
TOTAL TESTS: 37
COMPLEXITY: Low
```
### feature branch
```
backend/documents/
├── models.py (600+ lines, 8 models)
├── rules.py (200+ lines, 6+ predicates)
├── admin.py (enlarged)
├── signals.py (new)
├── tasks.py (new)
└── ...
backend/api/v1/documents/
├── views.py (500+ lines, 3+ ViewSets)
├── serializers.py (800+ lines, 10+ serializers)
├── permissions.py (150+ lines, 2+ classes)
└── urls.py (enlarged)
backend/tests/api/v1/documents/
├── test_documents_api.py (~1100 lines)
├── test_fsm_workflow.py (~700 lines)
├── test_status_visibility.py (~400 lines)
└── test_new_features.py (~400 lines)
backend/tests/integration/
└── test_related_documents_notifications.py
TOTAL FILES: ~15+
TOTAL LINES: ~4200+
TOTAL TESTS: 86
COMPLEXITY: High
```
---
**Дата создания:** 2025-01-XX
**Автор:** AI Agent после анализа обеих веток
**Статус:** Ожидание решения пользователя

View File

@ -0,0 +1,563 @@
# 🔍 Полный Аудит Фронтенд-Модуля Документов
**Дата:** 5 марта 2026 г.
**Версия:** EUSRR v2.0
**Статус:** 🔴 КРИТИЧЕСКИЕ ПРОБЛЕМЫ ОБНАРУЖЕНЫ
---
## 📊 Метрики Проекта
### Размер Кодовой Базы
- **Главный файл:** `page.tsx` - **1,202 строки** ⚠️ (норма: 200-400)
- **Компонентов:** 19 главных + 30+ вложенных
- **State переменных:** 20+ useState в одном компоненте ⚠️
- **Модальных окон:** 6+ в одном файле
### Архитектурные Проблемы
#### 🔴 КРИТИЧНЫЕ
1. **God Component Anti-Pattern**
- `page.tsx` содержит **1,202 строки кода**
- 20+ локальных state переменных
- Миксует логику документов, папок, поиска, фильтров, модалов
- **Проблема:** невозможно поддерживать, высокий риск багов
- **Рекомендация:** разбить на 5-7 умных компонентов
2. **Prop Drilling Hell**
```tsx
// Передача пропсов через 3-4 уровня вложенности
<Parent onUpdate={reload}>
<Child onUpdate={reload}>
<GrandChild onUpdate={reload} />
</Child>
</Parent>
```
- **Решение:** Context API или Zustand для глобального состояния
3. **Модальные Окна - Плохая Архитектура**
- ❌ Использовался устаревший Tailwind UI v1 подход (inline-block hack)
- ✅ **ИСПРАВЛЕНО:** переделано на Flexbox + fixed positioning
- **Но:** все модалы в одном файле = плохая изоляция
- **Проблема toast:** `console.log` вместо настоящей библиотеки
4. **Дублирование Кода**
- Логика CRUD повторяется для тегов, типов, папок
- Нет переиспользуемых хуков (`useCRUD`, `useModal`)
- Каждый модал переписывает одну и ту же логику
#### 🟡 СРЕДНЯЯ ВАЖНОСТЬ
5. **Отсутствие State Management**
- Локальный state разбросан по 20+ useState
- Каждый дочерний компонент запрашивает данные заново
- **Решение:** React Query или Zustand
6. **Неоптимальная Загрузка Данных**
```tsx
useEffect(() => {
loadDocuments();
loadFolders();
loadTags();
loadTypes();
}, []); // 4 параллельных запроса при монтировании
```
- Нет кеширования
- Нет debounce для поиска
- Рефетч при каждом изменении фильтров
7. **Accessibility (a11y) Проблемы**
- Модалы не фокусируются автоматически
- Нет Escape для закрытия
- Нет trap focus внутри модала
- Нет aria-labels для кнопок без текста
8. **TypeScript Type Safety**
```tsx
// Слабые типы
const [error, setError] = useState<string | null>(null);
// Лучше создать Error type
type AppError = {
code: string;
message: string;
field?: string;
}
```
#### 🟢 НИЗКАЯ ПРИОРИТЕТ
9. **Performance**
- Нет мемоизации `filteredDocuments` (пересчет каждый рендер)
- Нет виртуализации для длинных списков
- Большие компоненты не ленивозагружаются
10. **UX/UI Inconsistencies**
- Разные стили модалов (размеры, отступы)
- Нет единой системы Toast уведомлений
- Загрузка без skeleton screens
---
## 🎯 Детальный Анализ По Модулям
### 1. `/app/documents/page.tsx` (1,202 строки)
**Проблемы:**
- Содержит логику для:
- Управления документами
- Управления папками
- Фильтрации и поиска
- Тегов и типов
- Дашборда
- 6+ модальных окон
**Должно быть:**
```
/app/documents/
├── page.tsx (100-150 строк) - роутинг + layout
├── components/
│ ├── DocumentsView.tsx - список документов
│ ├── DocumentsFilters.tsx - поиск + фильтры
│ ├── DocumentsDashboard.tsx - статистика
│ └── DocumentsModals.tsx - все модалы
└── hooks/
├── useDocuments.ts
├── useFolders.ts
└── useDocumentFilters.ts
```
### 2. Модальные Окна
**Текущие модалы:**
1. ✅ `TagManagementModal` - управление тегами
2. ✅ `DocumentTypeManagementModal` - управление типами
3. `DocumentDetailModal` - детали документа
4. `DocumentUploadForm` - загрузка
5. `CreateFolder` - создание папки (inline, не модал)
6. `AcknowledgementsReport` - отчет по ознакомлениям
**Проблемы:**
- Все рендерятся в одном компоненте
- Нет единого Modal Manager
- Нет transition анимаций
- Нет Portal для z-index изоляции
**Рекомендация:**
```tsx
// Использовать библиотеку:
import { Modal } from '@radix-ui/react-dialog';
// или
import { Modal } from '@headlessui/react';
// Или создать свой ModalContext:
<ModalProvider>
<App />
</ModalProvider>
```
### 3. Формы (Tags & Types)
**✅ Что хорошо:**
- Валидация данных (уникальность, длина)
- Показ ошибок inline
- Loading states
- Disabled при загрузке
**❌ Что плохо:**
- Нет react-hook-form (ручная валидация)
- Нет zod для схем валидации
- Toast через console.log вместо react-hot-toast
**Рекомендация:**
```tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const tagSchema = z.object({
name: z.string()
.min(2, 'Минимум 2 символа')
.max(50, 'Максимум 50 символов'),
color: z.string().regex(/^#[0-9A-F]{6}$/i),
});
function TagForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(tagSchema)
});
}
```
### 4. API Клиент
**Проблемы:**
- Все запросы через голый fetch
- Нет retry логики
- Нет кеширования
- Нет оптимистичных обновлений
**Рекомендация:**
```tsx
// Использовать TanStack Query (React Query)
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function useDocuments() {
return useQuery({
queryKey: ['documents'],
queryFn: () => apiClient.getDocuments(),
staleTime: 5 * 60 * 1000, // 5 минут кеш
});
}
function useCreateTag() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: apiClient.createTag,
onSuccess: () => {
queryClient.invalidateQueries(['tags']);
toast.success('Тег создан!');
}
});
}
```
---
## 📋 Priority Roadmap
### 🔴 P0: Критичные (1-2 недели)
**Неделя 1:**
1. ✅ **Исправить модалы** (DONE TODAY)
- Флекс-центрирование вместо inline-block
- Правильный z-index layering
2. **Добавить Toast уведомления**
```bash
npm install react-hot-toast
```
- Заменить все console.log на toast
- Единый UX для успеха/ошибок
3. **Разбить page.tsx на части**
- Извлечь логику в хуки (useDocuments, useFolders)
- Создать отдельные компоненты для view/filters/modals
**Неделя 2:**
4. **Внедрить React Query**
```bash
npm install @tanstack/react-query
```
- Кеширование запросов
- Автоматический refetch
- Оптимистичные обновления
5. **Accessibility fixes**
- Focus trap в модалах
- Escape to close
- ARIA labels
### 🟡 P1: Важные (2-3 недели)
6. **Form Management**
```bash
npm install react-hook-form zod @hookform/resolvers
```
- Переписать все формы на RHF
- Схемы валидации через Zod
7. **State Management**
- Zustand для глобального стейта
- Вынести filters/search в store
8. **Performance оптимизации**
- useMemo для filteredDocuments
- React.memo для списков
- Виртуализация (@tanstack/react-virtual)
### 🟢 P2: Nice-to-have (месяц+)
9. **Testing**
- Unit тесты для хуков
- Integration тесты для форм
- E2E для CRUD операций
10. **Documentation**
- Storybook для компонентов
- JSDoc комментарии
- Архитектурные диаграммы
---
## 🏗️ Предложенная Архитектура
### Новая Структура
```
frontend/src/app/documents/
├── page.tsx # ~150 строк, главный роутинг
├── layout.tsx # Обертка с провайдерами
│
├── components/ # UI компоненты
│ ├── DocumentsList/
│ │ ├── DocumentsList.tsx # Список документов
│ │ ├── DocumentCard.tsx # Карточка документа
│ │ └── DocumentsEmpty.tsx # Пустое состояние
│ │
│ ├── DocumentsFilters/
│ │ ├── FiltersBar.tsx # Панель фильтров
│ │ ├── SearchInput.tsx # Поиск
│ │ ├── TagsFilter.tsx # Фильтр по тегам
│ │ └── TypesFilter.tsx # Фильтр по типам
│ │
│ ├── DocumentsModals/
│ │ ├── UploadModal.tsx
│ │ ├── DetailModal.tsx
│ │ ├── FolderModal.tsx
│ │ ├── TagManagementModal.tsx # ✅ Уже создан
│ │ └── TypeManagementModal.tsx # ✅ Уже создан
│ │
│ └── DocumentsDashboard/
│ ├── DashboardStats.tsx
│ └── DashboardCharts.tsx
│
├── hooks/ # Бизнес-логика
│ ├── useDocuments.ts # React Query для документов
│ ├── useFolders.ts # Логика папок
│ ├── useTags.ts # CRUD тегов
│ ├── useTypes.ts # CRUD типов
│ ├── useDocumentFilters.ts # Логика фильтрации
│ └── useDocumentSelection.ts # Bulk операции
│
├── store/ # Zustand stores
│ ├── documentsStore.ts # Глобальный стейт
│ └── filtersStore.ts # Состояние фильтров
│
└── utils/ # Утилиты
├── documentHelpers.ts
└── validators.ts
```
### Пример Рефакторинга
**Было (1,202 строки):**
```tsx
export default function DocumentsPage() {
const [documents, setDocuments] = useState<Document[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const loadDocuments = async () => {
setLoading(true);
try {
const response = await apiClient.getDocuments();
setDocuments(response.results || []);
} catch (err) {
setError('Failed');
} finally {
setLoading(false);
}
};
useEffect(() => { loadDocuments(); }, []);
// ... еще 1150 строк
}
```
**Стало (~100 строк):**
```tsx
export default function DocumentsPage() {
return (
<DocumentsProvider>
<DocumentsLayout>
<DocumentsFilters />
<DocumentsList />
<DocumentsModals />
</DocumentsLayout>
</DocumentsProvider>
);
}
// hooks/useDocuments.ts (40 строк)
export function useDocuments() {
return useQuery({
queryKey: ['documents'],
queryFn: apiClient.getDocuments,
});
}
// components/DocumentsList.tsx (80 строк)
export function DocumentsList() {
const { data, isLoading, error } = useDocuments();
const filters = useDocumentFilters();
const filtered = useMemo(() =>
applyFilters(data, filters),
[data, filters]
);
if (isLoading) return <Skeleton />;
if (error) return <Error />;
return <List documents={filtered} />;
}
```
---
## ✅ Что Уже Сделано Хорошо
1. ✅ TypeScript используется везде
2. ✅ Компонентная структура (не монолит)
3. ✅ Разделение на папки (tags, types, folders)
4. ✅ API клиент изолирован
5. ✅ Валидация форм работает
6. ✅ Responsive дизайн (Tailwind)
7. ✅ Loading states показываются
8. ✅ Модалы исправлены (только что)
---
## 🎯 Рекомендуемые Библиотеки
### Must Have
```bash
npm install @tanstack/react-query # Кеширование + data fetching
npm install react-hot-toast # Toast уведомления
npm install react-hook-form # Управление формами
npm install zod # Валидация схем
npm install @hookform/resolvers # RHF + Zod интеграция
```
### Highly Recommended
```bash
npm install zustand # State management (легче Redux)
npm install @radix-ui/react-dialog # Accessible модалы
npm install @tanstack/react-virtual # Виртуализация списков
npm install framer-motion # Анимации
```
### Nice to Have
```bash
npm install @dnd-kit/core # Drag & Drop (для Phase 3)
npm install recharts # Графики для дашборда
npm install date-fns # Работа с датами
```
---
## 📈 Ожидаемые Результаты
### После P0 (2 недели):
- ✅ Модалы работают корректно
- ✅ Toast уведомления вместо console.log
- ✅ Файл page.tsx < 300 строк
- ✅ Хуки изолированы и переиспользуемы
- ✅ Accessibility базовый уровень
### После P1 (месяц):
- ✅ React Query = моментальная загрузка (кеш)
- ✅ Формы с RHF = меньше багов
- ✅ Performance +50% (мемоизация)
- ✅ Zustand = предсказуемый стейт
### После P2 (2-3 месяца):
- ✅ 80%+ test coverage
- ✅ Storybook документация
- ✅ Виртуализация = 10,000+ документов без лагов
---
## 🚨 Срочные TODO (Сегодня/Завтра)
1. **✅ DONE:** Исправить модалы (flexbox вместо inline-block)
2. **TODO:** Установить react-hot-toast
```tsx
// app/layout.tsx
import { Toaster } from 'react-hot-toast';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Toaster position="top-right" />
</body>
</html>
);
}
```
3. **TODO:** Заменить console.log на toast в модалах
```tsx
// Было:
console.log('[SUCCESS] Тег создан');
// Стало:
import toast from 'react-hot-toast';
toast.success('Тег успешно создан!');
```
4. **TODO:** Добавить Escape key handler в модалы
```tsx
useEffect(() => {
if (!isOpen) return;
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose]);
```
---
## 💡 Ключевые Инсайты
### Главное Правило
> **"Компонент должен иметь одну ответственность"**
> Если файл > 300 строк → разбивай на части
### Best Practices
1. **Composition over Configuration**
- Маленькие composable компоненты лучше больших с пропсами
2. **Co-location**
- Держи styles, tests, types рядом с компонентом
3. **Server vs Client**
- Используй Server Components где возможно (Next.js App Router)
4. **TypeScript строго**
- `any` только в крайних случаях
- Создавай типы для всех API responses
---
## 📞 Контакты и Ресурсы
### Для вопросов:
- Архитектура: Лид Frontend-разработчик
- React Query: [TanStack Query Docs](https://tanstack.com/query/latest)
- Accessibility: [ARIA Practices](https://www.w3.org/WAI/ARIA/apg/)
### Полезные статьи:
- [React Component Patterns](https://bit.ly/react-patterns)
- [State Management Guide](https://bit.ly/state-mgmt)
- [Next.js Best Practices](https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts)
---
**Заключение:** Модуль документов требует серьезного рефакторинга, но фундамент хороший. Главная задача - декомпозировать God Component на умные части. Приоритет - P0 таски (2 недели).
**Статус исправлений:**
✅ Модалы работают (исправлено сегодня)
⏳ Toast notifications (TODO)
⏳ Рефакторинг page.tsx (TODO)
⏳ React Query integration (TODO)

View File

@ -0,0 +1,376 @@
# Отчет: Удаление типов документов, кабинетов и дашборда
**Дата:** 2024
**Статус:** ✅ Завершено
## Цель
Упрощение системы управления документами путем удаления избыточной функциональности:
- Типы документов (DocumentType)
- Кабинеты (Cabinet) - виртуальные коллекции
- Дашборд с аналитикой
Сохранить только основную функциональность: **документы, папки и теги**.
## Выполненные изменения
### 1. Backend - Django Models
**Удалено:**
- Поле `document_type` из модели `Document` ([backend/documents/models.py](backend/documents/models.py))
- ForeignKey связь с DocumentType
- Все связанные метаданные
**Результат:** Модель Document теперь использует только folder (папки) и tags (теги) для организации.
### 2. Backend - Serializers
**Файл:** [backend/api/v1/documents/serializers.py](backend/api/v1/documents/serializers.py)
**Удалено:**
- Класс `DocumentTypeSerializer` (~45 строк)
- Класс `CabinetSerializer` (~55 строк)
- Поле `document_type` из `DocumentReadSerializer`
- Поле `cabinet_ids` из `DocumentWriteSerializer`
- Метод `_set_cabinets()` (~40 строк)
- Обработка `document_type` и `cabinet_ids` в методах `create()` и `update()`
**Результат:** Сериализаторы работают только с папками и тегами.
### 3. Backend - ViewSets
**Файл:** [backend/api/v1/documents/views.py](backend/api/v1/documents/views.py)
**Удалено:**
- Импорты: `DocumentType`, `Cabinet`, `DocumentTypeSerializer`, `CabinetSerializer`
- Класс `DocumentTypeViewSet` (~50 строк)
- GET list, retrieve
- POST create
- PATCH/PUT update
- DELETE delete
- Метод `documents` (список документов по типу)
- Класс `CabinetViewSet` (~120 строк)
- CRUD операции
- Иерархическая структура
- Методы: `children`, `documents`, `add_document`, `remove_document`
**Результат:** API упрощено до работы только с документами, папками и тегами.
### 4. Backend - URL Routes
**Файл:** [backend/api/v1/urls.py](backend/api/v1/urls.py)
**Удалено:**
- Регистрация маршрута `r"document-types"` → `DocumentTypeViewSet`
- Регистрация маршрута `r"cabinets"` → `CabinetViewSet`
**Результат:** Больше нет API эндпоинтов:
- `/api/v1/document-types/`
- `/api/v1/cabinets/`
### 5. Frontend - TypeScript Interfaces
**Файл:** [frontend/src/types/api.ts](frontend/src/types/api.ts)
**Удалено:**
- `interface DocumentType` (13 полей)
- `interface CreateDocumentTypeData` (7 полей)
- `interface Cabinet` (9 полей)
- `interface CreateCabinetData` (3 поля)
- `interface CabinetHierarchy` (4 поля, рекурсивная)
- Поля `document_type` и `cabinets` из `interface Document`
**Результат:** Document interface содержит только: id, title, description, file, folder, tags, user, dates, acknowledgement.
### 6. Frontend - API Client
**Файл:** [frontend/src/lib/api.ts](frontend/src/lib/api.ts)
**Удалено методы:**
- `getDocumentTypes()`, `getDocumentType(id)`
- `createDocumentType(data)`, `updateDocumentType(id, data)`, `deleteDocumentType(id)`
- `getDocumentsByType(typeId)`
- `getCabinets()`, `getCabinet(id)`
- `createCabinet(data)`, `updateCabinet(id, data)`, `deleteCabinet(id)`
- `getCabinetDocuments(id)`, `getCabinetChildren(id)`, `getCabinetHierarchy(id)`
- `addDocumentToCabinet(cabinetId, documentId)`, `removeDocumentFromCabinet(cabinetId, documentId)`
**Удалено из методов:**
- `createDocument()`: параметры `document_type`, `cabinet_ids`
- `updateDocument()`: параметры `document_type`, `cabinet_ids`
**Результат:** ~150 строк кода удалено. API клиент работает только с документами, папками, тегами.
### 7. Frontend - Documents Page
**Файл:** [frontend/src/app/documents/page.tsx](frontend/src/app/documents/page.tsx)
**Удалена функциональность дашборда:**
- Импорт `LayoutDashboard` иконки
- Импорт компонента `DocumentsDashboard`
- Тип `ViewMode` ("documents" | "dashboard")
- Состояние `viewMode`
- Расчет `dashboardStats` (использовал useMemo)
- UI: кнопки переключения "Документы" / "Дашборд"
- UI: рендеринг компонента `<DocumentsDashboard />`
**Удалена функциональность типов и кабинетов:**
- Все импорты связанные с DocumentType и Cabinet
- Состояния: `selectedTypes`, `availableTypes`, `selectedCabinetId`, `availableCabinets`
- Функции загрузки типов и кабинетов
- Фильтры по типам и кабинетам в UI
- Модальные окна управления типами и кабинетами
**Результат:** Простой интерфейс с единственным представлением - списком документов. Фильтрация только по тегам и поиску.
### 8. Frontend - DocumentMetadataEditor
**Файл:** [frontend/src/components/documents/DocumentMetadataEditor.tsx](frontend/src/components/documents/DocumentMetadataEditor.tsx)
**Удалено:**
- Импорты: `DocumentType`, `Cabinet`, иконки `FileType`, `HardDrive`
- Состояния: `selectedDocumentType`, `selectedCabinets`, `documentTypes`, `cabinets`, `loadingTypes`, `loadingCabinets`
- Загрузка: вызовы `apiClient.getDocumentTypes()`, `apiClient.getCabinets()`
- useEffect для загрузки `document.cabinets`
- Функция `toggleCabinet()`
- UI секция "Тип документа" с dropdown селектором
- UI секция "Кабинеты" с multiselect чекбоксами
- Обработка `document_type` и `cabinet_ids` в `handleSave()`
**Результат:** Компонент редактирует только теги и папку. Размер файла: 385 → 241 строка (-144 строки, -37%).
### 9. Frontend - DocumentUploadForm
**Файл:** [frontend/src/components/documents/DocumentUploadForm.tsx](frontend/src/components/documents/DocumentUploadForm.tsx)
**Удалено:**
- Состояния: `selectedDocumentType`, `selectedCabinets`, `documentTypes`, `cabinets`, `loadingDocumentTypes`, `loadingCabinets`
- Загрузка типов и кабинетов в useEffect
- UI секция "Тип документа" с dropdown
- UI секция "Кабинеты" с multiselect
- Передача `document_type` и `cabinet_ids` в `createDocument()`
- Сброс этих полей при успешной загрузке
**Результат:** Форма загрузки работает только с тегами, папками и базовыми полями (название, описание, файл, получатели).
### 10. Frontend - Search Component
**Файл:** [frontend/src/components/documents/search/AdvancedSearch.tsx](frontend/src/components/documents/search/AdvancedSearch.tsx)
**Удалено:**
- Поле `documentTypes` из `interface SearchFilters`
- Значение по умолчанию `documentTypes: []` из `DEFAULT_FILTERS`
- Проверка `documentTypes` в подсчете активных фильтров
- UI секция "Тип документа" с multiselect
**Результат:** Расширенный поиск работает без фильтрации по типам. Остались: даты, статусы, теги, авторы, сортировка.
### 11. Frontend - Удаленные компоненты
**Удалены полностью (с директориями):**
**Папка `frontend/src/components/documents/cabinets/`:**
- `CabinetManagementModal.tsx` - модальное окно управления кабинетами
- `CabinetForm.tsx` - форма создания/редактирования кабинета
- `CabinetTree.tsx` - дерево иерархии кабинетов
- `CabinetIconPicker.tsx` - выбор иконки для кабинета
- `DocumentSelector.tsx` - выбор документов для кабинета
- `index.ts` - экспорты
**Папка `frontend/src/components/documents/types/`:**
- `DocumentTypeManagementModal.tsx` - модальное окно управления типами
- `DocumentTypeForm.tsx` - форма создания/редактирования типа
- `DocumentTypeIconPicker.tsx` - выбор иконки для типа
- `index.ts` - экспорты
**Результат:** ~10 файлов удалено, ~1500+ строк кода удалено.
## Статистика изменений
### Backend
- **Моделей изменено:** 1 (Document)
- **Сериализаторов удалено:** 2 (DocumentTypeSerializer, CabinetSerializer)
- **ViewSets удалено:** 2 (DocumentTypeViewSet, CabinetViewSet)
- **API эндпоинтов удалено:** 2 группы
- **Удалено строк кода:** ~300+
### Frontend
- **Интерфейсов удалено:** 5 (DocumentType, CreateDocumentTypeData, Cabinet, CreateCabinetData, CabinetHierarchy)
- **Методов API удалено:** 16
- **Компонентов удалено:** 10 (полностью)
- **Файлов изменено:** 6
- **Директорий удалено:** 2
- **Удалено строк кода:** ~2000+
### Общая статистика
- **Всего файлов изменено/удалено:** 20+
- **Всего строк кода удалено:** ~2500+
- **Сокращение сложности:** ~40% функциональности удалено
## Новая архитектура системы
### Организация документов
```
┌─────────────────────────────────────┐
│ ДОКУМЕНТЫ │
├─────────────────────────────────────┤
│ Организация: │
│ • Папки (Folders) - иерархия │
│ • Теги (Tags) - категоризация │
│ │
│ Метаданные: │
│ • Название, описание │
│ • Файл (с версионированием) │
│ • Получатели и отделы │
│ • Статус ознакомления │
│ • Автор и даты │
└─────────────────────────────────────┘
```
### API Endpoints (оставшиеся)
**Documents:**
- `GET /api/v1/documents/` - список документов
- `POST /api/v1/documents/` - создание документа
- `GET /api/v1/documents/{id}/` - детали документа
- `PATCH /api/v1/documents/{id}/` - обновление документа
- `DELETE /api/v1/documents/{id}/` - удаление документа
- `GET /api/v1/documents/{id}/versions/` - версии
- `POST /api/v1/documents/{id}/acknowledge/` - ознакомление
**Folders:**
- `GET /api/v1/folders/` - список папок
- `POST /api/v1/folders/` - создание папки
- `GET /api/v1/folders/{id}/` - детали папки
- `PATCH /api/v1/folders/{id}/` - обновление
- `DELETE /api/v1/folders/{id}/` - удаление
**Tags:**
- `GET /api/v1/document-tags/` - список тегов
- `POST /api/v1/document-tags/` - создание тега
- `PATCH /api/v1/document-tags/{id}/` - обновление
- `DELETE /api/v1/document-tags/{id}/` - удаление
## Преимущества изменений
### 1. Упрощение для пользователей
- ✅ Меньше выборов при загрузке документа
- ✅ Более простая навигация (нет переключения дашборд/список)
- ✅ Интуитивная организация: папки (структура) + теги (категории)
- ✅ Меньше отвлекающих элементов UI
### 2. Снижение технической сложности
- ✅ Меньше моделей данных для поддержки
- ✅ Проще тестирование (меньше комбинаций)
- ✅ Меньше API эндпоинтов
- ✅ Меньше кода для поддержки (~2500 строк удалено)
### 3. Производительность
- ✅ Меньше SQL запросов (нет JOIN с типами/кабинетами)
- ✅ Быстрее загрузка списка документов
- ✅ Меньше данных в API ответах
- ✅ Простые фильтры работают быстрее
### 4. Поддержка и развитие
- ✅ Легче добавлять новые функции
- ✅ Меньше мест для багов
- ✅ Проще онбординг новых разработчиков
- ✅ Меньше документации для поддержки
## Миграция данных
### ⚠️ Требуется Django миграция
После применения изменений в коде **необходимо** создать миграцию для удаления поля `document_type`:
```bash
cd backend
python manage.py makemigrations documents
python manage.py migrate
```
**Примечание:** Данные о типах документов будут потеряны. Если требуется сохранить историю, можно:
1. Создать снимок данных перед удалением
2. Добавить теги соответствующие старым типам
3. Применить миграцию
### Опциональное удаление таблиц
Если таблицы `DocumentType` и `Cabinet` больше не используются нигде в системе, их можно удалить вручную:
```sql
DROP TABLE IF EXISTS documents_document_cabinets;
DROP TABLE IF EXISTS documents_cabinet;
DROP TABLE IF EXISTS documents_documenttype;
```
## Тестирование
### Проверено:
- ✅ Компиляция TypeScript без ошибок
- ✅ Синтаксис Python валиден
- ✅ Нет импортов удаленных компонентов
- ✅ API методы используют только существующие поля
### Требует ручного тестирования:
- [ ] Загрузка документа с тегами и папкой
- [ ] Редактирование метаданных документа (теги, папка)
- [ ] Поиск документов по тегам
- [ ] Навигация по папкам
- [ ] Фильтрация документов
- [ ] Ознакомление с документами
## Возможные проблемы
### 1. Кэш TypeScript
**Проблема:** VSCode может показывать ошибки для удаленных файлов.
**Решение:**
```bash
cd frontend
rm -rf .next node_modules/.cache
# Перезапуск VSCode
```
### 2. Старые API запросы
**Проблема:** Могут быть скрытые вызовы `getDocumentTypes()` или `getCabinets()`.
**Решение:** Проверить логи Network в браузере на 404 ошибки.
### 3. Django миграции
**Проблема:** Конфликты при удалении поля `document_type`.
**Решение:** Создать промежуточную миграцию с `null=True`, потом удалить.
## Следующие шаги
### Немедленно
1. ✅ Очистить кэш frontend (`rm -rf .next`)
2. ⏳ Создать Django миграцию
3. ⏳ Применить миграцию на dev окружении
4. ⏳ Протестировать основные сценарии
### Краткосрочно
1. Удалить неиспользуемые таблицы БД (опционально)
2. Обновить документацию проекта
3. Добавить теги для категоризации (вместо типов)
4. Добавить unit-тесты для измененных компонентов
### Долгосрочно
1. Рассмотреть добавление "избранное" вместо дашборда
2. Улучшить поиск по тегам (фильтры в один клик)
3. Добавить статистику использования тегов
4. Визуализация структуры папок (дерево)
## Заключение
Система управления документами успешно упрощена:
- **Удалено:** типы документов, кабинеты, дашборд
- **Сохранено:** папки, теги, все основные функции (загрузка, версии, ознакомление)
- **Результат:** Более простая, быстрая и понятная система
Кодовая база стала **легче на ~2500 строк**, что упрощает поддержку и развитие проекта.
---
**Автор изменений:** GitHub Copilot
**Дата:** 2024
**Версия документа:** 1.0