← Назад к курсу

Пример проекта на Python/Django: CRM для учета обращений клиентов по купленным лицензиям на программные продукты

Мы создадим пример модели CRM для учета обращений клиентов по лицензиям на ПО. Основные сущности:

  1. Клиент (Client)
  2. Договор/заказ (Contract)
  3. Программный продукт (SoftwareProduct)
  4. Лицензия (License) - связывает клиента, договор и продукт, также содержит ключ лицензии и срок действия.
  5. Обращение (Ticket) - обращение клиента по проблеме или предложению, связанное с лицензией.

Дополнительно:

  • Пользователь (User) - сотрудники, которые работают с обращениями.

Схема:

Client:
name: CharField
contact_info: TextField (контакты, адрес и т.д.)
created_at: DateTimeField (авто-добавление)

Contract:
client: ForeignKey(Client)
contract_number: CharField (номер договора)
signed_date: DateField
amount: DecimalField (сумма договора)
created_at: DateTimeField

SoftwareProduct:
name: CharField
version: CharField
description: TextField (описание продукта)

License:
contract: ForeignKey(Contract) # договор, по которому приобретена лицензия
software_product: ForeignKey(SoftwareProduct)
license_key: CharField (уникальный ключ лицензии)
start_date: DateField
end_date: DateField
is_active: BooleanField (действующая ли лицензия)

Ticket:
license: ForeignKey(License) # лицензия, по которой возникло обращение
created_by: ForeignKey(User) # кто создал обращение (сотрудник)
created_at: DateTimeField
updated_at: DateTimeField
subject: CharField
description: TextField
STATUS_CHOICES = [
('open', 'Открыто'),
('in_progress', 'В работе'),
('resolved', 'Решено'),
('closed', 'Закрыто'),
]
status: CharField(choices=STATUS_CHOICES, default='open')
resolution: TextField (что сделано для решения)

Также можно добавить модель для комментариев к обращению, но для простоты ограничимся.

Примечание: Одно обращение (Ticket) привязано к одной лицензии. Если у клиента несколько лицензий, то обращение относится к конкретной лицензии.

Давайте создадим модели в коде:
Вот пример структуры CRM для учета обращений клиентов по лицензиям ПО на Django/PostgreSQL. Основные модели и их взаимосвязи:

1. Модели данных (models.py)

from django.db import models
from django.contrib.auth.models import User

class Client(models.Model):
    name = models.CharField("Название", max_length=255)
    contact_email = models.EmailField("Email")
    phone = models.CharField("Телефон", max_length=20)
    address = models.TextField("Адрес", blank=True)
    created_at = models.DateTimeField("Дата создания", auto_now_add=True)

    def __str__(self):
        return self.name

class SoftwareProduct(models.Model):
    name = models.CharField("Название продукта", max_length=100)
    version = models.CharField("Версия", max_length=50)
    description = models.TextField("Описание", blank=True)

    def __str__(self):
        return f"{self.name} v.{self.version}"

class Contract(models.Model):
    client = models.ForeignKey(Client, on_delete=models.CASCADE, verbose_name="Клиент")
    contract_number = models.CharField("Номер договора", max_length=50, unique=True)
    signed_date = models.DateField("Дата подписания")
    end_date = models.DateField("Дата окончания")
    file = models.FileField("Документ", upload_to="contracts/", blank=True)

    def __str__(self):
        return f"Договор {self.contract_number} ({self.client})"

class License(models.Model):
    contract = models.ForeignKey(Contract, on_delete=models.CASCADE, verbose_name="Договор")
    product = models.ForeignKey(SoftwareProduct, on_delete=models.CASCADE, verbose_name="Продукт")
    license_key = models.CharField("Ключ лицензии", max_length=255, unique=True)
    activation_date = models.DateField("Дата активации")
    is_active = models.BooleanField("Активна", default=True)

    def __str__(self):
        return f"Лицензия {self.license_key[:8]}... ({self.product})"

