Kommo + Sisense: BI-аналитика продаж из CRM-данных в реальном времени

Kommo + Sisense: BI-аналитика продаж из CRM-данных в реальном времени

При интеграции Kommo и Sisense данные сделок, контактов и активностей автоматически попадают в хранилище Sisense через scheduled sync. Руководитель видит актуальные дашборды - конверсия по этапам, средний чек, velocity сделок - без ручного экспорта из Kommo.

Sisense - enterprise BI-платформа с собственным столбчатым хранилищем ElastiCube, которое позволяет работать с большими объёмами данных без традиционного ETL. Популярна среди компаний, которым нужны встроенные (embedded) аналитические дашборды внутри собственных продуктов или глубокая кастомизация визуализаций.

Для компаний, у которых основная воронка продаж живёт в Kommo, Sisense решает ключевую проблему: стандартные отчёты Kommo дают агрегированные цифры, но не позволяют делать сложные срезы - по менеджеру + источнику + периоду + размеру сделки одновременно. Это требует выгрузки в BI.

В проектах по кастомным интеграциям Kommo мы видим устойчивый паттерн: руководство хочет видеть pipeline-данные в корпоративной BI-системе, но нативного коннектора между Kommo и Sisense нет. Данные экспортируются вручную через CSV раз в неделю, аналитик тратит часы на сводные таблицы, а к моменту отправки отчёт уже устарел. Задержка между событием в воронке и его отражением в дашборде - от нескольких дней до недели. В этой статье разберём архитектуру ETL-решения, которое закрывает этот разрыв: scheduled sync из Kommo API через промежуточное хранилище до Sisense ElastiCube.

Почему нативная интеграция не работает

Sisense не имеет готового коннектора для Kommo. Коннекторы для CRM в Sisense есть для Salesforce и HubSpot - но не для Kommo. Это означает, что стандартный путь (настроить data source в Sisense и нажать «Connect») не работает.

Alternative - ручной экспорт CSV из Kommo и загрузка в Sisense - имеет очевидные ограничения: данные устаревают, процесс требует регулярных ручных действий, объёмные выгрузки из Kommo ограничены пагинацией и лимитами API.

Запрос через Zapier здесь бессмысленен: Sisense - это аналитическое хранилище, а не операционный инструмент. Zapier не умеет инкрементально обновлять ElastiCube-датасеты.

Что реализовывается - архитектура решения

Kommo API (scheduled)
  |
  | Каждые N часов:
  | - выгрузить новые/изменённые сделки
  | - выгрузить активности (notes, calls, emails)
  | - выгрузить контакты и компании
  v
ETL-сервис (Python)
  |
  | Трансформация:
  | - нормализация статусов
  | - расчёт derived metrics (days_in_stage, deal_velocity)
  | - enrichment из кастомных полей
  v
Промежуточное хранилище (PostgreSQL / BigQuery)
  |
  v
Sisense ElastiCube (scheduled build)
  |
  v
Sisense Dашборды:
  - Pipeline overview
  - Manager performance
  - Stage conversion
  - Revenue forecast

Ключевое архитектурное решение - промежуточное хранилище. Прямая загрузка из API в Sisense каждый раз пересоздаёт весь датасет. Промежуточная БД хранит накопленную историю и позволяет делать инкрементальные обновления (только новые и изменённые записи).

Технические детали

Kommo API v4 использует Long-lived access token или OAuth 2.0. Для scheduled sync рекомендуется Long-lived token с правами leads, contacts, companies, notes, calls. API возвращает данные постранично (по 250 записей), для полной выгрузки нужна рекурсивная пагинация по параметру page.

Sisense поддерживает несколько способов загрузки данных в ElastiCube: JDBC-коннектор (подключение к PostgreSQL/BigQuery), Sisense REST API для управления builds и CSV-коннектор. Для production рекомендуется JDBC-подключение к промежуточной БД.

import requests
import psycopg2
from datetime import datetime, timedelta
import json

KOMMO_BASE_URL = "https://YOUR_ACCOUNT.kommo.com/api/v4"
KOMMO_TOKEN = "your_long_lived_token"

def fetch_modified_leads(since_timestamp: int) -> list:
    """Выгружает сделки, изменённые с заданного времени"""
    headers = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
    leads = []
    page = 1
    
    while True:
        resp = requests.get(
            f"{KOMMO_BASE_URL}/leads",
            headers=headers,
            params={
                "filter[updated_at][from]": since_timestamp,
                "page": page,
                "limit": 250,
                "with": "contacts,companies,loss_reason,pipeline"
            }
        )
        if resp.status_code == 204:  # Нет данных
            break
        resp.raise_for_status()
        
        data = resp.json()
        batch = data.get("_embedded", {}).get("leads", [])
        if not batch:
            break
        
        leads.extend(batch)
        
        # Проверка пагинации
        next_link = data.get("_links", {}).get("next")
        if not next_link:
            break
        page += 1
    
    return leads

