Conversation
…queries) Bug fixes: - Fix calculate_new_mrr_in_period using overly permissive status filter (where.not excluded+churned let through incomplete/unpaid; now uses where(status: 'active') to match MrrCalculator logic) - Fix calculate_new_subscribers inconsistency (wasn't filtering excluded statuses; now delegates to calculate_new_subscribers_in_period) - Move EXCLUDED_STATUSES/CHURNED_STATUSES to module level so MrrCalculator can reference them as Profitable::EXCLUDED_STATUSES - Use shared Profitable::EXCLUDED_STATUSES constant in MrrCalculator instead of hardcoded array DRY consolidation: - calculate_churn delegates to calculate_churn_rate_for_period - calculate_churned_customers delegates to calculate_churned_subscribers_in_period - calculate_churned_mrr delegates to calculate_churned_mrr_in_period - calculate_new_mrr delegates to calculate_new_mrr_in_period - calculate_new_subscribers delegates to calculate_new_subscribers_in_period - Add subscriptions_with_processor helper to avoid repeating .includes(:customer).select(...).joins(:customer) Dashboard performance optimization: - Batch monthly_summary: 72 queries → 5 (bulk load all subscription data for full 12-month range, group by month in Ruby) - Batch daily_summary: 60 queries → 2 (same pattern for 30 days) - Add period_data method: computes all period metrics in one pass, reusing intermediate values (9 → 6 queries per period) - Move all computation from view to controller (precompute as instance variables, eliminating duplicate mrr_growth_rate calls) - Verified on LicenseSeat: 38 queries, 2 cached, 71ms total New features: - monthly_summary(months:) and daily_summary(days:) public methods - period_data(in_the_last:) public method returning hash of NumericResult values (new_customers, churned_customers, churn, new_mrr, churned_mrr, mrr_growth, revenue) Test sync & coverage: - Full sync of test_helper.rb Profitable module with production code - 14 new tests (monthly_summary, daily_summary, period_data, new_subscribers filtering) - 249 tests, 382 assertions, 0 failures, 94.64% coverage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Owner
Author
New Features: Monthly & Daily Summary TablesThis PR also adds two new dashboard sections and their corresponding public API methods: Monthly Summary (last 12 months)
Daily Summary (last 30 days)
Dashboard UIBoth summaries render as tables in the dashboard view with:
These are the "Monthly summary" and "Daily summary" sections visible between the top metric cards and the period breakdowns. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Full review of the profitable gem against the Pay gem v11 source and LicenseSeat production schema. Fixes bugs, consolidates duplicated logic, and reduces dashboard query count from ~176 to 38.
Bug Fixes
calculate_new_mrr_in_periodtoo permissive: Was usingwhere.not(status: EXCLUDED_STATUSES + CHURNED_STATUSES)which let throughincomplete,incomplete_expired, andunpaidsubscriptions. Now useswhere(status: 'active')to match MrrCalculator's logic — only active subscriptions contribute MRR.calculate_new_subscribersinconsistent filtering: The public method wasn't filtering excluded statuses (trialing,paused), but the_in_periodversion was. Now delegates tocalculate_new_subscribers_in_periodfor consistent behavior.EXCLUDED_STATUSESandCHURNED_STATUSESwere defined insideclass << self, making them unreachable asProfitable::EXCLUDED_STATUSES. Moved to module level.['trialing', 'paused']with the sharedProfitable::EXCLUDED_STATUSESconstant.DRY Consolidation
Five period-based methods now delegate to their
_in_periodcounterparts instead of duplicating the query logic:calculate_churn→calculate_churn_rate_for_periodcalculate_churned_customers→calculate_churned_subscribers_in_periodcalculate_churned_mrr→calculate_churned_mrr_in_periodcalculate_new_mrr→calculate_new_mrr_in_periodcalculate_new_subscribers→calculate_new_subscribers_in_periodAdded
subscriptions_with_processorhelper to eliminate repeated.includes(:customer).select('...customer_processor').joins(:customer)chains.Dashboard Performance (~176 → 38 queries)
period_data)How:
monthly_summary: 5 bulk queries load all subscription data for the full 12-month range, then groups by month in Ruby. No DB-specific SQL needed.daily_summary: Same pattern — 2 bulk queries for the full 30-day range.period_datamethod: Computes all 7 period metrics in one pass, reusing intermediate values (e.g.,churned_countused for both the churned customers display and churn rate calculation;new_mrr/churned_mrrused for display andmrr_growth).Profitable.*calls from the view into the controller as instance variables.Verified on LicenseSeat production:
New Public API
Profitable.period_data(in_the_last:)— returns a hash ofNumericResultvalues:new_customers,churned_customers,churn,new_mrr,churned_mrr,mrr_growth,revenueTest plan
bundle exec rake test— 249 tests, 382 assertions, 0 failures, 0 errorsdiffbetweenlib/profitable.rbandtest/test_helper.rbshows only a comment block difference🤖 Generated with Claude Code