Граф Wiki

Карточка клиента

Всё, что открывается в карточке клиента: вкладки, действия оператора, история изменений.

Карточка клиента — всё в одном месте
Модуль «Биллинг-ядро» — на сайте продукта
Содержание раздела

9. Карточка клиента

URL: /admin/Abonents/<id>/. Открывается кликом по строке клиента в любом списке (папка, глобальный поиск, модалка счёта). Это центральная рабочая страница оператора — здесь видны все аспекты клиента и доступны все действия.

Карточка клиента

Аватарка и соцсети клиента

Карточка показывает аватарку клиента (в шапке профиля, в сайдбаре-drawer, в виджете «Биллинг» тикета Поддержки, в тултипах карты CRM) и иконки-ссылки на профили в VK / Telegram / MAX — цветные брендовые иконки без круга, открываются в новом окне. Данные хранятся в реквизитах клиента (атрибуты «Аватарка VK», «Аватарка TG», «VK ID», «Telegram ID», «MAX ID»), файлы аватарок — в GCS.

Карточка клиента — аватарка и соцсети

flowchart LR
    FS["Внешний источник
(фото, VK/TG id)"] -->|"импорт аватарок
матчинг: договор/email/телефон/
соцсети/тикеты/ФИО (транслит+fuzzy)"| ATTR VKAPI["VK / Telegram
(id из каналов Поддержки)"] --> ATTR ATTR["UserAttributes
Аватарка VK/TG · VK/TG/MAX ID"] --> GCS["Аватарки в GCS
(постоянные URL)"] ATTR --> UI1["Профиль клиента"] ATTR --> UI2["Сайдбар (drawer)"] ATTR --> UI3["Тикет Поддержки
виджет Биллинг + панель"] ATTR --> UI4["Карта CRM (тултип)"]

По VK/TG/MAX ID биллинг узнаёт клиента, когда тот пишет в соцсетях/мессенджерах — тикет сразу связывается с карточкой.

Шапка карточки

Сверху страницы — фиксированная панель с:

Все destructive-действия (Заблокировать / Удалить) защищены Bootstrap-confirm с превью последствий (паттерн).

Индикатор несохранённых изменений

Кнопка «Сохранить» (иконка-дискета в шапке карточки) показывает, есть ли в форме несохранённые правки, а изменённые поля подсвечиваются — оператор сразу видит, что нужно сохранить.

Состояния кнопки «Сохранить»:

Кнопка всегда физически кликабельна — серый вид это лишь подсказка «нечего сохранять», а не блокировка. Если клиент в этот момент обрабатывается биллингом (фоновая операция), кнопка не пульсирует, а tooltip объясняет, почему сохранение временно недоступно.

Подсветка изменённых полей. Любое поле формы, значение которого отличается от исходного, подсвечивается жёлтой рамкой и фоном (класс .ab-field-changed). Работает и для обычных полей, и для Select2-виджетов; есть отдельный вариант для тёмной темы.

Изменённое поле формы подсвечено жёлтой рамкой и фоном

Как это работает. При загрузке страницы JavaScript снимает «снимок» исходных значений всех полей формы #changelist-form (часть полей догружается через AJAX — снимок повторяется через короткие интервалы, чтобы захватить и их). На событиях change/input текущее значение сравнивается со снимком: поле получает/теряет подсветку, кнопка «Сохранить» — зелёный вид. Технические поля (CSRF-токен, management-формы formset'ов) из отслеживания исключены.

Индикатор — это только визуальная подсказка поверх формы. Он не участвует в самом сохранении: клик по кнопке отправляет форму всегда, независимо от состояния индикатора.

Быстрый доступ (под шапкой)

Шесть карточек-сниппетов над аккордеоном на вкладке «Информация» — самые востребованные данные по клиенту без переключения вкладок. Каждая карточка имеет ссылку «→» на соответствующую вкладку для полного просмотра.

КарточкаЧто показываетДействие «+»
📋 АудитПоследние 8 действий по клиенту (блокировки, смены тарифа, корректировки, входящие SMS) с датой и описанием
🎁 УслугиНазначенные услуги (трафик, скидки) + подсекция «Бонусы» со счётчиком баллов. Эффективная сумма с цветным dot-индикатором enabled/disabledДобавить услугу / бонус
💰 ОперацииПоследние финансовые операции с цветным бейджем направления (↓ приход / ↑ расход) и зачёркиванием для сторноДобавить финоперацию
🎧 HelpDeskАктивные и закрытые тикеты HelpDesk с бейджем статуса и временем последнего обновления
СообщенияПоследние SMS/Email/Push/Telegram/VK со статусом доставки (✓ или ⏳)Отправить сообщение
📡 СессииДо 5 учётных записей клиента с признаком онлайн/офлайн, IP, временем последнего обновления и трафиком ↓/↑

Данные загружаются одним AJAX-запросом /admin/Abonents/<id>/quick_cards/ сразу после открытия карточки. Шапка автообновляется раз в 60 секунд (статус Онлайн/Офлайн, баланс) — карточки операторов открыты часами, но всегда актуальны.

Настройки профиля клиента (layout 2-col)

Раздел /admin/settings/system/?tab=abonent_profile (вкладка «Профиль») — 33 глобальные настройки в 9 секциях, управляющие тем, что и как видит оператор на карточке клиента и в списках. Все настройки глобальные (действуют на всех операторов), хранятся в SystemSettings с категорией abonent_profile. Кеш Redis 60 секунд (ctxproc:abonent_profile_v2), сбрасывается при сохранении формы.

С версии 2.1.0 страница в 2-колоночном layout (на экранах ≥1280px). 9 секций укладываются masonry-style через CSS column-count:2 + break-inside:avoid — без выравнивания по высоте, плотно. На узких экранах автоматически 1 колонка.

Обзор вкладки «Профиль» — 2-колоночный layout, 9 свёрнутых секций

Также переименованы две соседние вкладки для краткости:

Вкладки «Скорость» и «RADIUS» в /admin/settings/system/ (переименованы)

Вкладка «Профиль» с раскрытыми секциями — 2-колоночный layout

Секция 0 — Быстрый доступ (Quick Cards)

Master-toggle + 6 индивидуальных тогглов по карточкам (Аудит / Услуги / Операции / HelpDesk / Сообщения / Сессии). При выключении master блок не загружается — карточка клиента грузится быстрее (нет AJAX-запроса /quick_cards/).

Секция «Быстрый доступ» — master + 6 индивидуальных тогглов

Секция 1 — Видимость секций карточки

Управление крупными элементами карточки клиента:

Секция «Видимость секций карточки»

Секция 2 — Колонки списка клиентов

4 тоггла-«дефолта» для колонок в /admin/Abonents/<folder>/ и глобальном поиске. Действует через def-флаги в COLS-массиве — каждый оператор может переопределить в своей user-config через модалку «Колонки».

Секция «Колонки списка»

Секция 3 — AJAX и автообновление

Секция «AJAX и автообновление»

Секция 4 — Дефолты карточки

Секция «Дефолты карточки»

Секция 4А — Управление вкладками карточки

Управление полным набором из 13 вкладок карточки клиента: видимость (показать/скрыть), порядок (drag&drop) и стиль отображения.

Возврат скрытых вкладок: «Задачи» (CRM_TASK, скрыта) и «Оборудование и подключения» — теперь можно вернуть в навбар одним кликом.

4 стиля отображения навигации вкладок (превью видно прямо в форме):

Drag&drop: каждую вкладку можно перетащить за иконку «☰» в любую позицию. Изменения сохраняются в ABONENT_PROFILE_TABS_CONFIG как JSON. Кнопка «Сбросить к дефолту» возвращает порядок и видимость из build_abonent_card_tabs в коде.

Секция «Управление вкладками карточки» — drag&drop + 4 стиля

Секция 5 — Стиль и плотность

Секция «Стиль и плотность»

Секция 7 — Баннеры уведомлений оператору

5 баннеров-подсказок в шапке карточки клиента, каждый показывается только при выполнении условия:

Секция «Баннеры уведомлений»

Секция 8 — Прочее

Секция «Прочее»

Технические детали

i-иконка рядом с заголовком «Быстрый доступ» на карточке клиента ведёт прямо в этот раздел документации.

Диагностика связи — плашка-светофор

В шапке карточки клиента — автоматический экспресс-диагноз состояния связи. Светофор из 3 цветов (🟢 норма / 🟡 возможны проблемы / 🔴 проблема) с раскрывающимися деталями и готовым ответом для клиента. Все данные читаются из БД read-only (никаких записей, ≤6 SQL-запросов).

Цель функционала — сократить время разбора жалобы «не работает интернет» с 5 минут до 10 секунд.

Что показывает плашка

Свёрнутая плашка — компактный светофор + 5 чипов проверок. Каждый чип имеет свой цвет (зелёный/жёлтый/красный/серый) и tooltip с детальным summary.

Жёлтая плашка диагностики — Lost-Carrier ×3 за сутки

5 проверок диагностики

ЧипЧто проверяетКогда красный
💰 Биллингenabled, deleted, баланс, активные блоки, тариф, ОПудалён / отключён / нет тарифа / b_admin / b_negbal
📶 СессияUsers.enabled/logged, активная RADIUS-сессия, свежесть UPDATE_TIMELOGGED=true без сессии (зависшая)
📜 РазрывыEND_REASON последних 10 сессий, Lost-Carrier за час/сутки2+ Lost-Carrier за час → выезд монтажника
📊 ТрафикIN_OCT/OUT_OCT текущей активной сессии(всегда green/yellow/grey)
📍 АдресAbonents.HOME_ID/FLAT == CONNECTION_POINTS.HOME_ID/FLATадрес и точка подключения не совпадают

Общий уровень (overall) = худший из всех (red > yellow > green > grey).

Развёрнутый вид и готовый ответ

Клик на кнопку «Подробнее» раскрывает 5 карточек с полным summary каждой проверки + блок «Готовый ответ для клиента» с кнопкой «Скопировать». Текст ответа автоматически подбирается по decision tree из 12 веток (нет тарифа → инструкция назначить тариф, 2+ Lost-Carrier → инструкция про кабель и роутер, активный трафик → «перезагрузите Wi-Fi», блок за неуплату → «внесите оплату», и т.д.).

Развёрнутая плашка с 5 карточками + advice + кнопка Скопировать

Sparkline 24ч и auto-actions

Sparkline «Сессии за 24 часа» — мини-график из 24 столбиков, каждый = 1 час. Зелёный = час с активной сессией, серый = тишина, красный = час с Lost-Carrier. Tooltip на каждом столбике показывает состояние.

Под графиком — 3 кнопки-действия:

Каждое действие защищено JS-confirm, показывает spinner во время выполнения, выводит toast «успех/ошибка», пишет запись в аудит.

Sparkline за 24 часа и три кнопки действий в развёрнутой плашке

UX-особенности

REST API и CLI

Для интеграции с внешними системами:

Пример CLI:

$ docker exec app-web-1 python manage.py diag 5927

═══ Диагноз: 🟡 YELLOW — #5927 Кельн Валерий Валерьевич ═══

  [GREEN] billing      — Активен, баланс 6.01 ₽, 2025_Для Тебя: 749.0 руб./мес.
  [GREEN] session      — Сессия не активна (клиент офлайн)
  [YELLOW] end_reasons — Lost-Carrier ×3 за сутки — нестабильная линия
  [GREY] traffic       — Активной сессии нет
  [GREEN] address      — Адрес клиента совпадает с точкой подключения

📋 Готовый ответ для клиента:
Здравствуйте! Проверим вашу линию и свяжемся с вами в течение 30 минут.

Реализация (для разработчиков)

Вкладки

Вкладки с подробной информацией. Часть из них объединена в группы: «Учётные записи + RADIUS», «Точки подключения + Оборудование» — соседние вкладки навигируются стрелками. Некоторые вкладки условные: «Банк» (иконка fa-university 🏛) появляется, когда у клиента есть платежи по банковским выпискам.

Информация

Основные поля клиента в двух колонках. После рестайлинга карточки вкладка перестроена под единый визуальный язык: hero-блоки с фирменной зелёной полосой слева, единый размер бейджей, общие классы для модалок и инпутов.

Слева — основной блок:

Справа — блок «Договор и контакты» (объединение бывших «Контактная информация» и «Договор»). Содержит:

Чекбокс «Юр. лицо» и валидация тарифа

При включении чекбокса «Юр. лицо» на физлице с физическим тарифом (Tarif.is_business=false) JavaScript показывает confirm-диалог и при подтверждении открывает модалку смены тарифа с автоматическим переключением на фильтр «Юр.лица». Серверная валидация (Abonents.clean()) дополнительно проверяет: если company=true и текущий тариф не Business — ValidationError на поле tarif с понятным текстом.

Маски и валидация email/телефон

Оба контактных поля имеют клиентскую и серверную валидацию:

Все ошибки серверной валидации собираются в один ValidationError(dict), чтобы оператор видел все проблемы сразу.

Блок «Смена тарифа / Блокировки»

Внутри блока JavaScript добавляет три визуальных подзаголовка-разделителя, чтобы поля даты не шли плоской простынёй:

Что убрано/скрыто на вкладке

По запросу администрации со вкладки «Информация» убраны:

Ревизия бейджей шапки

В нижнем ряду бейджей (.abon-summary-chips) убраны дубли:

Все чипы приведены к единому языку: единая высота 24px, полный pill (radius 999px), solid пастельный фон без градиентов, тонкая граница того же оттенка. Hover — лёгкое затемнение через filter: brightness(.96).

Плашка диагностики связи и заблокированные

Для заблокированных и отключённых клиентов плашка-светофор «Проблема со связью» не показывается — у них нет «проблем», есть ожидаемое состояние блокировки. Плашка появляется только для активных без записей в AbonentsBlock.

Mini-блок СОРМ под контактами показывает паспорт + ИНН с кнопкой быстрого редактирования (без перехода на отдельную вкладку). Для юр.лиц — ИНН/КПП/ОГРН + В лице/Директор.

Блок «Финансовая информация»

Под основной информацией клиента отдельная карточка с расчётами по балансу. Состоит из двух частей: таблица балансов и параметры порогов/прогнозов.

Таблица балансов

Три варианта расчёта с разделением списаний на предоплату и постоплату:

СтрокаФормулаЧто значит
Бух.admin_accounts.ostatok / 10¹⁰Бухгалтерский баланс — фактическое значение лицевого счёта.
ТекущийБух − Расход предоплата + ПриходС учётом обещанных платежей и предстоящих списаний за уже оказанные услуги.
На конец месяцаТекущий − Расход постоплатаПрогноз остатка после всех списаний до конца расчётного периода.

Колонки:

Параметры и прогнозы
ПолеНазначение
Порог предупр.Если ostatok < threshold — отправляется уведомление через MsgStack (Email/SMS/Push).
Порог откл.Если ostatok < thresholdbilling_worker ставит блок b_negbal и шлёт CoA Disconnect.
БонусыТекущий счёт бонусных баллов (программа лояльности, AbonentsLoyalty).
Рек. платёжМинимальная сумма для непрерывного обслуживания до конца следующего месяца.
Мин. для разблок.При активном b_negbal — сколько нужно внести сейчас чтобы блок снялся.
Хватит доПрогнозная дата выхода в минус: today + (ostatok / monthly_burn_rate).
Калькулятор «Рассчитать платёж»

Поле «Рассчитать до даты» + кнопка → возвращает требуемую сумму пополнения чтобы баланса хватило до указанной даты. Использует тот же алгоритм что billing_simulate — учитывает тариф, активные UsersUsluga, будущие sched_date, бонусы и скидки.

Где смотреть в коде

История

Лента всех изменений по клиенту, синхронизирована с разделом Аудит. Каждая запись содержит:

Drill-down ссылки ведут на конкретный объект изменения (FinOp, UsersUsluga, ABONENTS_BLOCK).

Тариф и услуги

Текущий тариф клиента + список индивидуальных услуг (UsersUsluga):

Бонусы

Подвид «Услуг», но с фильтром по знаку: компенсации и скидки (summa < 0) или начисления типа «бонус» по имени:

Финансовые операции (вкладка)

Журнал приходов, расходов и сторно по счёту клиента — компактная таблица с быстрыми фильтрами и формами создания операций. Открывается в карточке клиента по табу «Операции»: /admin/Abonents/<id>/operations/.

Вкладка Операции — общий вид

Содержание раздела
KPI-блок

Сразу под навигацией — 4 цветные KPI-карточки:

Клик по KPI «Приход» / «Расход» / «Сторно» работает как chip-фильтр — быстрая фильтрация без открытия toolbar.

Toolbar в одну строку

Все элементы фильтра помещаются в одну линию (на mobile уезжают в bottom-sheet по кнопке «Фильтры»). Слева направо:

  1. Кнопки Приход / Расход — открывают модалку создания.
  2. Chip-фильтры по знаку: «Все · ↓ · ↑ · ↻» (icon-only с tooltip).
  3. Диапазон дат: [13.05.2025] — [13.05.2026] в input-group, без подписей «с/по».
  4. Селектор типа операции (выпадающий список FinTypes).
  5. Поиск по описанию (растягивается на всё свободное место).
  6. Кнопка — применить фильтры.
  7. Кнопка — экспорт CSV выборки.

Период по умолчанию: 12 месяцев — сразу видна вся годовая история, при необходимости диапазон сужается вручную.

Модалка «Приход / Расход»

Одна модалка #addFinOpModal для двух режимов. Открывается из toolbar или из шапки на mobile. Цветной header (зелёный для прихода / красный для расхода) с белым крестиком закрытия.

Поля:

Модалка Приход (зелёный header)

Модалка Расход с раскрытой услугой

Привязка услуги к расходу

Только в режиме «Расход» под основными полями есть ссылка «+ Добавить услугу». Клик — раскрывается селект из 300 услуг каталога (/rest_api/v2/Usluga/?per_page=300&deleted=false). Услуги показаны с суффиксом-ценой для удобства выбора:

Подключение интернета · +1500.00 ₽
Скидка за электроэнергию 50 · -50.00 ₽
бонус 100 руб/мес · -100.00 ₽
Бонус 650р/мес · -650.00 ₽
…

Привязка услуги к финоперации сохраняется в FinanceOperations.usluga_id (FK → Usluga) — для отчётности по типам расходов.

Услуга опциональна — оператор может оформить «голый» расход без привязки.

Mobile (≤768px)

На мобильных:

Вкладка Операции на mobile

Модалка Расход на mobile (fullscreen)

Тёмная тема

Полная адаптация для тёмной темы: KPI-карточки, toolbar, таблица операций, модалка (background #1f2227, поля #2a2f37, borders #3a3f47, labels #c8d0d8).

Вкладка Операции в тёмной теме

Модалка Расход в тёмной теме

Действия со строкой операции
Права доступа и ограничения политики

Поведение всех действий с финансовыми операциями (создание, редактирование, удаление) определяется 5 настройками в /admin/settings/system/?tab=security. По умолчанию ничего не ограничено — стандартное поведение Django admin (любой is_staff=True может всё).

НастройкаЧто меняетDefault
RESTRICT_MANUAL_FINOPS_TO_SUPERUSER Кнопки «Приход»/«Расход», редактирование и удаление доступны только is_superuser=True или членам группы root False
ALLOW_USLUGA_IN_MANUAL_FINOPS Показывать ссылку «+ Добавить услугу» в модалке Расхода False
MANUAL_FINOP_MAX_AMOUNT Запрет операций с суммой выше N ₽ (защита от опечаток). 0 = без ограничения 0
MANUAL_FINOP_REQUIRE_DESCR Запрещает пустое поле «Описание» при ручных операциях False
ALLOW_FINOP_DELETE Если выключено — кнопка удаления скрыта, доступно только сторнирование (для бухгалтерской отчётности) True

Все ограничения проверяются и в backend (HTTP 403 на API endpoints finops_ajax, finops_crud_ajax, FinOpChangeView) и в frontend (кнопки и ссылки скрыты в HTML, чтобы не вводить пользователя в заблуждение).

Helpers backend (billing.services.settings_service):

Важно: ограничения касаются только ручных операций через UI. Автоматические процессы (Celery worker «billing_worker» для абонплаты, webhook ЮKassa, обещанный платёж, autoblock и т.п.) работают как обычно — без проверок. Это нужно чтобы политика «только суперюзер» не сломала автоматическое начисление.

Обновление баланса

Создание / редактирование / удаление FinOp автоматически обновляет account.ostatok ( hotfix — ранее create/edit писали запись в БД, но баланс не трогали; только delete делал rollback, что приводило к симметричному багу «удалил → баланс вырос»).

Логика:

При уходе баланса в минус — Celery beat task process_blocks (каждые 5 минут) автоматически создаст AbonentsBlock(b_negbal=True) + CoA-disconnect через _block_negative_balance().

Точки подключения — назначение и роль

Содержание раздела

Точка подключения (CONNECTION_POINTS) — это запись о физическом подключении клиента к сети провайдера: квартира, розетка, порт коммутатора. Хранится в таблице CONNECTION_POINTS. Один клиент может иметь несколько точек (две квартиры, резервная линия, разные приставки).

Зачем нужна точка подключения

Точка решает четыре задачи:

  1. Физическая привязка к инфраструктуре — какая розетка → какой порт свитча → какой VLAN → какой IP-пул. Используется техником для выезда на адрес;
  2. Предотвращение двойного подключения — порт SWITCH_P_ID резервируется. Биллинг не даст подключить двух клиентов к одному порту;
  3. OPT82-авторизация — сопоставление DHCP Option 82 (IP свитча + номер порта) с записью точки. Без точки OPT82 не работает;
  4. Резерв адресов — точку можно создать заранее (порт прокинут в подъезд), без привязки к клиенту. Когда клиент подпишется — она ему присваивается.
Поля и связи
ПолеСвязьОписание
NAMEПроизвольное имя («Кв. 12, розетка 1»)
HOME_IDFK → homesДом
FLAT + SOCKETКвартира + номер розетки
SWITCH_IDFK → switchКоммутатор уровня доступа
SWITCH_P_IDFK → SWITCH_PORTSКонкретный порт коммутатора
IP_PULL_IDFK → ip_pullИз какого пула выдаётся IP клиентам этой точки
ABONENT_IDFK → abonentsКому принадлежит (NULL = резерв)
USER_IDFK → usersКакая учётка работает через эту точку (NULL = резерв)
Когда нужно создавать точку
Что делать с точками без жителей

На рабочий сервер 2026-05: 914 точек без USER_ID и ABONENT_ID. Это не мусор:

Важно: при перемещении клиента из одной квартиры в другую правильнее не удалять старую точку, а отвязать (ABONENT_ID=NULL). Тогда историю подключения видно через USERS_USLUGA_HISTORY и аудит, а сама розетка остаётся в каталоге как «резерв» для нового жильца.

Точки подключения и Оборудование

Объединённая вкладка. Содержит:

Учётные записи (Users) — назначение и роль

Содержание раздела

Учётная запись (Users) — это запись для авторизации в RADIUS на конкретную услугу (Internet, IPTV, VoIP). Это не Django-юзер админки, а отдельная сущность, специфичная для биллинга.

Чем Users отличается от Abonents
СущностьЧто этоКратность
AbonentsДоговор / физ или юр лицо1 на клиента
UsersУчётная запись для одной услугиN на клиента

Пример: Иван Иванов (один клиент) подписан на:

На каждую — отдельная Users-запись с уникальным LOGIN, паролем, MAC и привязкой к NAS. Все три записи через ABONENT_ID ведут на один Abonents-договор.

Поля и связи
ПолеНазначение
LOGIN + PSWЛогин/пароль для PPP/CHAP/PAP-аутентификации
IP / SNATIPНазначенный IP (статика). При DHCP/PPPoE заполняется RADIUS-ом из пула
MACMAC-адрес устройства клиента (для DHCP/MAC-bind)
NAS_IDЧерез какой NAS работает (заполняется при первой авторизации)
PULL_IDИз какого IP-пула выдан адрес
SWITCH_ID + PORTЧерез какой коммутатор и какой порт
AUTH_TYPE_IDТип авторизации (см. ниже)
TARIF_IDОпционально — свой тариф для этой учётки (если NULL — наследует от Abonents.tarif)
ENABLEDВключена/выключена. RADIUS отказывает при enabled=false
LOGGEDСейчас в сессии (обновляется RADIUS Accounting)
Типы авторизации

Справочник auth_types (FK Users.AUTH_TYPE_ID) определяет как RADIUS аутентифицирует клиента:

Жизненный цикл
  1. Создание — вручную через карточку клиента (users_inline.html модалка) или автоматически через Abonents.add_service() при добавлении услуги с флагом create_login=True;
  2. Первая авторизация — RADIUS заполняет NAS_ID, IP, LOGGED=true, выдаёт IP из пула;
  3. Сессия — пишутся записи в RADIUS_SESSIONS; Acct-Interim каждые ~10 мин обновляет статус;
  4. Блокировка — при Abonents.block() все Users этого клиента получают enabled=false + CoA Disconnect (см. Блокировки);
  5. Удаление — мягкое (enabled=false) при удалении клиента, или жёсткое через UI после отсоединения от точек подключения и сессий.
Распространённая ошибка: «не могу установить тариф учётке» — потому что обычно тариф у Abonents, не у Users. Поле Users.TARIF_ID используется только в редких сценариях (когда у разных учёток одного клиента нужны разные скорости). Если у вас стандартная схема — тариф ставится на клиента, и все его Users автоматически работают на этом тарифе.

Учётные записи

Логины клиента (Users — один клиент может иметь несколько учёток для Internet/IPTV/VoIP). На каждую строку — 6 действий:

Inline-редактирование пароля: двойной клик на ячейке с паролем → input + кнопки 🎲 generate / ✓ save / ✕ cancel. Enter=save, Escape=cancel.

RADIUS-сессии — назначение и роль

Содержание раздела

RADIUS-сессия — запись о факте подключения клиента к сети: с какого момента, через какой NAS, какой IP, сколько байт прошло, когда закончилась. Хранится в таблицах RADIUS_SESSIONS (история) и USERS_RADIUSAUTH (текущее состояние).

Две таблицы: RADIUS_SESSIONS и USERS_RADIUSAUTH

Дублирование данных сделано сознательно ради производительности:

ТаблицаЧто хранитРазмер
RADIUS_SESSIONS История всех сессий за всю жизнь клиентов. Каждая сессия = одна запись с START_TIME, END_TIME, OCTETS_IN/OUT, END_REASON ~400K-7M записей
USERS_RADIUSAUTH Зеркало текущего состояния. Один user = одна запись с полями LOGGED, текущий IP, последние OCTETS_IN/OUT, RADIUS_UPDATE 1 строка на клиента (~5800)

Зачем две таблицы: виджет «Онлайн сейчас» в дашборде должен мгновенно показать «1234 онлайн». В RADIUS_SESSIONS для этого надо найти строки WHERE END_TIME IS NULL среди миллионов записей. В USERS_RADIUSAUTH — простой SUM(LOGGED) на 5800 строк = 10мс. Аналитика и история — только в RADIUS_SESSIONS.

Жизненный цикл сессии
  1. Acct-Start — NAS отправляет RADIUS Accounting-Request с типом Start. Биллинг создаёт запись в RADIUS_SESSIONS с END_TIME=NULL + обновляет USERS_RADIUSAUTH(LOGGED=1, IP=...);
  2. Acct-Interim-Update — NAS шлёт каждые ~10 минут (по Acct-Interim-Interval). Обновляются OCTETS_IN/OUT, SESSION_TIME, RADIUS_UPDATE — БЕЗ создания новой записи;
  3. Acct-Stop — при отключении клиента NAS шлёт Stop. Биллинг проставляет END_TIME=now(), END_REASON=<причина> + сбрасывает USERS_RADIUSAUTH.LOGGED=0.
Причины завершения (END_REASON)
ReasonЧто значит
User-RequestКлиент сам выключил роутер / отключил интернет
Idle-TimeoutNAS закрыл сессию по неактивности
Session-TimeoutДостигнут лимит сессии (если задан)
Lost-CarrierФизический обрыв линии (нет сигнала на порту)
Disconnect-ACKБиллинг отправил CoA Disconnect (блокировка) — NAS прервал сессию
NAS-RebootNAS перезагрузился, сессия закрыта системой
Manual-reset-by-operatorОператор вручную сбросил зависшую сессию через UI
Зомби-сессии и их чистка

Зомби-сессия — это когда NAS перезагрузился без отправки Acct-Stop, и в БД осталась запись с END_TIME=NULL, хотя реально сессии нет.

Async accounting

Учёт сессий идёт асинхронно, чтобы поток пакетов не упирался в запись в БД:

  1. FreeRADIUS принимает Acct-Update и сразу возвращает Accounting-Response;
  2. Сам пакет кладётся в Celery-очередь (radius_accounting_async);
  3. Воркеры разбирают очередь, дебаунсят множественные Interim в группу (раз в 10 секунд на user);
  4. Записывают батчем в БД.

Результат: 4M conflicting packets → 0, нагрузка на БД снизилась в 50 раз. Recovery «Stop без Start» — генерируется синтетический Start если пришёл Stop без записи (NAS reboot).

Чистка истории

RADIUS_SESSIONS растёт быстро (10K-100K записей в день). Без чистки таблица за год становится огромной. Авто-чистка:

Переполнение счётчика: NAS-Mikrotik после переполнения счётчика мог прислать Acct-Session-Time=4_261_480_885 (~135 лет). Колонка SESSION_TIME integer переполнялась → SQL-ошибка → запись не создавалась → виджет «Онлайн» расходился с реальностью. Фикс: clamp до int4_max в internet.py + миграция SESSION_TIME bigint. Виджет: 989 онлайн → 1001, расхождение 0%.

RADIUS / Монитор сессий

URL: /admin/Abonents/<id>/check/. Live-инструмент мониторинга RADIUS-сессий клиента.

Монитор сессий — общий вид: контекст тарифа, toolbar с KPI, таблица сессий, live-индикатор

Карточка контекста тарифа

Сверху страницы — компактный градиентный блок с информацией о тарифе клиента: имя, цена, шейпер из TarifRadiusParams (Mikrotik-Rate-Limit), Acct-Interim-Interval, статистика 7 дней (всего сессий / обрывов / процент). При drop_pct > 20% — иконка ⚠ предупреждения «выше нормы по тарифу».

Toolbar с KPI и переключателем периода

Над таблицей — 5 KPI-чипов:

Переключатель периода: Сутки / Неделя / Месяц / Всё. Клик → fetch GET /admin/Abonents/<ab_id>/sessions/kpi/?period=... → JSON → точечное обновление цифр.

KPI обновляются при выборе периода «Сутки»

Live-индикатор и auto-refresh

Справа в toolbar — индикатор «Авто • N с назад» с пульсирующей зелёной точкой. Каждые 30 секунд JS опрашивает GET /admin/Abonents/<ab_id>/sessions/list/, обновляет статус-бейджи и трафик в строках без перезагрузки страницы. При смене статуса (Online → Stale → Завершена или наоборот) — flash-анимация строки и toast-уведомление «🔌 Сессия 8142e97a завершена: User-Request».

Клик на индикатор — пауза. При переключении на другую вкладку браузера (document.hidden) — авто-пауза, при возврате — мгновенный refresh.

Колонки таблицы (12 на десктопе)
Причины завершения сессии — полная расшифровка

Значение в колонке «Причина» — это RADIUS-атрибут Acct-Terminate-Cause, который NAS (как правило MikroTik) присылает биллингу в пакете Accounting-Stop при закрытии сессии. Записывается в RADIUS_SESSIONS.END_REASON. Биллинг группирует десятки возможных кодов в 5 категорий (классификатор _REASON_CATEGORIES в billing/templatetags/user_filters.py, фильтр reason_category):

БейджЧто значитRADIUS-коды (Acct-Terminate-Cause)Что делать оператору
⏻ Норма Сессия завершена штатно — клиент сам отключился по своей инициативе или переавторизовался заново. User-Request, Logout, Admin-Rebooting Ничего. Это нормальное поведение.
⚡ Обрыв линии Физический обрыв соединения: NAS перестал получать пакеты от клиента — выключение оборудования, проблема с кабелем, пропало питание, перезагрузка NAS. Lost-Carrier, Lost-Service, Port-Error, NAS-Reboot, NAS-Error Единичный — норма (клиент выключил роутер). Много подряд у одного клиента → проблема с линией/оборудованием клиента, стоит проверить. KPI «Обрывов >25%» в toolbar — сигнал тревоги.
⏳ Таймаут Сессия завершена по таймауту — неактивность клиента или достигнута максимальная длительность сессии (настройки на NAS / в тарифе). Idle-Timeout, Session-Timeout Обычно норма. Если мешает клиенту — проверить Idle-Timeout в настройках NAS / профиля.
✨ Восстановлена Запись восстановлена биллингом синтетически: пришёл Accounting-Stop без соответствующего Accounting-Start (пакет Start был потерян). Чтобы трафик не пропал из отчётов, биллинг создаёт закрытую «синтетическую» запись. Synthetic, Recovered (внутренние пометки биллинга, не от NAS) Единичные — норма (потеря UDP-пакета). Массово → возможны проблемы со связью NAS↔биллинг или перегрузка accounting-очереди.
🛡 Админ Сессия принудительно завершена со стороны провайдера, а не оборвалась сама. Это команда, а не сбой. Admin-Reset, Admin-Reboot, NAS-Request, Callback-Reset, Manual-Reset Норма, если действие было намеренным: оператор нажал «Отсоединить» (CoA Disconnect), сработал автоблок по балансу, сменили тариф/IP, кто-то вручную убил PPPoE-сессию на MikroTik. Массово и неожиданно → проверить, кто и что делает с NAS.
— не классифицирована NAS не указал причину, либо прислал код, которого ещё нет в классификаторе. В бейдже показывается raw-значение как есть. Любой код вне списков выше, либо пустое значение Посмотреть raw-значение в тултипе. Если код встречается часто — стоит добавить его в _REASON_CATEGORIES.

Частые вопросы

Подсветка строк
Drawer-модалка деталей сессии

Клик на «i» в строке (или Enter на сфокусированной строке) открывает offcanvas-end справа шириной 540px с 4 вкладками:

  1. Обзор — 9 ключевых полей в <dl>: Начало / Завершение / Длительность / MAC / IP / NAS / Port / VLAN / Причина.

    Drawer — Обзор

  2. RADIUS — 20 raw-атрибутов: Acct-Session-Id, User-Name, NAS-IP-Address, Calling-Station-Id, Framed-IP-Address, NAS-Port-Id, Tunnel-Private-Group-Id, Acct-Session-Time, Acct-Input/Output-Octets, Acct-Terminate-Cause, raw timestamps в UTC, CEIL_IN/OUT, RATE_IN/OUT.

    Drawer — RADIUS-атрибуты

  3. Трафик — крупные цифры ↓ загрузка / ↑ выгрузка (в МБ/ГБ + точные байты), средний rate в Kbps, длительность.

    Drawer — Трафик

  4. Действия — CoA Disconnect (disabled для закрытых) + 3 копировать-кнопки (MAC / IP / Session ID) + ссылка «Открыть NAS».

    Drawer — Действия

CoA Disconnect — разрыв сессии оператором

Кнопка в строке с активной сессией (или из drawer-модалки → вкладка «Действия»). Клик открывает confirm-модалку с превью последствий:

Confirm-модалка перед CoA Disconnect

Подтверждение → POST /admin/Abonents/<ab_id>/sessions/coa_disconnect/ → backend запускает disconnect_user.delay(broadcast=True) в Celery (CoA на все NAS клиента) + audit-запись (table=RADIUS_SESSIONS) + toast «CoA Disconnect отправлен на все NAS». Через 8 секунд страница перезагружается чтобы обновить статус сессии на «Завершена».

Mobile-вид (ниже 992px)

Таблица из 12 колонок не помещается на телефоне — вместо неё отдельный layout d-block d-lg-none с карточками:

Mobile card-stack

Каждая сессия — карточка с зелёной полосой слева для Online, бейджем статуса/причины сверху, периодом + аптаймом, и 6 ключевыми полями (Логин / NAS / IP / MAC / Трафик / Порт+VLAN). Touch-targets ≥ 44px (a11y-touch-target-design).

Backend-endpoints
URLМетодНазначение
/admin/Abonents/<ab_id>/sessions/detail/<sid>/GETПолная карточка сессии (24+ поля) для drawer-модалки
/admin/Abonents/<ab_id>/sessions/kpi/?period=...GETАгрегаты за период (24h / 7d / 30d / all)
/admin/Abonents/<ab_id>/sessions/list/GETЛёгкий JSON для auto-refresh каждые 30 сек
/admin/Abonents/<ab_id>/sessions/coa_disconnect/POSTРазорвать активную сессию через CoA

Все endpoint-ы реализованы в billing/views/session_monitor_api.py.

Доступность (a11y) и UX-фиксы

СОРМ

Паспортные данные и реквизиты для выгрузки в РКН по СОРМ-3:

Реквизиты

Произвольные атрибуты клиента (UserAttributes + AttributeValues). Для юр.лиц при первом открытии вкладки авто-создаются 12 пустых атрибутов с default_person=True; для физлиц — с default_individual=True. Атрибуты можно использовать в шаблонах сообщений и печатных формах.

HelpDesk

Заявки HelpDesk, связанные с клиентом. Запросы идут через внутренний REST-клиент биллинга к HelpDesk. Из карточки можно:

Webhook-события из HelpDesk приходят обратно: conversation.agent_reply_created → push, conversation.assigned → Telegram админу, conversation.status_changed(closed) → AuditOperations.

Лояльность

Программы лояльности клиента и их история. Управляется через справочник Программы лояльности:

Сообщения

История уведомлений клиента (MsgStack) по всем каналам:

Аудит

Журнал действий пользователей и системных событий по конкретному клиенту. Подвыборка из AuditOperations с фильтром по abonent_id + связанным USERS-записям через object_id.

Вкладка Аудит карточки клиента — KPI-карточки сверху, под ними toolbar с фильтрами (даты, поиск, тип, CSV), таблица событий

Композиция

Сверху вниз:

  1. KPI-карточки — кликабельные плитки Всего, Финансовые операции, Услуги, Блокировки, Сообщения, Редактирование клиента, Другое со счётчиками. Клик по плитке = фильтр по этой категории. Карточка Всего в правом верхнем углу содержит i-иконку → ссылка на этот раздел документации.
  2. Toolbar (одна строка) — диапазон дат (компактные поля 96px без подписей «с/по», разделитель «—»), поиск по описанию, селект «Все типы», кнопка «Применить», кнопка экспорта CSV.
  3. Таблица с колонками Дата / ID, Событие, Описание, Исполнитель. Vertical-align top — длинные описания читаются проще. Колонка «Событие» = цветной лейбл + drill-иконка в одну строку (`flex nowrap`).
Период по умолчанию

При первом открытии вкладки сервер ставит диапазон последние 12 месяцев. Для более длинной истории — расширить вручную через datepicker. localStorage сохраняет выбранный текст-поиск и тип фильтра, но НЕ даты (даты могут устареть и ввести в заблуждение). Ключ хранилища: audit_filters_v2.

Дефолтный период (12 мес) настраивается в Настройки системы → Аудит — параметр AUDIT_TAB_DEFAULT_PERIOD_MONTHS (диапазон 1–60). Там же — лимит CSV-экспорта, записей на странице и опция «скрывать системные события».

CSV-экспорт

Зелёная кнопка с иконкой fa-file-csv справа от «Применить» в toolbar. Качается выборка с теми же фильтрами что в UI (даты, поиск, клиент). Endpoint: GET /admin/reports/audit_export/?abonent=<id>&date_from=YYYY-MM-DD&date_to=YYYY-MM-DD&search=.... Формат: UTF-8 BOM, разделитель ;, колонки Дата, Событие, Описание, Клиент ID, Клиент ФИО, Исполнитель. Лимит 50 000 строк.

Mobile-вид

Под KPI-плитками — компактная строка .ab-mobile-search-row: поле поиска + кнопка «Фильтры» (38px высота) в одну строку. KPI и десктопный toolbar скрыты (aa-hide-mobile) — фильтры открываются через offcanvas снизу с категориями, диапазоном дат, кнопками Применить / Сбросить. «Сбросить» закрывает offcanvas и редиректит на ?tab=audit с очисткой всех параметров.

Drill-down к объекту

Если у записи аудита есть table_name + object_id, рядом с лейблом события появляется иконка fa-external-link-alt. Клик открывает в новой вкладке прямую ссылку на изменённый объект — например, при изменении finance_operations переход на конкретную финоперацию, при изменении users_usluga — на услугу.

Категории событий
КатегорияЦветИсточник (table_name)
УслугисинийUSERS_USLUGA
ФиноперациизелёныйFINANCE_OPERATIONS, ARCH_ACCOUNT_STACK
Оплататёмно-зелёныйPAY_LOG, ADMIN_ACCOUNTS_LOG
БлокировкаоранжевыйABONENTS_BLOCK
СтатусыжёлтыйSTATUS, OBJECTS_STATUS
КлиентфиолетовыйABONENTS, ATTRIBUTE_VALUES, LOYALTY
Сообщениятёмно-синийMSG_STACK
СОРМсерыйSORM*
Прочеесветло-серыйостальные таблицы

Банк

Вкладка «Банк» (иконка fa-university 🏛) — платежи клиента из банковских выписок и выписанные ему счета с актами. Появляется в карточке при наличии хотя бы одного платежа по выпискам.

Вкладка «Банк» в карточке клиента — сумма зачисленного, таблица платежей из выписок, блок счетов и актов

URL вкладки: /admin/Abonents/<id>/bank/. Подробное описание модуля — в разделе Банковские выписки → Вкладка «Банк» в карточке.

Дополнительные действия