Граф Wiki

Дополнительные разделы

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

Дополнительные возможности
Содержание раздела

Внутренний чат персонала

Встроенный мессенджер для сотрудников — выдвижная панель «Чаты» доступна с любой страницы админки (иконка-колокольчик в навбаре). Личные диалоги, групповые чаты, чат «Поддержка разработчика» (AI-ассистент + эскалация специалисту), вкладка «Уведомления» с системными событиями.

Внутренний чат — панель

Отчёты по расписанию в чат

Любой SQL-отчёт (каталог отчётов) можно отправлять в выбранный чат автоматически: ежедневно / еженедельно (Пн) / ежемесячно (1-е число) в заданное время. Сообщение — компактная текстовая таблица (первые 4 колонки, до N строк).

flowchart LR
    BEAT["Celery beat
dispatch_scheduled_reports"] --> DUE{"Пора?
(час/мин, частота,
не слали сегодня)"} DUE -- да --> PAR["Авто-параметры:
даты = период по частоте
(день/7дн/30дн),
строковые фильтры пусты"] PAR --> SQL["_execute_sql()
(та же подстановка,
что в ручном запуске)"] SQL --> MSG["ChatMessage role=system
«📊 Отчёт …» в комнату"] MSG --> TOAST["Toast + бейдж
у участников"]

Параметризованные отчёты (:Дата_начала|date$ и т.п.) поддерживаются: даты авто-заполняются периодом по частоте расписания, строковые фильтры трактуются как «без ограничения».

Toast-уведомления о новых сообщениях

Новое личное или групповое сообщение показывает toast в правом нижнем углу (на мобильном — полноширинный снизу): аватарка отправителя (реальное фото профиля или инициалы), имя, время, многострочный текст, кнопки «Ответить» (открывает нужный чат) и закрыть. Эффект матового стекла (backdrop-blur), тёмная тема, авто-скрытие через 9 сек, стек до 4 штук. Открытая комната не тостится; свои сообщения не триггерят.

Toast новых сообщений чата

Интерфейс: темы и единые паттерны

Переключатель темы (Система / Светлая / Тёмная)

В выпадающем меню профиля (аватар в навбаре) — трёхпозиционный переключатель темы. «Система» следует настройке ОС (авто-смена день/ночь), выбор запоминается за пользователем. Тёмная тема покрывает весь админ-интерфейс (theme-smit.css).

Переключатель темы в меню профиля

Единые паттерны настроек

Видеонаблюдение — доп.услуга «под ключ»

Модуль продажи видеонаблюдения через биллинг: от заявки и обследования до монтажа, recurring-подписок и документов. Реализован как собственный сервисно-инсталляционный домен (объект → проект → камеры/оборудование → подписки), а не плоская интеграция. Использует существующее ядро биллинга: тарификацию Usluga/UsersUsluga, ежемесячное списание billing_worker, FinanceOperations, генерацию PDF (WeasyPrint), фискализацию.

3 слоя дохода — как продавать правильно

3 слоя дохода видеонаблюдения

Ключевой принцип: маржа бизнеса — в recurring-подписке, а не в разовом монтаже. Продажа только «установки под ключ» убыточна, потому что облако (хранилище, трафик) — постоянная затрата, которую нужно отбивать ежемесячной платой.

СлойУслугаЦенаТип
1. Разовый проектУстановка камеры под ключ13 500 ₽разово
2. Облако на камеруCloud Start (архив 7 дн)490 ₽/камера/месrecurring
2. Облако на камеруCloud Pro (архив 30 дн + аналитика)690 ₽/камера/месrecurring
3. Сервис на объектСервис Home (до 4 камер, ТО+гарантия)390 ₽/месrecurring
3. Сервис на объектСервис Pro (5+ камер, SLA-выезд)990 ₽/месrecurring

Пример экономики (частный дом, 4 камеры): установка 13 500 ₽ разово + recurring 4×490 (облако) + 390 (сервис) = 2 350 ₽/мес = 28 200 ₽/год. Recurring-слой — это то, чего нет при продаже «только установки».

Цены хранятся в каталоге услуг (Usluga.summa, тип «Видеонаблюдение») и редактируются оператором. Конкретная сумма подписки берётся из услуги/плана, но может быть переопределена индивидуально на клиенте.

Жизненный цикл сделки — от заявки до ежемесячной выручки:

flowchart LR
  L["📥 Заявка
VideoLead"] --> P["📋 Проект
обследование · КП · договор"] P --> I["🔧 Монтаж + ПНР
разовый платёж 13 500 ₽"] I --> O["🏠 Объект сдан
VideoObject"] O --> S["🔁 Подписки
облако + сервис"] S --> B["💰 billing_worker
списание/мес с видео-счёта"] B -.->|каждый месяц| S style L fill:#eef2ff,stroke:#6366f1 style I fill:#fef9c3,stroke:#ca8a04 style S fill:#dcfce7,stroke:#16a34a style B fill:#43b77a,stroke:#2d9a5f,color:#fff

Раздел /admin/video/

Раздел сайдбара «Видеонаблюдение» с 5 страницами: дашборд, проекты (канбан), объекты, сервисные планы, заявки.

Дашборд видеонаблюдения

Канбан проектов видеонаблюдения

UI/UX раздела

В разделе есть фильтры, редактирование в модальных окнах, просмотр камер, тёмная тема и работа с телефона:

Объекты — фильтры, чипы, цвет баланса

Модалка просмотра камеры — live + архив

Настройки видеонаблюдения в 2 колонки

Превью камер, просмотр в модалке и «Диск, Гб»

Доработка раздела «Объекты» и панели объекта: камеры теперь с превью и просмотром live/архива прямо в модальном окне, без перехода на другую страницу.

Камера в панели объекта (превью — snapshot go2rtc) → «Трансляция» → Модалка: live-плеер (go2rtc stream.html в iframe) · «Архив» → Модалка: embed-страница — live + таймлайн 24ч · «Поток» → Модалка: WebRTC (low-latency)
Сегменты архива: MediaMTX → через прокси (роль GCS) → Google Cloud Storage → подсчёт размера → Колонка «Диск, Гб» (по объекту)

Объекты: колонки «Компания» (компактная) и «Диск, Гб»

Панель объекта: камеры с превью и кнопками Трансляция / Архив / Поток

Модальное окно трансляции камеры (live-плеер go2rtc)

Модальное окно архива камеры: live + таймлайн записей за 24 часа

Архив в GCS через прокси (роль «GCS»)

Из российских сетей storage.googleapis.com и oauth2.googleapis.com недоступны напрямую. Чтобы биллинг мог считать размер архива и выгружать сегменты в облако, добавлена отдельная роль «GCS» у прокси-сервера.

Меню прокси: пункт «Добавить роль GCS» рядом с ролями Telegram / Нейросеть

Отчётность ЧОП (Фаза 4) — SLA, uptime, выручка

Раздел /admin/video/reports/ («Видео → Отчётность ЧОП») — единый экран по охраняемым объектам ЧОПа. Отвечает на три вопроса заказчика: насколько стабильна связь (SLA-uptime %), как быстро оператор реагирует на тревоги (время реакции + % в пределах SLA) и сколько объект приносит в месяц (recurring-выручка). Это доказуемый SLA — ключевая ценность, которую мы продаём охранным предприятиям.

Дашборд отчётности ЧОП: KPI, таблица по объектам, сводка по ЧОПам и операторам

KPI и как они считаются

KPIИсточник / формула
📡 Средний uptimedowntime из пар тревог offline → restore (SecurityAlarm); 1 − downtime/период. «—» если событий связи за период нет
⏱ Ср. реакцияacked_at − event_time по принятым тревогам (среднее + медиана)
✅ % в пределах SLAдоля тревог с реакцией ≤ VideoServicePlan.sla_react_minutes (числовое целевое время реакции по плану)
💰 Выручка/мес (MRR)Σ активных VideoSubscription.price_month (для облака × число камер)
🛡 Объектов охраныVideoObject(object_type='security') + сколько с заблокированными камерами
🚨 Тревог за периодвсего + сколько принято операторами

Под KPI — таблица по объектам (uptime, простой, тревоги, реакция, выручка), сводка по охранным предприятиям (группировка по security_company) и реакция по операторам (через acked_by).

Период и экспорт

Кнопки периода 7 / 30 / 90 дней / Год перезапрашивают данные через /admin/video/reports/json/ без перезагрузки. Экспорт CSV (/admin/video/reports/export/) — UTF-8 BOM + ; для Excel, колонки по объектам.

Отчётность ЧОП за 90 дней

SecurityAlarm (тревоги, реакция, offline/restore) + VideoSubscription (подписки) _collect_report() KPI + таблицы + CSV

Тёмная тема и мобильная вёрстка поддержаны; uptime начнёт наполняться, когда заработает приём сигналов с панелей (Фаза 3) — сейчас приёмник выключен до аудита оборудования ЧОПа.

Вкладка клиента и ЛК

На карточке клиента — вкладка «📹 Видеонаблюдение»: объекты → камеры / оборудование / подписки / проекты, с модалкой объекта (side-nav). У каждого объекта — отдельный лицевой счёт под видео (не смешивается с интернет-балансом). Recurring-подписки списываются с этого счёта; при минусе блокируются камеры/облако (настройка VIDEO_BLOCK_ON_NEGBAL), интернет не трогается.

Вкладка видеонаблюдения на карточке клиента

В личном кабинете (/lk/video/) и мобильном приложении (GET /mobile-api/v1/video/objects) клиент видит свои объекты, камеры, подписки, статус проекта, баланс видео-счёта и кнопку пополнения.

ЛК: объект «Ворота» — 4 камеры, подписка 390 ₽/мес, кнопка Пополнить

Как списываются подписки — пошагово

Видеоподписка — это recurring-услуга (UsersUsluga, system_type=20), которую billing_worker списывает 1-го числа каждого месяца. Деньги берутся с отдельного видео-счёта объекта (VideoObject.account) — интернет-баланс клиента не затрагивается.

🏠
Объект«Ворота» клиента
🔄
ПодпискаСервис на объект
390 ₽/мес
💳
Видео-счёт объектаотдельный от интернета
📅
1-го числаbilling_worker
списывает 390 ₽
🔒
Если баланс < 0камеры блокируются
(интернет работает)
Тип подпискиЦена считаетсяПример
Сервис / SLA (service)фиксировано за объектСервис Home 390 ₽, Сервис Pro 990 ₽
Облако (cloud)× число камерCloud Start 490 ₽ × 4 камеры = 1960 ₽/мес

Важно: цены подписок хранятся в каталоге услуг (тип «Видеонаблюдение»), а не в тарифах — линейка «Видеонаблюдение» в разделе «Тарифы» остаётся пустой намеренно. Скидки лояльности интернета на видео не распространяются — это отдельный продукт со своим счётом.

Архив записей в ЛК — календарь, выделение, выбор камеры

Страница архива камеры (/lk/video/archive/<camera>/) даёт клиенту просмотр и выгрузку записей за период хранения (retention из подписки).

Архив видео: календарь выбора даты с точками на днях с записью, таймлайн суток

Архив видео: выделение участка на таймлайне (5 ч 31 мин) + кнопки Просмотр / Скачать фрагмент / Сбросить

Уведомления о сбоях записи

Клиент может подключить уведомления о прерывании записи прямо в ЛК (вкладка «Архив» карточки объекта). Опция доступна только если у объекта активна запись. Канал — Telegram / Email / оба; контакты берутся из профиля клиента.

Серверный health-мониторинг (video-archive-health, каждые 10 мин) проверяет свежесть последнего записанного сегмента по каждой archive_enabled камере. Если записи нет дольше 15 минут — запись считается отвалившейся: billing_worker-независимая задача шлёт Telegram-алерт оператору, а при включённой подписке на уведомления — ещё и клиенту (группировка по объекту, анти-спам 6 ч).

ЛК: переключатель «Уведомлять о сбоях записи» включён, выбор канала Telegram/Email

Модуль «Документы»: реестр, шаблоны, реквизиты

Раздел меню «Документы» — единый документооборот: три подстраницы работают с одной таблицей выпущенных PDF и общими шаблонами.

flowchart LR
    T["Шаблоны
(редактор HTML)"] -->|"движок + реквизиты
организации"| G["Генерация PDF
WeasyPrint"] R["Реквизиты и печать
(per-org)"] --> G G --> REG["Реестр документов
(журнал PDF)"] REG -->|"скачать · предпросмотр
подписать · аннулировать"| U["Оператор"]

Реестр документов

/admin/documents/ — журнал всех выпущенных PDF (по клиенту, сделке, сотруднику). Фильтры: поиск по номеру/названию, категория, статус и единый компонент диапазона дат (как в отчётах). Организация выбирается в шапке навбара — отдельного фильтра на странице нет.

Реестр документов: фильтры (категория/статус/диапазон дат), таблица с колонкой Организация, действия

Шаблоны документов

/admin/documents/templates/ — редактор шаблонов по типам: Интернет / IPTV / Информ. услуги / Доп.услуга / Кадры / Склад. Вкладки типов, чьи модули выключены, скрываются автоматически. Тело шаблона (HTML с плейсхолдерами) правится визуально; шапка с реквизитами организации, логотип и подписи добавляются единым движком. Есть предпросмотр на демо-данных.

Шаблоны документов: вкладки типов сделок, включая вкладку Склад

Вкладка «Склад» — акты оборудования

При включённом модуле Склад появляется вкладка «Склад» с двумя шаблонами: Акт приёма-передачи оборудования (клиенту) и Акт подотчёта сотрудника (материальная ответственность). Генераторы PDF (/admin/stock/act/abonent/…, /admin/stock/act/staff/…) собирают таблицу оборудования и рендерят её через эти редактируемые шаблоны — правки в редакторе реально меняют выпускаемый акт. Если шаблон не менялся, берётся дефолтное тело. Плейсхолдеры: {{ rows_table }} — готовая таблица оборудования, {{ subject_name }} — клиент или сотрудник, {{ contract }} — номер договора, {{ total }} — итог, {{ executor_name }} и {{ executor_signer }} — организация и подписант, {{ doc_title }}, {{ doc_number }}, {{ today }} и {{ ref_note }} — заголовок, номер, дата и ссылка на основание.

Модалки на мобильном

Все модальные окна (предпросмотр PDF, редактор и предпросмотр шаблонов, формы, подтверждения) на телефоне (≤768px) разворачиваются на весь экран и выезжают справа полноэкранной слайд-панелью со «прилипающими» шапкой и кнопками действий. Это общесистемное поведение (единое правило SmitUI, opt-out — класс .modal-nofull), не только в «Документах».

Документы (КП / договор / акт / согласие 152-ФЗ)

Из проекта генерируются 5 типов PDF (WeasyPrint, реквизиты из настроек компании, загрузка в облако):

Сервисные планы видеонаблюдения

Настройки модуля

Страница /admin/video/settings/ — 4 группы настроек в карточках: Основные (включение, отдельный счёт), Биллинг и блокировки, Облако VSaaS. У облака — кнопка «Проверить соединение» прямо со страницы.

Блокировка камер по балансу

Настройка VIDEO_BLOCK_ON_NEGBAL реально работает: при отрицательном балансе видео-счёта камеры объекта блокируются, при пополнении — разблокируются. Срабатывает в трёх точках:

Логика облако-агностична: флаг cameras_blocked на объекте + вызов VSaaS-провайдера (в MVP без облака — только флаг и запись в аудит, интернет клиента не трогается).

Облако VSaaS (вариант B)

Ядро готово к реселлу облачного видеонаблюдения: абстракция провайдера (BaseVsaasProvider) + заглушка (MVP без облака) + реестр. Реальный облачный провайдер добавляется без переписывания вызывающего кода — выбирается в настройках (VIDEO_CLOUD_PROVIDER) вместе с API URL и ключом. Пока спрос/поставщик не определены — модуль работает в режиме «локальный NVR».

По исследованиям (финансовая модель + анализ платформ) платформа старта — TRASSIR Cloud (DSSL): провайдер TrassirProvider зарегистрирован в реестре (каркас, реальные вызовы API — при получении ключа). Стратегия: TRASSIR Cloud (0–12 мес) → собственное облако Xeoma при достижении ~1000 камер. На дашборде /admin/video/ — метрика «камер в облаке / порог 1000» с сигналом «пора на своё облако» и расчётом дополнительной прибыли.

Срок архива — параметр сервисного плана (retention_days: Дом 7 дн, Семья/Бизнес 30 дн, Бизнес+ 90 дн). Указывается в согласии на обработку ПДн (152-ФЗ — «видеозаписи хранятся не дольше N дней»).

Справочник моделей оборудования

Модели камер и оборудования ведутся в каталоге /admin/video/models/ (раздел «Видео» → «Модели оборудования»), а не вводятся текстом. Каждая модель — тип (камера/NVR/PoE/ИБП/диск), бренд, модель, характеристики (Мп/PTZ/уличная для камер), цена-ориентир. В модалках камеры и оборудования объекта — выбор из справочника с автозаполнением характеристик; свободный текст остаётся как fallback. Цена-ориентир используется в КП/смете проекта.

Мульти-орг: чей клиент

Один биллинг обслуживает несколько компаний. Клиент привязан к организации (с кем подписан договор); объект видеонаблюдения наследует организацию от клиента, при необходимости переопределяется вручную (один клиент — разные компании по разным объектам). От организации зависят реквизиты документов (КП/договор/акт/152-ФЗ). В карточке клиента в блоке «Договор и контакты» отображается «Чей клиент: <организация>».

Модуль в реестре и лицензии

Видеонаблюдение — продаваемый модуль тарифа Pro. Управляется на /admin/settings/modules/: включение/выключение, замок при отсутствии в тарифе. При выключении модуль недоступен везде — раздел скрыт из меню, прямой URL даёт редирект, AJAX/API отвечает 403, вкладка в карточке клиента и раздел в ЛК/мобильном скрыты.

Выключение модуля также скрывает связанные с ним сущности в тарификации и CRM: линейки услуг, тарифы этих линеек, вкладки типов сделок в разделе «Документы» CRM и типы сделок в воронке. Для модуля «Видеонаблюдение» это линейки Видеонаблюдение, Домофон и ЧОП (домофон и охрана — часть видеонаправления). Привязка линейки к модулю определяется полем «Модуль» в карточке линейки, а если оно пустое — по названию линейки. Услуги отдельно не скрываются: они низкоуровневые и уходят из вида вместе со своими тарифами.

Перед запуском: модуль выключен по умолчанию (VIDEO_ENABLED=false). Включается после заведения услуг-пакетов, реквизитов компании и юридического контура 152-ФЗ (согласия + уведомление РКН). Охранный мониторинг с реагированием оказывает партнёр-ЧОО (лицензия), оператор связи — технический сервис.

Домофонный домен

Домофония для новостроек реализована как рескин существующего биллинга, а не отдельный модуль: иерархия «оператор → ЖК → подъезд → квартира» укладывается на MPTT-дерево клиентов один-в-один, пакеты домофона — это тарифы, абонентская плата — recurring-услуги (списываются billing_worker). Все клиенты домофонных/видео-услуг привязаны к оператору домофонии (отдельная папка-юр.лицо).

Доменная модель домофонного проекта

Доменная модель

Что заведено в демо

40 квартир-клиентов · 3 тарифа-пакета · 40 лицевых счетов · 4 видео-объекта + 8 камер + 4 видео-подписки (390 ₽) · 2 подписки «дверь» (50 ₽) · 4 демо-платежа. Всего 46 живых recurring-услуг с sched_date — списываются billing_worker 1-го числа. Открыть: /admin/Abonents/9482/.

На карточке квартиры видны все три услуги сразу (вкладка «Услуги») и объект видеонаблюдения с камерами (вкладка «Видео»):

Услуги квартиры: домофон + видео + дверь

Демо-данные — для показа полного flow, не боевые клиенты.

Мульти-организации и SLA-сервис

Один экземпляр СмИТ Биллинг может обслуживать несколько компаний-операторов как самостоятельные юрлица. В контуре может быть несколько организаций: основная — провайдер связи (она же по умолчанию), рядом с ней дочерние или партнёрские компании со своими доменами — например, ИТ-услуги или охранное абонентское обслуживание. У каждой компании — свои реквизиты, платёжные кассы, счёт приёма оплат, документы, брендинг, домен ЛК и маршрутизация поддержки. Сотрудники видят данные только своих компаний.

Multi-org: один биллинг — несколько компаний

Обучающее видео 2:49

Список компаний и лимит лицензии, шесть вкладок карточки: основное и домен ЛК, брендинг, реквизиты для счетов, своя касса, почта и Telegram-бот организации.

1920×1080 с озвучкой Скачать
Содержание раздела

Модель организаций

Страница «Организации»: список компаний, статус, реквизиты, почта и платежи

Список компаний. В шапке — переход к лицензии, модулям и виджетам, настройка состава колонок и добавление организации.

Страница «Организации»

Раздел открывается по адресу /admin/settings/organizations/ и доступен только суперпользователю: здесь задаются сами компании, их реквизиты и каналы связи.

Карточка организации, вкладка «Реквизиты»: ИНН, КПП, банковские данные

Карточка открывается боковой панелью справа. «Сохранить» остаётся неактивной, пока в форме ничего не изменено.

На телефоне

Список превращается в карточки: каждая строка — компания с подписанными полями, без горизонтальной прокрутки. Кнопки шапки сжимаются до иконок, настройка колонок скрыта — в карточках настраивать нечего.

Список организаций на телефоне: карточки с подписями полей
Список — карточками
Карточка организации на телефоне: панель на весь экран, вкладки в одну строку
Панель на весь экран

Вкладки панели умещаются в одну строку: «Основное» и «Брендинг» остаются подписями, «Реквизиты», «Платежи», почта и Telegram показываются иконками.

Платежи и выручка по организации

Каждая компания принимает оплату на свой счёт через свою кассу. На вкладке «Платежи» в карточке организации задаются её YooKassa (Shop ID / секретный ключ / SCID), Wallet One и расчётный счёт приёма оплат (модель OrgPaymentSettings). Если у организации касса не задана — используются общие настройки провайдера (СмИТ).

Фискализация по организации (54-ФЗ)

По 54-ФЗ касса и ОФД привязаны к ИНН юрлица — у каждой компании свой ИНН и своя касса. Чек пробивается кассой той компании, на чей счёт пришли деньги, с её реквизитами продавца (ИНН, система налогообложения берутся из реквизитов организации). Касса привязывается к компании на странице /admin/settings/atol_config/ (поле «Компания» в карточке кассы). Если у компании своя касса не заведена — чек не пробивается по чужому ИНН.

Изоляция персонала по компаниям

Сотрудник видит клиентов, финоперации и счета только тех компаний, к которым он привязан. Привязка задаётся в карточке сотрудника (/admin/settings/staff/ → мультиселект «Доступные организации», можно несколько). Суперпользователь видит все компании. Селектор организаций в шапке показывает сотруднику только его компании; выбрать чужую нельзя. Функция управляется тумблером «Изоляция персонала» в разделе Настройки → Безопасность (мгновенный откат при необходимости).

SLA-документы: счета и акты по подписке

Для сервисных договоров (абонентское обслуживание со SLA) счёт и акт выставляются по подписке за период — не по факту банковского платежа. Документы генерируются в PDF (WeasyPrint) с реквизитами организации клиента, имеют отдельную нумерацию СЛА-ГГГГ-NNNN / АКТСЛА-ГГГГ-NNNN (атомарный счётчик). Каждая выдача журналируется в аудит (приказ ФСТЭК №21: кто/когда/что). SLA-подписки (пакеты «Базовый / Расширенный / Критический») списываются ежемесячно штатным механизмом абонплаты.

Поддержка со SLA-уровнями

Тикеты поддержки маршрутизируются по организации клиента: клиенты дочерней компании попадают в свой почтовый ящик поддержки (support@вашдомен.ru). Тикету проставляются теги организации и SLA-уровня (Базовый / Расширенный / Критический); уровень также сохраняется в карточке клиента. Критический уровень → приоритетная реакция.

Изоляция входа в ЛК по домену

Клиент может войти в личный кабинет только на домене своей организации: на домене дочерней компании — только её клиенты, на домене провайдера — только его. Проверка применяется во всех точках входа (логин/пароль, Telegram OAuth, автологин по IP, активная сессия). Попытка чужого клиента → отказ с записью в аудит. Это исключает перекрёстный доступ между компаниями.

Брендинг ЛК и писем по организации

Цвета, название, логотип и контакты в ЛК, письмах и служебных страницах берутся из брендинга организации клиента. У каждой организации своя палитра и своё название в письмах. Письма уходят с адреса организации (noreply@вашдомен.ru) под её SMTP-аккаунтом (корректные SPF/DKIM), темой с названием организации и её телефоном поддержки. Для клиентов без интернет-услуги (видео/домофон/SLA) скрываются интернет-специфичные разделы ЛК («Подключения», «Тест скорости»).

Весь визуальный брендинг компании настраивается прямо в карточке организации на вкладке «Брендинг» (/admin/settings/organizations/ → значок палитры в строке компании или кнопка «Редактировать» → вкладка «Брендинг»):

Изображения до 2 МБ (PNG/JPG/WEBP/SVG). Файлы можно загружать только у сохранённой организации. У организации по умолчанию (СмИТ) брендинг хранится в общей записи реквизитов, у остальных компаний — в собственной.

Вкладка «Брендинг» — фирменные цвета, логотип, фоны входа и дашборда

Личный кабинет организации

Почта организации (SMTP)

Карточка организации (/admin/settings/organizations/) открывается правым выезжающим сайдбаром — на мобильном он занимает всю ширину. Вкладка «Почта (SMTP)» задаёт, через какой сервер уходят письма клиентам этой компании.

Автонастройка и проверки

Домен имеет значение. Автонастройка берёт домен из вкладки «Основное». Если у организации указан поддомен личного кабинета (например lk.example.ru), почтовый хост mail.lk.example.ru может не резолвиться — тогда либо укажите почтовый домен, либо оставьте SMTP пустым и используйте глобальный.

Карточки Telegram-бота и супергруппы

На вкладке Telegram, если задан токен бота, подтягиваются его аватар, имя и @username (ссылка открывает бота в Telegram). При указанном chat_id показывается аватар и название супергруппы. Данные читаются через Telegram Bot API (запросы идут через прокси); если бот не добавлен в группу — вместо карточки выводится предупреждение.

Telegram-бот организации

У каждой компании может быть свой Telegram-бот для уведомлений в собственную супергруппу с топиками. Настройка — на вкладке «Telegram» в карточке организации:

Если поля пусты — используется глобальный бот системы (поведение СмИТ не меняется). Топики (thread_id) под конкретные типы уведомлений настраиваются отдельно; здесь задаётся бот и супергруппа организации.

Вкладка «Telegram» — токен бота, ссылка на супергруппу, Chat ID

API-прокси для внешних сервисов

Запросы к недоступным из РФ сервисам (AI-провайдеры, Telegram) идут через собственный forward-прокси на сервере с прямым доступом к этим API. Настройка API_PROXY_URL — единая точка для всех внешних вызовов (AI-чат, уведомления, фискализация, геокодер). Это устранило таймауты AI-ассистента: ответ приходит за ~1–2 сек.

Схема API-прокси

Статус: мульти-орг полностью реализован (2026-06-13, вкладки «Брендинг» и «Telegram» добавлены 2026-06-16). Per-организацию: реквизиты и документы, визуальный брендинг (цвета/логотип/favicon/фоны), платёжные кассы и счёт приёма оплат, маршрутизация онлайн-платежей и банковских выписок, фискализация (54-ФЗ), изоляция входа в ЛК и брендинг по домену, изоляция персонала по компаниям, свой Telegram-бот и супергруппа уведомлений, SLA-сервис и API-прокси. Состав компаний ведётся в карточках организаций. Платёжные/фискальные реквизиты вводятся в карточке организации по мере оформления юр.лиц.

AI-агент — единый универсальный AI-модуль

Всё, что касается искусственного интеллекта, собрано в одном нативном модуле /admin/ai/: мультипровайдерный чат с инструментами на прямом ORM, база знаний для бота и операторов, голосовой разбор звонков, собственный виджет для сайтов с настройками из админки и запись лидов в нашу CRM. Все ключи, промпты, история, дашборд и внешний вид виджета живут в биллинге (или на сервере лицензий), без зависимости от сторонних серверов.

Обучающее видео 2:48

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

1920×1080 с озвучкой Скачать

Архитектура AI-агента: каналы → движок → биллинг ORM → результат

Карта возможностей модуля:

flowchart LR
    AI(("AI-агент"))
    AI --- P["Провайдеры"]
    P --- P1["Подключённые провайдеры"]
    P --- P2["ключи в БД или на лиц-сервере"]
    AI --- CH["Каналы"]
    CH --- CH1["ЛК и Мобильное"]
    CH --- CH2["Виджет сайтов"]
    CH --- CH3["Поддержка TG / VK / MAX"]
    CH --- CH4["Email и Голос"]
    AI --- T["Инструменты"]
    T --- T1["Тарифы физ / бизнес"]
    T --- T2["Покрытие по адресу"]
    T --- T3["Баланс и сессия после SMS"]
    T --- T4["Оформить заявку / перезвон"]
    T --- T5["Обещанный платёж"]
    AI --- W["Виджет"]
    W --- W1["свой embed-скрипт"]
    W --- W2["конфиг из админки: цвет / тексты / аватар / домены"]
    AI --- R["Запись"]
    R --- R1["crm_intake → сделка"]
    R --- R2["эскалация → Поддержка"]
    AI --- AN["Аналитика"]
    AN --- AN1["токены по провайдеру + стоимость"]
    AN --- AN2["сессии и дашборд"]

Обзор модуля (хаб)

Главная страница /admin/ai/ — единая точка управления: статусы API-ключей подключённых провайдеров, список каналов с выбранной моделью, стоимость токенов за месяц в рублях, переходы к разделам (промпты, база знаний, журнал звонков, сессии и дашборд). Ключи хранятся в БД (SystemSettings), не в .env — настраиваются из интерфейса.

AI-агент — обзорная страница хаба

Мультипровайдер: выбор модели

Для каждого канала (Личный кабинет, Мобильное, Email/HelpDesk, Виджет, Голос, Подсказка оператору) выбирается провайдер и конкретная модель. Запросы маршрутизируются единым движком ai_prompt_builder.build(); ответы и токены логируются в ai_chat_log с разбивкой по провайдеру. Запросы к внешним API провайдеров идут через прокси (эти сервисы заблокированы из РФ).

База знаний

Раздел /admin/ai/kb/ — сценарии решения проблем (статьи KnowledgeArticle) с кодом, приоритетом и аудиторией (бот / оператор / оба). Интерфейс повторяет рабочую базу знаний поддержки: четыре вкладки (Сценарии решения, Дерево диагностики, Шаблоны ответов, Аналитика), фильтры по аудитории и приоритету, поиск. Карточка-аккордеон раскрывается в четыре колонки-шага (Уточнение → Диагностика → Решение → Если не помогло) с блоком эскалации и примечаниями. Активные статьи автоматически подмешиваются в системный промпт бота.

База знаний — список сценариев с фильтрами

База знаний — развёрнутый сценарий с четырьмя шагами

Инструменты на ORM (tool-use)

AI-ассистент умеет вызывать нативные функции напрямую через ORM, без HTTP к самому себе. Инструменты делятся на две группы:

Мультипровайдер: инструменты работают на всех подключённых провайдерах (tool-use loop реализован для каждого; токены суммируются по итерациям). Включается переключателем «Инструменты AI» в хабе (AI_TOOLS_ENABLED).

Диалог с инструментами (упрощённо):

flowchart TD
    U["Клиент пишет/говорит"] --> M["AI-модель"]
    M -->|"нужны данные"| T{"Инструмент"}
    T -->|"get_tariffs"| TR["Тарифы: скорость+цена
физ/бизнес"] T -->|"check_coverage"| CV["Покрытие по адресу
(Homes)"] T -->|"check_subscriber
(после SMS)"| SB["Баланс / тариф / статус"] T -->|"create_connection_lead"| LD["crm_intake →
сделка в CRM"] T -->|"request_callback"| TK["Тикет Поддержки"] TR --> M CV --> M SB --> M LD --> M TK --> M M --> R["Ответ клиенту"]

Голос: Mango → расшифровка → анализ

Раздел /admin/ai/calls/ — журнал голосовых звонков. Webhook Mango Office (/api/voice/mango/call/ и /record/) создаёт запись звонка, ищет клиента по телефону, скачивает запись и расшифровывает её через сервис распознавания речи, затем прогоняет транскрипт через AI (канал «Голос»): получается категория обращения, краткое резюме и признак необходимости эскалации — всё это попадает в тикет поддержки. Включается тумблером «Голос» в хабе (по умолчанию выключен — до подключения телефонии Mango).

Собственный виджет для сайтов (конфиг из админки)

Публичный анонимный AI-чат для встраивания на сайты организаций. Виджет полностью нашstatic/js/smit-ai-widget.js, без внешних зависимостей и сторонних серверов.

Все настройки — в БД биллинга (карточка «Виджет на сайтах» в хабе /admin/ai/): цвет, заголовок, приветствие, аватар, кнопки-подсказки, положение кнопки, разрешённые домены (CORS). Виджет самонастраивается — при загрузке тянет конфиг из GET /api/ai/widget/config/ и строится только если включён.

Подключение сайтом — одна строка (код берётся по кнопке «Код встраивания»):

<script src="адрес вашей установки/static/js/smit-ai-widget.js" data-api="адрес вашей установки" data-org="3" defer></script>

Брендинг по организации (data-org)

Один файл виджета обслуживает все сайты, а оформление и маршрутизация подстраиваются под организацию сайта. Атрибут data-org на теге <script> задаёт id организации — он виден в её карточке.

Если data-org не указывать, виджет работает в оформлении и маршрутизации основной организации.

Совместимость с оформлением сайта. Виджет встраивается в страницу напрямую (не в изолированный контейнер), поэтому его стили заданы явно и не зависят от CSS сайта-хозяина: иконки табов «Чат/Голос» стоят в одну строку с подписью даже на сайтах с Tailwind (где базовый стиль делает svg блочным), а поле ввода всегда с тёмным текстом на белом фоне (на тёмнотемных сайтах иначе наследовался бы светлый текст — нечитаемо).

Лиды и обращения попадают в собственную CRM биллинга: заявка → сделка в воронке Deal (через crm_intake, с дедупом по телефону, геокодом и UTM), эскалация чата → тикет во внутренней Поддержке (SupportConversation), без внешних систем.

Архитектура виджета (всё в биллинге):

flowchart LR
    SITE["Сайт клиента
<script src=smit-ai-widget.js>"] -->|"GET /config/"| CFG[("Настройки виджета
цвет/тексты/домены
в БД биллинга")] SITE -->|"POST /chat/"| ENG["AI-движок биллинга
+ инструменты"] ENG --> LEAD["create_connection_lead
/ widget lead"] LEAD --> INTAKE["crm_intake
(дедуп/геокод/UTM)"] INTAKE --> DEAL[("Сделка CRM")] ENG -->|"эскалация"| SUP[("Тикет Поддержки")]

Голосовой режим виджета

Виджет умеет вести живой голосовой диалог: клиент говорит в микрофон, ассистент отвечает голосом (голосовая модель в режиме live-аудио). В окне виджета — вкладки Чат и Голос; вкладка «Голос» появляется только если режим включён в админке.

Настройка — карточка «Виджет на сайтах» → блок «Голосовой режим» в хабе /admin/ai/:

Требует ключ провайдера и работающий голосовой прокси (контейнер smit-voice-ws).

Как устроено. Голосовой провайдер недоступен из России напрямую, а веб-сервер биллинга не держит WebSocket-сессии. Поэтому голос идёт через отдельный лёгкий прокси-контейнер smit-voice-ws на боевом сервере: браузер по защищённому WebSocket соединяется с ним, а он проксирует диалог через обходной канал. Ключ провайдера и системный промпт (с реальными тарифами) браузер получает разово по GET /api/ai/widget/voice-token/ — в коде страницы ключ не хранится.

flowchart LR
    MIC["Браузер клиента
микрофон 16кГц"] -->|"WSS"| WS["smit-voice-ws
(прокси на сервере)"] WS -->|"обходной канал"| G["Голосовая модель"] G -->|"аудио 24кГц"| WS --> MIC TOKEN["/api/ai/widget/voice-token/
ключ + промпт с тарифами"] -.-> MIC

Приватность и контроль. Ключ провайдера выдаётся только при включённом тумблере и не попадает в исходники страницы сайта. Разговор идёт напрямую браузер↔Google через наш прокси; текстовый чат работает независимо и без голосовых зависимостей.

Формы заказа на сайтах → лиды в CRM

В хабе /admin/ai/ есть конструктор форм подключения. Доступны три способа сбора заявок, все они шлют данные в один endpoint POST /api/crm/lead/ и создают сделку в CRM:

Состав полей и тексты настраиваются в модалке «Формы заказа» (заголовок, цвет, телефон, видимость блоков). Главный выключатель приёма заявок — CRM_PUBLIC_LEAD_ENABLED (по умолчанию выключен, включается тумблером в хабе).

Полностраничная форма заказа /order

Встраиваемый виджет формы заказа в модальном окне

Маршрут заявки:

flowchart LR
    W["Виджет
order_widget.js"] --> API["POST
/api/crm/lead/"] O["Страница
/order"] --> API CH["AI-чат"] --> API API --> KS{"Приём
включён?"} KS -->|"нет"| OFF["Отклонено
(disabled)"] KS -->|"да"| INTAKE["crm_intake:
поиск клиента
по телефону + дедуп"] INTAKE --> DEAL[("Сделка в CRM
воронка Deal
+ UTM")]

Тестовый AI-чат

Страница /admin/ai/test_chat/ — отработка сценариев ассистента так, как его видит клиент на сайте. Использует тот же движок, что и боевой виджет: создаёт реальные лиды и тикеты, показывает session_id, факт эскалации, номер тикета и ссылку на сессию в дашборде. Эскалация (фраза вроде «хочу оператора» или жалоба) создаёт тикет во внутренней Поддержке.

Тестовый AI-чат в админке с панелью состояния сессии

Эскалация чата в тикет:

flowchart LR
    MSG["Сообщение
клиента"] --> AI["AI-движок"] AI --> ESC{"Нужен
оператор?"} ESC -->|"нет"| ANS["Ответ
клиенту"] ESC -->|"да · ESCALATE_TO_HUMAN"| TK["support_tickets
create_ticket()"] TK --> CONV[("Тикет
Поддержки
SupportConversation")] TK --> ANS

Тест-кейсы и регрессионные прогоны

Страница /admin/settings/ai/eval/<канал>/ — банк тестовых вопросов и история прогонов для каждого из семи каналов (ЛК, мобильное, email, виджет, голос, телефон, подсказка оператору).

Тестовый AI-чат безопасен. Страница /admin/ai/test_chat/ работает в режиме симуляции: реальные заявки, тикеты, SMS, звонки-верификации и платёжные ссылки из тест-чата не создаются — ассистент получает ответ «действие выполнено» и продолжает диалог.

Дашборд AI-сессий

Дашборд /admin/reports/ai_chat_dashboard/ — единая аналитика всех AI-диалогов (ЛК, мобильное приложение, виджет на сайте). Это рабочий инструмент для оценки качества ассистента и обучения промпта. Сюда перенесена история диалогов из внешнего сервиса — 19 332 строки (1811 диалогов, 18 171 сообщение, 1161 вызов инструментов), сопоставленные с клиентами по телефону.

Дашборд AI-сессий: KPI, активность по дням, каналы

Показатели и графики

AI-чат: дашборд — показатели и графики

Пять карточек сверху отвечают на вопросы «сколько было разговоров», «насколько они были длинными», «дошли ли до конкретного клиента» и «сколько это стоило»:

ПоказательЧто считается
СессийОбращения за период. В мессенджерах одна переписка тянется годами (session_id — это chat_id), поэтому обращения разделяются паузой: перерыв дольше AI_DIALOG_GAP_MIN минут (по умолчанию 30) начинает новое. В подписи — сообщения клиента и сводка по оценкам (👍/👎), если их ставили.
Решено без человека
Contained rate — доля сессий, где ассистент справился сам: без эскалации на оператора и без создания тикета. Главная метрика качества. В подписи — повторные обращения того же клиента в течение 72 часов: если их много, вопросы на деле не решаются, даже когда эскалаций мало.
Реплик на диалогВсе сообщения (клиент + ассистент) ÷ число сессий. Длинный диалог обычно означает, что с первого раза не поняли.
Без клиентаДоля сессий, где клиента не удалось связать с карточкой клиента. Для голосового канала это главный признак, что разговор не довели до конкретного человека.
ЭскалацийОбращения, переданные оператору; рядом — сколько из них закончились тикетом. Под диаграммой каналов эскалации разделены по причине: клиент сам попросил человека, сорвалась проверка личности, оформлена заявка по регламенту или бот не справился. Метрика качества — последняя: нажатая кнопка «Позвать оператора» это штатная работа, а не провал.
ТокеныВход и выход отдельно + оценочная стоимость по настройкам AI_PRICE_IN_USD_PER_MTOK, AI_PRICE_OUT_USD_PER_MTOK, AI_USD_RUB.
Почему не «Помогло / Не помогло». До июля 2026 три карточки из пяти показывали оценки клиентов — и всегда ноль: за восемь месяцев их проставлено три штуки на 29 тысяч сообщений. Оценки никуда не делись (показываются строкой под «Сессиями»), но место на панели занимают метрики, которые действительно считаются из данных.

Расходы на модели и бюджет месяца

Блок «Расходы на модели» показывает, куда уходят деньги, в двух разрезах — по моделям и свёрнуто по провайдерам. Стоимость считается по прайсу каждой модели и курсу из настроек AI, поэтому расход по каждой модели виден отдельно.

КолонкаЧто показывает
Модель / ПровайдерПереключатель над таблицей. В режиме моделей рядом с названием — её провайдер.
СообщенийСколько ответов сгенерировано этой моделью за период.
Токенов вход / выходВход — промпт со всем контекстом, выход — сам ответ. Вход обычно в десятки раз больше.
Цена, $/MtokПрайс этой модели за миллион токенов: вход / выход.
Стоимость, ₽Себестоимость по прайсу и курсу. Это не сумма, начисленная по лицензии — она считается по тарифу на сервере лицензий и включает наценку.

Над таблицей — полоса бюджета месяца: потрачено с начала месяца, прогноз до конца и отметка прогноза на шкале. Цвет меняется при 80% и при превышении. Если лимит не задан (настройка AI_BUDGET_MONTH_RUB), вместо шкалы показываются суммы и ссылка на настройку — почтовый алерт о превышении при пустом лимите тоже молчит.

Почему «сессий» стало меньше, а «решено без человека» — ниже. До августа 2026 единицей счёта был session_id. В мессенджерах он не заканчивается никогда, поэтому одна «сессия» означала человека за всё время: внутри неё умещалось до 234 обращений, а одна эскалация помечалась на всю переписку. После разделения на обращения показатель «решено без человека» опустился с 94% до 73% — работа ассистента не изменилась, исчезла приписка.

Чья это заявка: как определяется организация

В мультиорганизационной установке каждый диалог, звонок и заявка должны принадлежать своей организации — от этого зависят и отчётность, и то, каким брендом бот представляется клиенту, и какие тарифы он предлагает. Признак берётся из первого источника, который смог ответить.

Откуда обращениеПорядок определения
Чат виджета Тест-площадка (pg_org) → явный org_id в запросе → домен сайта (Origin/Referer сверяется с доменами организаций) → организация по умолчанию.
Заявка из виджета Домен сайта → организация клиента, если он найден → упоминание бренда в тексте → организация по умолчанию. Домен идёт первым, поэтому заявка с сайта попадает в его воронку, даже если клиент ещё не в базе.
Входящий звонок Организация клиента → номер линии, на который позвонили (см. CRM → Телефонные линии: номер назначения задаёт сегмент, организацию и тип сделки) → организация по умолчанию.
Мессенджеры Организация ящика канала → организация клиента → организация по умолчанию.
Диалоги без опознанной организации. Если клиент не опознан, а канал организацию не передал, последним шагом в цепочке стоит организация по умолчанию. Обратная сторона: по цифрам не отличить «клиент точно наш» от «не смогли определить», поэтому у звонков и виджета важно держать заполненными телефонные линии и домены организаций — они дают точный признак вместо умолчания.
Телефон бота и телефонная линия — разные настройки. Номер, который бот называет клиенту, берётся из брендинга организации (Настройки → Организации → Брендинг → Телефон поддержки), а маршрутизация звонков — из телефонных линий. Если они разойдутся, бот продиктует номер, на котором звонки не разбираются автоматически: не определится ни организация, ни сегмент.

Карта раздела

- AI-чат: дашборд
  - Показатели
    - Сессий
    - Реплик на диалог
    - Без клиента
    - Эскалаций и тикеты
    - Токены и стоимость
  - Разрезы
    - Период до всего времени
    - Организация из шапки
    - Канал: голос, виджет, бот
    - Модель и провайдер
  - Деньги
    - Цена по прайсу модели
    - Бюджет месяца и прогноз
    - Себестоимость против начисления
  - Списки
    - Сессии страницами по 50
    - Поиск и фильтры на сервере
    - Последние отрицательные оценки
    - Проблемные eval-кейсы
  - Диалог
    - Клик по строке
    - Стенограмма в модалке
    - Форматирование ответов
    - Переход к клиенту

Откуда берутся цифры

flowchart LR
    LOG["Журнал диалогов
ai_chat_log"] --> SESS["Обращения
session_id + dialog_no"] SESS --> KPI["Показатели"] SESS --> DAY["Активность по дням"] SESS --> SRC["Каналы"] SESS --> LIST["Список сессий
страницами"] LOG --> TOK["Токены и стоимость"] AB["Клиенты"] --> ORG["Организация сессии"] SESS --> ORG ORG --> BYORG["По организациям
+ «Без организации»"] EVAL["Прогоны ai_eval"] --> CASES["Проблемные кейсы"]

Организация сессии определяется один раз — по первой записи с заполненным клиентом. Поэтому сумма по организациям сходится с общим числом сессий, а диалоги без опознанного клиента попадают в отдельную строку «Без организации», а не приписываются компании по умолчанию.

Список сессий

Стенограмма диалога в модальном окне

Список приходит страницами по 50 записей: поиск (по идентификатору сессии, каналу, ФИО и договору), фильтры канала и оценки, сортировка — всё считается на сервере. Клик по строке открывает стенограмму: реплики клиента и ассистента с временем, ответы в формате JSON показываются моноширинным блоком, имя клиента ведёт в его карточку.

Адрес /admin/reports/ai_chat_session/<id>/ при открытии человеком перенаправляет на дашборд с уже раскрытой сессией.

Тёмная тема и телефон

AI-дашборд в тёмной теме AI-дашборд на телефоне

Период задаётся единым date-range-picker (общий для всех отчётов). Стоимость токенов считается из настроек AI_PRICE_* и курса AI_USD_RUB.

Список сессий и транскрипт

Блок «Все сессии за период» — таблица с фильтрами по каналу (Все/LK/Mobile) и фидбеку (Все/👍/👎/Эскал.), живым поиском (ID/клиент/договор) и сортировкой по столбцам. Клик по строке открывает транскрипт диалога в стиле iMessage: реплики клиента — справа (светло-серый градиент), Аиды — слева, с поддержкой markdown-форматирования (**жирный**, заголовки #/##/###, `код`). Клик по имени клиента открывает slide-панель клиента (как в других разделах) — без ухода со страницы.

Транскрипт AI-сессии в стиле iMessage с markdown-анализом

Поток данных дашборда:

flowchart LR
    LK["ЛК-чат"] --> LOG[("ai_chat_log
PG")] MOB["Мобильное"] --> LOG WID["Виджет сайта"] --> LOG BOT["Внешний источник (ETL)"] --> LOG LOG --> VIEW["dashboard/data/
агрегаты SQL"] VIEW --> KPI["KPI + графики
ECharts"] VIEW --> SESS["Список сессий
фильтры/поиск"] SESS --> TR["Транскрипт
(iMessage)"] SESS --> DR["Панель клиента
(AbDrawer)"]

Тёмная тема, мобильная версия и клавиатурная доступность (Enter/Space на строках, aria-sort) — поддержаны. ECharts отдаётся локально (не с CDN).

Статус: все 7 фаз реализованы (2026-06-13). Мультипровайдер, единый хаб, база знаний, инструменты на ORM, голос, виджет с воронкой и перенос истории диалогов работают на боевом сервере. Виджет сайта организации работает на собственном endpoint. Голосовой разбор включается после подключения телефонии Mango.

Единые компоненты интерфейса

Каталог единых компонентов

Каталог живёт в Настройки → Лицензия и расширения → Единые компоненты (/admin/settings/components/) и содержит 33 компонента с описанием, публичным API, местами применения и живыми демо. Среди последних добавленных:

Библиотека переиспользуемых UI-компонентов админки — единый визуальный язык вместо десятков локальных копий одной и той же логики. Каждый компонент — самодостаточный <script> без сборки и внешних зависимостей: подключается один раз в base_adminlte.html, публикует своё API в window.* и работает в любом разделе. Все компоненты поддерживают тёмную тему, мобильную вёрстку и prefers-reduced-motion.

Живой каталог с демо: /admin/settings/components/ — карточка на каждый компонент с описанием, API и работающим примером.

Каталог единых компонентов интерфейса

Состав библиотеки

КомпонентНазначениеГде применяется
OrgBadgeЕдиный бейдж организации: favicon/иконка + акцентный цвет.Линейки услуг, канбан CRM, тарифы, staff, Реестр документов, video, ATOL, mailserver
PdfViewerМодалка-iframe предпросмотра PDF: загрузчик, ловля ошибок, кнопка скачивания.Реестр документов, CRM «Файлы», video-проекты, лицензии, captive-portal
SmitDateRangeДиапазон дат: пресеты (сегодня / 7 дн / месяц / …) + календарь, память выбора.Журнал сообщений, PayLog, FinOps, аудит, CRM-дашборд, тайм-трекинг (14 шаблонов)
SmitMultiSearchМульти-поиск: сам определяет тип запроса (телефон / договор / адрес / сумма).Воронки CRM (эталон), глобальный поиск клиентов
ProfileSelectСелект сотрудника: аватар + имя + группы + поиск. Кандидаты — только реальные операторы.Канбан CRM, дровер сделки, задачи, наряды, финоперации
SmitResponsibleSelectФильтр «Ответственный»: обёртка над ProfileSelect + «Все» / «Без ответственного» + счётчик-бейдж.Списки CRM, Воронки CRM, Поддержка (бейдж = открытые тикеты)
SmitTariffSelectКрасивый селект тарифа: обложка + скорость ↓/↑ + цена, поиск.Дровер сделки CRM (вкладка «Подключение»)
SmitPhoneНомер телефона: иконка + маска +7 (917) 727-17-96, тултип, click-to-call.Контакты клиента, карточки CRM/лидов, поиск, тикеты, staff, голосовая почта
SmitHelpIconАккуратная круглая i-иконка справки со ссылкой на документацию.Заголовки разделов CRM/Поддержка (get_help_link), карточки настроек
SmitWatchButtonКнопка «Наблюдаю» — только иконка глаза + счётчик, зелёный фон в активном состоянии.Поддержка (фильтр «Наблюдаю»); задел — «Наблюдать» в CRM/тикетах
CallButtonКнопка «Позвонить» (click-to-call через провайдера телефонии).Тикеты, CRM, поиск, карточка клиента
SmitDrawerБоковая панель: заголовок с иконкой и подзаголовком, вкладки, кнопки действий, загрузка содержимого по адресу, стопка вложенных панелей.Карточки позиции и единицы склада, тикеты Поддержки, дровер сделки и заявки CRM, карточка клиента, профиль, разделы AI, редактор ботов, монитор сессий
SmitToastВсплывающие уведомления (стек справа-сверху / снизу на мобильном).Уведомления во всех разделах
SmitConfirmМодалка подтверждения вместо confirm() (тот ломал тёмную тему).Подтверждение опасных действий
SmitCopyКопирование в буфер (clipboard + execCommand fallback + тост).Замена ~29 копий clipboard-логики (IP / MAC / договор / e-mail / телефон)

Боковые панели: одна механика на весь биллинг

Боковая панель одна на весь биллинг: раздел передаёт заголовок, иконку, вкладки и содержимое, а затемнение, обработку Esc, блокировку прокрутки и порядок слоёв (карточка клиента → видео) берёт на себя компонент.

flowchart LR
    T["Строка таблицы
или кнопка"] --> D1["Панель 1
карточка позиции"] D1 --> D2["Панель 2
карточка единицы"] D2 --> D3["Панель 3
вложенная — видео, IPTV"] D3 -. "Esc / клик мимо" .-> D2 D2 -. "Esc / клик мимо" .-> D1 D1 -. "закрытие" .-> T

Как это устроено

Каждый компонент следует одному паттерну: самовыполняющаяся функция с защитой от повторной инициализации (if (window.X) return), публичное API в window.X, инъекция своих стилей в <head>. CSS подключается в шапке base_adminlte.html, JS — в подвале. Разделу достаточно вызвать API или разметить элемент data-атрибутом — компонент сам себя инициализирует.

flowchart LR
    REG["base_adminlte.html
CSS в head + JS в подвале"] --> BUS["window.OrgBadge / SmitPhone /
ProfileSelect / SmitToast / …"] BUS --> M1["Разметка: data-org-badge
data-smit-phone / data-smit-daterange"] BUS --> M2["JS-вызов: OrgBadge.html id
ProfileSelect.create"] M1 --> RENDER["Автоинициализация
+ MutationObserver для AJAX"] M2 --> RENDER RENDER --> THEME["Тёмная тема · мобильная вёрстка
prefers-reduced-motion · a11y"]

Почему единые компоненты. Исправление и улучшение живут в одном файле, а эффект виден сразу во всех разделах: правка не теряется по копиям, тёмная тема не ломается точечно.

Важно при добавлении. Новые файлы static/js/*.js и *.css подхватываются whitenoise только при старте web-контейнера — после добавления компонента нужен manage.py collectstatic + перезапуск web (обычный build web новые статик-файлы не собирает).

Автообзвон и своя телефония

Модуль «Автообзвон» — исходящий обзвон роботом: система сама звонит клиенту и проигрывает голосовое сообщение (низкий баланс, обещанный платёж, опрос NPS). Робот только доносит информацию — без запроса нажатий и распознавания голоса. Раздел управления — /admin/ai/dialer/ (сценарии, журнал, тест-звонок, чёрный список, настройки).

Мультипровайдер

Способ дозвона выбирается настройкой AUTODIAL_PROVIDER:

ПровайдерТипКонтрольСтатус
zvonokоблако, робот-обзвоннизкий (чужой движок)живые звонки идут
asteriskсвоя телефонияполныйстенд готов, E2E подтверждён
mangocallback оператор→клиентсреднийзаготовка

Своя телефония на Asterisk

Отдельный Docker-стек smit-dialer рядом с биллингом (не трогает прод-compose): контейнер Asterisk 22.9.0 (host-network, ARI на порту 8088) и постоянный контейнер ari-listener (обрабатывает Stasis-события звонка). Синтез речи — Yandex SpeechKit (облако): биллинг превращает текст сценария в WAV-файл, кладёт его в общий каталог, а Asterisk проигрывает его в звонок. Локальный синтез на сервере невозможен (у процессора нет инструкций AVX), поэтому используется облачный TTS.

Поток робот-звонка: текст сценария → Yandex SpeechKit (WAV) → общий каталог → Asterisk (ARI originate) → проигрывание клиенту → завершение вызова. Цикл проверен вживую (StasisStart → play → playback finished → hangup).

Звонок не уйдёт, если: клиент не дал согласие на голосовые уведомления (поле в ЛК и мобильном приложении), номер в чёрном списке (DNC), сейчас «тихий час» или вне окна обзвона, исчерпан дневной лимит. С 01.09.2026 в РФ обязательна маркировка звонков от юрлиц — это учитывается при подключении телефонного транка.

Вкладка «Звонки» в карточке клиента

В карточке клиента (/admin/Abonents/<id>/calls/) есть вкладка «Звонки» — журнал голосовых звонков клиента и панель управления исходящими вызовами. Видна, когда модуль автообзвона включён.

Вкладка «Звонки»: панель управления и журнал звонков клиента

Панель содержит:

Журнал звонков показывает: дату, направление (входящий/исходящий с иконкой), статус (цветной бейдж), AI-категорию обращения, краткое AI-резюме. Если по звонку создан тикет в Поддержке — рядом с резюме появляется ссылка «Создан тикет #N», ведущая в Поддержку. Для звонков с записью кнопка ▶ открывает встроенный плеер в модальном окне (прослушать или скачать запись).

Плеер записи звонка в модальном окне

Статус: модуль и стенд своей телефонии готовы. Реальные звонки в телефонную сеть ждут подключения SIP-транка провайдера и маркировки звонков. До этого момента работает облачный провайдер Zvonok. Модуль по умолчанию выключен (kill-switch AUTODIAL_ENABLED).