def transform_lead(lead: dict) -> dict:
    """Трансформирует сырые данные Kommo в плоскую запись для BI"""
    created_at = datetime.fromtimestamp(lead["created_at"])
    updated_at = datetime.fromtimestamp(lead["updated_at"])
    
    # Расчёт производных метрик
    close_date = datetime.fromtimestamp(lead["closed_at"]) if lead.get("closed_at") else None
    days_to_close = (close_date - created_at).days if close_date else None
    
    return {
        "lead_id": lead["id"],
        "name": lead["name"],
        "price": lead.get("price", 0),
        "status_id": lead["status_id"],
        "pipeline_id": lead["pipeline_id"],
        "responsible_user_id": lead["responsible_user_id"],
        "created_at": created_at.isoformat(),
        "updated_at": updated_at.isoformat(),
        "closed_at": close_date.isoformat() if close_date else None,
        "days_to_close": days_to_close,
        "is_won": lead.get("status_id") == 142,  # WON status
        "is_lost": lead.get("status_id") == 143,  # LOST status
        "loss_reason": lead.get("loss_reason", {}).get("name") if lead.get("loss_reason") else None,
        # Кастомные поля
        "deal_type": get_custom_field(lead, "deal_type"),
        "lead_source": get_custom_field(lead, "lead_source"),
        "industry": get_custom_field(lead, "industry"),
        "company_size": get_custom_field(lead, "company_size")
    }

def get_custom_field(lead: dict, field_name: str) -> str:
    for field in lead.get("custom_fields_values", []) or []:
        if field.get("field_code") == field_name:
            values = field.get("values", [])
            return values[0]["value"] if values else None
    return None

def sync_to_postgres(leads: list, conn):
    """Записывает/обновляет сделки в промежуточной БД (upsert)"""
    cursor = conn.cursor()
    for lead in leads:
        cursor.execute("""
            INSERT INTO kommo_leads (
                lead_id, name, price, status_id, pipeline_id,
                responsible_user_id, created_at, updated_at, closed_at,
                days_to_close, is_won, is_lost, loss_reason,
                deal_type, lead_source, industry, company_size
            ) VALUES (
                %(lead_id)s, %(name)s, %(price)s, %(status_id)s, %(pipeline_id)s,
                %(responsible_user_id)s, %(created_at)s, %(updated_at)s, %(closed_at)s,
                %(days_to_close)s, %(is_won)s, %(is_lost)s, %(loss_reason)s,
                %(deal_type)s, %(lead_source)s, %(industry)s, %(company_size)s
            )
            ON CONFLICT (lead_id) DO UPDATE SET
                price = EXCLUDED.price,
                status_id = EXCLUDED.status_id,
                updated_at = EXCLUDED.updated_at,
                closed_at = EXCLUDED.closed_at,
                days_to_close = EXCLUDED.days_to_close,
                is_won = EXCLUDED.is_won,
                is_lost = EXCLUDED.is_lost
        """, transform_lead(lead))
    conn.commit()

Пошаговая реализация

Шаг 1. Спроектируйте схему данных под ваши BI-задачи

Определите, какие метрики нужны на дашборде: конверсия по этапам, средний чек по менеджеру, velocity по источнику лида. Под эти вопросы спроектируйте таблицы в промежуточной БД. Стандартные таблицы: kommo_leads, kommo_contacts, kommo_companies, kommo_activities, kommo_pipelines, kommo_users.

Шаг 2. Разверните ETL-сервис

Настройте cron-задачу для запуска ETL каждые 4-6 часов. При каждом запуске: читаем last_sync_timestamp из служебной таблицы, выгружаем только изменённые записи через filter[updated_at][from], трансформируем и upsert в PostgreSQL, обновляем last_sync_timestamp.

Шаг 3. Подключите Sisense к промежуточной БД

В Sisense Admin -> Data -> Add New Data Source выберите PostgreSQL. Укажите connection string. Создайте ElastiCube, добавьте таблицы из промежуточной БД. Настройте relationship между таблицами (leads -> users, leads -> pipelines).

Шаг 4. Создайте scheduled build в Sisense

