Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler serves analytics API endpoints.
All responses are tenant-scoped via RLS (postgres.TxTenant). Money is cents (int64), rates are fractions in [0, 1] (float64), and periods are closed-open windows [start, end) in UTC.
func NewHandler ¶
type MRRMovementPoint ¶
type MRRMovementPoint struct {
Date string `json:"date"`
New int64 `json:"new"`
Expansion int64 `json:"expansion"`
Contraction int64 `json:"contraction"`
Churned int64 `json:"churned"`
Net int64 `json:"net"`
}
MRRMovementPoint is one bucket (day or month) of MRR change breakdown.
type MRRMovementTotals ¶
type MRRMovementTotals struct {
New int64 `json:"new"`
Expansion int64 `json:"expansion"`
Contraction int64 `json:"contraction"`
Churned int64 `json:"churned"`
Net int64 `json:"net"`
}
MRRMovementTotals breaks MRR change into its standard SaaS components. Net = New + Expansion - Contraction - Churned.
type OverviewResponse ¶
type OverviewResponse struct {
Period string `json:"period"`
// Currency is the tenant's default currency. All money figures below are
// scoped to it — invoices in other currencies are excluded rather than
// summed into a meaningless mixed-currency total. The dashboard renders
// every amount labeled with this code.
Currency string `json:"currency"`
// Money (cents)
MRR int64 `json:"mrr"`
MRRPrev int64 `json:"mrr_prev"`
ARR int64 `json:"arr"`
ARRPrev int64 `json:"arr_prev"`
Revenue int64 `json:"revenue"`
RevenuePrev int64 `json:"revenue_prev"`
OutstandingAR int64 `json:"outstanding_ar"`
AvgInvoiceValue int64 `json:"avg_invoice_value"`
CreditBalance int64 `json:"credit_balance_total"`
// Counts
ActiveCustomers int `json:"active_customers"`
NewCustomers int `json:"new_customers"`
ActiveSubs int `json:"active_subscriptions"`
TrialingSubs int `json:"trialing_subscriptions"`
PaidInvoices int `json:"paid_invoices"`
FailedPayments int `json:"failed_payments"`
OpenInvoices int `json:"open_invoices"`
DunningActive int `json:"dunning_active"`
RefundsAttention int `json:"refunds_needing_attention"`
UsageEvents int64 `json:"usage_events"`
// Rates (0..1)
LogoChurnRate float64 `json:"logo_churn_rate"`
RevenueChurnRate float64 `json:"revenue_churn_rate"`
NRR float64 `json:"nrr"`
DunningRecoveryRate float64 `json:"dunning_recovery_rate"`
// MRR movement within the period (cents; churned/contraction are positive
// magnitudes — the UI subtracts them visually).
MRRMovement MRRMovementTotals `json:"mrr_movement"`
}
OverviewResponse aggregates the headline billing metrics for a tenant over a requested period, plus matching values for the prior period so the UI can render period-over-period deltas without a second round-trip.
type Period ¶
type Period struct {
Key string
Start time.Time
End time.Time
PrevStart time.Time
PrevEnd time.Time
Trunc string // "day" | "month"
}
Period models the closed-open comparison window [Start, End) against a matching prior window [PrevStart, PrevEnd) of equal length.
type RevenueDataPoint ¶
type RevenueDataPoint struct {
Date string `json:"date"`
RevenueCents int64 `json:"revenue_cents"`
InvoiceCount int `json:"invoice_count"`
}
RevenueDataPoint is one bucket of paid-invoice revenue.
type TopMeter ¶
type TopMeter struct {
MeterID string `json:"meter_id"`
MeterName string `json:"meter_name"`
Key string `json:"key"`
Events int64 `json:"events"`
Quantity decimal.Decimal `json:"quantity"`
}
TopMeter is one row of the top-N usage-by-meter breakdown.
type UsagePoint ¶
type UsagePoint struct {
Date string `json:"date"`
Events int64 `json:"events"`
Quantity decimal.Decimal `json:"quantity"`
}
UsagePoint is one bucket of aggregated usage events.
Quantity is decimal, not int64: usage_events.quantity is NUMERIC(38,12) (ADR-005/ADR-045), so SUM() comes back as a decimal string. Scanning it into an int64 made every one of these three queries fail at the driver — this endpoint returned 500 for any tenant that had ingested a single event. It wire-encodes as a string for the same reason Aggregate.TotalUnits does: truncating to an integer would silently lose fractional usage.