/admin/reports отвечает на вопросы, на которые не может ответить дашборд: как
прошёл квартал, какой тариф действительно продаётся, какая доля денег — это
продления, а не новые продажи, и превращаются ли триалы во что-нибудь.
Тридцатидневное окно дашборда остаётся ровно там, где было. Движущееся окно
нельзя сравнить между двумя визитами — а это то свойство, которое нужно
операционному обзору, и причина, по которой окно изначально зафиксировали. Здесь
окно двигается.
Три правила, определяющие цифры
Период ключуется на моменте оплаты заказа, и этот момент никогда не сдвигается.orders.paid_at записывается в первый раз, когда заказ переходит в оплаченное
состояние, и больше никогда. До появления этой колонки отчётность ключевалась на
updated_at — и запись возврата в марте тихо переносила февральскую продажу в
март, а один и тот же отчёт по одному и тому же закрытому периоду возвращал разные
числа. Интеграционный тест теперь записывает возврат в закрытый период и
утверждает, что цифры не меняются.
Деньги провайдера и кредит кошелька никогда не складываются. Это отдельные
колонки в каждой таблице экрана. Баланс кошелька уже был посчитан выручкой в
момент пополнения, поэтому сложение считает его дважды — ровно та ошибка, к
которой подталкивает единая цифра «выручка».
Возвраты отражаются в день их выдачи, а не против продажи, которую они
отменяют. «Сколько мы вернули в марте» — вопрос, по которому оператор может
действовать; «что мы продали в феврале и потом вернули» задним числом меняет уже
закрытый месяц.
Что показывает экран
Конверсия триалов — когортная мера, и экран говорит об этом рядом с
цифрой. Знаменатель — триалы, взятые внутри периода; числитель считает
конверсии в любой более поздний момент. Поэтому период, заканчивающийся
сегодня, читается низко: у вчерашнего триала был один день на конверсию. Это
свойство вопроса, а не дефект измерения.
Период
Четыре пресета покрывают то, о чём спрашивают на практике — 7, 30, 90 и 365 дней, — а два поля даты принимают что угодно ещё. Период длиннее двух лет отклоняется с названным пределом, а не превращается в запрос, работающий до таймаута на прокси. Дни группируются по вашему часовому поясу, взятому из браузера и переданному в API. «Продажи третьего» означает третье оператора; отчёт, сгруппированный в UTC, кладёт вечернюю продажу на несколько часов восточнее не в тот день, и интеграционный тест утверждает, что одна и та же продажа попадает на 2-е в UTC и на 3-е в Москве. Разрез по тарифам считывает код, версию и период оплаты с той версии тарифа, на которую действительно ссылался заказ, а не с текущего состояния тарифа. Ровно для этого версии тарифов и существуют: изменение цены на прошлой неделе не должно переоценивать продажу месячной давности.Здоровье платежей
Вторая вкладка отвечает на другой вопрос за тот же период: не что продано, а дошли ли деньги. При четырёх встроенных адаптерах эквайер, начавший отклонять карты, иначе виден как обращения в поддержку и растущая очередь зависших платежей, а не как сдвинувшаяся цифра. Две доли, потому что ломаются две разные вещи.
Уход клиента из оплаты и отказ шлюза по карте — не одно и то же событие, и только
второе относится к эквайеру. Схлопывание их в одну «успешность» даёт цифру,
падающую каждый раз, когда кампания доходит до людей, которые и не собирались
покупать, — так настоящая проблема с расчётами теряется в шуме.
Намерения, всё ещё в процессе, не входят ни в один знаменатель и считаются
отдельно. Созданное пять минут назад не провалилось, а его включение сделало бы
текущий час катастрофическим ровно тогда, когда на него смотрят. Возраст
старейшего открытого намерения показан отдельно — это очередь зависших платежей,
выраженная числом.
Доля отсутствует, а не равна нулю, когда ничего не дошло до решения. «Никто
не платил» и «все провалились» — противоположные факты, и экран, показывающий 0%
для тихого адаптера, отправил бы человека разбираться с тем, которым никто не
пользовался. Размер выборки идёт рядом с каждой долей по той же причине: 50% из
четырёх — совпадение, 50% из четырёхсот — инцидент.
Время до расчёта измеряется по событию расчёта, а не по
updated_at
намерения. Более поздняя сверка трогает строку, и ключевание на ней раздувало бы
задержку каждого платежа, на который кто-то посмотрел дважды.
Приём вебхуков
Расчёт и уведомление ломаются независимо: шлюз, который взял деньги и не смог доставить колбэк, выглядит здоровым со стороны клиента и создаёт зависшую очередь здесь. Таблица показывает, что пришло, что обработано, что упало и что отклонено по подписи — ненулевое число отклонений означает либо ротацию секрета, либо чей-то перебор по эндпойнту, и то и другое стоит видеть.Выгрузка
GET /v1/panel/reports/sales/export принимает те же четыре параметра и
возвращает один CSV с колонкой section — operation, plan, day, refund,
trial, — а не четыре файла, потому что разрезы — это четыре взгляда на один
период.
Суммы выгружаются в минорных единицах, без масштабирования, ровно так, как
хранятся. Деление на сто неверно для любой валюты без дробной части, а сделать
его читатель может сам, зная свою валюту.
Права
Оба маршрута требуютfinance.read — то же право, что и экраны заказов и
платежей. Отчёт о продажах — это деньги, и установка, решившая, кто может читать
деньги, уже приняла это решение; отдельное право позволило бы двум решениям
разойтись относительно одних и тех же цифр.
Чего здесь нет
Атрибуция рекламы и трафик по узлам — отдельные отсутствия, зафиксированные в роадмапе. Эти экраны показывают, что продано и дошли ли деньги, а не откуда пришёл клиент и какой узел его нёс.Смежные страницы
Операции администратора
Дашборд, его фиксированное окно и инциденты с расхождениями рядом.
Заказы и платежи
Состояния, через которые проходит заказ, и то, что его закрывает.
Финансовые инварианты
Почему деньги провайдера, кредит кошелька и возвраты — это три цифры, а не одна.
Кошелёк и реестр
Откуда берётся баланс и почему его трата не является новой выручкой.