SaaS usage reporting (#2690)

* Add saas:usage_report rake task and extract Subscription.paid scope

Add a rake task to generate a CSV usage report with per-account data:
Queenbee ID, sign up date, paid date, card count, storage used, and
last active date.

Extract the paid subscriptions query from Admin::StatsController into
an Account::Subscription.paid scope so both the controller and the
new rake task can share it.

* Add comped and account name columns to usage report

* Update saas/lib/tasks/fizzy/usage_report.rake

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Preload storage_total to avoid N+1 in usage report

* Batch last_active_at queries to avoid per-account aggregates

* Add tests for Account::Subscription.paid scope

* Update saas/app/models/account/subscription.rb

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Aggregate paid dates in SQL instead of loading all subscriptions

* Fix paid scope to derive plan keys from Plan.all instead of PLANS hash

* Move paid dates and comped lookups into per-batch queries

* Materialize batch IDs to avoid cross-database subquery

SaasRecord models live on a separate database (fizzy_saas) that doesn't
have the accounts table. Using batch.select(:id) generated a subquery
that ran on the saas database, causing a table-not-found error. Using
pluck(:id) materializes the IDs into an array instead.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Mike Dalessio
2026-03-11 10:11:13 -04:00
committed by GitHub
parent 801da17cfa
commit ab5283441d
6 changed files with 100 additions and 11 deletions
+3 -11
View File
@@ -6,9 +6,9 @@ class Admin::StatsController < AdminController
@accounts_last_7_days = Account.where(created_at: 7.days.ago..).count
@accounts_last_24_hours = Account.where(created_at: 24.hours.ago..).count
@paid_accounts_total = paid_subscriptions.distinct.count(:account_id)
@paid_accounts_last_7_days = paid_subscriptions.where(created_at: 7.days.ago..).distinct.count(:account_id)
@paid_accounts_last_24_hours = paid_subscriptions.where(created_at: 24.hours.ago..).distinct.count(:account_id)
@paid_accounts_total = Account::Subscription.paid.distinct.count(:account_id)
@paid_accounts_last_7_days = Account::Subscription.paid.where(created_at: 7.days.ago..).distinct.count(:account_id)
@paid_accounts_last_24_hours = Account::Subscription.paid.where(created_at: 24.hours.ago..).distinct.count(:account_id)
@identities_total = Identity.count
@identities_last_7_days = Identity.where(created_at: 7.days.ago..).count
@@ -21,12 +21,4 @@ class Admin::StatsController < AdminController
@recent_accounts = Account.order(created_at: :desc).limit(10)
end
private
def paid_subscriptions
Account::Subscription
.where(status: %w[active trialing past_due])
.where(plan_key: %w[monthly_v1 monthly_extra_storage_v1])
.where.not(account_id: Account::BillingWaiver.select(:account_id))
end
end