Django + Zoho CRM: как управлять данными без головной боли

от автора

Почти любой современный бизнес, когда приходит время выстраивать отношения с клиентами, полагается на CRM-системы (Customer Relationship Management). И сталкивается с тем, что ожидания не совпадают с реальностью: эффективность не такая высокая, да и управление данными кажется слишком сложным. Один из способов решить эти проблемы — интегрировать сервис со своим веб-приложением.

Привет! Меня зовут Денис, я Python-разработчик в Kokoc Group. Уже около года занимаюсь интеграцией одной из наших платформ на Django с Zoho CRM. Мы соединяем пользователей с широким спектром услуг, требования к обработке заявок и управлению партнерскими и пользовательскими профилями высокие, поэтому Zoho CRM стала ключевым элементом.

В статье хочу показать, как провести ее и сделать процессы проще и эффективнее. Мы рассмотрим все этапы: от подготовки среды до настройки вебхуков и асинхронной обработки данных с помощью Celery. Советую почитать всем разработчикам и менеджерам, так или иначе связанным с работой в CRM-системах.

О чем поговорим:

  • Как подготовить CRM и Django-приложение к интеграции.

  • Как взаимодействовать с API Zoho CRM через Python.

  • Как настроить правила рабочего процесса в Zoho CRM для отправки данных в Django-приложение.

  • Как обработать данные, полученные от вебхука Zoho CRM, и обновить базу данных Django.

  • Как использовать Celery для асинхронной обработки данных из Zoho CRM.

Немного о Zoho CRM

Скрытый текст

Zoho CRM — это комплексная система управления отношениями с клиентами (CRM) от компании Zoho. В ней есть инструменты для управления лидами, контактной информацией, сделками, маркетинговыми кампаниями, обслуживанием клиентов и аналитикой — реализованные, как набор модулей:

  • Лиды (Leads) — модуль для сбора и управления информацией о потенциальных клиентах.

  • Контакты (Contacts) — модуль для хранения контактной информации о клиентах как физических лицах.

  • Аккаунты (Accounts) — модуль для хранения информации о компаниях, с которыми вы взаимодействуете.

  • Продукты (Products) — модуль для хранения информации о продуктах компании.

  • Сделки (Deals) — Модуль для отслеживания прогресса продаж и управления сделками.

Стандартные модули можно редактировать, как и добавлять новые под свои бизнес-процессы. Еще один плюс — интеграция с другими продуктами Zoho: Zoho Mail, Zoho Campaigns, Zoho Desk, Zoho Books — из которых можно собрать полноценную платформу для управления бизнесом.

Готовим CRM-систему

Первым делом нужно зарегистрироваться с в системе и создать песочницу (дальше работать будем уже в ней).

  1. Заводим аккаунт и переходим в настройки:

  2. Выбираем Sandbox:

  3. Нажимаем Create New Sanbox, заполняем форму, нажимаем Create:

  4. Нажимаем Go to sunbox и оказываемся внутри нашей песочницы (о чем говорит флаг на скриншоте):

Получаем ключи API

Для взаимодействия понадобятся ключи. Идем сюда: https://api-console.zoho.eu/:

  1. Выбираем Self Client, нажимаем Create:

  1. Записываем вот эти значения — они еще понадобятся:

Указываем Scope

