Дополнительные разделы
Видеонаблюдение и домофония, мультиорганизация, AI-агент, автообзвон и телефония, интерфейсные компоненты и внутренний мессенджер.
Содержание раздела
- Внутренний чат персонала
- Отчёты по расписанию в чат
- Toast-уведомления о новых сообщениях
- Интерфейс: темы и единые паттерны
- Переключатель темы (Система / Светлая / Тёмная)
- Единые паттерны настроек
- Видеонаблюдение — доп.услуга «под ключ»
- 3 слоя дохода — как продавать правильно
- Раздел /admin/video/
- Отчётность ЧОП (Фаза 4) — SLA, uptime, выручка
- Вкладка клиента и ЛК
- Как списываются подписки — пошагово
- Архив записей в ЛК — календарь, выделение, выбор камеры
- Уведомления о сбоях записи
- Модуль «Документы»: реестр, шаблоны, реквизиты
- Документы (КП / договор / акт / согласие 152-ФЗ)
- Настройки модуля
- Блокировка камер по балансу
- Облако VSaaS (вариант B)
- Справочник моделей оборудования
- Мульти-орг: чей клиент
- Модуль в реестре и лицензии
- Домофонный домен
- Доменная модель
- Что заведено в демо
- Мульти-организации и SLA-сервис
- Модель организаций
- Платежи и выручка по организации
- Фискализация по организации (54-ФЗ)
- Изоляция персонала по компаниям
- SLA-документы: счета и акты по подписке
- Поддержка со SLA-уровнями
- Изоляция входа в ЛК по домену
- Брендинг ЛК и писем по организации
- Почта организации (SMTP)
- Telegram-бот организации
- API-прокси для внешних сервисов
- AI-агент — единый универсальный AI-модуль
- Обзор модуля (хаб)
- Мультипровайдер: выбор модели
- База знаний
- Инструменты на ORM (tool-use)
- Голос: Mango → расшифровка → анализ
- Собственный виджет для сайтов (конфиг из админки)
- Тест-кейсы и регрессионные прогоны
- Дашборд AI-сессий
- Единые компоненты интерфейса
- Состав библиотеки
- Боковые панели: одна механика на весь биллинг
- Как это устроено
- Автообзвон и своя телефония
- Мультипровайдер
- Своя телефония на Asterisk
- Согласие и ограничения
- Вкладка «Звонки» в карточке клиента
Внутренний чат персонала
Встроенный мессенджер для сотрудников — выдвижная панель «Чаты» доступна с любой страницы админки (иконка-колокольчик в навбаре). Личные диалоги, групповые чаты, чат «Поддержка разработчика» (AI-ассистент + эскалация специалисту), вкладка «Уведомления» с системными событиями.

- Комнаты: личные (direct), групповые (group, с обложкой), поддержка (support). Markdown в сообщениях (жирный/курсив/код/ссылки/списки), вложения (GCS), реакции-эмодзи, ответы на сообщения, галочки прочтения.
- Бейдж непрочитанных в навбаре — опрос каждые 30 сек, работает даже при закрытой панели.
Отчёты по расписанию в чат
Любой 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 штук. Открытая комната не тостится; свои сообщения не триггерят.

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

Единые паттерны настроек
- Кнопка «Сохранить» с dirty-отслеживанием — неактивна (серая),
пока на странице ничего не изменено; активируется при первом изменении. Общий
механизм (
.js-dirty-save) применён во всех разделах настроек (Поддержка, CRM, платежи, интеграции и др.). - i-иконки с popover — пояснения к опциям убраны из текста страницы в компактные серые иконки-подсказки рядом с названием опции. Ссылки-«i» у заголовков блоков ведут в соответствующий раздел этой документации.
- Единый вид блоков-«карточек» — радиус 10px, светло-серые заголовки-полосы, одинаковые отступы (эталон — дашборд).
- Стили вкладок — выбранный в «Настройки системы → Профиль» стиль (classic / pills / underline / compact / tubelight / glow) применяется ко всем разделам с вкладками: карточка клиента, Поддержка, настройки.
Видеонаблюдение — доп.услуга «под ключ»
Модуль продажи видеонаблюдения через биллинг: от заявки и обследования до монтажа, recurring-подписок и документов. Реализован как собственный сервисно-инсталляционный домен (объект → проект → камеры/оборудование → подписки), а не плоская интеграция. Использует существующее ядро биллинга: тарификацию Usluga/UsersUsluga, ежемесячное списание billing_worker, FinanceOperations, генерацию PDF (WeasyPrint), фискализацию.
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 страницами: дашборд, проекты (канбан), объекты, сервисные планы, заявки.

