Граф Wiki

Отчёты

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

Модуль «Отчёты и аналитика» — на сайте продукта
Содержание раздела

4. Отчёты

Раздел меню «Отчёты» объединяет аналитику для руководителя, библиотеку SQL-отчётов, аудит действий персонала и журналы.

4.0. Панель директора

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

Панель директора (/admin/reports/dashboard/, сайдбар «Отчёты → Панель директора») отвечает на четыре вопроса о бизнесе за выбранный период: сколько денег пришло, сколько в среднем приносит клиент, сколько людей подключилось и сколько заблокировано. Ниже — шесть графиков по тем же данным в динамике.

Панель директора — общий вид

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

- Панель директора
  - Показатели
    - Выручка за период
    - ARPU
    - Новые клиенты
    - Заблокировано за период
  - Графики
    - Популярность тарифов
    - Новые клиенты
    - Поступающие средства
    - Платящие клиенты
    - ARPU по месяцам
    - ARPPU по месяцам
  - Срезы
    - Период и пресеты
    - Сегмент физ-юр
    - Канал оплаты
    - Тариф
    - Точка подключения
    - Шаг день-неделя-месяц
    - Сравнение с прошлым или годом назад
  - Правила
    - Данные своей организации
    - Клик по показателю ведёт в список
    - Ошибка отличается от пустоты

Четыре показателя и как они считаются

Формулы важнее оформления: по этим цифрам принимают решения, поэтому каждая должна читаться однозначно.

ПоказательЧто именно считаетсяСравнение
Выручка за период Сумма приходных финансовых операций (OP_SUMMA > 0) с датой внутри периода. Верхняя граница берётся включительно, до 23:59:59 последнего дня. к предыдущему периоду такой же длины
ARPU Выручка периода ÷ база клиентов на конец периода (не удалённые, заведённые до этой даты). У прошлого периода — свой знаменатель, поэтому дельта ARPU не повторяет дельту выручки. к тому же показателю прошлого периода
Новых клиентов Клиенты с датой создания внутри периода. к предыдущему периоду
Заблокировано за период Клиенты, у которых блокировка началась внутри периода (ABONENTS_BLOCK.B_DATE). В подписи — сколько из них за неоплату и сколько клиентов активно сейчас. Рост показан красным: блокировок больше — это плохая новость. к предыдущему периоду
Почему «Заблокировано», а не «Отток». До июля 2026 карточка называлась «Отток» и показывала 2 099 на любом периоде: расчёт обращался к несуществующим полям журнала аудита, ошибка гасилась молча, и вместо оттока выводилось общее число отключённых за всю историю. Теперь метрика привязана к дате блокировки и меняется вместе с периодом.

Шесть графиков

Панель директора в тёмной теме
ГрафикЧто показывает
Популярность тарифовРаспределение клиентов по тарифам (без заблокированных). Хвост сворачивается в «Прочие».
Новые клиентыСколько подключилось в каждом периоде.
Поступающие средстваПриход по периодам — та же выручка в динамике.
Платящие клиентыСколько разных клиентов платили в периоде.
ARPUВыручка периода ÷ база клиентов на его конец.
ARPPUВыручка периода ÷ число заплативших — средний чек платящего.

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

Период и фильтры

Панель фильтров

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

ФильтрЗначенияЗачем
СегментВсе · Физлица · ЮрлицаУ юрлиц другой чек и другая сезонность — смешивать их с частниками бессмысленно
Канал оплатытипы приходных операцийВидно, через что реально идут деньги
Тарифмультивыбор, рядом число клиентовКак живёт конкретный тариф или линейка
Точка подключениямультивыбор по населённым пунктамСрез по географии: посёлок, СНТ, город
Шаг графиковМесяц · Неделя · ДеньДневной шаг показывает всплески внутри месяца
Сравнивать спредыдущим периодом · тем же периодом год назадГодовое сравнение снимает сезонность

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

Срез по юрлицам
Про точку подключения. Срез строится по записям CONNECTION_POINTS, названным по населённому пункту. Клиенты без точки в такой срез не попадают, поэтому под фильтром показано, скольких это касается. Массово назначить точки по адресу помогает команда manage.py assign_connection_points (с предпросмотром и откатом).

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