Настройте автоматическую пересборку ElastiCube каждые 6-8 часов. Sisense забирает свежие данные из PostgreSQL и обновляет аналитические таблицы внутреннего хранилища.

Шаг 5. Постройте дашборды

Стандартный набор дашбордов для Kommo-данных: Pipeline overview (объём и конверсия по этапам), Manager performance (сделки, средний чек, win rate по менеджеру), Lead source analysis (откуда приходят лучшие лиды), Deal velocity (средний цикл сделки по типам).

Реальный кейс с цифрами

B2B SaaS-компания, 20 человек, годовой ARR в районе $1-2M. Команда продаж 5 человек работает в Kommo, CEO и инвесторы хотят видеть pipeline data в реальном времени.

До интеграции: аналитик раз в неделю экспортировал CSV из Kommo, делал сводные таблицы в Excel и рассылал по почте. Данные устаревали сразу после отправки. На подготовку отчёта уходило 3-4 часа в неделю.

Отдельная проблема: при вопросе «почему упала конверсия на этапе Demo?» у команды не было инструмента для быстрого разреза - нужно было снова выгружать CSV и строить pivot вручную.

После интеграции Kommo + Sisense через Exceltic.dev:

  • CEO открывает дашборд в браузере и видит актуальные данные с задержкой 4-6 часов
  • Время на подготовку еженедельного отчёта: 0 (автоматически)
  • Ad-hoc анализ («покажи конверсию за последние 30 дней только по лидам из LinkedIn»): 2-3 минуты в Sisense вместо 2-3 часов в Excel

Об остальных функциях Kommo CRM для аналитики продаж - в обзорной статье.

Для кого подходит

Интеграция Kommo + Sisense актуальна для:

  • Компаний с Sisense как корпоративным стандартом BI, которым нужно добавить CRM-данные в существующую платформу
  • B2B с 5+ менеджерами по продажам, где нужна аналитика производительности команды
  • Компаний с отчётностью перед бордом или инвесторами - Sisense позволяет создать красивые презентационные дашборды
  • SaaS с embedded analytics - данные из Kommo можно встроить в клиентский интерфейс вашего продукта

Если у вас нет корпоративной лицензии Sisense - для задач аналитики Kommo-данных рассмотрите более доступные варианты: Metabase (self-hosted, бесплатно), Looker Studio (бесплатно), Power BI.

Часто задаваемые вопросы

Sisense поддерживает PostgreSQL как источник данных?

Да. Sisense поддерживает PostgreSQL через JDBC-коннектор из коробки. Это один из наиболее распространённых источников данных для Sisense ElastiCube. Также поддерживаются MySQL, SQL Server, BigQuery, Redshift и другие OLAP-хранилища.

С какой задержкой данные попадают из Kommo в Sisense?

Задержка определяется частотой запуска ETL-сервиса и расписанием rebuild ElastiCube в Sisense. Типовая конфигурация: ETL каждые 4 часа + rebuild ElastiCube каждые 4 часа со сдвигом = данные обновляются каждые 4-8 часов. Для real-time аналитики можно сократить интервал до 1 часа, но это увеличивает нагрузку на Kommo API.

Как обеспечить историчность данных при изменении стадии сделки?

Kommo не хранит историю изменений этапов в стандартном API (только текущий статус). Для SCD (Slowly Changing Dimensions) нужно либо использовать Kommo events/activities API для восстановления истории, либо фиксировать каждое изменение статуса при каждом ETL-прогоне. Второй подход проще: при каждом sync создаём snapshot-запись с текущим status_id и временем фиксации.

Можно ли использовать другую BI-платформу вместо Sisense?

Да. Архитектура с промежуточной PostgreSQL-БД универсальна. Sisense можно заменить на Tableau, Power BI, Looker или Metabase - они все поддерживают PostgreSQL как источник. Сам ETL-сервис остаётся неизменным, меняется только последний шаг подключения. О других вариантах аналитики для Kommo CRM - в нашем обзоре.

Сколько времени занимает разработка интеграции Kommo + Sisense?

Базовый ETL (выгрузка сделок + контактов + активностей в PostgreSQL) + подключение к Sisense + 2-3 стандартных дашборда - 4-5 недель. Включая настройку промежуточной БД, ETL-сервиса с обработкой ошибок и пагинацией. Embedded analytics или дополнительные источники данных - оцениваем отдельно.


Если вам нужна BI-аналитика по данным Kommo в Sisense - опишите задачу команде Exceltic.dev. Разберём архитектуру хранилища и оценим объём работ.

Ещё статьи

Все →