class Ticket(models.Model):
    TICKET_TYPES = (
        ('problem', 'Проблема'),
        ('suggestion', 'Предложение'),
        ('question', 'Вопрос'),
    )
    STATUSES = (
        ('open', 'Открыт'),
        ('in_progress', 'В работе'),
        ('resolved', 'Решен'),
        ('closed', 'Закрыт'),
    )

    license = models.ForeignKey(License, on_delete=models.CASCADE, verbose_name="Лицензия")
    title = models.CharField("Заголовок", max_length=200)
    description = models.TextField("Описание")
    created_at = models.DateTimeField("Создано", auto_now_add=True)
    updated_at = models.DateTimeField("Обновлено", auto_now=True)
    ticket_type = models.CharField("Тип обращения", max_length=20, choices=TICKET_TYPES)
    status = models.CharField("Статус", max_length=20, choices=STATUSES, default='open')
    assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Исполнитель")

    def __str__(self):
        return f"{self.get_ticket_type_display()} #{self.id} - {self.license}"

2. Схема базы данных

erDiagram
    CLIENT ||--o{ CONTRACT : has
    CLIENT {
        string name
        string contact_email
        string phone
        text address
        datetime created_at
    }
    
    CONTRACT ||--o{ LICENSE : includes
    CONTRACT {
        string contract_number
        date signed_date
        date end_date
        string file_path
    }
    
    SOFTWARE_PRODUCT ||--o{ LICENSE : belongs_to
    SOFTWARE_PRODUCT {
        string name
        string version
        text description
    }
    
    LICENSE ||--o{ TICKET : references
    LICENSE {
        string license_key
        date activation_date
        bool is_active
    }
    
    TICKET {
        string title
        text description
        datetime created_at
        datetime updated_at
        enum ticket_type
        enum status
    }
    
    USER ||--o{ TICKET : assigned_to

3. Основные функции системы

  1. Управление клиентами

    • Регистрация новых клиентов
    • История договоров и лицензий
  2. Учет договоров

    • Хранение сканов договоров
    • Отслеживание сроков действия
  3. Управление лицензиями

    • Активация/деактивация ключей
    • Привязка к продуктам и договорам
  4. Обработка обращений

    • Классификация по типу (проблема/предложение)
    • Назначение исполнителей
    • Контроль сроков решения
    • История коммуникаций

4. Пример API-запросов (DRF)

# serializers.py
from rest_framework import serializers
from .models import Ticket, License

class TicketSerializer(serializers.ModelSerializer):
    class Meta:
        model = Ticket
        fields = '__all__'

# views.py
class TicketViewSet(viewsets.ModelViewSet):
    queryset = Ticket.objects.all()
    serializer_class = TicketSerializer
    filterset_fields = ['status', 'ticket_type', 'license__product']

5. Особенности реализации

  1. Аутентификация

    • JWT-авторизация для мобильных клиентов
    • Ролевая модель доступа (менеджер/техподдержка)
  2. Уведомления

    • Автоматические оповещения при:
      • Истечении срока лицензии
      • Изменении статуса обращения
      • Просроченных задачах
  3. Отчетность

    • Статистика по типам обращений
    • Время решения проблем
    • Рейтинг продуктов по количеству проблем

6. Миграции

python manage.py makemigrations
python manage.py migrate

7. Рекомендуемые улучшения

  1. Добавить комментарии к обращениям
  2. Реализовать SLA-контроль сроков
  3. Интеграция с почтой (входящие письма → тикеты)
  4. Дашборды аналитики
  5. История изменений статусов

Для запуска минимальной версии:

  1. Установите Django, DRF, psycopg2
  2. Настройте подключение к PostgreSQL в settings.py
  3. Создайте суперпользователя python manage.py createsuperuser

Это базовая структура, которую можно расширять под конкретные бизнес-процессы.