flowchart LR
    FO["Финансовые операции
finance_operations"] --> REV["Выручка
Поступающие средства"] FO --> PAY["Платящие клиенты
ARPPU"] AB["Клиенты
abonents"] --> BASE["База клиентов"] BASE --> ARPU["ARPU"] REV --> ARPU AB --> NEW["Новые клиенты"] AB --> TAR["Популярность тарифов"] BL["Блокировки
ABONENTS_BLOCK"] --> BLK["Заблокировано
за период"] ORG["Организация"] -.->|"фильтр"| FO ORG -.->|"фильтр"| AB ORG -.->|"фильтр"| BL

Теперь все шесть считаются из финансовых операций и карточек клиентов, то есть из тех же данных, что и KPI.

Мульти-организации

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

Фильтрация применяется ко всем шести графикам и ко всем четырём показателям, а также к спискам тарифов и точек в самих фильтрах: под чужую организацию не попадут ни её тарифы, ни её посёлки.

Переход к деталям

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

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

Графики перерисовываются вместе со сменой темы, перезагрузка не нужна. На телефоне показатели встают в две колонки, фильтры — в одну, горизонтальной прокрутки нет.

Панель директора на телефоне Фильтры на телефоне

Пользовательские SQL-отчёты — назначение и роль

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

AdminCustomReports — таблица сохранённых SQL-запросов, доступных оператору через UI. Каждая запись = один отчёт с именем, описанием и SQL-кодом. Хранится в таблице ADMIN_CUSTOM_REPORTS, редактируется на /admin/reports/AdminCustomReports/.

Зачем нужны кастомные отчёты

Биллинг — это система с ~150 таблицами и 600+ полей. Готовых отчётов в админке всегда не хватает: бухгалтер хочет один формат, директор — другой, техподдержка — третий. Решение:

  1. Гибкость без релизов — оператор-аналитик пишет SQL прямо через UI, без ожидания deployment'а;
  2. Параметризация — встроенный синтаксис :Имя|тип$ позволяет добавлять параметры (даты, клиент, сумма) без знания Python;
  3. Экспорт — результат отчёта сразу в Excel/CSV/DBF без программирования;
  4. Историческая часть — каждый запуск сохраняется в AdminCustomReportsHistory, можно вернуться к старому результату.
Поля и связанные таблицы
Поле ADMIN_CUSTOM_REPORTSНазначение
NAMEНазвание отчёта (видно в UI)
DESCRIPTIONОписание для оператора (что отчёт показывает)
REPORT_TEMPLATESQL-код запроса (text)
REPORT_TYPEFK на AdminCustomReportsType — категория
ALLOW_IN_CABINETВидим ли клиенту в ЛК (раздел «Мои отчёты»)
TECH_ADMIN / FIN_ADMIN / FULL_ADMIN / CARD_ADMIN / READ_ADMINМатрица прав по ролям оператора (1 = доступно)

Связанные таблицы:

Параметры в SQL — синтаксис

Внутри SQL можно использовать спец-плейсхолдеры в формате :Имя|тип$ или :Имя|тип[аргументы]$. Когда оператор запускает отчёт — UI генерирует форму с этими параметрами, подставляет значения и выполняет SQL.

ТипПример SQLUI
dateWHERE op_date >= :Начало|date$Date-picker
sumWHERE op_summa > :Минимум|sum$Поле для рублей с конвертацией ×10^10
select[Model]WHERE abonent_id = :Клиент|select[Abonents]$Select2-AJAX по модели
choices[1^Янв|2^Фев|3^Мар]WHERE month = :Месяц|choices[1^Январь|2^Февраль]$Dropdown с фиксированными значениями

Парсинг — regex в AdminCustomReports._get_sql_fields(), подстановка в _execute_sql() с защитой от SQL-injection.

Типы отчётов

Справочник ADMIN_CUSTOM_REPORTS_TYPE — 7 категорий с цветными бейджами:

Один отчёт может быть без типа (NULL) или привязан к одной категории. На странице есть фильтр-вкладки по типам.

CodeMirror + AI Builder

CodeMirror 5.65.16 — встроенный SQL-редактор в модалке:

AI Builder: кнопка «✨ AI» в toolbar. Открывает чат с AI-ассистентом:

  1. Оператор описывает что нужно: «Топ-10 должников за месяц с балансом и телефоном»;
  2. Ассистент задаёт уточняющие вопросы (период, фильтры);
  3. Возвращает готовый SQL с правильными именами таблиц/полей и параметрами;
  4. Оператор проверяет, при необходимости правит, сохраняет.

Контекст БД для ассистента — schema из information_schema.columns для 15 ключевых таблиц + критические правила биллинга (DB_MONEY_KOEF, MPTT-фильтры, регистр имён).

Отчёты в ЛК клиента

Если у отчёта ALLOW_IN_CABINET=true — он показывается клиенту в ЛК (раздел «Мои отчёты»). При запуске:

Защита удаления отчёта: report_executions и AdminCustomReportsHistory ссылаются на отчёт с FK NO ACTION. Если есть история запусков — удаление падает с ошибкой. Сначала очистите историю или используйте архивирование (поле archive=true).

4.1. Библиотека отчётов

URL: /admin/reports/AdminCustomReports/

Каталог пользовательских SQL-отчётов. Отчёт — это сохранённый SQL-запрос с настраиваемыми параметрами, сгруппированный по типам (Техсервис, Бухгалтерия, Руководство, Поддержка и т.д.), который оператор запускает кликом и получает таблицу с возможностью экспорта в Excel, CSV или DBF.

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

Как найти готовый отчёт, запустить его с параметрами, выгрузить результат и завести свой — включая AI-помощника по SQL.

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

Библиотека отчётов — список с типами и поиском

Список отчётов

Запуск отчёта

URL: /admin/reports/AdminCustomReports/<id>/executesql/

Кнопка ▶ Запустить в строке списка ведёт на форму с параметрами отчёта. Параметры извлекаются из SQL-кода по синтаксису :Имя|тип$ и рендерятся в форму автоматически.

Форма параметров отчёта (executesql)

На скриншоте — форма отчёта «Действия сотрудника» (#136): три параметра — Дата с, Дата по, Сотрудник. По умолчанию период — последний месяц (07.04 → 07.05), сотрудник = «Все сотрудники».

Внизу формы — четыре кнопки действий:

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

Выбор конкретного сотрудника из выпадающего списка choices:

Форма с заполненными параметрами

На скриншоте — выбран сотрудник uspeshnyy (Aleksandr Uspeshnyy), период 01.05.2026 → 07.05.2026. Список доступных значений для параметра :Sotrudnik|choices[...]$ формируется на лету: SQL-генератор выбирает уникальных owner из AuditOperations и подставляет их в опции.

Пример: результат выполнения

После клика «Выполнить» срабатывает редирект на /admin/reports/AdminCustomReports/<id>/history/<run_id>/ — каждое выполнение получает свой run_id (хранится 30 дней для повторного открытия результата без перезапуска SQL):

Результат выполнения отчёта — HTML-таблица

На скриншоте — результат отчёта «Действия сотрудника» за месяц: 604 строки, колонки #, Дата/время, Сотрудник, ФИО, Событие, IP-адрес, Подробности. Каждая строка — один аудит-event с детализацией («FIN_TYPES: Тип фин. операции #28 «Перевод средств…»», «ABONENTS: создан клиент…», «USERS_USLUGA: услуга подключена…»).

Возможности страницы результата:

Модалка редактирования отчёта

Открывается кликом по имени отчёта или по карандашу. Делится на две колонки:

Live-counter под редактором обновляется на каждом change-event: «N строк · M парам.», справа от него — список найденных параметров с типами.

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

Параметры записываются в SQL как :Имя|тип$ и автоматически становятся полями формы запуска:

ТипСинтаксисUI-элементПередаётся в SQL как
date :Дата с|date$ Календарь (jQuery datepicker) String '2026-05-07' (ISO)
sum :Сумма|sum$ Числовое поле (₽) Float (умножается на 10^10 при необходимости)
select[Model] :Клиент|select[Abonents]$ Select2 с AJAX-поиском Integer (pk выбранной записи)
choices[v^L|v^L] :Месяц|choices[1^Янв|2^Фев]$ Выпадающий список фикс. значений String (значение выбранной опции)
choices[[code^]Label] :Sotrudnik|choices[[uspeshnyy^]Aleksandr] Расширенный выпадающий с экранированными значениями String (code)

Пример SQL с параметрами:

SELECT a.id, a.name, a.contract_number,
       aa.ostatok / 10000000000.0 AS balance_rub
FROM abonents a
LEFT JOIN admin_accounts aa ON aa.id = a.account_id
WHERE a.is_folder = false
  AND a.deleted = false
  AND aa.ostatok < -:Долг|sum$ * 10000000000
  AND a.create_date BETWEEN :Дата с|date$ AND :Дата по|date$
ORDER BY aa.ostatok ASC
LIMIT :Лимит|sum$;

AI-помощник написания SQL

Кнопка «✨ AI» в шапке открывает чат с ИИ-ассистентом. Ассистент знает структуру 15 ключевых таблиц биллинга (abonents, admin_accounts, tarif, finance_operations, users_usluga, pay_log, users, homes, nas, connection_points и др.) и формат параметров, умеет писать SQL по словесному описанию задачи.

Типичный сценарий:

  1. Оператор пишет: «топ-10 должников за месяц с балансом и телефоном»
  2. AI задаёт уточняющие вопросы: какой период (последний месяц / произвольный), какой минимальный долг, нужен ли фильтр по тарифу
  3. Оператор отвечает свободным текстом
  4. AI возвращает готовый SQL с блоком JSON {"name": "...", "description": "...", "sql": "..."}
  5. Зелёный draft-banner с кнопкой «➡ Создать отчёт» передаёт черновик в основную модалку с предзаполненными name/description/sql
  6. Оператор проверяет SQL в CodeMirror, при необходимости правит, нажимает «Сохранить»

Каждый AI-запрос пишется в AuditOperations (table_name='ADMIN_CUSTOM_REPORTS', descr='AI-консультация: …'). Запросы идут через прокси (внешний API провайдера недоступен для RU).

Права и audit

Переработка раздела

Раздел переработан: нерабочие отчёты вычищены, редактор запросов приведён к тому же виду, что в СОРМ, у отчётов появились обложки и разделение по организациям.

Библиотека — список с обложками и колонкой «Орг.»
- Библиотека отчётов
  - Список
    - Вкладки по отделам
    - Колонка Орг при мульти-орг
    - Обложки-миниатюры
    - Чипы и поиск Ctrl+K
  - Модалка отчёта
    - Редактор на всю высоту
    - Кнопки AI и Формат
    - Справка в popover
    - Квадратная обложка
    - Чекбоксы организаций
  - Запуск
    - Единый выбор периода
    - Выбор сотрудника с аватаром
    - История выполнения
  - Безопасность
    - Параметры связанные
    - Нормализация дат
    - Привязка к организации
Три критические находки
БылоСтало
Редактор открывался пустым (чёрный прямоугольник)Пересчёт размеров после показа окна — SQL виден сразу
Значение параметра вставлялось в запрос текстом (риск подмены, апостроф ломал фильтр молча)Значения передаются связанными параметрами; даты нормализуются — подмена невозможна
Поля периода шли «Дата конца → Дата начала» (по алфавиту)Порядок по появлению в запросе — начало перед концом
Как выполняется отчёт с параметрами
flowchart LR
    FORM["Форма запуска"] --> P["Параметры формы"]
    P --> SUB["Подстановка плейсхолдеров %s"]
    SUB --> NORM["Нормализация дат
дд.мм.гггг в дату"] NORM --> EXEC["Выполнение со связанными
значениями"] EXEC --> HIST["Запись в историю"] EXEC --> RES["Результат или ошибка"]

Значение больше не встраивается в текст запроса, поэтому апостроф или «;» внутри него не могут изменить смысл SQL. База работает в режиме, где строку «23.06.2026» она читала как «месяц 23» и падала — теперь дата передаётся отдельным типом и понимается однозначно.

Чистка: 75 → 62 отчёта
ДействиеОтчётовПричина
Удалено18Отчёты на пустых устаревших таблицах (старая телефония, старый лог RADIUS) и дубли
Починено3Выручка по организациям (был пустой запрос), расходы за период
Добавлено5Сводка по организациям, выручка по дням и по месяцам, обещанные платежи, отток
Редактор как в СОРМ + обложка
Модалка отчёта: редактор на всю высоту, кнопки AI и Формат, обложка, чекбоксы организаций
Разделение по организациям
flowchart TB
    R["Отчёт"] --> Q{"Привязан к организациям?"}
    Q -- "Нет" --> ALL["Общий — виден всем"]
    Q -- "Да" --> ORG["Виден отмеченным организациям"]
    ORG --> COL["Колонка «Орг.» в списке
в режиме «Все организации»"] ORG --> CHK["Чекбоксы в шапке модалки"]

Организации отмечаются чекбоксами по центру шапки модалки; пустой список означает «общий отчёт». В режиме «Все организации» в списке между «№» и названием появляется колонка «Орг.» с бейджами — как в тарифах.

Единые компоненты
Форма запуска: единый выбор периода вместо двух полей дат
ГдеЧто былоСтало
Форма запуска — датыДва отдельных поля с календарёмЕдиный выбор периода с пресетами (работает в любом отчёте с парой дат)
Форма запуска — сотрудникОбычный выпадающий списокСелектор сотрудника с аватаром
Редактор кодаСвой код в каждом разделеЕдиный компонент (каталог компонентов интерфейса)
Тёмная тема и телефон
Библиотека в тёмной теме Библиотека на телефоне

Тёмная тема выдержана целиком; на телефоне список сжимается до имени и кнопки запуска, а модалки раскрываются на весь экран выдвижной панелью.

4.2. Аудит

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

Аудит

Каждая запись содержит дату/время, оператора, тип события, клиента и описание. Для большинства событий есть drill-down: клик по записи открывает изменённый объект в нужной вкладке.

Возможности списка:

4.3. Журнал платежей

Сводный журнал финансовых операций по всем клиентам — приходы, списания, сторно, обещанные платежи. Это представление таблицы FinanceOperations с серверной пагинацией (поддерживает сотни тысяч записей).

Журнал платежей

Возможности:

Переработка журналов

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

Журнал платежей в едином виде SmitUI
- Журналы: Аудит и Платежи
  - Единый вид SmitUI
    - Чистая таблица без серой шапки
    - Бейджи-таблетки с точкой
    - Тёмная тема из общего источника
    - Токены отступов и границ
  - Разделение по организациям
    - Колонка Орг в режиме Все
    - Фильтр по доступным компаниям
    - Записи без организации видны всем
  - Работа с клиентом
    - Клик открывает боковую панель
    - Панель всегда поверх меню
  - Телефон
    - Строки становятся карточками
    - Подписи полей у значений
    - Полоса прихода или расхода слева
Разделение по организациям

Теперь данные разделены. У платежа организация берётся из самой операции; у аудит-записи — из связанного объекта.

flowchart TB
    A["Аудит-запись"] --> Q{"Есть клиент?"}
    Q -- "Да" --> AB["Организация клиента"]
    Q -- "Нет" --> OBJ{"Известен объект?"}
    OBJ -- "Платёж / видеопроект /
тикет / NAS" --> OO["Организация объекта"] OBJ -- "Нет" --> ALL["Без организации —
общесистемное, видно всем"] AB --> COL["Колонка «Орг.» + фильтр
по доступным компаниям"] OO --> COL
РазделКак определяется организацияПокрытие
Журнал платежейПоле организации у самой финоперацииПрактически все записи
АудитОрганизация клиента; для системных — по объекту (платёж, видеопроект, тикет, NAS)≈ 74% записей; остальное — общесистемные события «—», видны всем
Единый вид SmitUI и работа с клиентом
Аудит в едином виде SmitUI Боковая панель клиента поверх журнала
Телефон

На телефоне строки обоих журналов становятся карточками: у каждого значения — подпись поля, ничего не обрезается. В Журнале платежей слева остаётся цветная полоса прихода/расхода.

Журнал платежей на телефоне — карточки Аудит на телефоне — карточки

4.3.1. Очередь чеков (54-ФЗ)

Раздел Отчёты → Очередь чеков (/admin/reports/fiscal_queue/) показывает, какие операции ждут кассового чека, по каким чек уже пробит и где ОФД вернул ошибку. Отсюда же чек отправляется вручную — по одной операции или пачкой.

Модуль фискализации, настройка «какие платежи фискализируем» и чек-лист готовности описаны в разделе 6.3.1. Модуль фискализации 54-ФЗ; сама очередь — там же.

4.4. Журнал сообщений

Единый сводный журнал всех сообщений (MsgStack) по всем клиентам — SMS, Email, Telegram, Push, VK, ЛК. И точечные отправки из карточки клиента, и массовые рассылки попадают в один список. Доступен по адресу /admin/reports/messages/ (пункт меню «Отчёты → Журнал сообщений»,, единый вид SmitUI и мульти-орг).

Журнал сообщений: KPI, фильтры, чипы каналов, таблица SmitUI с колонкой Орг.

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

- Журнал сообщений
  - Показатели
    - Всего за период
    - Доставлено SMS
    - В очереди
    - Ошибки SMS
  - Фильтры
    - Поиск текст договор
    - Диапазон дат
    - Чипы каналов
    - SMS-статус
    - Отправитель
    - Организация
  - Таблица SmitUI
    - Колонка Орг
    - Бейджи каналов
    - Полоса ошибок SMS
    - Карточки на телефоне
  - Рассылка
    - Категории
    - Тарифы
    - Папки MPTT
    - NAS
    - Поиск
    - Скоуп по орг
  - Мульти-орг
    - Фильтр по клиенту
    - Записи без орг видны всем
    - Рассылка своим клиентам

Что внутри

Разделение по организациям

Раздел учитывает мульти-организации. Сообщение связано с клиентом (MsgStack.abonent), у клиента есть организация — журнал фильтруется по ней, а рассылка не может уйти клиентам чужой компании.

flowchart TD
    REQ["Запрос журнала
или рассылки"] --> MO{"Мульти-орг
включён?"} MO -->|"нет"| ALL["Без фильтра —
все записи"] MO -->|"да"| CUR{"Выбрана орг
в шапке?"} CUR -->|"да"| ONE["Только клиенты
этой организации"] CUR -->|"нет"| ISO{"Изоляция
персонала?"} ISO -->|"выключена"| ALLORG["Все организации
+ колонка «Орг.»"] ISO -->|"включена"| PERM["Только доступные
оператору орг"] ONE --> NULL["+ записи без клиента
видны всем (NULL)"] ALLORG --> NULL PERM --> NULL

Журнал, KPI и CSV заскоуплены через _apply_msg_org() по abonent__organization_id. Контракт: записи без определимой организации (системные, без клиента) видны всем — иначе «ничьи» уведомления пропали бы у всех.

Рассылка — критично (P0). Набор получателей строится из единого base-queryset, который заранее ограничен доступными организациями оператора. Поэтому все режимы выбора (категории / тарифы / папки / NAS / поиск / выбор по ID) не могут захватить клиентов чужой компании — ни в предпросмотре, ни при отправке.

Массовая рассылка

Кнопка «Рассылка» в правом верхнем углу открывает модальное окно для отправки одинакового текста группе клиентов через выбранные каналы (SMS / Email / Telegram / Push). Hard-limit — 500 получателей на одну рассылку (защита от случайного спама).

5 способов выбора получателей:

  1. Категории — 8 готовых chip-фильтров: Должники, Сейчас онлайн, Заблокированные, Об. платёж, Юр. лица, Без email, Без телефона, Не платил 30 дн.
  2. Тарифы — multi-select из ~78 тарифов. Отправит всем клиентам на выбранных тарифах.
  3. Папки — древовидный picker с MPTT-обходом всех вложенных подпапок автоматически. Поиск по имени, expand/collapse, кнопка очистки выбора.
  4. NAS — multi-select NAS-серверов. Отправит всем клиентам с учётками на выбранных NAS (через Users.nas_id).
  5. Поиск — Select2 AJAX по имени/договору для точечного выбора.

Рассылка: 5 вкладок, категория Должники, предпросмотр 1620 получателей с разбивкой по каналам SMS/Email/TG/Push

Live-preview снизу всегда показывает: фактическое количество получателей, первые 10 имён + «…и ещё N», статистику доступности каналов («SMS: 267 · Email: 13 · TG: 0 · Push: 1»). Кнопка «Отправить» дизаблится при count = 0 или count > 500 (бейдж становится красным «макс 500!»).

Счётчик символов и SMS-сегментов справа от лейбла «Текст» обновляется на каждый ввод. Кириллица (UCS-2) → 70 символов на сегмент, латиница (GSM 7-bit) → 160. Цвет меняется на оранжевый при ≥2 сегментов и красный при ≥3.

Дерево папок

Вкладка «Папки» отрисована как полноценное tree-list с иконками и счётчиками клиентов в каждой папке (включая подпапки). Поддерживается keyboard-навигация (Tab → Enter/Space), live-фильтр по имени с автоматическим раскрытием родителей, кнопки Раскрыть всё / Свернуть всё / Очистить выбор.

Tree папок: SMIT (5554), ртк-червленое (49), Дубовый (27, выбран)

Рассылка по NAS-серверам

Полезно когда нужно предупредить клиентов конкретного NAS (профилактические работы, замена оборудования, миграция). Каждый NAS показывается с IP, статусом (включён / выкл) и количеством клиентов.

Рассылка по NAS: 2 выбранных NAS, preview 408 клиентов

Аудит рассылок

Каждая отправка пишется в AuditOperations (категория «Сообщения», см. раздел 4.2 Аудит):

Поле MsgStack.owner_id хранит pk оператора, поэтому в журнале можно отфильтровать «все мои отправки» или «всё что отправлял Петров».

Модалка деталей

Клик по строке открывает полный текст сообщения, дату, клиента и статусы всех каналов доставки. Из неё — переход в карточку клиента.

Модалка деталей сообщения: дата, клиент, заголовок, полный текст, каналы доставки

Адаптивность и единый вид SmitUI

Раздел приведён к единому виду SmitUI: чистая шапка таблицы без серого фона и sticky, бейджи-таблетки, единый источник тёмной темы. Красная полоса у строк с ошибкой SMS сохранена.

Endpoint'ы

URLНазначение
GET /admin/reports/messages/Главная страница
GET /admin/reports/messages_json/Server-side список (фильтры + pagination)
GET /admin/reports/messages_kpi/JSON для KPI-карточек
GET /admin/reports/messages/export/CSV экспорт текущей выборки
POST /admin/reports/messages/broadcast/Массовая отправка
GET /admin/reports/messages/recipients_preview/Preview получателей перед рассылкой
GET /admin/reports/messages/tariffs_list/Список тарифов (для multi-select)
GET /admin/reports/messages/folders_list/Дерево папок (для tree-picker)
GET /admin/reports/messages/nas_list/Список NAS-серверов
GET /admin/reports/messages/senders_list/Список операторов с рассылками (для select-фильтра)

4.5. Разработка и логи

Раздел для администратора-разработчика. Доступен только пользователям с правами root. Делится на две вкладки.

Отчёты — встроенный markdown-просмотрщик внутренних документов проекта (отчёты по разработке, планы, документация).

Отчёты разработки

Поддерживается сортировка по дате/имени или ручной drag-and-drop, inline-переименование, создание папок, загрузка новых .md-файлов через интерфейс.

Возможности просмотрщика .md

Markdown-просмотрщик поддерживает удобную навигацию и работу с документами:

Логи — просмотр серверных логов прямо из админки.

Серверные логи

Доступны каналы:

Показываются последние 500 строк, есть кнопка обновления. Файлы автоматически ротируются (10 МБ × 5 копий).

4.6. Должники

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

Отдельная страница /admin/debtors/ — список клиентов с отрицательным балансом или активной блокировкой b_negbal. Раздел рассчитан на ежедневную работу бухгалтерии и операторов поддержки: KPI-блок сверху помогает быстро оценить ситуацию по компании, а drawer-панель — разобраться с конкретным клиентом без ухода на полную карточку.

KPI и статистика по периодам

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

Фильтры и чипы
Таблица и сортировка

Колонки: ФИО, договор, тариф, точка подключения, баланс, дней без оплаты, телефон, email, статус. По умолчанию сортировка — по убыванию суммы долга. Клик на заголовок любой колонки переключает порядок (▲▼). Цветовое кодирование: красные строки — заблокированные за неуплату, жёлтые — на грани блокировки, серые — с активным обещанным платежом. В каждой строке справа — кнопка ℹ Подробнее, которая открывает drawer.

Drawer-панель клиента

Клик на ℹ открывает боковую панель справа (overlay поверх таблицы) с краткой сводкой по клиенту — без перехода на полную карточку и потери контекста списка. Внутри панели:

Массовые действия и экспорт

4.7. Миграция с других биллингов

Раздел /admin/settings/migration/ — мастер импорта данных клиентов из других биллинговых систем. Пошаговый wizard с предпросмотром и dry-run.

Импорт из предыдущей версии (Firebird)

/admin/settings/migration/cb4/ — специализированный импорт данных из предыдущей версии биллинга (Firebird). Учитывает специфику:

Загрузка из CSV

/admin/settings/migration/csv_loading/ — общий импорт клиентов/услуг/платежей через CSV файлы. Поддерживается:

Авто-импорт CSV (схемы)

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

FrameworkFormGroupFields — назначение и роль

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

FrameworkFormGroupFields (FFG) — таблица настроек UI для всех админских форм биллинга. Управляет тем, какие поля видны на форме редактирования, как они сгруппированы в вкладки и в каком порядке. По сути это «генеральный настройщик» админки без правки кода.

Что это и зачем

Биллинг — это унаследованный движок с десятками моделей, у каждой по 30-100 полей. Большая часть из них — технический шум для оператора. Решения:

Это безопасный способ упростить UI без боязни сломать логику.

Ключевые поля
ПолеНазначение
TABLE_NAMEИмя модели (USERS, ABONENTS, USLUGA — UPPERCASE)
FIELD_NAMEИмя поля Django-модели (то же что в Model._meta.fields)
INTERFACE_IDFK на интерфейс (NULL = дефолт для всех ролей)
GROUP_IDНомер вкладки/группы в форме (1 = основные, 2 = доп., и т.д.)
SHOW_ON_FORM0 = скрыто, 1 = видно (главное поле управления)
VERBOSE_NAMEРусское имя поля для UI (заменяет автоматическое из модели)
ORDERПорядок в группе (для drag-and-drop)
Как работает
  1. При открытии формы редактирования модели (например /admin/Abonents/<id>/) view вызывает safe_globals.get_groups_from_model('ABONENTS');
  2. Функция читает все записи FFG где TABLE_NAME='ABONENTS' и INTERFACE_ID IS NULL;
  3. Записи группируются по GROUP_ID → каждая группа = одна вкладка формы;
  4. Внутри группы поля сортируются по ORDER, скрытые (SHOW_ON_FORM=0) исключаются;
  5. Применяются VERBOSE_NAME для русификации лейблов;
  6. Кеш Django сохраняет результат — после изменения FFG нужен cache.clear().
Опасные моменты
  1. INTERFACE_ID=NULL — это дефолт, не «отсутствие настройки». Если поле SHOW_ON_FORM=0 для INTERFACE_ID=NULL, оно скрыто во всех интерфейсах. Чтобы показать только в одном — нужны 2 записи: NULL с 0 и INTERFACE_ID=1 с 1;
  2. Дубликаты Python dict — баг был с 'Users' ключом, появлявшимся дважды в _EXCLUDE_FORMS. Второе значение тихо перезаписывало первое. Перед добавлением нового ключа всегда grep;
  3. UPPERCASE имя таблицы — критично для PostgreSQL. 'abonents' != 'ABONENTS'. Используйте Model._meta.db_table.upper() чтобы не ошибиться;
  4. FIELD_NAME — Django-имя, не SQL-имя. Поле create_date в FFG, не "CREATE_DATE".
Реальные примеры скрытия полей
BuildМодельСкрыто
Users (учётные записи)17 полей: IPV6, MASK, PHONE, EXT_ID, NAS_IP_LOCK, OPT82, IS_TEMPLATE, SNAT_PULL, SNATIP, SERVER, ALWAYS_LOGGED, HOST_IP, HOST_PULL, CREATE_DATE, OPT82_PARAM, IS_ALLOW_SEARCH, GPON_MODEM_PORT, HW_SERIAL, EQUIPMENT, SERVICE_TYPE
UslugaSERVICE_TYPE (87% услуг имели его NULL)
Users группа 325 «дополнительных»: DHCP_MASK, DHCP_ROUTE_IP, ASK_PASSWORD_LK, DEACTIVATE_STRING, ACTIVATE_STRING
Где править: UI /admin/settings/interface_settings/ (FrameworkFormGroupFieldsForm в billing/forms/framework_forms.py). После сохранения обязательно очистите кеш — иначе изменения не подхватятся: docker exec app-web-1 python manage.py shell -c "from django.core.cache import cache; cache.clear()"

4.8. Настройки интерфейса (формы)

/admin/settings/interface_settings/ — конфигурация полей форм для каждой модели. Через FrameworkFormGroupFields можно:

Пример использования: на форме Users скрыты 17 технических полей (IPV6, MASK, EXT_ID, IS_TEMPLATE, и т.д.) — оставлены только реально используемые. Изменения применяются для всех операторов одновременно после cache.clear().

5. Игры (Маркетинг)

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

Список игр: Pacman, Tetris, Snake, Minesweeper, 2048, Breakout, Flappy Bird, Helicopter. Доступны в админке, мобильном приложении и ЛК.

5.1. Архитектура модуля

Единый источник настроек (GAMES_* в SystemSettings) питает три точки потребления: профиль-меню админки, карточку клиента, мобильное приложение. Данные результатов хранятся в GameScore, история победителей — в GameMonthWinner.

Архитектура модуля Маркетинг → Игры

КомпонентНазначение
billing/services/games_registry.pyЕдиный реестр 8 игр (admin + mobile)
billing/models/game_score.pyGameScore (результаты) + GameMonthWinner (победители)
billing/services/game_rewards.pyОпределение лидера + безопасное начисление бонуса
billing/tasks/game_rewards.pyCelery-подведение итогов 1-го числа
settings/_marketing_tab.htmlUI настроек (вкладка «Маркетинг»)
abonents_form/marketing_games.htmlВкладка результатов в карточке клиента

5.2. Настройки модуля «Маркетинг»

Настройки находятся в /admin/settings/system/ → вкладка «Маркетинг» (рядом с Брендинг / Безопасность / … / Профиль). Сейчас Маркетинг = Игры; в будущем туда добавятся другие маркетинговые механики (акции, реферальные программы, бонусы за активность).

Вкладка Маркетинг — полная страница настроек

Страница содержит 5 секций:

Шапка вкладки Маркетинг — Общее + Мобильное

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

Мобильный вид вкладки Маркетинг

Вкладка Маркетинг в тёмной теме

5.3. «Игра месяца» — бесплатный интернет

Главная механика. Клиент с максимальным счётом за календарный месяц получает бесплатный месяц интернета — кредит-финоперацию на сумму месячной стоимости его тарифа.

Поток Игра месяца

Настройки розыгрыша:

Подведение итогов — Celery-задача game_of_month_finalize запускается 1-го числа в 02:00, определяет лидера прошлого месяца и (в авто-режиме) начисляет приз; в ручном — ставит «ожидает подтверждения» + Telegram-алерт оператору.

Ручное начисление. В блоке «Текущий лидер» кнопка «Начислить победителю вручную» открывает окно подтверждения с данными победителя — игра, ФИО, договор, набранные очки и сумма приза. Если для турнира заданы условия участия (см. §5.4), оператор отмечает их выполнение прямо в окне; кнопка «Начислить» становится доступной только после подтверждения всех условий.

Окно подтверждения начисления приза победителю

🔒 Безопасное начисление: бонус начисляется только через FinanceOperations (как онлайн-платёж), не правит баланс напрямую — EBS-синхронизация его не затирает. Сотрудники в розыгрыше не участвуют. Заблокированный/удалённый победитель пропускается + алерт оператору. При равном максимуме побеждает достигший раньше (tie-break).

5.4. Условия участия в турнире

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

УсловиеНастройка
⭐ Оценка приложения (Google Play / App Store)GAMES_OF_MONTH_REQUIRE_APP_RATING
🗺 Оценка на Яндекс КартахGAMES_OF_MONTH_REQUIRE_YANDEX_MAPS
📍 Оценка на 2ГИСGAMES_OF_MONTH_REQUIRE_2GIS

Игра месяца + условия участия

Включённые условия показываются клиенту в мобильном приложении как требования перед участием в розыгрыше. По умолчанию все выключены.

Под переключателями условий задаются ссылки, по которым клиент оставляет оценку. Источники ссылок разделены:

Источники ссылок условий участия

Проверка оценки. Мобильное приложение получает ссылки через /mobile-api/v1/game/config и показывает их клиенту. Факт того, что клиент действительно оставил оценку, подтверждает оператор вручную в окне начисления приза — автоматической проверки отзыва на стороне магазинов и карт нет.

5.5. Вкладка «Маркетинг» в карточке клиента

В карточке клиента появляется вкладка «Маркетинг» — только если модуль игр включён и у клиента есть лицевой счёт.

Вкладка Маркетинг в карточке клиента

Содержимое вкладки:

Оператор видит игровую активность клиента прямо в его профиле, не переходя в раздел игр.

5.6. Профиль-меню и мобильное приложение

Профиль-меню (справа у аватара) — пункт «Игры» появляется, когда включены GAMES_ENABLED + GAMES_ADMIN_ENABLED. Управляется через context-processor — без жёсткой проверки прав, видимость определяется единым тогглом.

Пункт Игры в профиль-меню

Мобильное приложение (/mobile-api/v1/game/config) отдаёт список доступных игр (из GAMES_MOBILE_ALLOWED) и данные текущего розыгрыша (мой счёт, счёт лидера, дни приза). Фильтрация submit_score / leaderboard по GAMES_MOBILE_ENABLED.

Ключи настроек (SystemSettings, категория games)

GAMES_ENABLED / GAMES_ADMIN_ENABLED / GAMES_MOBILE_ENABLED / GAMES_MOBILE_ALLOWED
GAMES_OF_MONTH_ENABLED / GAMES_OF_MONTH_KEY / GAMES_OF_MONTH_REWARD_DAYS
GAMES_OF_MONTH_MIN_SCORE / GAMES_OF_MONTH_AUTO_AWARD / GAMES_OF_MONTH_EXCLUDE_STAFF
GAMES_OF_MONTH_NOTIFY / GAMES_OF_MONTH_OPTYPE
GAMES_OF_MONTH_REQUIRE_APP_RATING / _YANDEX_MAPS / _2GIS
GAMES_OF_MONTH_YANDEX_URL / GAMES_OF_MONTH_2GIS_URL

Ссылка на приложение для условий участия берётся из MOBILE_FORCE_UPDATE_URL_ANDROID / _IOS (раздел ЛК → Мобильное).