Corp/docs/reports/DOCUMENTS_BRANCH_COMPARISON.md
lizerk a662642864 docs: добавление документации по рефакторингу
- Добавлен отчет о рефакторинге хранилища медиафайлов
- Добавлено сравнение веток feature/django-filer-documents и develop
- Добавлен аудит frontend документов
- Добавлен отчет об очистке types, cabinets и dashboard
2026-03-06 12:33:18 +03:00

28 KiB
Raw Blame History

Сравнение реализаций документов: 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 простые модели

# 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 сложных моделей

# 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

Документ либо создан, либо нет. Всё.

# Document создан → сразу доступен получателям
document = Document.objects.create(
    title="Регламент",
    file=file,
    sent_to_all=True
)
# Готово! Пользователи могут read + acknowledge

feature: 7 состояний FSM

# 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: Шаблонный код (НЕ РАБОТАЕТ)

# 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:

# 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

# 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:
# 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
  1. FSM transition decorators:
@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

file = models.FileField(upload_to='documents/%Y/%m/%d/')
  • Простое хранение в /media/documents/YYYY/MM/DD/
  • Нет папок, нет организации

feature: Full django-filer

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

# 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

# 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 на:

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:

# 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 после анализа обеих веток
Статус: Ожидание решения пользователя