В API Zoho, он определяет разрешения, которые приложение запрашивает для доступа к различным ресурсам (подробнее советую почитать тут: https://www.zoho.com/crm/developer/docs/api/v6/scopes.html). 

Мы возьмем «ZohoCRM.modules.ALL», который дает доступ ко всем модулям в Zoho CRM. То есть приложение сможет выполнять операции (чтение, создание, обновление, удаление) с любыми модулями: контактами, сделками, компаниями и прочим.

  1. Нажимаем Create и выбираем кабинет, для которого создается ключ:

  1. Выбираем, для какой версии CRM создать токен: для производственной или песочницы. Фича удобная: достаточно взять однажды все токены и больше не возвращаться к этому вопросу.

Получаем токены

Отправляем POST-запрос на этот url со следующими параметрами. Пример:
Заменить токены на условные <token>
https://accounts.zoho.eu/oauth/v2/token?client_id=<client_id>&client_secredt=<client_secret>&code=<token>&grant_type=authorizationcode  
В ответ нам придет access_token и refresh_token. Сохраняем и идем дальше.

Готовим Django-приложение

Первым делом устанавливаем пакеты, которые понадобятся для работы:

  • Django — основной фреймворк для разработки веб-приложений.

  • djangorestframework — пакет для создания API.

  • requests — библиотека для отправки HTTP-запросов.

  • Celery — система для создания и обработки асинхронных задач.

Далее прописываем модели, я для примера буду использовать эти:

  1. Расширенная модель юзера:

class User(AbstractBaseUser):     first_name = models.CharField(_('first name'), max_length=30,  blank=True, null=True)     last_name = models.CharField(_('last name'), max_length=30,  blank=True, null=True)     phone_number = models.CharField(_('phone number'), max_length=20,  unique=True)     email = models.EmailField(_('email address'), unique=True)      USERNAME_FIELD = 'email'     objects = UserManager()      class Meta:         verbose_name = _('User')         verbose_name_plural = _('Users')
  1. Модель продукта:

class Product(models.Model):     name = models.CharField(verbose_name=_("Product name"), max_length=200)     description = models.TextField(verbose_name=_("Product description"), blank=True, null=True)     price = models.DecimalField(verbose_name=_("Price"), decimal_places=2, max_digits=10)     zoho_product_id = models.CharField(verbose_name=_("Zoho product ID"),  max_length=50, blank=True, null=True, unique=True)      def __str__(self):         return self.name
  1. Модель ExternalResource. Она нужна для хранения данных, полученных при настройке Zoho CRM (Client ID, Client Secret, access_token, refresh_token):

class ExternalResource(models.Model):     name = models.CharField(verbose_name=_("Name"), max_length=200,  unique=True)     client_id = models.CharField(verbose_name=_("Client ID"),  max_length=200, null=True, blank=True)     client_secret = models.CharField(verbose_name=_("Client Secret"),  max_length=200, null=True, blank=True)     token = models.CharField(verbose_name='Token', max_length=256,  null=True, blank=True, default=None)     refresh_token = models.CharField(verbose_name='Refresh token',  max_length=256, null=True, blank=True, default=None)      class Meta:         verbose_name = _('Third-party service')         verbose_name_plural = _('Third-party services')      @classmethod     def get_zoho(cls):         return cls.objects.get(name='ZohoAPI')

Регистрируем ExternalResource в админке. И создаем объект модели с полученными данными:

Настраиваем взаимодействие с Zoho API. Понадобятся следующие классы:

  1. ExternalResourceManager — базовый класс для работы с API внешних сервисов:

class ExternalResourceManager(ABC):     """         Базовый класс для работы с API внешних сервисов, таких как ZohoCRM.     """      @abstractmethod     def db_object(self):         """             Метод реализует получение объекта модели ExternalResource         """         pass      @abstractmethod     def send_request(self, method, url, data, **kwargs):         """             Метод реализует отправку запроса к внешнему ресурсу         """         pass
  1. ZohoBaseManager — класс для работы с ZohoCRM API, реализующий методы отправки данных в ZohoCRM и обновления токенов:

class ZohoBaseManager(ExternalResourceManager):      @cached_property     def db_object(self):         return ExternalResource.get_zoho()      def send_request(self, method, url, data=None, auth=False, **kwargs):         """         Отправка запроса к ZohoCRM API.         """         if auth:             kwargs.update({"Authorization": f"Zoho-oauthtoken {self.db_object.token}"})          try:             response = requests.request(method=method, url=url, data=data, headers=kwargs)         except RequestException as e:             pass             # как то обрабатываем исключение...         return response      def handle_response(self, response):         """         Обработка ответа от API.         Проверяем на наличие ошибок и возвращаем необходимые данные.         """         if response is None:             raise ValueError("Ответ от API отсутствует")          try:             response_json = response.json()         except ValueError:             raise ValueError("Невозможно преобразовать ответ в JSON")          if response.status_code != 200:             error_message = response_json.get('error', 'Неизвестная ошибка')             raise Exception(f"Ошибка API: {error_message} (код {response.status_code})")          return response_json       def update_token(self):         """             Отправка запроса к Zoho API для  обновления token         """         url = 'https://accounts.zoho.eu/oauth/v2/token'          data = {             'client_id': self.db_object.client_id,             'client_secret': self.db_object.client_secret,             'refresh_token': self.db_object.refresh_token,             'grant_type': 'refresh_token',         }          response = self.send_request(             method='post',             url=url,             data=data         )          try:             response_json = self.handle_response(response)         except Exception as e:             # как то обрабатываем исключение...             return          new_token = response_json.get('access_token')         self.db_object.token = new_token         self.db_object.save() 
  1. RegistrationDataManager — класс для управления данными при регистрации пользователя.

class RegistrationDataManager(ZohoBaseManager):      @staticmethod     def get_registration_data(user):         data = {             "data": [                 {                     'First_Name': user.first_name,                     'Last_Name': user.last_name,                     'Email': user.email,                     'Mobile': user.phone_number,                 }             ]         }         return data      def create_lead(self, user):         """         Отправляет данные для создания лида в ZohoCRM.         """         data = self.get_registration_data(user)         json_data = json.dumps(data)         url = "https://www.zohoapis.eu/crm/v2/Leads"         method = 'post'         response = self.send_request(method, url, json_data, auth=True)         json_response = self.handle_response(response)         # Обрабатываем ответ от ZohoCRM (проверяем статус,  получаем ID лида и т.д.)

Обеспечиваем бесперебойный доступ к API Zoho CRM

Чтобы доступ оставался постоянным, необходимо обновлять токен. Напишем задачу, которая будет делать это за нас каждые 10 минут.

  1. Создаем Celery-таск, и в нем — экземпляр ZohoBaseManager и вызываем метод update_access_token(), который отвечает за фактическое обновление токена.

@shared_task def refresh_zoho_token():     """Обновляет токен доступа для API Zoho CRM."""     manager = ZohoBaseManager()     manager.update_token token()
  1. Настраиваем расписание для выполнения задачи в файле settings.py (там будем определять, как часто вызывается задача). Создаем запись для задачи refresh_zoho_token в словаре CELERY_BEAT_SCHEDULE:

CELERY_BEAT_SCHEDULE = {     'refresh_zoho_token': {         'task': 'zoho_integration.tasks.refresh_zoho_token',         'schedule': crontab(minute='*/10'),         'args': (),         'kwargs': {},         'options': {             'expires': 60.0,         },     }, }

Что именно прописали

Скрытый текст
  • task — указывает путь к нашей задаче.

  • schedule — использует crontab(minute=’*/10′), то есть “выполнение задачи каждые 10 минут”.

  • args и kwargs — пустые, так как задача не требует дополнительных параметров.

  • options — гарантирует, что если задача не будет выполнена в течение минуты, то станет считаться устаревшей и отменится.

Создаем лида в ZohoCRM при регистрации пользователя

Здесь все просто:

  1. Используем Django-сигналы, чтобы отслеживать создание пользователей:

@receiver(post_save, sender=User) def send_user_data(sender, instance, created, **kwargs):     if created:         create_zoho_lead.delay(instance.id)
  1. Используем Celery-задачи для асинхронной отправки данных в Zoho CRM:

@shared_task def create_zoho_lead(user_id: int) -> None:     try:         user = User.objects.get(pk=user_id)     except User.DoesNotExist:         return      manager = RegistrationDataManager()     manager.create_lead(user)

Вот и все, теперь при каждой регистрации данные о пользователе будут отправляться в ZohoCRM.

Проводим интеграцию с помощью Workflow Rules

Представим, что каждый раз, когда в Zoho создается или обновляется продукт, нам нужно забирать его в свою базу данных. Реализовать это можно как правила рабочего процесса и функций в Zoho CRM.

Подробнее о правила рабочего процесса

Скрытый текст

В Zoho CRM, Workflow Rules —- это по сути триггеры, которые автоматизируют задачи. Правила могут срабатывать автоматически в конкретное время или запускаются, когда:

  • создается новая запись;

  • обновляется существующая запись;

  • удаляется запись;

  • создаются, изменяются, удаляются примечания к записи.

Создадим правило, которое при создании или обновлении продукта будет присылать нам данные по нему и обновлять значение в базе.

  1. Переходим в настройки и нажимаем на вкладку Workflow Rules:

  1. Create Rule и заполняем форму:

  1. Нажимаем Next и попадаем на эту страницу:

  1. В первом селекте выбираем Record Action. Во втором — Create or Edit и Repeat this workflow whenever a Product is edited. Эти настройки говорят о том, что правило будет срабатывать при каждом редактировании записи:

  1. Нажимаем Next, выбираем All Products (так правило будет применяться ко всем записям):

  1. Выбираем, какое именно действие будет происходить. В нашем случае — Function :

  1. В появившемся окне выбираем Write your own:

  1. Заполняем форму и нажимаем Create:

  1. Попадаем на страницу создания функции:

Разберем типичную функцию в Zoho CRM

Что это вообще такое?

Скрытый текст

Это фрагменты кода, написанные на языке Deluge. Deluge разработан специально для Zoho CRM и позволяет выполнять операции более сложные, чем простое изменение полей или отправка писем. Вот пара примеров:

  • Обработка данных. Вы можете манипулировать данными из разных записей, использовать условные операторы и циклы для логического управления потоком выполнения.

  • Вызов API. Вы можете использовать функции для вызова внешних API, например, для получения данных из других сервисов или для отправки запросов в приложение.

В качестве примера возьмем функцию, которая будет отправлять данные о продукте из Zoho CRM в наше Django-приложение.

  1. Указываем, что функция принимает ID записи, и нажимаем Edit Arguments:

  1. В появившемся окне, в поле Argument Name задаем имя аргумента. В поле Argument Value вводим «#». Выбираем Product Id. Нажимаем Save:

Теперь ID продукта будет подставляться автоматически при срабатывании правила и вызове функции. И мы сможем его использовать в коде функции, чтобы получить создаваемую или редактируемую запись.

Вот пример функции с пояснениями:

Напишем view, которая будет принимать запрос

View

class ZohoProductView(APIView)     serializer_class = ZohoProductSerializer     permission_classes = (CheckZohoTokenPermission,)      def post(self, request):         serializer = self.serializer_class(data=request.data)         serializer.is_valid(raise_exception=True)         sync_product.delay(data=serializer.validated_data)         return Response(serializer.data, status=status.HTTP_202_ACCEPTED

Serializer

class ZohoProductSerializer(serializers.Serializer):     zoho_product_id = serializers.CharField()     name = serializers.CharField()     description = serializers.CharField()     price = serializers.DecimalField(max_digits=10, decimal_places=2)

Permission

class CheckZohoTokenPermission(BasePermission):      def has_permission(self, request, view):         if request.META.get("HTTP_ZOHO_REQUEST_AUTH_TOKEN") == settings.ZOHO_REQUEST_AUTH_TOKEN:             return True

Celery Task

@shared_task def sync_product(data):     zoho_product_id = data.get("zoho_product_id")     product = Product.objects.filter(zoho_product_id=zoho_product_id)     if product.exists():         product.update(**data)     else:         Product.objects.create(**data)

Теперь, когда в Zoho CRM, в модуле Products будет создаваться или редактироваться запись, сработает правило и система вызовет нужную функцию. Функция в свою очередь отправит данные на наш Django API-endpoint, который принимает данные о продукте и передает их на обработку в Celery-задачу sync_product. Задача проверит наличие продукта в базе данных по zoho_product_id. 

Наконец, если продукт существует, данные обновятся. Если нет — появится новый объект Product с полученными данными. 

Такой подход обеспечивает синхронность Zoho CRM и Django-приложением, то есть все изменения в данных продуктов будут отражены в обеих системах.

Заключение

Я постарался максимально полно показать, как интегрировать Zoho CRM с Django-приложением для автоматизации работы с данными о клиентах и продуктах. Но важно помнить, что эта статья — лишь начало, и интеграция с Zoho CRM — это гибкий и мощный инструмент, который можно настроить под любые потребности. Экспериментируйте, чтобы сделать сделать свой бизнес еще эффективнее.

И пара советов напоследок:

  • Добавьте логирование, чтобы отслеживать ошибки и успешную обработку данных.

  • Документируйте код. Это поможет и вам, и вашей команде лучше ориентироваться в структуре интеграции и легко вносить изменения в будущем. 

  • Тестируйте. Это поможет убедиться, что интеграция работает корректно и изменения не нарушат ее работу. 

  • Изучите документацию Zoho CRM и Django. В них вы найдете много полезной информации, которая поможет реализовать более сложные сценарии.


ссылка на оригинал статьи https://habr.com/ru/articles/861430/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *