Skip to content
shaerware edited this page Nov 2, 2026 · 9 revisions

CRM (amoCRM)

⭐ КП прямо из мессенджера. Во вкладках CRM «КП» и «Подтверждение» — коммерческие предложения, которые ассистент собирает из переписки сам; утвердить их можно и отсюда, и кнопкой в Telegram / командой в WhatsApp. Подробно: KP.

Интеграция с amoCRM v4 API: OAuth2 аутентификация, синхронизация контактов, лидов и задач, автоматическое создание сделок.

Скриншот

CRM

Концепция

Интеграция с amoCRM v4 API через OAuth2 с автоматическим обновлением токенов. Система позволяет:

  • Двусторонняя синхронизация: импорт контактов и лидов из amoCRM → экспорт данных из бота в CRM
  • Автоматическое создание сделок: при обращении в Telegram бот или виджет
  • Webhook-интеграция: получение событий от amoCRM (обновление контактов, сделок, задач)
  • Управление воронками: выбор pipeline и статуса для новых лидов
  • Docker/VPN proxy: поддержка прокси для работы из контейнера

Страница CRM

Управление интеграцией через CrmView.vue с 8 вкладками: Подтверждение (очередь ответов ассистента, открывается первой), КП (коммерческие предложения в PIM), Клиенты (что ассистент помнит о клиентах), Мониторинг (здоровье каналов), Settings (настройки), Kanban (доска сделок), Deals (таблица сделок), Inbox (сообщения). «Подтверждение», «КП», «Клиенты» и «Мониторинг» доступны и без подключённого amoCRM.


Вкладка: Подтверждение (очередь ответов)

Ассистент не отвечает клиенту сам, пока ответ не одобрит человек.

Режим по каналам. Для каждого WhatsApp-номера выбирается режим:

  • Сразу клиенту (auto, по умолчанию) — как раньше, ответ уходит немедленно.
  • Подтверждение (approval) — ответ ассистента ложится в очередь, а клиент сразу получает только автоответ о приёме («Здравствуйте! Ваше сообщение получено, менеджер ответит вам в ближайшее время.»). Текст автоответа можно задать свой — без цен, товаров, сроков и обещаний. Он уходит один раз на серию сообщений (не чаще раза в 30 минут на номер).

Режим переключается сразу, без перезапуска бота.

Очередь. Карточка показывает, что написал клиент, и ответ ассистента. Действия:

  • Отправить — ответ уходит клиенту в WhatsApp.
  • Изменить → Отправить с правкой — уходит исправленный текст; исходный черновик остаётся виден под спойлером.
  • Отклонить — клиент ничего не получит.
  • Удалить — убрать запись из очереди (только sales:manage).

Фильтры: ждут / ошибки / отправлены / отклонены / все.

Особенности:

  • После правки или отклонения история диалога переписывается под то, что клиент реально получил — ассистент не «помнит» черновик, которого клиент не видел.
  • Заметка в сделку amoCRM пишется с отправленным текстом, а не с черновиком.
  • Если доставка не удалась (телефон отключён), запись уходит в «Ошибки» с причиной, её можно отправить ещё раз. Двойное нажатие не отправит ответ дважды.
  • Если бот не смог связаться с оркестратором, он ведёт себя по последнему известному режиму: в режиме подтверждения ничего не отправляет.

Пока через очередь идёт только WhatsApp; Telegram и виджет — только мониторинг.

Вкладка: КП (коммерческие предложения)

Сами КП живут в PIM клиента (ar-gs.ru): там номер, PDF и ссылка для клиента. Секретарь отвечает за то, чего PIM не делает: кто подтвердил КП, как оно дошло до клиента и что делать, пока клиент молчит или срок истекает. Подключение задаётся ключом ARGS_PIM_API_KEY в .env (ключ выпускают в PIM: Настройки → API).

Черновик. Когда клиент в WhatsApp просит КП («пришлите КП», «выставьте счёт»), ассистент читает переписку, находит позиции в каталоге с ценами и создаёт в PIM черновик. Позиции без точного совпадения или без цены в КП не попадают: менеджер видит их отдельным списком «Не вошло в КП». КП можно создать и вручную: кнопка Новое КП, позиции вводятся руками или собираются из диалога по ID сессии.

Подтверждение. Клиенту ничего не уходит без человека. WhatsApp-КП появляется и здесь, и во вкладке «Подтверждение» с пометкой «КП-00XX». Подтверждение в любом из двух мест — одно и то же действие. Ссылка из PIM подставляется в момент отправки, а просроченное КП отправить нельзя. Способы отправки: WhatsApp (через очередь), письмом из PIM (PDF во вложении) или ссылкой, которую менеджер отправит сам.

Действия: изменить текст и способ отправки, сменить срок действия, отклонить, отметить «принято клиентом» или «проиграно», перевыпустить с новым сроком и пересчётом цен по каталогу, открыть PDF или КП в PIM, посмотреть историю. Фильтры: на подтверждении / у клиента / истекли / ошибки / приняты / закрыты.

Срок действия. Варианты 1, 2 и 3 дня, по умолчанию 3 (меняется в настройках вкладки). «N дней» значит: до сегодняшней даты + N включительно, по времени Казахстана. Истёкшее КП не продлевается молча: его нужно перевыпустить, потому что цены и наличие могли измениться.

Таймеры (проверка раз в 10 минут, правила можно добавлять, менять и выключать):

Таймер по умолчанию Когда Что делает
Черновик ждёт подтверждения 2 ч после создания уведомляет менеджера
Клиент не открыл КП 12 ч после отправки уведомляет менеджера
Открыл, но не ответил 12 ч после просмотра готовит сообщение клиенту
Срок заканчивается за 6 ч до конца срока готовит напоминание клиенту
КП истекло сразу после срока «пересчитать и перевыпустить»
Закрыть как проигранное 72 ч после срока ставит статус «проиграно»

Сообщения клиенту от таймеров тоже ждут подтверждения в очереди. Уведомления менеджеру собираются в блоке «Требуют внимания» и дублируются в Telegram.

Синхронизация с PIM. Статусы «отправлено / просмотрено / принято» подтягиваются из PIM. КП, созданные вручную прямо в PIM, тоже попадают во вкладку (за последние 30 дней), и таймеры следят за ними. Если менеджер отправил КП из PIM сам, копия в очереди снимается: дважды КП не уйдёт.

КП в мессенджере. Уведомление о новом черновике приходит в Telegram и на служебные WhatsApp-номера (настройки вкладки: «WhatsApp-номера для уведомлений» и номер-отправитель) и содержит сам КП: что просит клиент, что подобрал ассистент (с тем, как клиент назвал каждую позицию), что не вошло в КП и текст, который уйдёт клиенту. Действовать можно прямо оттуда:

  • Telegram — кнопки «✅ Отправить», «✏️ Изменить текст», «❌ Отменить». Для правки бот просит ответить на его сообщение новым текстом и присылает обновлённую карточку.
  • WhatsApp — ответом: «ок 18» (отправить), «текст 18: …» (заменить текст, не отправляя), «отмена 18», «кп» (что ждёт подтверждения). «18» — номер КП без нулей; если черновик один, номер можно не писать.

Это те же действия, что на вкладке, и отправку клиенту по-прежнему подтверждает человек. Сообщения со служебных номеров ассистенту не передаются — директор не получит консультацию как клиент.

Каких методов не хватает в API PIM (продление без смены номера, статус «проиграно», удаление черновика, уведомления), описано для разработчика PIM в docs/pim-api-for-kp.md. Секретарь начнёт пользоваться этими методами сам, как только они появятся.

Вкладка: Клиенты (память ассистента)

Ассистент в WhatsApp больше не начинает каждый разговор с нуля. Когда диалог затихает (по умолчанию на 30 минут), он дописывает в карточку клиента то новое, что узнал: кто это, компания и город, что спрашивал, что ему предлагали, о чём договорились и что осталось открытым. В следующем разговоре карточка идёт ассистенту в контекст вместе с заметками менеджера и актуальным списком КП этого клиента (номер, статус, срок действия). Карточка привязана к номеру клиента, а не к конкретному диалогу, поэтому переживает и перезапуск бота.

Цены и наличие в карточку не попадают никогда — строки с суммами и «в наличии» удаляются автоматически, даже если их вписать руками. Цены ассистент берёт только из свежего поиска по каталогу.

Включение. Отдельно для каждого WhatsApp-канала — галочкой в верхнем блоке вкладки; там же время паузы, после которой диалог считается законченным. По умолчанию выключено. Перезапуск бота не нужен.

Действия с карточкой: изменить имя, текст карточки и заметки менеджера (заметки ассистент видит, но никогда не переписывает), Обновить по диалогу — дописать новые сообщения сразу, не дожидаясь паузы, и удалить — ассистент забудет клиента полностью, сами диалоги останутся. Поиск — по имени, номеру и тексту карточки.

Вкладка: Мониторинг

Сводка здоровья каналов, обновляется каждые 30 секунд:

  • Общий статус и список проблем (бот не запущен, телефон не на связи, диалоги без ответа, просроченная очередь, недоставленные ответы, amoCRM не подключён).
  • Очередь: сколько ждёт подтверждения, возраст самого старого, сколько не доставлено и отправлено сегодня.
  • По каждому WhatsApp-номеру: работает ли бот, связь телефона (QR-мост), последнее входящее и последний ответ, число диалогов за 72 часа и диалоги без ответа — клиент написал, а ответа нет дольше порога. Это ловит ситуацию, когда телефон «на связи», а бот молчит.
  • Telegram и виджет: последнее входящее/ответ, диалоги без ответа.
  • amoCRM: подключён ли, создаются ли лиды автоматически.
  • Пороги тревоги: через сколько минут диалог без ответа считается проблемой (по умолчанию 10) и ответ в очереди — просроченным (30).

API: GET /admin/outbound/monitor, GET /admin/outbound/messages?status=, PUT /admin/outbound/messages/{id}, POST /admin/outbound/messages/{id}/approve ({"text": "..."} — необязательная правка), POST /admin/outbound/messages/{id}/reject, DELETE /admin/outbound/messages/{id}, GET/PUT /admin/outbound/settings. Права: просмотр — sales:view, действия — sales:edit, настройки и удаление — sales:manage.


Вкладка: Kanban (Доска сделок)

Визуальная доска сделок в стиле Trello/Kanban (CrmKanban.vue):

Функция Описание
Выбор воронки Выпадающий список всех pipelines из amoCRM
Колонки по статусам Горизонтальные колонки — каждая соответствует статусу воронки
Drag & Drop Перетаскивание сделок между статусами через vuedraggable@next (SortableJS)
Изменяемая ширина колонок Перетаскивание разделителя между колонками, ширина сохраняется в localStorage
Сворачивание колонок Кнопка ChevronLeft в заголовке сворачивает колонку в узкую вертикальную полоску (48px) с цветной точкой, счётчиком и вертикальным названием. Клик по свёрнутой полоске раскрывает обратно. Состояние сохраняется в localStorage (crm-kanban-collapsed). Разделители ресайза скрываются между свёрнутыми колонками
Сброс ширины Кнопка ↺ сбрасывает все колонки до ширины по умолчанию (288px)
Горизонтальная прокрутка Drag-to-scroll по горизонтали
Ленивая загрузка Карточки подгружаются порциями по 30 шт., IntersectionObserver автоматически загружает ещё при прокрутке
Неразобранные сделки Автоматически подгружаются в первую колонку воронки (статус «Неразобранное»)
Авто-обновление Каждые 30 секунд
Карточка сделки Название, сумма, ответственный, контакт

Вкладка: Deals (Таблица сделок)

Табличный вид всех сделок (CrmDeals.vue):

Функция Описание
Поиск Фильтрация сделок по названию
Пагинация Кнопки «назад/вперёд» в тулбаре (между поиском и кнопкой «Создать»)
Детали сделки Модальное окно: контакты, примечания, теги
Создание сделки Диалог с полями: название, сумма, воронка, статус, контакт

Вкладка: Inbox (Единый хаб диалогов)

Unified inbox с двумя подвкладками (CrmInbox.vue):

Подвкладка: AI Chats (по умолчанию)

Все клиентские диалоги с ИИ-ботами, сгруппированные по источнику:

Функция Описание
Группировка по источнику Сессии разделены на сворачиваемые группы: Telegram, WhatsApp, Widget
Сворачиваемые группы Chevron-toggle (▶/▼) на заголовке группы, со счётчиком сессий
Поиск Фильтрация сессий по названию и тексту последнего сообщения (по всем группам)
Иконки источника Каждый источник отображается своей иконкой: Send (Telegram), MessageCircle (WhatsApp), Globe (Widget)
Просмотр переписки Выбор сессии → сообщения с markdown-рендерингом (marked + DOMPurify)
Ответ через стриминг Ввод текста → chatApi.streamMessage() → streaming AI reply
Авто-обновление Каждые 15 сек — список сессий, каждые 10 сек — сообщения

Данные: используется GET /admin/chat/sessions?group_by=source (группы: telegram, whatsapp, widget; admin-чаты исключены).

Подвкладка: amoCRM Inbox

Мессенджер-интерфейс для переписки с контактами через Amojo API (CrmInboxAmoCRM.vue). Видна только если настроены Amojo credentials.

Функция Описание
Список контактов Поиск контактов из amoCRM
Разрешение чата Выбор контакта → автоматический запрос getContactChats() → получение UUID чата
История сообщений Загрузка переписки из Amojo API
Отправка сообщений Ввод текста → POST /admin/crm/chats/{chat_id}/messages
Авто-обновление Каждые 10 секунд

Требования: для amoCRM Inbox необходимо настроить Amojo credentials (Amojo Scope ID, Channel Secret) во вкладке Settings.


Вкладка: Settings (Настройки)

Connection Status (Статус подключения)

Индикатор Описание
🟢 Подключено OAuth2 токены активны, аккаунт доступен
🔴 Не подключено Нет токенов или истекли
🟡 Ошибка Ошибка при последнем запросе

Settings Form (Настройки)