- Дашборд — 8 KPI: объекты, камеры, активные проекты, подписки, MRR (recurring/мес), сумма воронки, конверсия, attach-rate; воронка проектов по 9 статусам; последние проекты.
- Проекты (канбан) — drag-drop по статусам (Заявка → Обследование → Проект/КП → Договор → Монтаж → ПНР → Сдан → На сервисе); на карточке — кнопки документов.
- Сервисные планы — CRUD SLA-пакетов (камеры от/до, цена/мес, SLA-реакция, health-check, связь с услугой каталога).

UI/UX раздела
В разделе есть фильтры, редактирование в модальных окнах, просмотр камер, тёмная тема и работа с телефона:
- Объекты — фильтры по типу и компании, чипы «С подписками / Заблокированные / Долг» со счётчиками, редактирование и удаление в модальных окнах (с выбором организации), цветовая индикация баланса (красный долг / зелёный плюс).
- Проекты — на карточке лейбл организации; меню «⋮» содержит Редактировать и Удалить (модалка редактирования сметы + confirm-удаление).
- Медиа-сервер — отдельная колонка «Клиент», фильтры (онлайн / оффлайн / архив / не привязан), модалка просмотра камеры (live-видео через HLS + список архивных записей за 24ч), добавление/редактирование потока с привязкой к клиенту и автоматическим заведением в go2rtc.
- Настройки — раскладка в 2 колонки на десктопе; новая группа «Архив записей (MediaMTX + GCS)» с тумблером
Записывать в GCS(резервное копирование сегментов архива в облако через Celery-задачуvideo-archive-gcs-upload). - Тёмная тема и мобильный — бейджи и блоки кода читаемы в тёмной теме; на экранах уже 768 px широкие таблицы показываются карточками.



Превью камер, просмотр в модалке и «Диск, Гб»
Доработка раздела «Объекты» и панели объекта: камеры теперь с превью и просмотром live/архива прямо в модальном окне, без перехода на другую страницу.
- Превью камер в панели объекта — при клике на клиента в списке «Объекты» открывается боковая панель (drawer), а в ней блок «Видеонаблюдение» → панель объекта со списком камер. Каждая камера показана стоп-кадром (snapshot go2rtc
/api/frame.jpeg), а если поток не заведён — плейсхолдером. - Кнопки «Трансляция / Архив / Поток» у каждой камеры. Трансляция и Архив открываются в модальном окне (плеер в iframe поверх панели), не в новой вкладке. Клик по самому превью также открывает трансляцию. Внутри модалки есть кнопка «открыть в новой вкладке» и закрытие по Esc / клику вне / крестику (поток и звук останавливаются при закрытии).
- Страница архива камеры (
/admin/video/camera/<id>/archive/) — live-видео (HLS через hls.js) + таймлайн архивных записей за 24 часа с возможностью проиграть любой фрагмент. Для встраивания в модалку используется облегчённый режим?embed=1(без сайдбара). - Колонка «Диск, Гб» вместо «Счёт, ₽» в списке объектов — показывает, сколько гигабайт занимает архив объекта в Google Cloud Storage (суммарно по всем камерам). «—» если выгрузка архива в GCS выключена.
- Компактная колонка «Компания» — без фона-бейджа, текст в одну строку с сокращением (полное название во всплывающей подсказке), приглушённая иконка.
stream.html в iframe)
· «Архив» →
Модалка: embed-страница — live + таймлайн 24ч
· «Поток» →
Модалка: WebRTC (low-latency)




Архив в GCS через прокси (роль «GCS»)
Из российских сетей storage.googleapis.com и oauth2.googleapis.com недоступны напрямую. Чтобы биллинг мог считать размер архива и выгружать сегменты в облако, добавлена отдельная роль «GCS» у прокси-сервера.
- В разделе Настройки → Система → Прокси у каждого прокси в меню «⋮» появился пункт «Добавить / Снять роль GCS» — это дополнительная роль, не сбрасывает роли Telegram и Нейросеть.
- Для GCS используется HTTP-прокси (CONNECT) — он нативно поддерживается клиентом Google Cloud Storage. Доступность прокси проверяется по
storage.googleapis.com. - Подсчёт «Диск, Гб» делает один запрос со списком объектов GCS (с кэшем 10 минут) и суммирует размеры по каждой камере объекта.

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

