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

Чего здесь нет

Атрибуция рекламы и трафик по узлам — отдельные отсутствия, зафиксированные в роадмапе. Эти экраны показывают, что продано и дошли ли деньги, а не откуда пришёл клиент и какой узел его нёс.

Смежные страницы

Операции администратора

Дашборд, его фиксированное окно и инциденты с расхождениями рядом.

Заказы и платежи

Состояния, через которые проходит заказ, и то, что его закрывает.

Финансовые инварианты

Почему деньги провайдера, кредит кошелька и возвраты — это три цифры, а не одна.

Кошелёк и реестр

Откуда берётся баланс и почему его трата не является новой выручкой.