Поле Описание
Subdomain Поддомен amoCRM (например, mycompany для mycompany.amocrm.ru)
Client ID OAuth2 Client ID из настроек интеграции
Client Secret OAuth2 Client Secret
Redirect URI URI для OAuth callback (автозаполняется: https://yourdomain.com/admin/crm/callback)
Sync Contacts Включить синхронизацию контактов
Sync Leads Включить синхронизацию лидов
Sync Tasks Включить синхронизацию задач
Auto Create Lead Автоматически создавать лид при первом обращении
Lead Pipeline ID ID воронки для новых лидов
Lead Status ID ID статуса для новых лидов

OAuth2 Flow

Шаг 1: Настройка интеграции в amoCRM

  1. Зайдите в Настройки → Интеграции → Создать интеграцию
  2. Выберите тип Private app (для приватного доступа) или OAuth2 (для публичной интеграции)
  3. Скопируйте Client ID и Client Secret
  4. Укажите Redirect URI: https://yourdomain.com/admin/crm/callback
  5. Выберите права доступа (scope): crm (контакты, сделки, задачи)

Шаг 2: Подключение в админ-панели

  1. Вставьте Subdomain, Client ID и Client Secret в форму
  2. Нажмите "Получить URL авторизации" → появится кнопка "Подключить amoCRM"
  3. Кликните на кнопку → откроется popup с OAuth-страницей amoCRM
  4. Подтвердите доступ в amoCRM
  5. После редиректа система получит токены и отобразит статус "Подключено"

Приватные интеграции (без OAuth redirect)

Для приватных интеграций amoCRM код авторизации (authorization_code) получается из настроек интеграции:

  1. Создайте приватную интеграцию в amoCRM
  2. Скопируйте код авторизации из раздела "Получить код"
  3. Используйте этот код вместо OAuth2 flow
  4. Система автоматически обменяет код на токены

Account Info (Информация об аккаунте)

После подключения отображается:

Поле Описание
Account Name Название аккаунта
Subdomain Поддомен
User Name Имя пользователя
User Email Email пользователя

Sync Controls (Управление синхронизацией)

Кнопка Действие
Синхронизировать Запустить синхронизацию контактов/лидов
Отключить Отозвать токены OAuth2

Sync Log (Лог синхронизации)

История операций синхронизации:

Поле Описание
Timestamp Дата и время
Operation Тип операции (sync_contacts, sync_leads, create_lead)
Status Статус (success, error)
Details Детали (количество синхронизированных записей, ошибки)

Статистика

Метрика Описание
Contacts Count Количество контактов в amoCRM
Leads Count Количество сделок
Last Sync Time Время последней успешной синхронизации

CRM Dataset (База знаний из amoCRM)

Синхронизация данных amoCRM в RAG-коллекцию для поиска по сделкам через LLM. Позволяет ИИ-секретарю отвечать на вопросы про конкретные сделки, клиентов, суммы и менеджеров.

Как работает

  1. Сбор данных — POST /admin/crm/dataset-sync загружает все воронки, сделки, контакты и пользователей из amoCRM
  2. Обогащение — контакты дополняются телефонами/email (get_contacts_by_ids()), ответственные менеджеры резолвятся в имена (get_users())
  3. Генерация markdown — по каждой воронке создаётся crm-pipeline-{id}.md + общая сводка crm-summary.md
  4. Индексация — файлы пишутся в data/crm-dataset/, создаётся коллекция "amoCRM", BM25/embedding индекс обновляется

Содержимое документов

Каждая сделка записывается как секция с полными данными для поиска:

## Сделка #8847291: Разработка сайта — Переговоры (100 000 ₽)
- **ID сделки**: 8847291
- **Сумма**: 100 000 ₽ (100000)
- **Контакт**: Иванов Пётр | тел: +7 (999) 123-45-67 (79991234567) | email: ivanov@mail.ru
- **Тип продукта**: Корпоративный сайт
- **Источник**: Яндекс Директ
- **Теги**: VIP, срочно
- **Создана**: 15.01.2026
- **Обновлена**: 18.02.2026
- **Ответственный**: Алексей Сидоров

Оптимизация для поиска (BM25)

Проблема Решение
Форматированная цена 100 000 не матчит запрос 100000 Цена дублируется: 100 000 ₽ (100000)
Телефон +7 (999) 123-45-67 фрагментируется токенизатором Digits-only дубль: (79991234567)
responsible_user_id — числовой ID Резолвится в имя менеджера через get_users()
Кастомные поля (товар, источник) игнорировались custom_fields_values извлекаются в markdown
ID сделки не записывался Заголовок ## Сделка #8847291: + строка **ID сделки**

API эндпоинты

Метод Endpoint Описание
POST /admin/crm/dataset-sync Полная синхронизация: воронки + сделки + контакты + пользователи → markdown → RAG
GET /admin/crm/dataset-status Статус: количество документов, секций, дата последней синхронизации
DELETE /admin/crm/dataset Очистить датасет (только admin)

Ответ dataset-sync

{
  "status": "ok",
  "pipelines": 3,
  "leads_total": 150,
  "contacts_enriched": 89,
  "users_resolved": 5,
  "files_written": 4,
  "files_removed": 4,
  "collection_id": 2,
  "synced_at": "19.02.2026 14:30"
}

Архитектура

app/routers/amocrm.py (crm_dataset_sync)
  ├── get_pipelines()           → воронки + статусы
  ├── get_all_leads_paginated() → все сделки (250/стр)
  ├── get_contacts_by_ids()     → контакты с телефонами/email (50/батч)
  ├── get_users()               → пользователи → {id: имя}
  └── app/services/crm_dataset_service.py
        ├── build_pipeline_document()  → markdown по воронке
        └── build_summary_document()   → сводка + статистика

Файлы: data/crm-dataset/crm-pipeline-*.md, data/crm-dataset/crm-summary.md Коллекция: slug amocrm, base_dir="data/crm-dataset"


Docker/VPN Proxy

Если Docker-контейнер не может напрямую достучаться до amoCRM (например, VPN настроен на хосте), используйте прокси:

Шаг 1: Запустить прокси на хосте

python scripts/amocrm_proxy.py

Прокси запустится на порту 8888 и пробросит запросы к amoCRM.

Шаг 2: Указать прокси в Docker

Добавьте в .env:

AMOCRM_PROXY=http://172.17.0.1:8888

Где 172.17.0.1 — IP хост-машины из Docker-сети.

Webhook

Endpoint для получения событий от amoCRM: POST /webhooks/amocrm

Настройка вебхука в amoCRM

  1. Перейдите в Настройки → Интеграции → Вебхуки
  2. Укажите URL: https://yourdomain.com/webhooks/amocrm
  3. Выберите события: add, update, delete для контактов, сделок, задач
  4. Сохраните

Обрабатываемые события

Событие Описание
contacts:add Создан контакт
contacts:update Обновлён контакт
leads:add Создана сделка
leads:update Обновлена сделка
leads:status Изменён статус сделки
tasks:add Создана задача
tasks:update Обновлена задача

Автоматическое создание лидов

Если включено Auto Create Lead, при первом обращении пользователя из Telegram бота или виджета:

  1. Система проверяет, существует ли контакт с этим Telegram User ID
  2. Если нет — создаёт контакт
  3. Создаёт сделку (лид) в указанной воронке (Lead Pipeline ID) и статусе (Lead Status ID)
  4. Связывает сделку с контактом

API эндпоинты

Метод Endpoint Описание
GET /admin/crm/config Получить конфигурацию CRM
PUT /admin/crm/config Обновить конфигурацию
GET /admin/crm/auth-url Получить URL для OAuth2 авторизации
GET /admin/crm/callback OAuth2 callback (обмен кода на токены)
GET /admin/crm/status Статус подключения (токены активны?)
POST /admin/crm/disconnect Отключить amoCRM (отозвать токены)
GET /admin/crm/contacts Список контактов из amoCRM
GET /admin/crm/leads Список сделок из amoCRM
GET /admin/crm/pipelines Список воронок и статусов
POST /admin/crm/sync Запустить синхронизацию
GET /admin/crm/sync/log Получить лог синхронизации
GET /admin/crm/account Информация об аккаунте
GET /admin/crm/leads/{id} Детали сделки
PATCH /admin/crm/leads/{id} Обновить сделку (статус, pipeline)
GET /admin/crm/leads/by-pipeline/{pipeline_id} Сделки по воронке (для Kanban)
GET /admin/crm/events Лента событий
GET /admin/crm/contacts/{id}/chats Чаты контакта (Amojo)
GET /admin/crm/chats/{chat_id}/history История сообщений чата (Amojo)
POST /admin/crm/chats/{chat_id}/messages Отправить сообщение в чат (Amojo)
POST /admin/crm/dataset-sync Синхронизация данных в RAG-коллекцию
GET /admin/crm/dataset-status Статус CRM-датасета
DELETE /admin/crm/dataset Очистить CRM-датасет (admin only)

Пример запроса: Получить контакты

GET /admin/crm/contacts?limit=50&offset=0
Authorization: Bearer <jwt_token>

Пример ответа: Список контактов

{
  "_embedded": {
    "contacts": [
      {
        "id": 12345,
        "name": "Иван Иванов",
        "custom_fields_values": [
          {
            "field_code": "PHONE",
            "values": [{"value": "+79001234567"}]
          },
          {
            "field_code": "EMAIL",
            "values": [{"value": "ivan@example.com"}]
          }
        ]
      }
    ]
  }
}

Error Handling (Обработка ошибок)

AmoCRMAPIError

Общая ошибка API amoCRM:

raise AmoCRMAPIError(f"API error: {response.status_code}")

AmoCRMTokenExpired

Истёк токен доступа (401):

raise AmoCRMTokenExpired("Access token expired")

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

Rate Limiting (429)

При превышении лимита запросов (429 Too Many Requests):

  • Максимум повторов: 3 (MAX_429_RETRIES)
  • Задержка между повторами: 1.5 секунды (RETRY_DELAY_SECONDS)
  • Система использует экспоненциальный backoff
if response.status_code == 429 and retry_count < MAX_429_RETRIES:
    await asyncio.sleep(RETRY_DELAY_SECONDS * (retry_count + 1))
    return await self._request(...)

Redis-кэширование API

Для снижения нагрузки на amoCRM API (лимит ~7 req/s) используется Redis-кэширование ответов. Kanban-доска обновляется каждые 30 секунд, при этом каждый рефреш вызывает GET /leads/by-pipeline/{id}, который пагинирует все сделки (250/стр). Без кэша это создаёт большое количество запросов к amoCRM.

Кэшируемые эндпоинты

Эндпоинт TTL Ключ кэша
GET /pipelines 300с (5 мин) amocrm:pipelines:{subdomain}
GET /leads/by-pipeline/{id} 60с (1 мин) amocrm:pipeline_leads:{subdomain}:{pipeline_id}
GET /leads/unsorted 30с amocrm:unsorted:{subdomain}:{page}:{limit}
GET /leads/{id} 120с (2 мин) amocrm:lead:{subdomain}:{lead_id}

GET /leads (поиск с query) не кэшируется — динамические запросы, низкая частота.

Инвалидация кэша

Кэш автоматически сбрасывается при мутациях:

Мутация Что инвалидируется
PATCH /leads/{id} amocrm:lead:*:{lead_id} + amocrm:pipeline_leads:* + amocrm:unsorted:*
POST /leads amocrm:pipeline_leads:* + amocrm:unsorted:*
POST /webhooks/amocrm (events: leads[*]) amocrm:pipeline_leads:* + amocrm:unsorted:* + amocrm:lead:*
POST /disconnect amocrm:* (все ключи)

Graceful fallback

Redis опционален. Если Redis недоступен — все эндпоинты работают напрямую с amoCRM API, как и до внедрения кэша. При восстановлении Redis кэширование возобновляется автоматически.

Проверка

# Посмотреть ключи кэша
redis-cli keys "amocrm:*"

# Очистить весь CRM-кэш
redis-cli keys "amocrm:*" | xargs redis-cli del

Архитектура сервиса

app/routers/amocrm.py
  → app/services/amocrm_service.py (AmoCRMService)
    → AmoCRMService (modules/crm/service.py)
      → AmoCRMRepository (db/repositories/amocrm.py)
        → AmoCRMConfig, AmoCRMToken (modules/crm/models.py)

AmoCRMService

Чистый async HTTP-клиент без прямого DB-доступа. Методы:

  • get_contacts(limit, offset) — список контактов
  • get_contacts_by_ids(contact_ids, batch_size=50) — батч-запрос контактов по ID с телефонами/email
  • get_leads(limit, offset) — список сделок
  • get_all_leads_paginated() — все сделки (250/стр, пагинация до конца)
  • get_users() — все пользователи аккаунта → {id: имя}
  • create_lead(name, price, pipeline_id, status_id, contact_id) — создать сделку
  • get_pipelines() — список воронок и статусов
  • refresh_tokens() — обновить токены при 401
  • exchange_code(code) — обмен authorization_code на токены

AmoCRMService

Доменный сервис в modules/crm/service.py (ранее AsyncAmoCRMManager в db/integration.py) для работы с конфигурацией и токенами в БД:

  • get_config() — получить конфигурацию
  • save_config(config) — сохранить конфигурацию
  • get_tokens() — получить токены
  • save_tokens(tokens) — сохранить токены
  • delete_tokens() — удалить токены (отключение)

Proxy Support

Если установлена переменная окружения AMOCRM_PROXY, все запросы проксируются:

proxies = {"http://": os.getenv("AMOCRM_PROXY"), "https://": os.getenv("AMOCRM_PROXY")}
async with httpx.AsyncClient(proxies=proxies) as client:
    response = await client.get(url)

RBAC

  • Admin — полный доступ к CRM-интеграции
  • User — только своя конфигурация (по owner_id)
  • Guest — только чтение

← Sales | Usage →

Clone this wiki locally