KPI и как они считаются
| KPI | Источник / формула |
|---|---|
| 📡 Средний uptime | downtime из пар тревог 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, колонки по объектам.

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

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

Как списываются подписки — пошагово
Видеоподписка — это recurring-услуга (UsersUsluga, system_type=20), которую billing_worker списывает 1-го числа каждого месяца. Деньги берутся с отдельного видео-счёта объекта (VideoObject.account) — интернет-баланс клиента не затрагивается.
390 ₽/мес
списывает 390 ₽
(интернет работает)
| Тип подписки | Цена считается | Пример |
|---|---|---|
| Сервис / SLA (service) | фиксировано за объект | Сервис Home 390 ₽, Сервис Pro 990 ₽ |
| Облако (cloud) | × число камер | Cloud Start 490 ₽ × 4 камеры = 1960 ₽/мес |
Важно: цены подписок хранятся в каталоге услуг (тип «Видеонаблюдение»), а не в тарифах — линейка «Видеонаблюдение» в разделе «Тарифы» остаётся пустой намеренно. Скидки лояльности интернета на видео не распространяются — это отдельный продукт со своим счётом.
Архив записей в ЛК — календарь, выделение, выбор камеры
Страница архива камеры (/lk/video/archive/<camera>/) даёт клиенту просмотр и выгрузку записей за период хранения (retention из подписки).
- Календарь даты — выбор дня в пределах глубины архива; дни с записью помечены точкой.
- Таймлайн суток — зелёным показаны интервалы доступной записи; клик по таймлайну запускает просмотр с выбранного момента.
- Выделение участка (как обрезка в онлайн-сервисах) — потяните по таймлайну, появятся две ручки и панель «Просмотр / Скачать фрагмент / Сбросить». Вырезанный фрагмент скачивается одним файлом MP4.
- Выбор камеры — если у объекта несколько камер с записью, в шапке появляется селектор камеры (дата сохраняется при переключении).


Уведомления о сбоях записи
Клиент может подключить уведомления о прерывании записи прямо в ЛК (вкладка «Архив» карточки объекта). Опция доступна только если у объекта активна запись. Канал — Telegram / Email / оба; контакты берутся из профиля клиента.
Серверный health-мониторинг (video-archive-health, каждые 10 мин) проверяет свежесть последнего записанного сегмента по каждой archive_enabled камере. Если записи нет дольше 15 минут — запись считается отвалившейся: billing_worker-независимая задача шлёт Telegram-алерт оператору, а при включённой подписке на уведомления — ещё и клиенту (группировка по объекту, анти-спам 6 ч).

Модуль «Документы»: реестр, шаблоны, реквизиты
Раздел меню «Документы» — единый документооборот: три подстраницы работают с одной таблицей выпущенных PDF и общими шаблонами.
flowchart LR
T["Шаблоны
(редактор HTML)"] -->|"движок + реквизиты
организации"| G["Генерация PDF
WeasyPrint"]
R["Реквизиты и печать
(per-org)"] --> G
G --> REG["Реестр документов
(журнал PDF)"]
REG -->|"скачать · предпросмотр
подписать · аннулировать"| U["Оператор"]
Реестр документов
/admin/documents/ — журнал всех выпущенных PDF (по клиенту, сделке,
сотруднику). Фильтры: поиск по номеру/названию, категория, статус и
единый компонент диапазона дат (как в отчётах). Организация
выбирается в шапке навбара — отдельного фильтра на странице нет.

- Действия в строке: предпросмотр PDF (модалка), скачивание, отметка «подписан», аннулирование (статус void, файл сохраняется).
- Мульти-орг: реестр показывает документы организации клиента (по выбору в навбаре); документы без организации видны всем. Колонка «Организация» (OrgBadge) — только в режиме «Все организации». Скачивание/предпросмотр/аннулирование по ID заскоуплены — оператор не видит и не трогает документы чужой компании.
- Реквизиты и печать (
/admin/documents/requisites/) — единый экран поверх реквизитов организаций (те же данные, что в карточке организации и «Брендинге»), per-org.
Шаблоны документов
/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, реквизиты из настроек компании, загрузка в облако):
- Акт обследования объекта
- Коммерческое предложение (оборудование + работы + сервис)
- Договор на установку
- Акт сдачи-приёмки с 11-пунктным чек-листом приёмки
- Согласие на обработку ПДн (152-ФЗ) — обязательно до начала съёмки (видео с людьми = персональные данные)

Настройки модуля
Страница /admin/video/settings/ — 4 группы настроек в карточках: Основные (включение, отдельный счёт), Биллинг и блокировки, Облако VSaaS. У облака — кнопка «Проверить соединение» прямо со страницы.
Блокировка камер по балансу
Настройка VIDEO_BLOCK_ON_NEGBAL реально работает: при отрицательном балансе видео-счёта камеры объекта блокируются, при пополнении — разблокируются. Срабатывает в трёх точках:
- После списания подписки в
billing_worker— мгновенная блокировка при уходе в минус; - При пополнении видео-счёта — мгновенная разблокировка;
- Ежечасная фоновая задача (
video.enforce_balances) — подстраховка, обходит все объекты с отдельным счётом.
Логика облако-агностична: флаг 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). Все клиенты домофонных/видео-услуг привязаны к оператору домофонии (отдельная папка-юр.лицо).
Доменная модель
- Оператор = папка клиентов (
is_folder=True, company=True) с реквизитами юр.лица. - ЖК / подъезд = вложенные папки.
- Квартира = клиент (
Abonents) с тарифом-пакетом, адресом (Homes) и отдельным лицевым счётом. - Пакеты домофона = тарифы: Standard 52 ₽, IP Smart 75 ₽, Integrated 145 ₽ (цена через
TarifUsersUsluga → Usluga.summa). - Видеонаблюдение = модуль
VideoObject(объект → камеры → подписки) с отдельным видео-счётом; дверь = доп.услуга «Мобильный доступ к двери».
Что заведено в демо
40 квартир-клиентов · 3 тарифа-пакета · 40 лицевых счетов · 4 видео-объекта + 8 камер + 4 видео-подписки (390 ₽) · 2 подписки «дверь» (50 ₽) · 4 демо-платежа. Всего 46 живых recurring-услуг с sched_date — списываются billing_worker 1-го числа. Открыть: /admin/Abonents/9482/.
На карточке квартиры видны все три услуги сразу (вкладка «Услуги») и объект видеонаблюдения с камерами (вкладка «Видео»):

Демо-данные — для показа полного flow, не боевые клиенты.
Мульти-организации и SLA-сервис
Один экземпляр СмИТ Биллинг может обслуживать несколько компаний-операторов как самостоятельные юрлица. В контуре может быть несколько организаций: основная — провайдер связи (она же по умолчанию), рядом с ней дочерние или партнёрские компании со своими доменами — например, ИТ-услуги или охранное абонентское обслуживание. У каждой компании — свои реквизиты, платёжные кассы, счёт приёма оплат, документы, брендинг, домен ЛК и маршрутизация поддержки. Сотрудники видят данные только своих компаний.
Список компаний и лимит лицензии, шесть вкладок карточки: основное и домен ЛК, брендинг, реквизиты для счетов, своя касса, почта и Telegram-бот организации.
Содержание раздела
- Модель организаций
- Платежи и выручка по организации
- Фискализация по организации (54-ФЗ)
- Изоляция персонала по компаниям
- SLA-документы (счёт/акт по подписке)
- Поддержка со SLA-уровнями
- Изоляция входа в ЛК по домену
- Брендинг ЛК и писем по организации
- Telegram-бот организации
- API-прокси для внешних сервисов
Модель организаций
- Организация (
Organization):name,slug(provider/partner),domain,is_default, иконка, SMTP-отправитель рассылок. - Клиент привязан к организации (
Abonents.organization) — «чей клиент». Финоперации и документы наследуют организацию от клиента. - Реквизиты (
CompanyBranding) — свои для каждой организации: счета/акты печатаются с реквизитами той компании, с которой у клиента договор. - Управление в одном окне:
/admin/settings/organizations/— список компаний + редактирование в модалке с шестью вкладками:- Основное — название, код, домен ЛК, иконка, статус;
- Брендинг — фирменные цвета (светлая/тёмная тема), логотип и favicon, фоны страницы входа в ЛК и дашборда (см. ниже);
- Реквизиты — ИНН/КПП/ОГРН, юр./почтовый адрес, директор, банковские реквизиты, ставка НДС, гл. бухгалтер, шрифт PDF, печать и подписи (загрузка прямо в окне);
- Платежи — своя касса YooKassa/W1 и расчётный счёт приёма оплат (см. ниже);
- Почта (SMTP) — отдельный отправитель рассылок организации;
- Telegram — бот организации и супергруппа с топиками для уведомлений (см. ниже).

Список компаний. В шапке — переход к лицензии, модулям и виджетам, настройка состава колонок и добавление организации.
Страница «Организации»
Раздел открывается по адресу /admin/settings/organizations/ и доступен
только суперпользователю: здесь задаются сами компании, их реквизиты и каналы связи.
- Шапка. Слева — тариф и число организаций с лимитом (ссылка ведёт в «Лицензию»), справа — кнопки «Управление лицензией», «Модули», «Виджеты», настройка колонок и «Организация» для добавления новой. Когда лимит тарифа исчерпан, кнопка добавления неактивна и объясняет причину.
- Таблица. Название компании показывается фирменным бейджем — иконка и акцентный цвет берутся из брендинга. Дальше идут домен личного кабинета, число клиентов, статус, заполненность реквизитов (полностью / частично с счётчиком / пусто), наличие Telegram-бота, почта и платёжные системы. Состав и порядок колонок настраиваются и запоминаются в профиле сотрудника.
- Действия в строке — брендинг, редактирование и деактивация. Деактивация спрашивает подтверждение и перечисляет последствия: компания исчезает из выбора и фильтров, данные и документы сохраняются, действие пишется в аудит.

Карточка открывается боковой панелью справа. «Сохранить» остаётся неактивной, пока в форме ничего не изменено.
На телефоне
Список превращается в карточки: каждая строка — компания с подписанными полями, без горизонтальной прокрутки. Кнопки шапки сжимаются до иконок, настройка колонок скрыта — в карточках настраивать нечего.
Вкладки панели умещаются в одну строку: «Основное» и «Брендинг» остаются подписями, «Реквизиты», «Платежи», почта и Telegram показываются иконками.
Платежи и выручка по организации
Каждая компания принимает оплату на свой счёт через свою кассу. На вкладке «Платежи» в карточке организации задаются её YooKassa (Shop ID / секретный ключ / SCID), Wallet One и расчётный счёт приёма оплат (модель OrgPaymentSettings). Если у организации касса не задана — используются общие настройки провайдера (СмИТ).
- Онлайн-оплата (ЛК / мобильное приложение): платёж клиента уходит на кассу его компании. Возврат после оплаты ведёт на домен ЛК этой компании. Webhook верифицирует платёж той же кассой, что его приняла.
- Банковская выписка: платёж относится к компании по счёту получателя в выписке (на чей расчётный счёт реально пришли деньги), с резервным определением по организации клиента.
- Выручка: в каждую финоперацию записывается организация — доход корректно разносится между компаниями. Готовые отчёты «Выручка по организациям» (за месяц и за год) показывают суммы по каждой компании с разбивкой банк / онлайн.
Фискализация по организации (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/ → значок палитры в строке компании или кнопка «Редактировать» → вкладка «Брендинг»):
- Фирменные цвета — акцентный цвет для светлой и тёмной темы (выбор пипеткой + HEX-поле с предпросмотром). Применяется к кнопкам, ссылкам и шапке в ЛК организации. Пустой цвет тёмной темы → используется цвет светлой.
- Логотип и favicon — логотип (свет/тёмная тема) и favicon вкладки браузера, загрузка прямо в окне с превью.
- Фон страницы входа в ЛК — отдельные изображения для светлой и тёмной темы.
- Фон дашборда админки — свет/тёмная тема.
Изображения до 2 МБ (PNG/JPG/WEBP/SVG). Файлы можно загружать только у сохранённой организации. У организации по умолчанию (СмИТ) брендинг хранится в общей записи реквизитов, у остальных компаний — в собственной.


Почта организации (SMTP)
Карточка организации (/admin/settings/organizations/) открывается правым выезжающим сайдбаром — на мобильном он занимает всю ширину. Вкладка «Почта (SMTP)» задаёт, через какой сервер уходят письма клиентам этой компании.
- Пусто → используется глобальный SMTP системы.
- Заполнено → письма организации уходят с её адреса и домена.
Автонастройка и проверки
- «Автонастроить ящик» — создаёт
noreply@<домен-организации>в почтовом сервере, прописывает DNS (MX/SPF/DKIM/DMARC) и заполняет параметры SMTP. Подробнее — 6.4.6. Автонастройка почтовых ящиков и DNS. - «Проверить» — реальное подключение к SMTP с авторизацией и TLS; понятная ошибка, если логин/пароль неверны или хост не резолвится.
- «Отправить тест-письмо» — HTML-письмо на e-mail текущего пользователя: сразу видно, доходит ли почта.
lk.example.ru), почтовый хост mail.lk.example.ru может не резолвиться — тогда либо укажите почтовый домен, либо оставьте SMTP пустым и используйте глобальный.
Карточки Telegram-бота и супергруппы
На вкладке Telegram, если задан токен бота, подтягиваются его аватар, имя и @username (ссылка открывает бота в Telegram). При указанном chat_id показывается аватар и название супергруппы. Данные читаются через Telegram Bot API (запросы идут через прокси); если бот не добавлен в группу — вместо карточки выводится предупреждение.
Telegram-бот организации
У каждой компании может быть свой Telegram-бот для уведомлений в собственную супергруппу с топиками. Настройка — на вкладке «Telegram» в карточке организации:
- Токен бота — токен из
@BotFather(хранится как секрет; при редактировании пустое поле = «не менять»). Бот должен быть администратором супергруппы. - Ссылка на супергруппу —
https://t.me/…на супергруппу с топиками для уведомлений. - Chat ID супергруппы — числовой идентификатор (например
-1002910452601) для отправки через Bot API.
Если поля пусты — используется глобальный бот системы (поведение СмИТ не меняется). Топики (thread_id) под конкретные типы уведомлений настраиваются отдельно; здесь задаётся бот и супергруппа организации.

