Отчёты
Готовые и произвольные отчёты, аудит операций, журналы платежей и сообщений, должники. Здесь же — раздел геймификации.
Содержание раздела
- Отчёты
- Панель директора
- Пользовательские SQL-отчёты — назначение и роль
- Библиотека отчётов
- Аудит
- Журнал платежей
- Очередь чеков (54-ФЗ)
- Журнал сообщений
- Разработка и логи
- Должники
- Миграция с других биллингов
- FrameworkFormGroupFields — назначение и роль
- Настройки интерфейса (формы)
- Игры (Маркетинг)
- Настройки модуля «Маркетинг»
- «Игра месяца» — бесплатный интернет
- Условия участия в турнире
- Вкладка «Маркетинг» в карточке клиента
- Профиль-меню и мобильное приложение
Отчёты
Раздел меню «Отчёты» объединяет аналитику для руководителя, библиотеку SQL-отчётов, аудит действий персонала и журналы.
Панель директора
Содержание раздела
Панель директора (/admin/reports/dashboard/, сайдбар «Отчёты → Панель директора») отвечает на четыре вопроса о бизнесе за выбранный период: сколько денег пришло, сколько в среднем приносит клиент, сколько людей подключилось и сколько заблокировано. Ниже — шесть графиков по тем же данным в динамике.
Выпуск 3.7.0: общий каркас и одно правило денег
Шапка страницы — общий каркас: название со справкой, календарь периода и кнопка фильтров в одной строке. Блоки — карточками одного вида, звонки — отдельной строкой показателей. На телефоне период и фильтры стоят в одну линию, показатели идут в две колонки.
Что считается поступлением и списанием. Раньше в «поступления» попадали все положительные операции, в «списания» — все отрицательные. Туда же уезжали обещанный платёж (это кредит клиенту, а не деньги), сторнированные операции вместе с обратными и возвраты начислений. Теперь правило одно для всех сводок:
flowchart LR
OP["Финансовая операция"] --> ST{"Сторнирована?"}
ST -- да --> X["Не считается"]
ST -- нет --> SG{"Знак суммы
и тип операции"}
SG -- "сумма > 0, тип «приход»,
не обещанный платёж" --> PAY["Поступление"]
SG -- "сумма < 0, тип «расход»,
не списание обещанного" --> CH["Списание"]
SG -- иначе --> X
ARPU — поступления за месяц, делённые на клиентскую базу этого месяца (не удалённые клиенты, созданные до конца месяца). Прошлый период для сравнения берётся той же длины, что выбранный. «Активных» — не удалённые клиенты без блокировки.
Карта раздела
- Панель директора
- Показатели
- Выручка за период
- ARPU
- Новые клиенты
- Заблокировано за период
- Графики
- Популярность тарифов
- Новые клиенты
- Поступающие средства
- Платящие клиенты
- ARPU по месяцам
- ARPPU по месяцам
- Срезы
- Период и пресеты
- Сегмент физ-юр
- Канал оплаты
- Тариф
- Точка подключения
- Шаг день-неделя-месяц
- Сравнение с прошлым или годом назад
- Правила
- Данные своей организации
- Клик по показателю ведёт в список
- Ошибка отличается от пустоты
Четыре показателя и как они считаются
Формулы важнее оформления: по этим цифрам принимают решения, поэтому каждая должна читаться однозначно.
| Показатель | Что именно считается | Сравнение |
|---|---|---|
| Выручка за период | Сумма приходных финансовых операций (OP_SUMMA > 0) с датой внутри периода. Верхняя граница берётся включительно, до 23:59:59 последнего дня. |
к предыдущему периоду такой же длины |
| ARPU | Выручка периода ÷ база клиентов на конец периода (не удалённые, заведённые до этой даты). У прошлого периода — свой знаменатель, поэтому дельта ARPU не повторяет дельту выручки. | к тому же показателю прошлого периода |
| Новых клиентов | Клиенты с датой создания внутри периода. | к предыдущему периоду |
| Заблокировано за период | Клиенты, у которых блокировка началась внутри периода (ABONENTS_BLOCK.B_DATE). В подписи — сколько из них за неоплату и сколько клиентов активно сейчас. Рост показан красным: блокировок больше — это плохая новость. |
к предыдущему периоду |
Шесть графиков
| График | Что показывает |
|---|---|
| Популярность тарифов | Распределение клиентов по тарифам (без заблокированных). Хвост сворачивается в «Прочие». |
| Новые клиенты | Сколько подключилось в каждом периоде. |
| Поступающие средства | Приход по периодам — та же выручка в динамике. |
| Платящие клиенты | Сколько разных клиентов платили в периоде. |
| 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+ полей. Готовых отчётов в админке всегда не хватает: бухгалтер хочет один формат, директор — другой, техподдержка — третий. Решение:
- Гибкость без релизов — оператор-аналитик пишет SQL прямо через UI, без ожидания deployment'а;
- Параметризация — встроенный синтаксис
:Имя|тип$позволяет добавлять параметры (даты, клиент, сумма) без знания Python; - Экспорт — результат отчёта сразу в Excel/CSV/DBF без программирования;
- Историческая часть — каждый запуск сохраняется в
AdminCustomReportsHistory, можно вернуться к старому результату.
Поля и связанные таблицы
Поле ADMIN_CUSTOM_REPORTS | Назначение |
|---|---|
NAME | Название отчёта (видно в UI) |
DESCRIPTION | Описание для оператора (что отчёт показывает) |
SMTP | SQL-код запроса (text) — исторически поле называется SMTP, verbose «SQL запрос»; именно из него исполняется отчёт |
REPORT_TYPE | FK на AdminCustomReportsType — категория |
ALLOW_IN_CABINET | Видим ли клиенту в ЛК (раздел «Мои отчёты») |
TECH_ADMIN / FIN_ADMIN / FULL_ADMIN / CARD_ADMIN / READ_ADMIN | Унаследованные поля прав по ролям (int). В текущей версии доступом к разделам управляет RBAC (section_access, deny-by-default) — эти поля в гейтинге не участвуют |
Связанные таблицы:
AdminCustomReportsType— справочник типов (см. ниже);AdminCustomReportsHistory— история запусков (user_id, params JSON, row_count, execution_time, sql_hash);report_executions— текущие выполнения (для предотвращения двойного запуска);report_favorites— закладки оператора (звёздочки).
Параметры в SQL — синтаксис
Внутри SQL можно использовать спец-плейсхолдеры в формате :Имя|тип$ или :Имя|тип[аргументы]$. Когда оператор запускает отчёт — UI генерирует форму с этими параметрами, подставляет значения и выполняет SQL.
| Тип | Пример SQL | UI |
|---|---|---|
date | WHERE op_date >= :Начало|date$ | Date-picker |
sum | WHERE op_summa > :Минимум|sum$ | Поле для рублей с конвертацией ×10^10 |
select[Model] | WHERE abonent_id = :Клиент|select[Abonents]$ | Список с поиском по модели |
choices[1^Янв|2^Фев|3^Мар] | WHERE month = :Месяц|choices[1^Январь|2^Февраль]$ | Dropdown с фиксированными значениями |
Парсинг — regex в AdminCustomReports._get_sql_fields(), подстановка в _execute_sql() с защитой от SQL-injection.
Типы отчётов
Справочник ADMIN_CUSTOM_REPORTS_TYPE — 7 категорий с цветными бейджами:
- 🛠 Техсервис — для техников (NAS, RADIUS, оборудование)
- 📈 Руководство — KPI, статистика, тренды
- ⛑ Техподдержка — заявки, тикеты, проблемы клиентов
- 🎧 ЦОК (call-центр) — обращения, ответственные
- 💰 Бухгалтерия — финансы, оплаты, счета
- 📞 Телефония — VoIP, звонки
- 🛡 ЦУС — централизованное управление сетью
Один отчёт может быть без типа (NULL) или привязан к одной категории. На странице есть фильтр-вкладки по типам.
CodeMirror + AI Builder
CodeMirror 5.65.16 — встроенный SQL-редактор в модалке:
- Подсветка синтаксиса PostgreSQL (тема material-darker, чёрный фон);
- Live-counter параметров — автоматически считает
:Имя|тип$в коде; - Подсветка плейсхолдеров жёлтым overlay'ем;
- Hot-keys: Ctrl+/ comment, Ctrl+F find, Tab indent;
- Auto-fix SQL —
_fix_sql_column_case()для запросов, перенесённых из предыдущей версии (uppercaseID→ корректный регистр колонок PostgreSQL).
AI Builder: кнопка «✨ AI» в toolbar. Открывает чат с AI-ассистентом:
- Оператор описывает что нужно: «Топ-10 должников за месяц с балансом и телефоном»;
- Ассистент задаёт уточняющие вопросы (период, фильтры);
- Возвращает готовый SQL с правильными именами таблиц/полей и параметрами;
- Оператор проверяет, при необходимости правит, сохраняет.
Контекст БД для ассистента — schema из information_schema.columns для 15 ключевых таблиц + критические правила биллинга (DB_MONEY_KOEF, MPTT-фильтры, регистр имён).
Отчёты в ЛК клиента
Если у отчёта ALLOW_IN_CABINET=true — он показывается клиенту в ЛК (раздел «Мои отчёты»). При запуске:
- Параметры
:Клиент|select[Abonents]$автоматически фиксируются текущим клиентом (нельзя посмотреть чужие данные); - Параметры дат/сумм клиент задаёт сам;
- Результат отображается с пагинацией, экспорт в CSV.
report_executions ссылаются на отчёт с FK NO ACTION. Если есть история запусков — удаление падает с ошибкой. Сначала очистите историю запусков. Отдельного «архивирования» отчётов нет — только удаление (hard-delete).
Библиотека отчётов
URL: /admin/reports/AdminCustomReports/
Каталог пользовательских SQL-отчётов. Отчёт — это сохранённый SQL-запрос с настраиваемыми параметрами, сгруппированный по типам (Техсервис, Бухгалтерия, Руководство, Поддержка и т.д.), который оператор запускает кликом и получает таблицу с возможностью экспорта в Excel, CSV или DBF.
Как найти готовый отчёт, запустить его с параметрами, выгрузить результат и завести свой — включая AI-помощника по SQL.
Список отчётов
- Вкладки по типам с цветными бейджами: 🛠 Техсервис, 📈 Руководство, ⛑ Техподдержка, 🎧 ЦОК, 💰 Бухгалтерия, 📞 Телефония, 🛡 ЦУС. Тип хранится в
AdminCustomReportsType, редактируется через кнопку «+» в правой части toolbar. - Поиск по имени и SQL-коду с шорткатом Ctrl+K.
- Звёздочка избранного — отмеченные отчёты попадают в виджет «Избранные отчёты» на дашборде директора и в профиле сотрудника.
- Бейдж «🤳 ЛК» у отчётов с
allow_in_cabinet=True— они доступны клиенту в его ЛК (например, «История платежей»). - Sticky toolbar при скролле — поиск, фильтр избранного, кнопки «+ Типы», «+ Добавить отчёт», «✨ AI» остаются видны.
- Кнопки в строке: ▶ Запустить, ✏ Редактировать, 🗑 Удалить с подтверждением с подтверждением.
- Bulk-операции: чекбокс-колонка с master-чекбоксом, sticky-toolbar при выборе — Удалить / Сменить тип / Экспорт CSV выбранных.
- На мобильном (≤575px) — read-only режим: только имя + кнопка ▶ Запустить, CRUD скрыт.
Запуск отчёта
URL: /admin/reports/AdminCustomReports/<id>/executesql/
Кнопка ▶ Запустить в строке списка ведёт на форму с параметрами отчёта. Параметры извлекаются из SQL-кода по синтаксису :Имя|тип$ и рендерятся в форму автоматически.
На снимке — форма отчёта «Новые подключения за период». Два параметра-даты (:Начало|date$ и :Конец|date$) форма объединяет в одно поле периода; по умолчанию — последний месяц. Справа — история выполнения этого отчёта.
Под формой — кнопки:
- Выполнить — открывает результат в HTML-таблице.
- Экспорт — меню с форматами: Excel (.xlsx), CSV (UTF-8 BOM, разделитель
;для Excel RU) и DBF (для обмена с 1С). - К списку — назад в библиотеку.
Пример: выбор периода и списка
Поле периода открывает календарь с готовыми периодами:
Параметр-список (например, :Сотрудник|choices в отчёте «Действия сотрудника») показывается выпадающим списком. Его значения формируются на лету: SQL-генератор выбирает уникальных owner из AuditOperations и подставляет их в опции.
Пример: результат выполнения
После клика «Выполнить» срабатывает редирект на /admin/reports/AdminCustomReports/<id>/history/<run_id>/ — каждое выполнение получает свой run_id (хранится 30 дней для повторного открытия результата без перезапуска SQL):
На снимке — результат отчёта «Новые подключения за период»: 75 строк, колонки ID, Договор, Абонент, Тариф, Создан, Активирован, Улица, Дом, Телефон, Статус. Сверху — плашка «Сохранённый результат» с автором и периодом и кнопка «Изменить и выполнить»; под ней — экспорт, печать и «Поделиться».
Возможности страницы результата:
- Sticky-заголовок таблицы при прокрутке — колонки видны всегда.
- Поиск по таблице (Ctrl+K) — клиентский фильтр без перезапуска SQL.
- Кнопки экспорта в шапке — Excel / CSV / DBF (тот же набор что на форме параметров, но без перезапуска).
- Кнопка «← Назад к форме» — возврат к форме параметров для перезапуска с другими значениями.
- История запусков — каждый запуск сохраняется в
AdminCustomReportsHistoryс user_id, params, row_count, execution_time.
Модалка редактирования отчёта
Открывается кликом по имени отчёта или по карандашу. Делится на две колонки:
- Слева: название, описание, тип (select), переключатель «🤳 Доступен в ЛК», развёрнутая справка по 4 типам параметров (
:Имя|date$,:Имя|sum$,:Имя|select[Model]$,:Имя|choices[v1^Лейбл1|v2^Лейбл2]$) с примером SQL-сниппета. - Справа: SQL-редактор CodeMirror 5 с подсветкой синтаксиса PostgreSQL, тёмная тема
material-darker, номера строк, перенос длинных строк, soft-wrap. Hot keys: Ctrl+/ toggle comment, Ctrl+F find. Параметры:Имя|тип$подсвечиваются жёлтым фоном через CodeMirror-overlay.
Live-counter под редактором обновляется на каждом change-event: «N строк · M парам.», справа от него — список найденных параметров с типами.
Синтаксис параметров
Параметры записываются в SQL как :Имя|тип$ и автоматически становятся полями формы запуска:
| Тип | Синтаксис | UI-элемент | Передаётся в SQL как |
|---|---|---|---|
| date | :Дата с|date$ |
Календарь | String '2026-05-07' (ISO) |
| sum | :Сумма|sum$ |
Числовое поле (₽) | Float (умножается на 10^10 при необходимости) |
| select[Model] | :Клиент|select[Abonents]$ |
Список с поиском по мере ввода | Integer (pk выбранной записи) |
| choices[v^L|v^L] | :Месяц|choices[1^Янв|2^Фев]$ |
Выпадающий список фикс. значений | String (значение выбранной опции) |
| choices[[code^]Label] | :Sotrudnik|choices[[uspeshnyy^]Aleksandr] |
Расширенный выпадающий с экранированными значениями | String (code) |
| org | :Организация|org$ |
Выбор организации (бейджи): «Все доступные» и каждая доступная сотруднику; по умолчанию — из навбара | String с id через запятую: '1,3' |
Параметр «Организация» (build 3085)
В форму запуска попадают только организации, к которым привязан отчёт (не привязан — все активные)
и которые доступны сотруднику. Список строится на сервере, поэтому подставить в запрос чужую
организацию нельзя: форма отклонит такое значение. Ничего не доступно — в запрос уходит 0,
и отчёт пуст. В SQL значение разбирается так:
WHERE COALESCE(p."ORGANIZATION_ID", 1) = ANY(string_to_array(:Организация|org$, ',')::int[])
Если первая колонка результата называется «Организация», рассылка в чат делит сообщение на блоки по организациям (до N строк в каждом) и показывает четыре следующие колонки. Пример — отчёт «Где заявкам не хватает сети (техвозможность)».
Параметр ищется по шаблону :Имя|тип$.
Незахватывающая группа регулярного выражения вида (?:ул|пер) в том же запросе похожа на
начало параметра, если дальше в тексте есть $. Используйте обычную группу (ул|пер).
Пример 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 по словесному описанию задачи.
Типичный сценарий:
- Оператор пишет: «топ-10 должников за месяц с балансом и телефоном»
- AI задаёт уточняющие вопросы: какой период (последний месяц / произвольный), какой минимальный долг, нужен ли фильтр по тарифу
- Оператор отвечает свободным текстом
- AI возвращает готовый SQL с блоком JSON
{"name": "...", "description": "...", "sql": "..."} - Зелёный draft-banner с кнопкой «➡ Создать отчёт» передаёт черновик в основную модалку с предзаполненными name/description/sql
- Оператор проверяет SQL в CodeMirror, при необходимости правит, нажимает «Сохранить»
Каждый AI-запрос пишется в AuditOperations (table_name='ADMIN_CUSTOM_REPORTS', descr='AI-консультация: …'). Запросы идут через прокси (внешний API провайдера недоступен для RU).
Права и audit
- SQL выполняется с правами Django БД-юзера (с полным доступом к БД). Это значит, что любой оператор с правом «Запуск отчётов» может прочитать любую таблицу. Опасные SQL (UPDATE, DELETE, DROP) не блокируются на уровне приложения — защита только через PostgreSQL-роль (рекомендуется отдельный read-only role для отчётов).
- Каждое выполнение пишется в AdminCustomReportsHistory: user_id, params (JSON), row_count, execution_time, sql_hash. Доступно через «История запусков» в правом меню отчёта.
- allow_in_cabinet=True: отчёт доступен клиенту в ЛК через
/lk/reports/<id>/. Для безопасности параметры могут содержать только типdateиsum(запрещеныselect[Model]иchoices, иначе клиент мог бы перебирать ID).
Переработка раздела
Раздел переработан: нерабочие отчёты вычищены, редактор запросов приведён к тому же виду, что в СОРМ, у отчётов появились обложки и разделение по организациям.
- Библиотека отчётов
- Список
- Вкладки по отделам
- Колонка Орг при мульти-орг
- Обложки-миниатюры
- Чипы и поиск 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 (помощник по SQL) и Формат (автоформатирование) над редактором.
- Справка по параметрам вынесена из левой колонки в ссылку-подсказку «Параметры в SQL» под редактором.
- Квадратная обложка: любую картинку модуль обрезает по центру до 512×512 и кладёт в облачное хранилище; в списке она показывается миниатюрой.
Разделение по организациям
flowchart TB
R["Отчёт"] --> Q{"Привязан к организациям?"}
Q -- "Нет" --> ALL["Общий — виден всем"]
Q -- "Да" --> ORG["Виден отмеченным организациям"]
ORG --> COL["Колонка «Орг.» в списке
в режиме «Все организации»"]
ORG --> CHK["Чекбоксы в шапке модалки"]
Организации отмечаются чекбоксами по центру шапки модалки; пустой список означает «общий отчёт». В режиме «Все организации» в списке между «№» и названием появляется колонка «Орг.» с бейджами — как в тарифах.
Единые компоненты
| Где | Что было | Стало |
|---|---|---|
| Форма запуска — даты | Два отдельных поля с календарём | Единый выбор периода с пресетами (работает в любом отчёте с парой дат) |
| Форма запуска — сотрудник | Обычный выпадающий список | Селектор сотрудника с аватаром |
| Редактор кода | Свой код в каждом разделе | Единый компонент (каталог компонентов интерфейса) |
Тёмная тема и телефон
Тёмная тема выдержана целиком; на телефоне список сжимается до имени и кнопки запуска, а модалки раскрываются на весь экран выдвижной панелью.
Аудит
Журнал всех значимых действий в биллинге: создание, изменение, удаление клиентов, подключение услуг, платежи, блокировки, изменения настроек, действия СОРМ.
Каждая запись содержит дату/время, оператора, тип события, клиента и описание. Для большинства событий есть drill-down: клик по записи открывает изменённый объект в нужной вкладке.
Возможности списка:
- Фильтр периода с кнопками Сегодня / Неделя / Месяц / Год.
- Фильтр по типу события и текстовый поиск.
- Цветные бейджи категорий (status / block / payment / SORM).
- Sticky-заголовок таблицы при прокрутке.
Журнал платежей
Сводный журнал финансовых операций по всем клиентам — приходы, списания, сторно, обещанные платежи. Это представление таблицы FinanceOperations с серверной пагинацией (поддерживает сотни тысяч записей).
Возможности:
- Быстрые периоды: Сегодня / Неделя / Месяц / Квартал / Год.
- Селектор количества строк: 50 / 100 / 200 / 500.
- Цветовое кодирование сумм: приход — зелёный, расход — красный.
- Клик по клиенту открывает его карточку в новой вкладке.
- Серверный фильтр по типу операции и текстовый поиск.
- По умолчанию показан текущий месяц.
Переработка журналов
У обоих журналов общий набор инструментов: выбор периода, показатели, цветные категории, поиск и экспорт. К ним добавлены разделение по организациям, работа с телефона и единый визуальный язык.
- Журналы: Аудит и Платежи
- Единый вид SmitUI
- Чистая таблица без серой шапки
- Бейджи-таблетки с точкой
- Тёмная тема из общего источника
- Токены отступов и границ
- Разделение по организациям
- Колонка Орг в режиме Все
- Фильтр по доступным компаниям
- Записи без организации видны всем
- Работа с клиентом
- Клик открывает боковую панель
- Панель всегда поверх меню
- Телефон
- Строки становятся карточками
- Подписи полей у значений
- Полоса прихода или расхода слева
Разделение по организациям
Теперь данные разделены. У платежа организация берётся из самой операции; у аудит-записи — из связанного объекта.
flowchart TB
A["Аудит-запись"] --> Q{"Есть клиент?"}
Q -- "Да" --> AB["Организация клиента"]
Q -- "Нет" --> OBJ{"Известен объект?"}
OBJ -- "Платёж / видеопроект /
тикет / NAS" --> OO["Организация объекта"]
OBJ -- "Нет" --> ALL["Без организации —
общесистемное, видно всем"]
AB --> COL["Колонка «Орг.» + фильтр
по доступным компаниям"]
OO --> COL
| Раздел | Как определяется организация | Покрытие |
|---|---|---|
| Журнал платежей | Поле организации у самой финоперации | Практически все записи |
| Аудит | Организация клиента; для системных — по объекту (платёж, видеопроект, тикет, NAS) | ≈ 74% записей; остальное — общесистемные события «—», видны всем |
Единый вид SmitUI и работа с клиентом
- Обе таблицы выглядят одинаково: чистая шапка без заливки, воздушные строки, бейджи-таблетки с точкой-индикатором, тёмная тема без отдельных настроек.
- В Журнале платежей знак операции показывает тонкая цветная полоса слева и цвет суммы (вместо заливки всей строки); суммы выровнены по правому краю.
- Клик по клиенту открывает боковую панель с его данными (баланс, контакты, последняя сессия) прямо поверх журнала — не уводя со страницы. Панель всегда поверх бокового меню.
Телефон
На телефоне строки обоих журналов становятся карточками: у каждого значения — подпись поля, ничего не обрезается. В Журнале платежей слева остаётся цветная полоса прихода/расхода.
Очередь чеков (54-ФЗ)
Раздел Отчёты → Очередь чеков (/admin/reports/fiscal_queue/)
показывает, какие операции ждут кассового чека, по каким чек уже пробит и где ОФД вернул
ошибку. Отсюда же чек отправляется вручную — по одной операции или пачкой.
Модуль фискализации, настройка «какие платежи фискализируем» и чек-лист готовности описаны в разделе Модуль фискализации 54-ФЗ; сама очередь — там же.
Журнал сообщений
Все сообщения клиентам в одном списке — SMS, почта, Telegram, push, VK и уведомления в
личном кабинете: и отправленные из карточки клиента, и массовые рассылки, и служебные
уведомления биллинга. Раздел: Отчёты → Журнал сообщений
(/admin/reports/messages/). Отсюда же отправляют и планируют рассылки.
Кому отправить, как составить текст и посчитать SMS, запланировать отправку, согласовать и изменить рассылку, черновик от AI-помощника.
Карта раздела
- Журнал сообщений
- Сводка за период
- Сообщений
- SMS доставлено
- SMS ждут отчёта шлюза
- SMS с ошибкой
- Запланированные рассылки
- Карточка рассылки
- Изменить
- Согласовать
- Получатели сейчас
- Отменить
- Фильтры
- Период
- Каналы
- Поиск
- SMS-статус
- Отправитель
- Колонки и CSV
- Рассылка
- Категории
- Тарифы по линейкам
- Папки
- Услуги по типам
- NAS
- Клиенты
- Сейчас или по расписанию
- AI-помощник
- Создать рассылку
- Доставка SMS
- Права
- Создают четыре роли
- Отправляют маркетолог и директор
Что на странице
- Шапка — название раздела и кнопка «Рассылка». Кнопку видят только сотрудники, которым разрешено создавать рассылки (см. права).
- Сводка одной строкой: сколько сообщений за период, сколько SMS доставлено, сколько ждут отчёта шлюза и сколько с ошибкой.
- Запланированные рассылки — карточки рассылок, которые ещё не отправлены,
и свёрнутый список завершённых и отменённых. Ссылка
?mailing=Nоткрывает страницу с выделенной рассылкой — такие ссылки приходят в рабочий чат. - Строка фильтров: период, каналы (Все, SMS, Email, Telegram, Push, VK, Только ЛК — не поместившиеся уходят в «Ещё»), поиск по тексту, клиенту и договору (ищет по мере ввода), SMS-статус, отправитель, выбор колонок и выгрузка CSV.
- Таблица: дата, организация (в режиме «Все организации»), клиент, договор, заголовок и текст, отправитель, каналы с отметкой доставки. Строки выровнены по верхнему краю. Клик по строке открывает сообщение целиком — с каналами доставки и переходом в карточку клиента.
- На телефоне строки становятся карточками, фильтры — в выезжающей панели, окна — на весь экран.
Рассылка: кому отправить
Кнопка «Рассылка» открывает окно. Получателей выбирают одним из шести способов — вкладками вверху окна. Под вкладками всегда видно, сколько клиентов получится, первые имена и сколько из них можно достать каждым каналом.
| Вкладка | Кому уйдёт |
|---|---|
| Категории | Готовые группы: должники, в минусе после изменения цены, сейчас онлайн, заблокированные, с обещанным платежом, с активным IPTV, юрлица, без почты, без телефона, без платежей 30 дней. |
| Тарифы | Сначала линейка (показаны только линейки, на тарифах которых есть клиенты, и отдельно «Без линейки»), потом тарифы этой линейки с числом клиентов. Без отметок уйдёт всем клиентам выбранных линеек, с отметками — только клиентам отмеченных тарифов. |
| Папки | Дерево папок клиентов открывается свёрнутым; папка выбирается вместе с вложенными. Поиск по имени раскрывает ветки до найденной папки. |
| Услуги | Сначала тип услуги (Трафик, IPTV, ЧОП, Видео…) с числом клиентов, потом услуги этого типа. Считаются только действующие услуги: включены, не удалены, срок начался и не закончился. |
| NAS | Клиенты с учётками на выбранных NAS — с адресом NAS и числом клиентов. |
| Поиск | Конкретные клиенты: поле ищет по имени, договору, телефону и адресу, выбранные показываются плашками. |
Сводка «по каналам» — сколько сообщений за неделю ушло каждым каналом: SMS, почта, Telegram-бот, ВКонтакте, аккаунты агента (Telegram, ВКонтакте, MAX — значок с человечком) и только в личный кабинет. Клик по значку ставит этот канал фильтром. Фильтр каналов над таблицей — цветные значки (подпись в подсказке), отдельно — «Агент»: все сообщения аккаунтов, которые пишут первыми. В колонке «Каналы» у сообщения агента тоже значок с человечком.
Текст, каналы и время
- Текст набирается в редакторе. Над ним — счётчик: знаки, число SMS и алфавит. Одна SMS кириллицей — до 70 знаков, в длинном сообщении — по 67 на часть (латиницей 160 и 153). Когда включён канал SMS, счётчик оранжевый со второй части и красный с третьей: каждая часть оплачивается.
- Подстановки подсвечиваются:
%(debt)s— сколько не хватает до нуля, в рублях;%(balance)s— баланс;%(contract_number)s— договор;%(abonent_name)s— клиент. С%(debt)sсообщение уйдёт только тем, у кого есть долг на момент отправки. - Каналы — цветные переключатели: SMS, почта, push, Telegram — бот, ВКонтакте — сообщество и аккаунты агента (Telegram, ВКонтакте, MAX). В личном кабинете сообщение появляется всегда. ВКонтакте и аккаунты агента отправляют только «Сейчас»; аккаунт агента пишет клиенту одно сообщение — в мессенджере, где тот писал последним, иначе в первом отмеченном, где найдётся номер.
- Рекламная рассылка не уходит клиентам, отказавшимся от рекламы. Сообщения о долге, блокировке и авариях оставляйте служебными.
- Когда отправить: «Сейчас» — сразу в очередь, не больше 500 получателей; «Запланировать» — дата и время, темп (сообщений в минуту), название, номер обращения для отчёта, чат для предупреждения и за сколько минут предупредить; до 1000 получателей.
flowchart TD
R["Рассылка: получатели + текст"] --> L["Личный кабинет — всегда"]
R --> CH{"Отмеченные каналы"}
CH --> SMS["SMS"] & EM["Почта"] & PU["Push"] & TB["Telegram-бот
(если привязан)"] & VC["VK-сообщество
(если привязан VK)"]
CH --> AG{"Аккаунты агента"}
AG --> LAST["Мессенджер, где клиент
писал последним"]
LAST -- "нет такого" --> FIRST["Первый отмеченный,
где найден номер"]
AG -. "паузы, лимиты, тихие часы,
согласие клиента" .-> LAST
Перед отправкой и перед планированием открывается подтверждение: сколько получателей сейчас, каналы, число SMS на человека, текст, время старта и предупреждение. Отправленное сразу отменить нельзя — об этом сказано в окне.
Запланированные рассылки
Запланированная рассылка не отправляется сразу. Получатели считаются заново в момент старта, сообщения уходят в общую очередь порциями — не больше заданного темпа в минуту — с её тихим часом, стоп-листом и защитой от повторов.
- За указанное число минут до старта в рабочий Telegram-чат уходит предупреждение: сколько получателей и по каким каналам, пример сообщения, когда закончится, и ссылка на отмену.
- Пришёл тихий час — рассылка ждёт его окончания и продолжается.
- Не началась за два часа после назначенного времени — отмечается «Не состоялась», в чат уходит причина.
- После отправки итог (сколько поставлено, доставлено, пропущено и почему) приходит в чат и ответом в указанное обращение.
stateDiagram-v2
[*] --> Ждёт_согласования: создал сотрудник
без права отправки
[*] --> Запланирована: создал маркетолог
или директор
Ждёт_согласования --> Запланирована: «Согласовать»
Запланирована --> Ждёт_согласования: изменил сотрудник
без права отправки
Запланирована --> Отправляется: наступило время
Отправляется --> Завершается: все поставлены в очередь
Завершается --> Отправлена: очередь отправила
Ждёт_согласования --> Не_состоялась: не согласована
за 2 часа после старта
Запланирована --> Не_состоялась: не началась за 2 часа
Ждёт_согласования --> Отменена
Запланирована --> Отменена
Отправляется --> Отменена: «Отменить»
Отправлена --> [*]
Отменена --> [*]
Не_состоялась --> [*]
Права и согласование
| Роль | Создать и изменить | Отправить сразу | Согласовать |
|---|---|---|---|
| Старший сэйлс, старший менеджер | да | нет | нет |
| Маркетолог (группа «Маркетинг»), Директор | да | да | да |
| Суперпользователь | да | да | да |
| Остальные | нет — журнал только для просмотра | нет | нет |
Права проверяет сервер при каждом действии; отказ записывается в аудит. Рассылка, которую создал сотрудник без права отправки, получает статус «Ждёт согласования»: в рабочий чат уходит сообщение, а в момент предупреждения — напоминание. Маркетолог или директор нажимает «Согласовать», проверив текст и получателей в подтверждении. Согласуется ровно та версия, которую он видел: если рассылку успели изменить, согласование не пройдёт.
flowchart TD
NEW["Рассылка → Запланировать"] --> RIGHT{"Может отправлять?
маркетолог, директор"}
RIGHT -->|"да"| SCH["Запланирована"]
RIGHT -->|"нет"| PEND["Ждёт согласования
сообщение в рабочий чат"]
PEND --> REM["В момент предупреждения —
напоминание в чат"]
PEND --> APR{"«Согласовать»
та же версия?"}
APR -->|"да"| SCH
APR -->|"изменили"| RELOAD["Обновить список
и проверить заново"]
PEND --> LATE["Не согласована за 2 часа
после старта → не состоялась"]
SCH --> WARN["Предупреждение в чат"] --> RUN["Старт: получатели
считаются заново"]
Изменить запланированную рассылку
Кнопка «Изменить» есть у рассылки, которая ещё не началась и до старта которой больше двух минут. Открывается то же окно с заполненными получателями, текстом, каналами, временем, темпом, чатом и обращением; «Сейчас» недоступно. По «Сохранить» окно показывает, что изменится — «было → стало», — и только после подтверждения записывает правку.
- Если предупреждение уже ушло, в тот же чат приходит «Рассылка изменена», а новое предупреждение придёт заново — с новым временем и текстом.
- Согласованную рассылку изменил сотрудник без права отправки — её нужно согласовать снова.
- Если рассылку одновременно изменил кто-то другой или она уже началась, сохранение не пройдёт.
sequenceDiagram
actor S as Сотрудник
participant W as Окно «Рассылка»
participant B as Биллинг
participant C as Рабочий чат
S->>W: Изменить
W->>B: поля рассылки
B-->>W: получатели, текст, время, каналы
S->>W: правит и нажимает «Сохранить»
W->>B: пробное сохранение
B-->>W: что изменится: было → стало
S->>W: подтверждает
W->>B: сохранить
B->>B: проверка: не началась, никто не менял
B->>B: аудит «изменена: …»
B-->>C: «Рассылка изменена», если предупреждение уже было
AI-помощник
Кнопка помощника в углу страницы знает группы получателей с числом клиентов, запланированные рассылки, недавние тексты, правила SMS и тихого часа. Заготовки: «Создать рассылку», «Доставка SMS», «Запланированные».
На «Создать рассылку» помощник уточняет, о чём и кому писать, предлагает группу, каналы и время и возвращает черновик. Кнопка «Открыть в окне рассылки» открывает окно с текстом, названием, группой, каналами и временем — дальше решает сотрудник. Сам помощник ничего не отправляет.
Разделение по организациям
Раздел учитывает мульти-организации. Журнал, сводка, CSV и списки для выбора получателей ограничены организациями сотрудника; сообщения без клиента (системные) видны всем. Рассылка не может уйти клиентам чужой компании — ни при отправке сразу, ни при старте запланированной.
- Запланированная рассылка помнит организации автора и при старте не выходит за них.
- Автор с одной организацией — рассылка записывается на неё. Рассылку «по всем организациям» видят и меняют только сотрудники без ограничения по компаниям.
- В режиме «Все организации» на карточке рассылки показано, чья она.
flowchart TD
REQ["Журнал, выбор получателей
или рассылка"] --> MO{"Мульти-орг
включён?"}
MO -->|"нет"| ALL["Без ограничений"]
MO -->|"да"| CUR{"Выбрана организация
в шапке?"}
CUR -->|"да"| ONE["Только клиенты
этой организации"]
CUR -->|"нет"| LIM{"Сотрудник ограничен
компаниями?"}
LIM -->|"нет"| ALLORG["Все организации
+ колонка «Орг.»"]
LIM -->|"да"| PERM["Только доступные
организации"]
PERM --> SH["Рассылки «по всем
организациям» не видны"]
ALLORG --> SYS["Системные сообщения
без клиента видны всем"]
ONE --> SYS
PERM --> SYS
Аудит
Всё пишется в аудит:
- отправка сразу — одна запись на рассылку: кто, кому, какими каналами, сколько поставлено, текст;
- запланированная (
SCHEDULED_MAILING): создана или ждёт согласования, согласована, изменена (что было и что стало), предупреждение и напоминание в чат (отправлено или нет), ожидание конца тихого часа, начата, завершена с итогом, не состоялась с причиной, отменена; - отказ по правам — кто и что пытался сделать.
Адреса
| URL | Назначение |
|---|---|
GET /admin/reports/messages/ | Страница; ?mailing=N — выделить рассылку |
GET /admin/reports/messages_json/ | Таблица: фильтры и страницы |
GET /admin/reports/messages_kpi/ | Сводка |
GET /admin/reports/messages/export/ | CSV текущей выборки |
POST /admin/reports/messages/broadcast/ | Отправить сразу или запланировать (when=later) |
GET /admin/reports/messages/recipients_preview/ | Сколько получателей и кто |
GET /admin/reports/messages/tariffs_list/ | Тарифы с числом клиентов и линейки |
GET /admin/reports/messages/services_list/ | Действующие услуги с числом клиентов и типы услуг |
GET /admin/reports/messages/folders_list/ | Дерево папок |
GET /admin/reports/messages/nas_list/ | NAS |
GET /admin/reports/messages/senders_list/ | Отправители для фильтра |
GET /admin/reports/messages/scheduled/ | Запланированные и недавние рассылки, права сотрудника |
POST /admin/reports/messages/scheduled/<id>/ | action= get · update (dry=1 — только сравнить) · approve · preview · cancel |
Разработка и логи
Раздел для администратора-разработчика. Доступен только пользователям с правами root. Делится на две вкладки.
Отчёты — встроенный markdown-просмотрщик внутренних документов проекта (отчёты по разработке, планы, документация).
Поддерживается сортировка по дате/имени или ручной drag-and-drop, inline-переименование, создание папок, загрузка новых .md-файлов через интерфейс.
Возможности просмотрщика .md
Markdown-просмотрщик поддерживает удобную навигацию и работу с документами:
- Поиск по документам — поле над списком файлов. Ввод фильтрует
список по имени мгновенно; нажатие Enter запускает полнотекстовый поиск
по содержимому всех документов — с подсветкой числа совпадений и сниппетом. Горячая
клавиша
Ctrl+Fставит фокус в поиск. - Кликабельные ссылки на разделы биллинга — пути вида
/admin/...,/lk/...и полныеhttp(s)прямо в тексте документа становятся ссылками (с иконкой) и открываются в новой вкладке. - Переходы между документами — ссылки на другие .md-файлы (отчёт → план → ТЗ) открываются в том же просмотрщике, превращая документы в связанную базу знаний.
- Чек-листы — пункты
- [ ]/- [x]отображаются как ☐ / ☑ (выполненные зачёркнуты). - Вложенные списки — многоуровневые перечни сохраняют отступы.
- Копирование кода — кнопка «копировать» при наведении на блок кода (команды деплоя, SQL).
- Диаграммы Mermaid и комментарии к заголовкам (командное обсуждение прямо в документе).
Логи — просмотр серверных логов прямо из админки.
Доступны каналы:
error.log— общие ошибки приложения;payment.log— платежи (ЮKassa, W1, webhook);radius.log— авторизация и accounting;sorm.log— выгрузка СОРМ;staff.log— действия сотрудников;fiscal.log— фискализация (кассовые чеки 54-ФЗ);celery_worker.log,celery_beat.log— фоновые задачи;gunicorn_access.log,gunicorn_error.log— веб-сервер приложения (Gunicorn);nginx_access.log,nginx_error.log— веб-сервер (nginx);freeradius.log— нативные логи FreeRADIUS.
Всего 13 каналов (список — billing/views/dev_reports.py::LOG_FILES).
Показываются последние 500 строк, есть кнопка обновления. Файлы автоматически ротируются (10 МБ × 5 копий).
Просмотр логов с подсветкой
Страница /admin/reports/dev/logs/ (раздел «Отчёты → Разработка и логи», только root-доступ) показывает 12 каналов журналов слева с текущим размером, справа — содержимое выбранного файла.
- Чтение с конца файла — функция
_tail_log()читает последние 500 строк, двигаясь от конца файла чанками, не загружая его целиком в память. Поэтому даже лог на десятки гигабайт открывается мгновенно (в шапке показано «Показано: 500 / N строк»). Это защищает от переполнения памяти при открытии многогигабайтногоfreeradius.log. - Подсветка по уровням (
colorizeLog) — каждая строка раскрашивается по содержимому: красный дляerror/traceback/exception, оранжевый дляwarning, серый дляdebug, обычный дляinfo. Цвета адаптируются под тёмную тему. - Кнопки: обновить, скачать файл, поделиться ссылкой (
?log=имя), очистить лог / очистить все логи (только суперадмин).
Ротация логов на сервере
Часть логов пишет Django через RotatingFileHandler (error.log,
radius.log, payment.log, sorm.log, staff.log —
maxBytes 10 МБ, backupCount 5, в просмотрщике видны как radius.log.1….5).
Но нативный freeradius.log пишет сам FreeRADIUS (C-side), и Django его
не ротирует. Если FreeRADIUS запущен в trace-режиме (-xx), этот файл растёт на
~5 ГБ/сутки — он однажды вырос до 43 ГБ.
Лечение:
- Уровень детализации FreeRADIUS задаётся переменной
RADIUS_DEBUG_LEVELв.env(читаетсяdocker/freeradius/entrypoint.sh):info(без флага, ~1 ГБ/сутки, только Accept/Reject и блокировки),debug(-x),trace(-xx, не использовать в проде). Если переменная не задана вовсе, контейнер стартует именно сtrace— на новой установке задайтеinfoявно. После правки —docker compose up -d --force-recreate freeradius. /etc/logrotate.d/smit-freeradius— ротация нативного лога (daily, rotate 5, maxsize 100M, compress,copytruncate— обязателен, т.к. FreeRADIUS держит файл открытым,su root root— т.к. каталог volume world-writable). Файл больше не превышает ~600 МБ./etc/cron.daily/smit-radacct-cleanup— удаляет accounting detail-файлыradacct/<NAS>/detail-YYYYMMDDстарше 14 дней (эти данные уже продублированы в таблицеRADIUS_SESSIONS).
Уровень логирования и текущий размер каждого файла задаются и видны на вкладке
«Настройки системы → Логи»; после фикса
freeradius.log держится в районе десятков мегабайт.
Должники
Содержание раздела
Отдельная страница /admin/debtors/ — список клиентов с отрицательным балансом
или активной блокировкой b_negbal. Раздел рассчитан на ежедневную работу
бухгалтерии и операторов поддержки: KPI-блок сверху помогает быстро оценить
ситуацию по компании, а drawer-панель — разобраться с конкретным клиентом без
ухода на полную карточку.
KPI и статистика по периодам
Над таблицей — четыре карточки-показателя: общее количество должников, суммарный долг (в рублях), средний долг и доля должников с активным обещанным платежом. Карточки реагируют на выбранный период: переключатель chips сверху позволяет видеть динамику за сегодня / неделю / месяц / квартал / год. Цифры пересчитываются прямо в браузере без перезагрузки — у каждой карточки есть мини-индикатор изменения относительно предыдущего периода.
Фильтры и чипы
- Период — chip-фильтр (сегодня, неделя, месяц, квартал, год, всё время). Активный чип подсвечивается.
- Категория — отдельные чипы: с обещанным платежом, с контактом и небольшим долгом (кому имеет смысл звонить), заблокированные, без оплат за 60 дней.
- Поиск — по ФИО, договору, телефону, email — мгновенный фильтр без перезагрузки.
- Состояние сохраняется — выбранный период и чип запоминаются в URL, ссылку можно отправить коллеге.
Таблица и сортировка
Колонки: ФИО, договор, тариф, точка подключения, баланс, дней без оплаты, телефон, email, статус. По умолчанию сортировка — по убыванию суммы долга. Клик на заголовок любой колонки переключает порядок (▲▼). Цветовое кодирование: красные строки — заблокированные за неуплату, жёлтые — на грани блокировки, серые — с активным обещанным платежом. В каждой строке справа — кнопка ℹ Подробнее, которая открывает drawer.
Drawer-панель клиента
Клик на ℹ открывает боковую панель справа (overlay поверх таблицы) с краткой сводкой по клиенту — без перехода на полную карточку и потери контекста списка. Внутри панели:
- Шапка: ФИО, договор, тариф, тек. баланс, дата последней оплаты, статус блокировки.
- График: динамика баланса за последние 3 месяца — наглядно видно, когда «ушёл в минус».
- Последние финансовые операции (топ-10): дата, тип (приход/расход), сумма, описание.
- Сообщения: история отправленных SMS/Email/Push с галочками доставки.
- Комментарии операторов: можно добавить новый комментарий прямо из drawer (хранится в
AbonentsComments), история подписана автором и временем. - Лицевые счета: связанные счета с остатками (если у клиента несколько).
- Действия: отправить SMS-напоминание о долге, активировать обещанный платёж, открыть полную карточку.
Массовые действия и экспорт
- Чекбоксы в каждой строке + выбрать все на странице — для bulk-операций над отмеченными должниками.
- Действия над выбранными: массовая отправка SMS/Email, блокировка, прощение долга (создаёт компенсирующий FinOp).
- Экспорт CSV (UTF-8 BOM, разделитель
;) — выгружает текущую выборку с учётом фильтров для сверки с бухгалтерией или загрузки в 1С.
Миграция с других биллингов
Раздел /admin/settings/migration/ — мастер импорта данных клиентов из других
биллинговых систем. Пошаговый wizard с предпросмотром и dry-run.
Импорт из биллинга на Firebird
/admin/settings/migration/cb4/ — специализированный импорт данных
из биллинга на базе Firebird. Учитывает специфику:
- Денежные поля × 10⁷ → нормализуются в × 10¹⁰ (новый
DB_MONEY_KOEF). - MPTT-дерево клиентов с папками сохраняется через
parent_id. - Услуги маппятся через таблицу соответствий (legacy
USLUGA.ID→ новый pk). - RADIUS-сессии и
USERS_RADIUSAUTHмигрируются последним этапом.
Загрузка из CSV
/admin/settings/migration/csv_loading/ — общий импорт клиентов/услуг/платежей
через CSV файлы. Поддерживается:
- Загрузка через UI (drag-and-drop) или путь к файлу на сервере.
- Маппинг колонок CSV ↔ поля модели через интерактивную форму.
- Dry-run перед применением — показывает что будет создано/обновлено/пропущено.
- Отчёт по результатам с подсветкой ошибок построчно.
Авто-импорт CSV (схемы)
/admin/settings/autocsv_upload_config/ — настройка повторяющихся импортов:
оператор сохраняет «схему» (маппинг колонок + способ обработки дубликатов), и потом
загрузка одной кнопкой берёт CSV и применяет схему без повторной настройки. Полезно для
ежемесячных выгрузок из бухгалтерии или экспорта из биллинга мобильного оператора (массовое
зачисление платежей).
FrameworkFormGroupFields — назначение и роль
Содержание раздела
FrameworkFormGroupFields (FFG) — таблица настроек UI для всех админских форм биллинга. Управляет тем, какие поля видны на форме редактирования, как они сгруппированы в вкладки и в каком порядке. По сути это «генеральный настройщик» админки без правки кода.
Что это и зачем
Биллинг — это унаследованный движок с десятками моделей, у каждой по 30-100 полей. Большая часть из них — технический шум для оператора. Решения:
- Без FFG — generic-форма со всеми 100 полями. Оператор не знает что важно;
- С FFG — оператор видит только нужные 10 полей, разбитые по 3 вкладкам с понятными русскими названиями. Скрытые поля остаются в БД и работают.
Это безопасный способ упростить UI без боязни сломать логику.
Ключевые поля
| Поле | Назначение |
|---|---|
TABLE_NAME | Имя модели (USERS, ABONENTS, USLUGA — UPPERCASE) |
FIELD_NAME | Имя поля Django-модели (то же что в Model._meta.fields) |
INTERFACE_ID | FK на интерфейс (NULL = дефолт для всех ролей) |
GROUP_ID | Номер вкладки/группы в форме (1 = основные, 2 = доп., и т.д.) |
SHOW_ON_FORM | 0 = скрыто, 1 = видно (главное поле управления) |
VERBOSE_NAME | Русское имя поля для UI (заменяет автоматическое из модели) |
ORDER | Порядок в группе (для drag-and-drop) |
Как работает
- При открытии формы редактирования модели (например
/admin/Abonents/<id>/) view вызываетsafe_globals.get_groups_from_model('ABONENTS'); - Функция читает все записи FFG где
TABLE_NAME='ABONENTS'иINTERFACE_ID IS NULL; - Записи группируются по
GROUP_ID→ каждая группа = одна вкладка формы; - Внутри группы поля сортируются по
ORDER, скрытые (SHOW_ON_FORM=0) исключаются; - Применяются
VERBOSE_NAMEдля русификации лейблов; - Кеш Django сохраняет результат — после изменения FFG нужен
cache.clear().
Опасные моменты
INTERFACE_ID=NULL— это дефолт, не «отсутствие настройки». Если полеSHOW_ON_FORM=0дляINTERFACE_ID=NULL, оно скрыто во всех интерфейсах. Чтобы показать только в одном — нужны 2 записи:NULLс 0 иINTERFACE_ID=1с 1;- Дубликаты Python dict — баг был с
'Users'ключом, появлявшимся дважды в_EXCLUDE_FORMS. Второе значение тихо перезаписывало первое. Перед добавлением нового ключа всегдаgrep; - UPPERCASE имя таблицы — критично для PostgreSQL.
'abonents' != 'ABONENTS'. ИспользуйтеModel._meta.db_table.upper()чтобы не ошибиться; - 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 | |
| Usluga | SERVICE_TYPE (87% услуг имели его NULL) | |
| Users группа 32 | 5 «дополнительных»: DHCP_MASK, DHCP_ROUTE_IP, ASK_PASSWORD_LK, DEACTIVATE_STRING, ACTIVATE_STRING |
/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()"
Настройки интерфейса (формы)
/admin/settings/interface_settings/ — конфигурация полей форм для каждой
модели. Через FrameworkFormGroupFields можно:
- Скрывать/показывать поля без правки кода (флаг
show_on_form). - Менять порядок полей (drag-and-drop).
- Группировать поля по вкладкам (
FrameworkFormGroup). - Делать поля required / readonly через UI.
Пример использования: на форме Users скрыты 17 технических полей (IPV6, MASK,
EXT_ID, IS_TEMPLATE, и т.д.) — оставлены только реально используемые. Изменения применяются
для всех операторов одновременно после cache.clear().
Игры (Маркетинг)
Модуль «Игры» — часть раздела «Маркетинг» — геймификация для удержания и вовлечения клиентов: 8 встроенных аркад, турнирная таблица и розыгрыш «Игра месяца» с бесплатным месяцем интернета победителю.
Список игр: Pacman, Tetris, Snake, Minesweeper, 2048, Breakout, Flappy Bird, Helicopter. Доступны в админке, мобильном приложении и ЛК.
Архитектура модуля
Единый источник настроек (GAMES_* в SystemSettings)
питает три точки потребления: профиль-меню админки, карточку клиента,
мобильное приложение. Данные результатов хранятся в GameScore,
история победителей — в GameMonthWinner.
| Компонент | Назначение |
|---|---|
billing/services/games_registry.py | Единый реестр 8 игр (admin + mobile) |
billing/models/game_score.py | GameScore (результаты) + GameMonthWinner (победители) |
billing/services/game_rewards.py | Определение лидера + безопасное начисление бонуса |
billing/tasks/game_rewards.py | Celery-подведение итогов 1-го числа |
settings/_marketing_tab.html | UI настроек (вкладка «Маркетинг») |
abonents_form/marketing_games.html | Вкладка результатов в карточке клиента |
Настройки модуля «Маркетинг»
Настройки находятся в /admin/settings/system/ → вкладка
«Маркетинг» (рядом с Брендинг / Безопасность / … / Профиль).
Сейчас Маркетинг = Игры; в будущем туда добавятся другие маркетинговые механики
(акции, реферальные программы, бонусы за активность).
Страница содержит 5 секций:
- Общее — Включить игры (глобальный выключатель) + Пункт «Игры» в меню профиля.
- Мобильное приложение — Разрешить игры клиентам + чекбоксы 8 доступных игр.
- Игра месяца — розыгрыш, игра, дни бесплатного интернета, минимальный счёт, режим начисления (авто/ручной).
- Условия участия — оценки в сторах / на картах (см. §5.4).
- Текущий лидер и история — live-превью лидера + список победителей.
«Игра месяца» — бесплатный интернет
Главная механика. Клиент с максимальным счётом за календарный месяц получает бесплатный месяц интернета — кредит-финоперацию на сумму месячной стоимости его тарифа.
Настройки розыгрыша:
- Игра розыгрыша — какая из 8 игр участвует в текущем месяце.
- Дней бесплатного интернета — 30 по умолчанию.
- Мин. счёт для участия — отсекает «нулевые» результаты.
- Авто-начисление / ручное — по умолчанию оператор подтверждает победителя кнопкой (защита от ошибок).
- Исключать сотрудников — результаты из админки в розыгрыше не участвуют.
- Уведомлять победителя — MsgStack + Push.
Подведение итогов — Celery-задача game_of_month_finalize
запускается 1-го числа в 02:00, определяет лидера прошлого месяца и
(в авто-режиме) начисляет приз; в ручном — ставит «ожидает подтверждения»
+ Telegram-алерт оператору.
Ручное начисление. В блоке «Текущий лидер» кнопка «Начислить победителю вручную» открывает окно подтверждения с данными победителя — игра, ФИО, договор, набранные очки и сумма приза. Если для турнира заданы условия участия (см. §5.4), оператор отмечает их выполнение прямо в окне; кнопка «Начислить» становится доступной только после подтверждения всех условий.
FinanceOperations (как онлайн-платёж), не правит баланс напрямую —
EBS-синхронизация его не затирает. Сотрудники в розыгрыше не участвуют.
Заблокированный/удалённый победитель пропускается + алерт оператору.
При равном максимуме побеждает достигший раньше (tie-break).
Условия участия в турнире
Для участия в турнире можно требовать действия, повышающие рейтинг компании:
| Условие | Настройка |
|---|---|
| ⭐ Оценка приложения (Google Play / App Store) | GAMES_OF_MONTH_REQUIRE_APP_RATING |
| 🗺 Оценка на Яндекс Картах | GAMES_OF_MONTH_REQUIRE_YANDEX_MAPS |
| 📍 Оценка на 2ГИС | GAMES_OF_MONTH_REQUIRE_2GIS |
Включённые условия показываются клиенту в мобильном приложении как требования перед участием в розыгрыше. По умолчанию все выключены.
Ссылки на оценку
Под переключателями условий задаются ссылки, по которым клиент оставляет оценку. Источники ссылок разделены:
- Ссылка на приложение — берётся из раздела
ЛК → Мобильное (
Google PlayиApp Store), чтобы не дублировать. В настройках Маркетинга она показана только для справки; если ссылки в ЛК не заполнены — выводится подсказка с переходом в нужный раздел. - Яндекс Карты и 2ГИС — задаются прямо
в настройках Маркетинга (
GAMES_OF_MONTH_YANDEX_URL,GAMES_OF_MONTH_2GIS_URL).
/mobile-api/v1/game/config и показывает их клиенту. Факт того, что
клиент действительно оставил оценку, подтверждает оператор вручную в окне
начисления приза — автоматической проверки отзыва на стороне магазинов и карт нет.
Вкладка «Маркетинг» в карточке клиента
В карточке клиента появляется вкладка «Маркетинг» — только если модуль игр включён и у клиента есть лицевой счёт.
Содержимое вкладки:
- Карточка «Игра месяца» — результат клиента vs лидер, прогресс-бар, бейдж «Лидер!» если он первый.
- Лучшие результаты по играм — по каждой игре, в которую играл клиент: лучший счёт + глобальный ранг (🏆 за 1 место, 🥈🥉 за 2–3).
- Empty-state — если клиент ещё не играл.
Оператор видит игровую активность клиента прямо в его профиле, не переходя в раздел игр.
Профиль-меню и мобильное приложение
Профиль-меню (справа у аватара) — пункт «Игры»
появляется, когда включены 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 (раздел ЛК → Мобильное).