Kommo + Redash: BI-дашборд продаж на собственных данных без вендор-локина
Redash — open-source BI-инструмент с SQL-интерфейсом, поддержкой 35+ источников данных и встроенным шерингом дашбордов. В отличие от Superset (колонок и агрегаций) или Metabase, Redash заточен на прямые SQL-запросы к данным — аналитик пишет запрос, получает визуализацию, публикует дашборд. Связка с Kommo строится через ETL: данные CRM -> реляционная база -> Redash.
Redash vs Superset vs Metabase: когда выбирать Redash
Все три — open-source BI с self-hosting. Выбор зависит от аудитории и паттерна использования:
| Параметр | Redash | Apache Superset | Metabase |
|---|---|---|---|
| Основной интерфейс | SQL-редактор | Drag-and-drop + SQL | Drag-and-drop + SQL |
| Целевая аудитория | Аналитики, разработчики | Data engineers | Бизнес-пользователи |
| Порог входа | Средний (нужен SQL) | Высокий | Низкий |
| API | REST (query results, embeds) | REST (Guest Token) | REST |
| Self-hosting | Docker, прост | Docker Compose, сложнее | Docker, прост |
| Лицензия | BSD | Apache 2.0 | AGPL / Commercial |
Redash выигрывает когда аналитика в команде — это SQL-запросы, а не GUI-конструктор. Для нетехнических пользователей — Metabase. Для сложной дата-инженерии — Superset. Сравнение с Apache Superset для Kommo-аналитики — в статье Kommo + Apache Superset.
Архитектура: Kommo -> Postgres -> Redash
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Kommo API │──ETL─▶│ PostgreSQL │◀─SQL──│ Redash │
│ /api/v4/ │ │ (ваш сервер)│ │ Dashboard │
└──────────────┘ └──────────────┘ └──────────────┘
▲ │
│ (embed / share)
│ ┌──────────────────────┐
│ │ Head of Sales │
└───────────────────────────────│ (браузер или Slack) │
ETL-скрипт запускается по расписанию (cron или Celery beat). Альтернативы Postgres: BigQuery, ClickHouse (лучше для больших объёмов), MySQL.
ETL: выгрузка данных Kommo в Postgres
Схема таблиц:
CREATE TABLE kommo_deals (
id BIGINT PRIMARY KEY,
name TEXT,
status_id INTEGER,
stage_name TEXT,
pipeline_id INTEGER,
responsible_user_id INTEGER,
responsible_name TEXT,
price DECIMAL(15,2),
created_at TIMESTAMP,
updated_at TIMESTAMP,
closed_at TIMESTAMP,
loss_reason TEXT
);
CREATE TABLE kommo_contacts (
id BIGINT PRIMARY KEY,
name TEXT,
email TEXT,
company TEXT,
created_at TIMESTAMP
);
CREATE TABLE kommo_deal_contacts (
deal_id BIGINT,
contact_id BIGINT,
PRIMARY KEY (deal_id, contact_id)
);
ETL-скрипт (incremental load):
import requests
from datetime import datetime, timedelta
import psycopg2
KOMMO_DOMAIN = 'yourcompany'
KOMMO_TOKEN = 'your_token'
PG_DSN = 'postgresql://user:pass@localhost/analytics'
def fetch_kommo_leads(updated_since: datetime):
params = {
'limit': 250,
'with': 'contacts',
'filter[updated_at][from]': int(updated_since.timestamp())
}
page = 1
while True:
response = requests.get(
f'https://{KOMMO_DOMAIN}.kommo.com/api/v4/leads',
headers={'Authorization': f'Bearer {KOMMO_TOKEN}'},
params={**params, 'page': page}
)
if response.status_code == 204: # нет данных
break
data = response.json()
leads = data.get('_embedded', {}).get('leads', [])
if not leads:
break
yield from leads
page += 1
def upsert_deal(conn, lead: dict):
with conn.cursor() as cur:
cur.execute('''
INSERT INTO kommo_deals
(id, name, status_id, price, created_at, updated_at, responsible_user_id)
VALUES (%s, %s, %s, %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
''', (
lead['id'], lead['name'], lead['status_id'],
lead.get('price', 0),
lead['created_at'], lead['updated_at'],
lead.get('responsible_user_id')
))
def run_etl():
conn = psycopg2.connect(PG_DSN)
updated_since = datetime.now() - timedelta(hours=1) # incremental: за последний час
for lead in fetch_kommo_leads(updated_since):
upsert_deal(conn, lead)
conn.commit()
conn.close()
Redash: ключевые SQL-запросы для дашборда продаж
Конверсия воронки по этапам:
SELECT
stage_name,
COUNT(*) AS deals_count,
SUM(price) AS pipeline_value,
AVG(price) AS avg_deal_size,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 1) AS pct_of_total
FROM kommo_deals
WHERE created_at >= NOW() - INTERVAL '30 days'
AND status_id NOT IN (142, 143) -- исключить Won/Lost для pipeline
GROUP BY stage_name
ORDER BY deals_count DESC;
Динамика закрытых сделок по неделям:
SELECT
DATE_TRUNC('week', closed_at) AS week,
responsible_name AS manager,
COUNT(*) FILTER (WHERE status_id = 142) AS won_count,
SUM(price) FILTER (WHERE status_id = 142) AS won_revenue,
COUNT(*) FILTER (WHERE status_id = 143) AS lost_count
FROM kommo_deals
WHERE closed_at >= NOW() - INTERVAL '12 weeks'
GROUP BY 1, 2
ORDER BY 1 DESC, 3 DESC;
Win Rate по менеджеру:
SELECT
responsible_name,
COUNT(*) FILTER (WHERE status_id = 142) AS won,
COUNT(*) FILTER (WHERE status_id = 143) AS lost,
ROUND(100.0 *
COUNT(*) FILTER (WHERE status_id = 142) /
NULLIF(COUNT(*) FILTER (WHERE status_id IN (142, 143)), 0)
, 1) AS win_rate_pct,
SUM(price) FILTER (WHERE status_id = 142) AS total_revenue
FROM kommo_deals
WHERE closed_at >= NOW() - INTERVAL '90 days'
GROUP BY responsible_name
ORDER BY win_rate_pct DESC;
Redash REST API: получение результатов запроса
Redash предоставляет REST API для программного доступа к результатам. Используется для интеграций (например, отправить данные в Slack):
REDASH_URL = 'https://redash.yourcompany.com'
REDASH_API_KEY = 'your_user_api_key' # из Settings -> API Key
def get_query_results(query_id: int) -> dict:
# Сначала запустить (обновить) запрос
requests.post(
f'{REDASH_URL}/api/queries/{query_id}/refresh',
headers={'Authorization': f'Key {REDASH_API_KEY}'}
)
# Получить результаты
response = requests.get(
f'{REDASH_URL}/api/queries/{query_id}/results.json',
headers={'Authorization': f'Key {REDASH_API_KEY}'}
)
return response.json()['query_result']['data']['rows']
# Пример: получить топ-3 менеджера за неделю и отправить в Slack
rows = get_query_results(WEEKLY_MANAGERS_QUERY_ID)
for row in rows[:3]:
print(f"{row['responsible_name']}: {row['won_count']} сделок")
Redash Embed в Slack/Notion
Redash поддерживает публикацию дашбордов по ссылке. Для внутреннего использования — без дополнительной аутентификации через ?api_key= в URL iframe.
Еженедельный автоматический отчёт в Slack:
import schedule
def send_weekly_report():
rows = get_query_results(WEEKLY_SUMMARY_QUERY_ID)
text_lines = [f'*Итоги недели:*']
for row in rows:
text_lines.append(
f"{row['responsible_name']}: {row['won_count']} Won, "
f"${row['won_revenue']:,.0f} выручка"
)
text_lines.append(f'\n<{REDASH_URL}/dashboard/sales|Открыть дашборд>')
requests.post(SLACK_WEBHOOK_URL, json={'text': '\n'.join(text_lines)})
schedule.every().monday.at('09:00').do(send_weekly_report)
Реальный кейс
B2B SaaS-компания (30 сотрудников, EU-рынок, Kommo + self-hosted infrastructure):
- До: head of sales каждый понедельник запрашивал отчёты у менеджеров вручную. Данные приходили в разных форматах, занимали 2–3 часа на сводку.
- После: ETL-скрипт каждый час синхронизирует Kommo -> Postgres. Redash-дашборд с конверсией воронки, win rate по менеджерам и динамикой выручки обновляется в реальном времени. Еженедельный отчёт приходит в Slack каждый понедельник в 9:00 автоматически.
- Выбор Redash: команда технически грамотная, аналитик пишет SQL — Redash идеально подходит. Metabase сочли избыточным по UI, Superset — сложным в настройке.
Схожий паттерн с Apache Superset — в статье Kommo + Superset: там мощнее визуализации, но выше порог настройки.
Для кого актуально
- В команде есть аналитик или разработчик, который пишет SQL
- Нужна self-hosted BI без Looker, Tableau или Metabase Cloud
- Важен контроль над данными — никаких сторонних облаков с данными клиентов
- Задача: дашборды для head of sales с конверсией, win rate, pipeline по менеджерам
- Kommo не даёт достаточно гибкости в стандартных отчётах
Часто задаваемые вопросы
Redash self-hosted — насколько сложно развернуть?
Redash разворачивается через Docker Compose за 15–30 минут на любом VPS. Официальный docker-compose.yml включает Postgres (для метаданных Redash), Redis и сам сервер. Основная сложность — настройка подключения к вашему Postgres с данными Kommo и выпуск SSL-сертификата для домена.
Как часто нужно синхронизировать данные из Kommo?
Для оперативных дашбордов (менеджеры смотрят в течение дня) — каждые 15–30 минут. Для еженедельных отчётов руководителю — раз в час или по расписанию. Kommo API поддерживает фильтрацию по updated_at — incremental load не нагружает ни API, ни базу.
Можно ли подключить Redash напрямую к Kommo без базы данных?
Нет. Redash работает с SQL-совместимыми источниками: Postgres, MySQL, BigQuery, ClickHouse и др. Kommo API не является SQL-источником. Промежуточная БД обязательна — это часть архитектуры.
Redash vs Google Data Studio (Looker Studio) для Kommo-аналитики?
Looker Studio — облачный инструмент, не требует инфраструктуры, проще для нетехнических пользователей. Redash — self-hosted, полный контроль данных, SQL-ориентированный. Если данные клиентов не могут покидать EU-периметр — Redash. Если нужна быстрая визуализация без DevOps — Looker Studio.
Нужен ли платный Redash?
Redash полностью open-source (BSD лицензия). Коммерческая поддержка от airSlate доступна, но для большинства компаний self-hosted версия достаточна. Hosted-версия (redash.io) была закрыта — сейчас только self-hosting.
Итого
- Паттерн: ETL Kommo API -> Postgres (incremental, каждые 15–30 мин) -> Redash SQL-запросы -> дашборды
- Ключевые метрики: конверсия воронки, win rate по менеджерам, динамика выручки по неделям
- Redash REST API: GET
/api/queries/{id}/results.json— получение результатов для внешних интеграций (Slack-отчёты) - Self-hosted на Docker Compose: полный контроль данных, никакого вендор-локина
- Типовой срок: ETL + схема — 1 неделя, дашборды — ещё 1 неделя
Если у вас Kommo и вы хотите self-hosted BI без Tableau и Looker — опишите какие метрики нужны head of sales. Exceltic.dev спроектирует схему БД и ключевые запросы для дашборда.