API-прокси для внешних сервисов
Запросы к недоступным из РФ сервисам (AI-провайдеры, Telegram) идут через собственный forward-прокси на сервере с прямым доступом к этим API. Настройка API_PROXY_URL — единая точка для всех внешних вызовов (AI-чат, уведомления, фискализация, геокодер). Это устранило таймауты AI-ассистента: ответ приходит за ~1–2 сек.
Статус: мульти-орг полностью реализован (2026-06-13, вкладки «Брендинг» и «Telegram» добавлены 2026-06-16). Per-организацию: реквизиты и документы, визуальный брендинг (цвета/логотип/favicon/фоны), платёжные кассы и счёт приёма оплат, маршрутизация онлайн-платежей и банковских выписок, фискализация (54-ФЗ), изоляция входа в ЛК и брендинг по домену, изоляция персонала по компаниям, свой Telegram-бот и супергруппа уведомлений, SLA-сервис и API-прокси. Состав компаний ведётся в карточках организаций. Платёжные/фискальные реквизиты вводятся в карточке организации по мере оформления юр.лиц.
AI-агент — единый универсальный AI-модуль
Всё, что касается искусственного интеллекта, собрано в одном нативном модуле /admin/ai/: мультипровайдерный чат с инструментами на прямом ORM, база знаний для бота и операторов, голосовой разбор звонков, собственный виджет для сайтов с настройками из админки и запись лидов в нашу CRM. Все ключи, промпты, история, дашборд и внешний вид виджета живут в биллинге (или на сервере лицензий), без зависимости от сторонних серверов.
Где живёт поведение бота, что он умеет вызывать сам, почему цены нельзя писать в промпт и что делать с очередью «бот не смог».
Карта возможностей модуля:
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 — настраиваются из интерфейса.

