Схемы Что нового

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

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

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

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

URL: /admin/Abonents/<id>/. Открывается кликом по строке клиента в любом списке (папка, глобальный поиск, модалка счёта) и сразу переходит на вкладку по умолчанию из настроек профиля — обычно это сводка /admin/Abonents/<id>/overview/. У каждой вкладки свой адрес (/operations/, /calls/, /messages/…): ссылку на нужную вкладку можно переслать коллеге.

flowchart TB
    subgraph R1 [" "]
        direction LR
        L["Список клиентов
папка, поиск, счёт"] --> U["Карточка
/Abonents/ID/"] end subgraph R2 [" "] direction LR O["Сводка
/overview/"] --> T["Вкладки
/operations/ · /calls/ · /messages/"] end R1 -->|"вкладка по умолчанию
из настроек профиля"| R2 style R1 fill:none,stroke:none style R2 fill:none,stroke:none
Обучающее видео 2:51

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

1920×1080 с озвучкой Скачать
Карточка клиента: шапка, полоса вкладок и сводка
Карточка клиента: шапка, полоса вкладок, показатели и блоки сводки

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

Карточка показывает аватарку клиента (в шапке профиля, в сайдбаре-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 биллинг узнаёт клиента, когда тот пишет в соцсетях/мессенджерах — тикет сразу связывается с карточкой.

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

Над вкладками — строка с тем, что нужно видеть всегда:

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

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

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

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

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

Изменённое поле подсвечено
Изменённое поле подсвечено

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

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

Сводка клиента

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

Показатели

КарточкаЧто показываетДействие
БалансСумма и на сколько её хватит, под ней — сколько в месяц списывается по услугам. Фон зелёный при плюсе и красный при долге.«ОП» — обещанный платёж, если он доступен клиенту
ТарифЦена в месяц и дата следующего списания. Если у тарифа нет своей цены и плата берётся услугами, показана сумма услуг с подписью «в месяц по услугам» — а не «0 ₽».«Изменить тариф»
СвязьАктивна или заблокирована, сколько учётных записей включено. При блокировке карточка красная: причина и с какого числа.«Разблокировать»
ОбращенияОткрытых сейчас и всего.«Создать» — новое обращение
За 30 днейИтог движения денег: приход и расход.«Все операции»
Показатели сводки: баланс, тариф с платой по услугам, связь, обращения, итог за 30 дней
Тариф без своей цены: в плитке сумма услуг в месяц. Суммы — без копеек, разряды через пробел

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

Блоки

Блок показывается, только если в нём есть что показать: у клиента без файлов нет блока «Файлы», а «Заполненность для СОРМ» пропадает, когда заполнены все поля.

КолонкаБлокЧто внутри
СверхуПлашки состоянияблокировка, обещанный платёж, диагностика связи
СверхуТребует вниманиядолг, незаполненные контакты, открытые обращения
СлеваКомментариизаметки сотрудников с автором; удаление — с подтверждением
СлеваПлатежипоследние поступления, средний платёж, в какой день клиент обычно платит
СлеваЧто происходилолента событий с фильтром «Деньги / Сообщения / Звонки / Оператор»; подробнее
СлеваСессиипоследние подключения: IP, причина разрыва; «Инфо» открывает боковую панель, «Разорвать» обрывает сессию
СлеваАудиткто из сотрудников что менял — с именем, а не номером
СлеваСообщенияуведомления клиенту и дошли ли они
СправаКонтактыпочта, телефон, соцсети; правка на месте с проверкой дублей и домена почты
СправаЗаполненность СОРМполоса готовности данных к выгрузке
СправаАдрес и подключениеадрес — улица, дом и квартира отдельно (подробнее), учётные записи, IP; пароль открывается по «глазу», просмотр пишется в аудит
СправаОборудование и сетьтехника со склада, точка подключения, порт
СправаДоговорномер и дата договора, тип клиента
СправаУслугиактивные услуги и сумма в месяц; по клику — список с ценами
СправаЛояльность и скидкипрограмма лояльности клиента
СправаПоддержкапоследние обращения: открытые первыми, кто кого ждёт, ответственный, приоритет; клик открывает обращение в боковой панели
СправаЗвонкипоследние разговоры, записи, AI-резюме
СправаФайлывложения клиента
СправаЗадачи и нарядыоткрытые задачи CRM и выезды монтажников
СправаДокументыдоговор, акты и счета клиента
СправаОткуда пришёлсделка CRM, рекламная кампания, UTM-метки
СправаКлиент в других разделахбыстрые переходы по вкладкам
Сводка, левая колонка: комментарии, платежи, лента событий
Сводка, левая колонка: комментарии, платежи, лента событий
Лента «Что происходило»

Лента собирает в одну колонку платежи и списания, сообщения клиенту, звонки и действия сотрудников. Под записью журнала стоит раздел словами — «Услуги», «Звонки», «Личный кабинет», — а не имя таблицы. Звонок подписан «Входящий звонок» или «Исходящий звонок» с номером, время — начало разговора, статус — словами. Суммы — со знаком и без копеек: «+8 089 ₽».

Лента событий: сообщение, запись журнала с разделом «Услуги», платёж
Лента: под записью журнала — раздел, у платежа — сумма со знаком
Сводка, правая колонка: контакты, заполненность СОРМ, адрес и подключение
Сводка, правая колонка: контакты, заполненность СОРМ, адрес и подключение
Сводка, левая колонка ниже: сессии, аудит, сообщения
Сводка, левая колонка ниже: сессии, аудит, сообщения
Сводка, правая колонка ниже: услуги, лояльность, поддержка, задачи
Сводка, правая колонка ниже: услуги, лояльность, поддержка, задачи
flowchart LR
    D["Данные клиента
деньги, связь, обращения, звонки"] --> B["Сборка блоков сводки"] S["Настройки → Профиль
какие блоки и в каком порядке"] --> B B --> K["Показатели и плашки
«требует внимания»"] B --> C{"Ширина экрана"} C -->|"992 px и шире"| TWO["Две колонки"] C -->|"уже"| ONE["Одна колонка
длинные блоки свёрнуты"]

Адрес и подключение: улица, дом и квартира

Адрес клиента состоит из двух частей, и хранятся они в разных местах. Дом — запись справочника адресов, одна на здание: по ней работают карта, проверка покрытия, выгрузка СОРМ и монтаж. Квартира — поле самого клиента. Поэтому адрес правится тремя полями.

Блок «Адрес и подключение»: длинный адрес в две строки
Блок «Адрес и подключение»: длинный адрес в две строки
ПолеКак заполняется
УлицаПодсказки ФИАС от города до улицы — сюда же сёла и СНТ. Если выбрать город, он дописывается в поле, и подсказки продолжаются до улицы.
ДомПодсказки только домов выбранной улицы. Номер можно вписать руками — сервер проверит его в ФИАС; номер, которого там нет, не принимается.
КвОбычное поле. В справочник адресов не попадает.
Форма адреса: улица, дом и квартира
Форма адреса: улица, дом и квартира
Поле «Дом» подсказывает только дома выбранной улицы
Поле «Дом» подсказывает только дома выбранной улицы

По кнопке «Сохранить» сервер сам находит дом в ФИАС по улице и номеру, берёт запись справочника или заводит новую и привязывает к ней клиента. Если поменяли только квартиру, запись дома не трогается. У записанного адреса дома той же улицы подсказываются сразу, без повторного выбора улицы — по коду КЛАДР дома. Старый адрес без кода КЛАДР попросит выбрать улицу из подсказок.

Длинный адрес показывается в две строки; если не помещается и в две — обрывается многоточием, полный адрес виден во всплывающей подсказке. На телефоне подпись «Адрес» стоит над адресом, а карандаш — в строке подписи, чтобы адрес занимал всю ширину блока.

Блок адреса на телефоне
Блок адреса на телефоне

Подъезда в блоке нет: записанный подъезд при сохранении адреса из сводки не меняется.

Кто может менять адрес

Пустой адрес вписывает любой сотрудник с доступом к разделу «Клиенты». Заполненный меняют только Администратор, Директор, Старший сэйлс, Старший менеджер и суперпользователь — это право «Клиенты · Смена заполненного адреса» в блоке «Разделы админки», список ролей правится там же. У остальных вместо карандаша замок, а по нажатию окно объясняет, кто может изменить адрес.

Без права вместо карандаша замок
Без права вместо карандаша замок
Окно объясняет, кто может изменить адрес
Окно объясняет, кто может изменить адрес

Запрет проверяет и сервер: сохранение из блока отклоняется, а если адрес попытаться поменять через форму карточки, остаётся прежний. Смена записанного адреса всегда подтверждается окном «Было / Станет» — по адресу едет монтажник и уходит выгрузка СОРМ.

Подтверждение смены адреса: было и станет
Подтверждение смены адреса: было и станет
flowchart TB
    E["Карандаш у адреса"] --> F{"Адрес уже записан?"}
    F -- нет --> S["Улица · дом · кв"]
    F -- да --> R{"Есть право на смену?"}
    R -- нет --> L["Окно: кто может изменить"]
    R -- да --> S
    S --> C{"Меняется записанный адрес?"}
    C -- да --> K["Подтверждение
«Было / Станет»"] C -- нет --> V K --> V["Сервер: дом из ФИАС →
запись справочника → аудит"]
Что пишется в аудит
ЗаписьКогда
«Адрес указан из сводки: …»адрес вписан впервые
«Адрес изменён из сводки: было → стало»смена записанного адреса или квартиры
«В справочник добавлен дом из ФИАС: …»дома ещё не было в справочнике
«Смена адреса отклонена — нет права: было → попытка»попытка без права из блока
«Смена адреса в форме карточки отклонена — нет права: …; адрес оставлен прежним»попытка без права через форму карточки

На телефоне и в тёмной теме

На телефоне колонки складываются в одну. «Что происходило» уходит в самый низ, «Сессии» и «Аудит» — ниже «Поддержки»: сначала то, с чем работают, потом история. Длинные блоки сворачиваются по нажатию на заголовок, и какие блоки свёрнуты, браузер запоминает для сотрудника. В тёмной теме показатели сохраняют цвет состояния — долг и блокировка остаются красными.

Настройки профиля клиента (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 колонка.

Вкладка «Профиль»: секции настроек карточки
Вкладка «Профиль»: секции настроек карточки

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

Вкладки настроек системы
Вкладки настроек системы
Вкладка «Профиль» с раскрытыми секциями
Вкладка «Профиль» с раскрытыми секциями
Секция 0 — Блоки сводки клиента

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

Настройки → Профиль: блоки сводки клиента — порядок и видимость
Настройки → Профиль: блоки сводки клиента — порядок и видимость
flowchart TB
    subgraph R1 [" "]
        direction LR
        P["Настройки → Профиль
блоки сводки"] -->|"Сохранить"| DB["ABONENT_PROFILE_OVERVIEW_BLOCKS"] end subgraph R2 [" "] direction LR CACHE["Кеш профиля
60 секунд, сброс при сохранении"] --> CARD["Сводка клиента
видимость и порядок блоков"] end R1 --> R2 style R1 fill:none,stroke:none style R2 fill:none,stroke:none
Секция 1 — Видимость секций карточки

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

Секция «Видимость секций карточки»
Секция «Видимость секций карточки»
Секция 2 — Колонки списка клиентов

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

Секция «Колонки списка клиентов»
Секция «Колонки списка клиентов»
Секция 3 — AJAX и автообновление
Секция «AJAX и автообновление»
Секция «AJAX и автообновление»
Секция 4 — Дефолты карточки
Секция «Дефолты карточки»
Секция «Дефолты карточки»
Секция 4А — Управление вкладками карточки

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

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

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

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

Секция «Вкладки карточки клиента»
Секция «Вкладки карточки клиента»
Секция 5 — Стиль и плотность
Секция «Стиль и плотность»
Секция «Стиль и плотность»
Секция 7 — Баннеры уведомлений оператору

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

Секция «Баннеры уведомлений оператору»
Секция «Баннеры уведомлений оператору»
Секция 8 — Прочее
Секция «Прочее»
Секция «Прочее»
Технические детали

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

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

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

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

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

Жёлтая плашка диагностики связи: возможны проблемы
Жёлтая плашка диагностики связи: возможны проблемы

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», блок за неуплату → «внесите оплату», и т.д.).

Развёрнутая красная плашка: карточки проверок, совет и готовый ответ
Развёрнутая красная плашка: карточки проверок, совет и готовый ответ

Sparkline 24ч и auto-actions

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

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

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

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

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

REST API и CLI

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

Пример CLI:

$ docker compose exec web python manage.py diag 100042

═══ Диагноз: 🟡 YELLOW — #100042 Иванов Иван Иванович ═══

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

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

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

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

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

Проверка по всем шаблонам карточки нашла и заменила:

Что былоСколькоЧем заменено
bootstrap-бейджи пяти подтонов62smit-badge с сохранением тона
синие кнопки12фирменные зелёные
скрытие колонок на телефоне32снято: строка рисуется карточкой с подписями
нативные alert()23уведомления SmitToast
группы кнопок-фильтров3SmitChipBar
ряды кнопок в строке таблицыдо 6 иконокодно меню SmitActions
Вкладка «Аккаунты» на общих компонентах
Вкладка «Аккаунты» на общих компонентах
flowchart LR
    CARD["Карточка клиента"] --> T["Таблицы вкладок"]
    CARD --> F["Фильтры и счётчики"]
    CARD --> A["Действия"]

    T --> T1["smit-tbl
подписи ячеек"] T1 --> T2["Десктоп: таблица"] T1 --> T3["Телефон: карточка
с подписью у поля"] T --> T4["SmitTable
набор колонок в профиле"] F --> F1["SmitChipBar
полоса фильтров"] F --> F2["smit-kpi-pill
счётчики"] F --> F3["SmitStat
сводка операций"] A --> A1["SmitActions
меню строки"] A --> A2["SmitBulkBar
выбранные строки"] A --> A3["SmitConfirm
подтверждение"]

Телефон: карточка вместо урезанной таблицы

Прятать данные на узком экране нельзя — оператор в поле открывает карточку именно ради того, что пряталось. У «Аккаунтов» скрывалось двенадцать колонок из пятнадцати: VLAN, трафик, дата последнего входа, NAS и MAC девайса. У «Операций» — баланс, услуга и исполнитель. У «Поддержки» — агент и время решения.

Теперь таблицы карточки размечены классом smit-tbl и подписью data-label у каждой ячейки: ниже 768 px строка превращается в карточку, где подписано каждое поле, и ничего не исчезает.

flowchart TB
    R["Строка таблицы
15 ячеек с data-label"] --> W{"Ширина экрана"} W -->|"768 px и шире"| D["Таблица
колонки по настройке сотрудника"] W -->|"уже 768 px"| M["Карточка
каждое поле с подписью"] D --> DK["Действия: меню в строке"] M --> MK["Действия: то же меню
палец попадает"]
Та же вкладка на телефоне — строки карточками с подписями
Та же вкладка на телефоне — строки карточками с подписями

Набор колонок — в профиле сотрудника

Кнопка выбора колонок везде одинаковая: квадратная, с иконкой столбцов и подписью в подсказке, на телефоне скрыта — там настраивать нечего. Набор, порядок и ширины хранятся в профиле сотрудника (SmitTable), а не в браузере, поэтому переезжают на другую машину.

Колонки, которые нужны время от времени, по умолчанию скрыты и включаются кнопкой. У «Аккаунтов» это коммутатор с портом (обход по подъезду), комментарий монтажника и дата создания учётной записи — раньше за ними приходилось открывать отдельную карточку.

Панель выбора колонок
Панель выбора колонок

Действия строки — одним меню

В строке учётной записи стояло шесть кнопок пятью цветами: правка синяя, отключение жёлтое, тест голубой, удаление красное. На широком экране это пестрило, на телефоне не помещалось.

Компонент SmitActions получил метод collapse: он сворачивает готовые кнопки строки в одно меню, читая подпись, иконку и «опасность» из самой кнопки, а по выбору нажимает исходную. Обработчики переписывать не нужно — меняется только вид. Короткую подпись пункта можно задать атрибутом data-act-label, иначе берётся title без скобочного пояснения.

SmitActions.collapseRows('#usrTable', {label: 'Действия с учётной записью'});
flowchart TB
    subgraph R1 [" "]
        direction LR
        B["Готовые кнопки строки
правка · отключить · тест · удалить"] --> C["SmitActions.collapse"] --> READ["Читает из кнопки:
подпись, иконку, опасность"] end subgraph R2 [" "] direction LR MENU["Меню строки"] -->|"выбор пункта"| CLICK["Нажимает исходную кнопку"] --> H["Прежний обработчик
переписывать не нужно"] end R1 --> R2 style R1 fill:none,stroke:none style R2 fill:none,stroke:none
Действия строки — одним меню
Действия строки — одним меню

Вкладок пятнадцать, полей на них — под две сотни. «Где комментарий монтажника», «на какой вкладке паспорт» оператор помнит не всегда. В строке вкладок стоит поиск (SmitSectionSearch): подсказывает поле и открывает нужную вкладку, вызывается Ctrl+K из любого места страницы.

Вкладка «Операции»: сводка и полоса фильтров
Вкладка «Операции»: сводка и полоса фильтров
Вкладка «Услуги»: счётчики и фильтры
Вкладка «Услуги»: счётчики и фильтры

Обзор карточки: что где лежит

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

Вкладок до семнадцати, и по названию не всегда понятно, что внутри. Часть появляется, только когда включён модуль («Видео», «Оборудование», «Банк», «Маркетинг») или есть что показать («Звонки», «Реквизиты» у юр.лиц). Ниже — карта карточки и снимки групп вкладок, сняты с демонстрационного клиента, у которого наполнены и деньги, и связь, и переписка.

flowchart TB
    H["Шапка: договор, ФИО, тариф, баланс, действия"] --> N["Полоса вкладок + поиск по карточке (Ctrl+K)"]
    N --> G1["Кто клиент"]
    N --> G2["Деньги"]
    N --> G3["Связь"]
    N --> G4["История и общение"]
    G1 --> I["Информация
сводка"] G1 --> S["СОРМ"] G1 --> R["Реквизиты
у юр.лиц"] G2 --> O["Операции"] G2 --> U["Услуги · Бонусы"] G2 --> L["Лояльность"] G2 --> BK["Банк · Маркетинг"] G3 --> A["Аккаунты"] G3 --> SE["Сессии"] G3 --> E["Оборудование · Видео"] G4 --> AU["Аудит"] G4 --> M["Сообщения"] G4 --> C["Звонки"] G4 --> SU["Поддержка · Файлы"]

У каждой вкладки слева вверху стоит её название и значок i с короткой подсказкой: что здесь делают и на что смотреть. Если у вкладки есть панель инструментов, название встаёт с ней в одну строку. Текст подсказок живёт во вьюхе (_TAB_HINTS в abonents.py).

Кто клиент

Сводка клиента
Сводка: показатели, «требует внимания», блоки в две колонки

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

Деньги: от платежа до снятия блокировки

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

Вкладка «Операции»
Операции: сводка с графиком за 30 дней, фильтры, выбор строк
flowchart LR
    P1["Платёж
касса, банк, ЛК"] --> FO["Финансовая операция"] P2["Абонплата
ежемесячно"] --> FO P3["Обещанный платёж"] --> FO FO --> BAL["Баланс клиента"] BAL -->|"стал положительным"| UNB["Снятие финблокировки"] BAL -->|"ушёл в минус"| BL["Автоблокировка"] UNB --> MSG["Уведомление клиенту"] BL --> MSG FO --> AUD["Запись в аудит"]

Сторно не удаляет операцию, а добавляет обратную и связывает её с исходной: история остаётся целой, а строка получает свой бейдж.

Вкладка «Услуги»
Услуги клиента: счётчики, полоса чипов, цена и дата следующего списания

«Услуги» и «Бонусы» — одна таблица с разным отбором. В «Бонусы» попадает то, что уменьшает счёт: нулевая или отрицательная цена, скидки и компенсации. Пустая индивидуальная сумма означает, что берётся цена из шаблона услуги — источник подписан прямо в окне правки.

Связь

Вкладка «Аккаунты»
Учётные записи: логин, IP, статус, меню действий в строке

Учётные записи — то, по чему работает авторизация в сети: логин, пароль, IP, точка подключения. Колонки прячутся не на телефоне, а по выбору сотрудника: на узком экране строка превращается в карточку с подписями у полей.

История: аудит, сообщения, обращения

Вкладка «Аудит»
Аудит: карточки категорий сверху фильтруют журнал

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

Вкладка «Сообщения»
Сообщения: форма со строкой стоп-листа слева, история с пропущенными SMS справа

В «Сообщениях» видно не только что отправили, но и чем закончилось: значок канала несёт статус доставки, а входящие ответы клиента отмечены полосой слева. Каналы переключаются полосой чипов, период — единым компонентом. Если клиент в стоп-листе или отписался от рассылок в мессенджере, над формой видна строка запрета, а неотправленное SMS помечено «SMS пропущено» с причиной.

Помощник знает, где вы стоите

AI-помощник в карточке получает три вещи: выжимку по клиенту, открытую вкладку и отмеченные галочками строки. Вопрос «а что тут?» на вкладке операций и на вкладке услуг — разные вопросы, и отмеченные строки почти всегда и есть предмет вопроса. Полная выжимка по клиенту остаётся всегда: разбирать операцию, не зная баланса и блокировок, бессмысленно.

flowchart LR
    UI["Карточка клиента"] -->|"вкладка + отмеченные строки"| ASK["Запрос помощника"]
    DB["Данные клиента
баланс, блокировки, услуги, обращения"] --> ASK ASK --> ANS["Ответ по делу"] ANS --> DRAFT["Черновик письма клиенту
отправляет сотрудник"]

Вкладки, которые не поместились, — в «Ещё»

У каждой вкладки остаётся подпись: значки без подписей сотрудники не узнавали. Вкладки, которые не поместились в ширину, уходят в меню «Ещё N» справа — со значками и счётчиками. Четыре первые вкладки видны всегда.

Открытая вкладка всегда на виду. Если её открыли из «Ещё», она встаёт последней из видимых, а остальные остаются на своих местах: порядок не меняется от перехода к переходу, и нужная вкладка каждый раз на том же месте. Только если подпись слишком длинная и последней не помещается, вкладка встаёт второй.

Полоса пересчитывается при загрузке страницы, после загрузки шрифтов и при изменении ширины окна. На телефоне (уже 768 px) «Ещё» нет: полоса прокручивается, открытая вкладка встаёт в начало.

Полоса вкладок: открыта «Лояльность», меню «Ещё 10» раскрыто
Открыта «Лояльность» — она последняя из видимых, остальные вкладки в «Ещё 10»
flowchart LR
    A{"Все вкладки
помещаются?"} -->|"да"| F["Полоса без «Ещё»"] A -->|"нет"| B["Не поместившиеся —
в «Ещё N»"] B --> C{"Открытая вкладка
в «Ещё»?"} C -->|"нет"| G["Порядок не меняется"] C -->|"да"| D["Встаёт последней
из видимых"]

Знакомство с карточкой

При первом открытии карточки сотрудник видит короткое знакомство: первым экраном — обучающий ролик, дальше пять шагов с обложками — вкладки, деньги, связь, история и AI-помощник. Обложки подстраиваются под светлую и тёмную тему. Знакомство показывается один раз, открыть его заново — кнопкой с шапочкой в шапке карточки.

Знакомство с карточкой клиента: шаг с обложкой
Знакомство с карточкой клиента: шаг с обложкой

Пустая карточка ничему не учит. Если разворачиваете систему с нуля и хотите посмотреть карточку на данных, есть команда manage.py seed_demo_abonent: она заводит клиента с договором DEMO-0001 и наполняет вкладки — платежи и начисления за полгода, услуги, бонус, истёкший обещанный платёж, сторно, переписка по каналам. --wipe удаляет его вместе со всеми записями. Справочники команда не трогает: тариф, услуги, адрес и программа лояльности берутся существующие — свои попали бы в прайс и отчёты как настоящие.

Вкладки

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

Информация

Вкладка открывается сводкой клиента. Поля формы — тариф, договор, контакты, адрес, признак юр.лица — встроены в блоки сводки и сохраняются кнопкой «Сохранить» в шапке; комментарии добавляются в блоке «Комментарии». Ниже — правила этих полей.

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

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

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

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

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

Проверка домена почты в сводке

Правильный по форме адрес ещё не значит, что письмо дойдёт: ivan@gmail.ru или ivan@iclaud.com проходят проверку формата, но письма туда возвращаются или уходят постороннему. Поэтому при сохранении почты в блоке «Контакты» сводки биллинг проверяет домен:

flowchart TD
    A["Оператор вводит почту
и нажимает «Сохранить»"] --> F{"Формат адреса
верный?"} F -- "нет" --> E["«Это не похоже на адрес почты»"] F -- "да" --> T{"Домен похож на
опечатку или без почты?"} T -- "нет" --> D{"Адрес уже есть
у другого клиента?"} T -- "да" --> W["Окно «Проверьте адрес почты»"] W -- "Исправить на …" --> D W -- "Сохранить как есть" --> D W -- "ввести заново" --> F D -- "нет" --> S["Сохранено"] D -- "да" --> Q["Подтвердить дубль"] --> S

Это предупреждение, а не запрет: если клиент действительно пользуется таким адресом, выберите «Сохранить как есть». Если DNS не ответил, проверка по DNS пропускается — сохранить хороший адрес она не мешает. Ответы DNS запоминаются на сутки. Найти такие адреса, записанные раньше, — команда clean_bad_emails --domains («Письмо ушло, но не дошло»).

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

Внутри блока 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 < threshold — billing_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 (зелёный для прихода / красный для расхода) с белым крестиком закрытия.

Поля:

Окно «Приход»
Окно «Приход»
Окно «Расход»
Окно «Расход»
Привязка услуги к расходу

Только в режиме «Расход» под основными полями есть ссылка «+ Добавить услугу». Клик — раскрывается селект из 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)

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

Вкладка «Операции» на телефоне
Вкладка «Операции» на телефоне
Окно «Расход» на телефоне
Окно «Расход» на телефоне
Тёмная тема

Полная адаптация для тёмной темы: 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-сессий клиента.

Вкладка «Сессии»: тариф, показатели, таблица сессий
Вкладка «Сессии»: тариф, показатели, таблица сессий
Карточка контекста тарифа

Сверху страницы — компактный градиентный блок с информацией о тарифе клиента: имя, цена, шейпер из 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 → точечное обновление цифр.

Показатели за сутки
Показатели за сутки
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 / Причина.
    Панель сессии — «Обзор»
    Панель сессии — «Обзор»
  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.
    Панель сессии — «RADIUS»
    Панель сессии — «RADIUS»
  3. Трафик — крупные цифры ↓ загрузка / ↑ выгрузка (в МБ/ГБ + точные байты), средний rate в Kbps, длительность.
    Панель сессии — «Трафик»
    Панель сессии — «Трафик»
  4. Действия — CoA Disconnect (disabled для закрытых) + 3 копировать-кнопки (MAC / IP / Session ID) + ссылка «Открыть NAS».
    Панель сессии — «Действия»
    Панель сессии — «Действия»
CoA Disconnect — разрыв сессии оператором

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

Подтверждение перед разрывом сессии
Подтверждение перед разрывом сессии

Подтверждение → 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 с карточками:

Вкладка «Сессии» на телефоне
Вкладка «Сессии» на телефоне

Каждая сессия — карточка с зелёной полосой слева для 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. Атрибуты можно использовать в шаблонах сообщений и печатных формах.

Поддержка

Обращения клиента в Поддержку: номер, тема с числом сообщений, статус, ответственный агент, время решения и дата обновления. У открытых обращений время решения показывает, сколько они уже открыты, у закрытых — сколько заняло решение; закрытые строки приглушены. Стрелка в строке открывает обращение в Поддержке, «Создать тикет» — окно нового обращения; кнопка «Создать» на карточке «Обращения» в сводке ведёт сюда и сразу открывает это окно.

Вкладка «Поддержка»: обращения клиента
Вкладка «Поддержка»: обращения клиента

Лояльность

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

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

Вкладка «Лояльность»: счётчики-пилюли и полоса чипов
Вкладка «Лояльность»: счётчики-пилюли и полоса чипов

Сообщения

Слева — форма «Отправить сообщение»: треть ширины, при прокрутке остаётся на экране. Справа — история на две трети.

Форма «Отправить сообщение»: тихий час и стоп-лист клиента
Форма «Отправить сообщение»: тихий час и стоп-лист клиента
История: ответ клиента и пропущенные SMS
История: ответ клиента и пропущенные SMS
Рассылки бота по чатам клиента
Рассылки бота по чатам клиента
flowchart TB
    F["Форма
шаблон, текст, каналы"] --> Q["Очередь сообщений"] Q --> P{"Правила отправки"} P -->|"нельзя"| SK["Пропущено: стоп-лист, канал выключен,
тихий час для SMS и push, повтор текста"] P -->|"можно"| J["Журнал отправок:
уже ушло — не повторяем"] J --> SEND["Отправка по каналам"] SEND --> H["История
статус в значке канала"] SK --> H

Звонки

Разговоры с клиентом из телефонии: входящие и исходящие, записи, расшифровки и AI-разбор. Вкладка появляется, когда включён модуль автообзвона и у клиента есть телефон или хотя бы один звонок: без телефона история видна, но позвонить нельзя.

Вкладка «Звонки»: строка действий, метрики за период, фильтр
Вкладка «Звонки»: строка действий, метрики за период, фильтр
Фильтр звонков: пункты со счётчиками за период
Фильтр звонков: пункты со счётчиками за период
Раскрытый звонок: плеер записи, разбор и текст разговора абзацами
Раскрытый звонок: плеер записи, разбор и текст разговора абзацами
Отмеченные звонки: скопировать номера, CSV, AI-разбор
Отмеченные звонки: скопировать номера, CSV, AI-разбор
flowchart LR
    T["Телефония"] --> V["Звонок
начало, номер, запись"] V --> A["Привязка к клиенту"] V --> R["Расшифровка
и AI-разбор"] A --> TAB["Вкладка «Звонки»"] R --> TAB TAB --> M["Метрики и фильтр
за выбранный период"]

Аудит

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

Журнал аудита: разделы «Услуги» и «Звонки», записи робот-звонков словами
Журнал: раздел словами в колонке «Событие», робот-звонки без кода сценария (номер телефона на снимке скрыт)
Композиция

Сверху вниз:

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

При первом открытии вкладки сервер ставит диапазон последние 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 — на услугу.

Текст записей

Журнал пишут разные модули, и в запись попадала внутренняя кухня: имя таблицы, код сценария робота, название платформы телефонии. Перед показом текст переводится на язык оператора — одинаково в сводке, на вкладке «Аудит» и в боковой карточке клиента. Сами записи в базе не меняются.

Название сценария берётся из справочника сценариев автообзвона; неизвестный код показывается как есть. Новые записи о робот-звонках пишутся сразу в таком виде.

Разделы событий

Раздел определяется по таблице, которую изменило действие. Цвет общий для группы разделов; в тёмной теме — светлые оттенки тех же цветов.

РазделЦветТаблицы
УслугизелёныйUSERS_USLUGA
Операциитёмно-зелёныйFINANCE_OPERATIONS
Лицевые счетатёмно-зелёныйADMIN_ACCOUNTS
ОплатабирюзовыйPAY_LOG
БанкбирюзовыйBANK_*
БлокировкиоранжевыйABONENTS_BLOCK
СтатусыянтарныйSTATUS, OBJECTS_STATUS
Карточка клиентаграфитовыйABONENTS, ATTRIBUTE_VALUES
ЛояльностьграфитовыйABONENTS_LOYALTY
АдресграфитовыйHOMES
Сообщениясеро-синийMSG_STACK
Звонкисеро-синийVOICE_CALL
Обращениясеро-синийSUPPORT_CONVERSATION
СОРМсерыйSORM*
Личный кабинетсветло-серыйLK_*
Учётные записисветло-серыйUSERS
Сессиисветло-серыйRADIUS_SESSIONS
Видеонаблюдениесветло-серыйVIDEO_*
Оборудованиесветло-серыйSTOCK_MOVEMENT
Телевидениесветло-серыйIPTV_ACCOUNT_MAPPING
Документысветло-серыйDOCUMENT
Прочеесветло-серыйостальные таблицы

Банк

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

Вкладка «Банк»: платежи из банковских выписок, счета и акты
Вкладка «Банк»: платежи из банковских выписок, счета и акты

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

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