Kommo + Grafana: дашборд продаж на основе данных CRM без вендор-локина
Grafana — open-source платформа визуализации данных: 50+ datasource plugins (Postgres, InfluxDB, Prometheus, Elasticsearch, MySQL), гибкие дашборды, алерты. Широко используется для мониторинга инфраструктуры, но отлично подходит для business-аналитики когда данные CRM выгружаются в SQL-базу. В отличие от Metabase или Redash, Grafana — инструмент для команд где уже есть Postgres и нет желания платить за BI SaaS. Нет вендорного lock-in, нет лицензий — только ваши данные и SQL.
Grafana vs Metabase vs Redash для CRM-аналитики
| Параметр | Grafana | Metabase | Redash |
|---|---|---|---|
| Open-source | Да | Да (Community) | Да |
| Time-series фокус | Да | Нет | Нет |
| Datasources | 50+ (Postgres, InfluxDB, Prometheus…) | SQL-базы, MongoDB | SQL-базы |
| Self-hosted | Да | Да | Да |
| Алерты | Нативные, multi-channel | Через Slack | Ограниченно |
| Grafana Cloud | Есть | Нет | Нет |
| Подходит для | DevOps + business метрики в одном | Бизнес-аналитика | SQL-дашборды |
Grafana выбирают команды где уже есть DevOps-дашборды в Grafana и хотят добавить business-метрики в тот же инструмент.
Архитектура: Kommo -> ETL -> Postgres -> Grafana
Прямого Grafana datasource для Kommo нет. Правильная архитектура:
Kommo REST API -> Python ETL (cron) -> Postgres -> Grafana
- Python ETL (cron каждые 30–60 мин): вытягивает данные из Kommo API (сделки, контакты, статусы)
- Postgres (или TimescaleDB для time-series): хранит нормализованные данные
- Grafana: PostgreSQL datasource -> SQL-запросы -> панели и дашборды
ETL: выгрузка данных Kommo в Postgres
import requests
import psycopg2
from datetime import datetime, timezone, timedelta
KOMMO_SUBDOMAIN = "youraccount"
KOMMO_TOKEN = "your_access_token"
KOMMO_BASE_URL = f"https://{KOMMO_SUBDOMAIN}.kommo.com/api/v4"
KOMMO_HEADERS = {"Authorization": f"Bearer {KOMMO_TOKEN}"}
DB_DSN = "postgresql://user:pass@localhost:5432/analytics"
def fetch_leads(updated_after: datetime) -> list:
# Paginated fetch всех сделок обновлённых после даты
ts = int(updated_after.timestamp())
leads = []
page = 1
while True:
resp = requests.get(
f"{KOMMO_BASE_URL}/leads",
headers=KOMMO_HEADERS,
params={
"updated_at[from]": ts,
"page": page,
"limit": 250,
},
)
if resp.status_code == 204:
break
resp.raise_for_status()
data = resp.json().get("_embedded", {}).get("leads", [])
if not data:
break
leads.extend(data)
page += 1
return leads
def upsert_leads_to_postgres(leads: list):
conn = psycopg2.connect(DB_DSN)
cur = conn.cursor()
create_sql = (
"CREATE TABLE IF NOT EXISTS kommo_leads ("
"id BIGINT PRIMARY KEY, name TEXT, status_id INT, "
"pipeline_id INT, responsible_user_id BIGINT, price NUMERIC, "
"created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, "
"closed_at TIMESTAMPTZ, loss_reason_id INT)"
)
cur.execute(create_sql)
for lead in leads:
insert_sql = (
"INSERT INTO kommo_leads "
"(id, name, status_id, pipeline_id, responsible_user_id, "
"price, created_at, updated_at, closed_at, loss_reason_id) "
"VALUES (%s,%s,%s,%s,%s,%s,"
"to_timestamp(%s),to_timestamp(%s),to_timestamp(%s),%s) "
"ON CONFLICT (id) DO UPDATE SET "
"status_id=EXCLUDED.status_id, price=EXCLUDED.price, "
"updated_at=EXCLUDED.updated_at, closed_at=EXCLUDED.closed_at, "
"loss_reason_id=EXCLUDED.loss_reason_id"
)
cur.execute(insert_sql, (
lead["id"], lead.get("name"),
lead.get("status_id"), lead.get("pipeline_id"),
lead.get("responsible_user_id"), lead.get("price"),
lead.get("created_at"), lead.get("updated_at"),
lead.get("closed_at"), lead.get("loss_reason_id"),
))
conn.commit()
cur.close(); conn.close()
def run_etl():
updated_after = datetime.now(timezone.utc) - timedelta(hours=2)
leads = fetch_leads(updated_after)
if leads:
upsert_leads_to_postgres(leads)
print(f"ETL: {len(leads)} leads upserted")
if __name__ == "__main__":
run_etl()
SQL-запросы для Grafana панелей
После настройки Postgres datasource в Grafana — строите панели через SQL.
Воронка конверсии (Bar Chart):
SELECT
s.name AS stage,
COUNT(*) AS leads,
SUM(l.price) AS pipeline_value
FROM kommo_leads l
JOIN kommo_statuses s ON l.status_id = s.id
WHERE l.pipeline_id = $pipeline_id
AND l.created_at >= $__timeFrom()
GROUP BY s.name, s.sort
ORDER BY s.sort;
Lead Velocity (Leads created per week):
SELECT
date_trunc('week', created_at) AS week,
COUNT(*) AS new_leads
FROM kommo_leads
WHERE created_at >= $__timeFrom()
AND created_at <= $__timeTo()
GROUP BY 1
ORDER BY 1;
Сравнение менеджеров (Won deals):
SELECT
u.name AS manager,
COUNT(*) AS won_deals,
SUM(l.price) AS total_revenue,
AVG(EXTRACT(EPOCH FROM (l.closed_at - l.created_at))/86400) AS avg_days_to_close
FROM kommo_leads l
JOIN kommo_users u ON l.responsible_user_id = u.id
WHERE l.status_id = $won_status_id
AND l.closed_at >= $__timeFrom()
GROUP BY u.name
ORDER BY total_revenue DESC;
Win Rate по месяцам:
SELECT
date_trunc('month', closed_at) AS month,
COUNT(*) FILTER (WHERE status_id = $won_status_id) AS won,
COUNT(*) FILTER (WHERE status_id = $lost_status_id) AS lost,
ROUND(
100.0 * COUNT(*) FILTER (WHERE status_id = $won_status_id)
/ NULLIF(COUNT(*), 0), 1
) AS win_rate_pct
FROM kommo_leads
WHERE closed_at >= $__timeFrom()
GROUP BY 1
ORDER BY 1;
Алерты: Grafana -> уведомление команде
Grafana Alert Rules позволяют: если new_leads_today = 0 -> Slack-уведомление. Если avg_deal_age > 30 дней -> email team lead.
Настройка: Grafana -> Alerting -> Alert Rules -> создать правило на основе любого SQL-запроса.
Реальный кейс
SaaS-стартап (EU, 30 человек, Kommo + Grafana + Postgres):
- До: аналитика продаж — Excel раз в неделю. Conversion rate, lead velocity — вручную. DevOps уже использовали Grafana для infrastructure-мониторинга.
- После: добавили бизнес-метрики в тот же Grafana. Python ETL каждый час -> Postgres -> Grafana. Один дашборд с инфра и продажами на одном экране. Нет нового инструмента — только datasource добавлен.
- Ключевой инсайт: lead velocity упала на 40% в одну неделю -> Grafana alert -> провели ретроспективу. Оказалось: отключили один лидогенерирующий канал. Обнаружили через 2 дня, а не через 2 недели.
Для кого актуально
- Технические команды с Grafana для DevOps — проще добавить бизнес-метрики чем внедрять новый BI-инструмент
- Стартапы с Postgres в стеке — нет дополнительных расходов на BI SaaS
- Компании с требованиями к self-hosted аналитике (финансы, healthcare)
- Команды где аналитика нужна на уровне custom SQL без BI-ограничений
Часто задаваемые вопросы
Grafana Cloud vs self-hosted — что выбрать для CRM-аналитики?
Grafana Cloud Free: 3 пользователя, 10k metrics, достаточно для small team. Self-hosted: полный контроль, нет ограничений, нужен сервер (Docker или Kubernetes). Для CRM-аналитики небольшой команды — Grafana Cloud Free + Postgres на том же сервере что и ETL.
Kommo API rate limits — как не превысить при ETL?
Kommo: 7 запросов в секунду, 5000 запросов в час. При incremental ETL (только обновлённые за последние N часов) — обычно 1–5 запросов на запуск. При full sync (все сделки) — пагинация по 250, с задержкой 0.2 сек между запросами. Full sync нужен только при первом запуске.
Grafana переменные (Variables) — как параметризовать дашборд?
Grafana -> Dashboard Settings -> Variables. Тип Query: SQL-запрос из Postgres. Например, SELECT DISTINCT pipeline_id FROM kommo_leads -> dropdown в дашборде. Используется как $pipeline_id в SQL-запросах панелей. Это позволяет переключать воронку/менеджера без редактирования SQL.
Как добавить custom fields Kommo в Postgres?
Custom fields в Kommo хранятся в _embedded.custom_fields_values в виде массива. При ETL: распарсить массив по field_id -> записать в отдельные столбцы таблицы или в JSONB-колонку. JSONB позволяет делать запросы: WHERE custom_fields->>'plan' = 'growth'.
Итого
- Архитектура: Kommo API -> Python ETL (cron) -> Postgres -> Grafana PostgreSQL datasource
- ETL: incremental по
updated_at[from], upsert поid, пагинация 250 лидов на запрос - Ключевые панели: воронка, lead velocity, win rate, сравнение менеджеров — всё через SQL
- Grafana Variables: параметризовать по pipeline_id, менеджеру, периоду
- Алерты: Lead velocity = 0, avg deal age > порог — Slack/email уведомление
Если вы используете Kommo и хотите построить дашборд в Grafana — опишите какие метрики критичны и есть ли уже Postgres в инфраструктуре. Exceltic.dev настроит ETL и базовый набор панелей.