Мультипровайдер: выбор модели
Для каждого канала (Личный кабинет, Мобильное, Email/HelpDesk, Виджет, Голос, Подсказка оператору) выбирается провайдер и конкретная модель. Запросы маршрутизируются единым движком ai_prompt_builder.build(); ответы и токены логируются в ai_chat_log с разбивкой по провайдеру. Запросы к внешним API провайдеров идут через прокси (эти сервисы заблокированы из РФ).
База знаний
Раздел /admin/ai/kb/ — сценарии решения проблем (статьи KnowledgeArticle) с кодом, приоритетом и аудиторией (бот / оператор / оба). Интерфейс повторяет рабочую базу знаний поддержки: четыре вкладки (Сценарии решения, Дерево диагностики, Шаблоны ответов, Аналитика), фильтры по аудитории и приоритету, поиск. Карточка-аккордеон раскрывается в четыре колонки-шага (Уточнение → Диагностика → Решение → Если не помогло) с блоком эскалации и примечаниями. Активные статьи автоматически подмешиваются в системный промпт бота.


Инструменты на ORM (tool-use)
AI-ассистент умеет вызывать нативные функции напрямую через ORM, без HTTP к самому себе. Инструменты делятся на две группы:
- Справочные (по клиенту) — после подтверждения личности по SMS: проверка клиента и баланса, статус RADIUS-сессии, диагностика «нет интернета», история платежей, проверка массовой аварии по району, активация обещанного платежа.
- Диалоговые (лид/подключение) — бот сам работает с заявкой прямо в разговоре:
get_tariffs(название, скорость, цена; фильтр физ/бизнес),check_coverage(проверка покрытия по адресу через базуHomes),create_connection_lead(оформить заявку → сделка в CRM, максимально заполнив карточку),request_callback(обращение/перезвон → тикет Поддержки). Личность для этих инструментов не требуется.
Мультипровайдер: инструменты работают на всех подключённых провайдерах (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 организации — он виден в её карточке.
- Фирменный цвет. Для организации, отличной от основной, цвет виджета берётся из её акцентного цвета в брендинге (карточка организации → Брендинг).
AI_WIDGET_COLOR, зелёный#43b77a). Цвет применяется к кнопке-лаунчеру, шапке окна, пузырям сообщений, кнопкам и полю ввода. - Маршрутизация чата и лидов. Диалог и заявки уходят в свою организацию: ассистент отвечает её персоной, показывает её тарифы, лид/тикет создаётся в её воронке и Поддержке.
- Определение организации. Приоритет — явный
data-org; если не задан, организация определяется по домену сайта (Origin) из списка организаций. Пусто и домен не распознан → основная организация.
Если 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/:
- Голос ассистента — 8 голосов с русскими именами и характером: Заря, Ксения, Лада, Арина (женские), Пётр, Захар, Фёдор, Олег (мужские). Кнопка «Прослушать» проигрывает образец выбранного голоса прямо в админке.
- Модель — выбирается из доступных голосовых моделей; язык распознавания/синтеза (BCP-47, по умолчанию
ru-RU). - Приветствие в голосе, автостарт разговора при открытии вкладки «Голос», показ транскрипта диалога, лимит длительности сессии (сек, 0 = без ограничения).
- WSS-URL прокси — адрес голосового прокси (
wss://адрес вашей установки/voicews).
Требует ключ провайдера и работающий голосовой прокси (контейнер 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:
- Встраиваемый виджет формы —
order_widget.js: кнопка в углу сайта + модалка с формой (имя, телефон, e-mail, адрес, тариф, доп.услуги). Вставляется одной строкой<script ... data-auto-init>, изолирован через Shadow DOM, сам собирает UTM-метки страницы. Тарифы подгружаются из/api/crm/tariffs/. - Полностраничная форма
/order— многосекционная форма с экранами проверки и подтверждения; поддерживает UTM в query-строке. Демо встраивания виджета —/order-widget-demo. - AI-чат — при намерении подключиться бот предлагает оставить заявку.
Состав полей и тексты настраиваются в модалке «Формы заказа» (заголовок, цвет, телефон, видимость блоков). Главный выключатель приёма заявок — CRM_PUBLIC_LEAD_ENABLED (по умолчанию выключен, включается тумблером в хабе).


Маршрут заявки:
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, факт эскалации, номер тикета и ссылку на сессию в дашборде. Эскалация (фраза вроде «хочу оператора» или жалоба) создаёт тикет во внутренней Поддержке.

Эскалация чата в тикет:
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, виджет, голос, телефон, подсказка оператору).
- Кейс — вопрос клиента + критерии проверки: что ответ должен и не должен содержать, ожидается ли эскалация, лимит слов, плюс тестовый контекст клиента (имя, баланс, тариф, статус).
- Прогон идёт тем же движком, что настроен на канале: кейсы проверяются на той же модели, что настроена на канале, а не на модели «по умолчанию».
- «В тесты» из журнала диалогов — в карточке сессии на
/admin/ai/playground-log/кнопка заводит регрессионный кейс прямо из неудачного диалога: вопрос клиента и комментарий оператора «как надо» переносятся автоматически. Кейс создаётся черновиком — останется дописать критерии и включить его. - Кейсы хранятся в базе и редактируются в интерфейсе; файловый датасет репозитория продолжает подмешиваться к прогону.
/admin/ai/test_chat/
работает в режиме симуляции: реальные заявки, тикеты, SMS, звонки-верификации и платёжные ссылки
из тест-чата не создаются — ассистент получает ответ «действие выполнено» и продолжает
диалог.
Дашборд AI-сессий
Дашборд /admin/reports/ai_chat_dashboard/ — единая аналитика всех AI-диалогов (ЛК, мобильное приложение, виджет на сайте). Это рабочий инструмент для оценки качества ассистента и обучения промпта. Сюда перенесена история диалогов из внешнего сервиса — 19 332 строки (1811 диалогов, 18 171 сообщение, 1161 вызов инструментов), сопоставленные с клиентами по телефону.

Показатели и графики
Пять карточек сверху отвечают на вопросы «сколько было разговоров», «насколько они были длинными», «дошли ли до конкретного клиента» и «сколько это стоило»:
| Показатель | Что считается |
|---|---|
| Сессий | Обращения за период. В мессенджерах одна переписка тянется годами (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. |
Расходы на модели и бюджет месяца
Блок «Расходы на модели» показывает, куда уходят деньги, в двух разрезах — по моделям и свёрнуто по провайдерам. Стоимость считается по прайсу каждой модели и курсу из настроек AI, поэтому расход по каждой модели виден отдельно.
| Колонка | Что показывает |
|---|---|
| Модель / Провайдер | Переключатель над таблицей. В режиме моделей рядом с названием — её провайдер. |
| Сообщений | Сколько ответов сгенерировано этой моделью за период. |
| Токенов вход / выход | Вход — промпт со всем контекстом, выход — сам ответ. Вход обычно в десятки раз больше. |
| Цена, $/Mtok | Прайс этой модели за миллион токенов: вход / выход. |
| Стоимость, ₽ | Себестоимость по прайсу и курсу. Это не сумма, начисленная по лицензии — она считается по тарифу на сервере лицензий и включает наценку. |
Над таблицей — полоса бюджета месяца: потрачено с начала месяца, прогноз до
конца и отметка прогноза на шкале. Цвет меняется при 80% и при превышении. Если лимит не задан
(настройка AI_BUDGET_MONTH_RUB), вместо шкалы показываются суммы и ссылка на
настройку — почтовый алерт о превышении при пустом лимите тоже молчит.
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>/ при открытии человеком перенаправляет на дашборд с уже раскрытой сессией.
Тёмная тема и телефон
Период задаётся единым date-range-picker (общий для всех отчётов). Стоимость токенов считается из настроек AI_PRICE_* и курса AI_USD_RUB.
Список сессий и транскрипт
Блок «Все сессии за период» — таблица с фильтрами по каналу (Все/LK/Mobile) и фидбеку (Все/👍/👎/Эскал.), живым поиском (ID/клиент/договор) и сортировкой по столбцам. Клик по строке открывает транскрипт диалога в стиле iMessage: реплики клиента — справа (светло-серый градиент), Аиды — слева, с поддержкой markdown-форматирования (**жирный**, заголовки #/##/###, `код`). Клик по имени клиента открывает slide-панель клиента (как в других разделах) — без ухода со страницы.

Поток данных дашборда:
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,
местами применения и живыми демо. Среди последних добавленных:
- SmitDrawer — боковая панель проекта: карточки склада, тикеты, профиль, настройки AI. Панели складываются стопкой, закрываются по Esc и клику мимо, возвращают фокус на кнопку, из которой открылись.
- SmitIconSelect — селект с иконками; на нём все фильтры «Линейка услуг».
- SmitTable — таблица-основа: карточки на телефоне плюс перетаскивание, ширина и состав колонок с сохранением в аккаунте.
- SmitOrgSwitch — смена организации у объекта: меню, подтверждение со списком последствий и запрос на сервер.
- SmitInfiniteTable.fromArray — порционная отрисовка уже загруженного списка: строки рисуются по мере прокрутки.
Библиотека переиспользуемых 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, блокировку прокрутки и порядок слоёв (карточка клиента → видео) берёт на себя компонент.
- Стопка. Панель, открытая поверх другой, ложится сверху и слегка сдвигает нижнюю. 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 подтверждён |
mango | callback оператор→клиент | средний | заготовка |
Своя телефония на 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/) есть вкладка «Звонки» — журнал голосовых звонков клиента и панель управления исходящими вызовами. Видна, когда модуль автообзвона включён.

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

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