Analytics & Reporting
Tashil's reporting comes in two layers, each for a different question:
AnalyticsService— live aggregate metrics ("what is MRR right now?"). Computed withtpetry/laravel-query-expressions, so they're cross-database and batched, with no raw SQL.- The event log + snapshots + usage logs — append-only history for "what was true on date X?". Used for audit, point-in-time queries, and replay.
Layer 1 — live aggregates
The entry point is Tashil::analytics(). Everything reads directly from the live tables — there are
no materialized report tables to keep in sync.
Subscription metrics
| Method | Returns |
|---|---|
totalSubscriptionCount() | All subscriptions, every status. |
activeSubscriptionCount() | Active + OnTrial. |
subscriptionCountByStatus() | One count per status. |
subscribersByPackage() | Active + trial counts grouped by plan. |
trialConversionRate() | Percentage of trials that reached Active. |
subscriptionGrowth(int $months = 12) | Monthly new-subscription counts. |
Revenue & invoice metrics
| Method | Returns |
|---|---|
calculateMRR() | All active + trial subs, normalized to monthly. |
averageRevenuePerUser() | MRR ÷ active count. |
totalRevenue() | Sum of paid invoice amounts. |
revenueByPeriod(int $months = 12) | Monthly revenue trend from paid invoices. |
revenueByPackage() | Revenue grouped by plan. |
pendingInvoiceCount() / overdueInvoiceCount() | Invoice health. |
Churn
| Method | Returns |
|---|---|
churnRate(int $days = 30) | Cancelled-in-window ÷ total. |
churnTrend(int $months = 12, int $windowDays = 30) | Batched — 2 queries total, computed in PHP. |
One-shot dashboards
dashboardSummary() and packageAnalytics() consolidate the metrics above into one or two batched
queries — use these in admin views to avoid N+1:
use Foysal50x\Tashil\Facades\Tashil;
$summary = Tashil::analytics()->dashboardSummary();
$byPlan = Tashil::analytics()->packageAnalytics();
// Individual metrics are available too:
$mrr = Tashil::analytics()->calculateMRR();
$churn = Tashil::analytics()->churnRate(days: 30);
$conv = Tashil::analytics()->trialConversionRate();
$trend = Tashil::analytics()->revenueByPeriod(months: 12);Cached automatically
AnalyticsService reads through the cache decorator, so repeated dashboard loads are cheap. Writes
that affect an aggregate invalidate the relevant keys. Turn caching off with
TASHIL_CACHE_ENABLED=false — see Configuration.
Layer 2 — audit, replay, and point-in-time
When aggregates aren't enough — "what plan did this customer have on March 3rd, and how much had they used?" — the append-only tables are your source of truth.
The event log
Every transition writes a row to tashil_subscription_events (see Events). Read it
per subscription:
$subscription->events()->get(); // ordered by sequence_num
$subscription->events()->ofType('subscription.switched')->get();
$subscription->events()->upTo(now()->subWeek())->get();Feature snapshots
Snapshots are immutable, so you can ask what features were in effect at any moment:
app(\Foysal50x\Tashil\Contracts\SubscriptionFeatureRepositoryInterface::class)
->asOf($subscription, now()->subDays(30));Usage logs
Every increment, reset, report, and adjust is captured with previous_usage and new_usage, so you
can reconstruct any counter at any moment, build histograms, or drive a usage-based billing system —
without modifying Tashil. The trait's dailyUsageFor() is built on exactly this:
$user->dailyUsageFor('api-calls', days: 30);Composing a point-in-time picture
To answer "what plan, with what features, with what usage did subscription S have at moment T?":
- Plan — walk
subscription_eventsfor S bysequence_num; take the lastsubscription.createdorsubscription.switchedwithoccurred_at <= T. The payload carries the package id. - Features —
SubscriptionFeatureRepository::asOf($sub, $T). - Usage — sum the
usage_logsdeltas wherecreated_at <= Tfor each feature.
What Tashil does not report
These are intentionally out of scope — the event log and invoice rows are sufficient inputs to build them in a downstream tool:
- Card-level revenue, chargebacks, and refunds executed by the gateway.
- The MRR movement waterfall (new / expansion / contraction / churn / reactivation).
- Cohort retention curves.
- Multi-currency normalization with an FX rate table.
- Coupon redemption attribution.