whisper-money/app/Console/Commands
Víctor Falcón 57f8c93e28
feat(stats): weekly paywall stuck-cohort report to Discord (#563)
## What

Adds a weekly `stats:stuck-cohort-report` command that measures the
percentage of users "stuck" on the paywall, compares it to the previous
week, and posts the result to Discord.

## Definition of "stuck"

A user with a non-deleted banking connection and **no** valid
subscription:

- has a banking connection (`bankingConnections()` — SoftDeletes already
excludes deleted)
- has no subscription with `stripe_status` in `active` / `trialing` /
`past_due`, nor a `canceled` one still in grace (`ends_at > now()`)

Built with Eloquent (`whereHas` / `whereDoesntHave`), no raw SQL.

## Metric

`stuck_pct = stuck / onboarded users` (`onboarded_at` not null — the
population that reached the paywall). The Discord message also shows the
raw counts, not just the percentage.

## Week-over-week

Each run snapshots `date`, `onboarded_count`, `stuck_count`, `stuck_pct`
into a new `stuck_cohort_snapshots` table (`updateOrCreate` per day,
idempotent), then compares against the most recent prior snapshot to
report the delta in percentage points and stuck count. First run has no
prior → reports current only.

## Discord

Reuses the `SendAiCohortReportCommand` pattern: posts an embed via
`DiscordWebhook` to `config('services.discord.ai_cohort_webhook_url')`
(fallback `webhook_url`). No direct `env()`.

## Schedule


`Schedule::command('stats:stuck-cohort-report')->weekly()->mondays()->at('09:00')->timezone('Europe/Madrid')`
in `routes/console.php`.

## Tests

7 tests (percentage calc across subscription statuses incl. canceled
in/out of grace, soft-deleted connection, non-onboarded excluded from
denominator, snapshot persistence, delta vs previous, single upsert per
day, Discord POST asserted via `Http::fake`). `pint` clean.
2026-06-19 14:12:51 +00:00
..
Concerns refactor(real-estate): remove Pennant gating (#308) 2026-04-20 13:31:49 +01:00
AgentDatabaseCommand.php feat(console): add agent:db command for querying local and prod DB (#522) 2026-06-12 18:35:14 +02:00
ApplyRealEstateRevaluationCommand.php fix(real-estate): compound annual revaluation monthly (#337) 2026-04-27 07:35:51 +01:00
BackfillAccountIbans.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
BackfillUserCurrencyCode.php Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
CancelFreeEnableBankingConnectionsCommand.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
CategorizeBackfillCommand.php AI auto-categorization: open to pro + consent, nudge free users (#561) 2026-06-19 14:08:40 +00:00
CheckBankLogosCommand.php Add weekly bank logo audit command (#211) 2026-03-06 11:05:13 +00:00
DeleteUserCommand.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
DisconnectBankingConnectionsCommand.php feat(banking): add command to disconnect connections by id (#497) 2026-06-06 11:16:01 +02:00
E2eBankingFixtureCommand.php test(open-banking): e2e coverage for Enable Banking connection flows (#509) 2026-06-09 11:58:50 +02:00
EnsureLaunchCouponsCommand.php feat(leads): cohort-based launch invitations with per-user Stripe coupons (#333) 2026-04-30 15:10:28 +01:00
FeatureDisableCommand.php Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
FeatureEnableCommand.php Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
GenerateBudgetPeriods.php fix: Budget period not found on last day of period (#91) 2026-02-01 11:33:10 +01:00
GenerateLandingAuthLinkCommand.php feat(landing): add signed auth links (#312) 2026-04-21 08:28:59 +01:00
GenerateMonthlyLoanBalances.php feat(accounts): add loan amortization projections for loan accounts (#246) 2026-03-26 15:06:09 +01:00
GenerateStripePromotionCodesCommand.php feat(stripe): add promo code generator (#311) 2026-04-20 18:15:28 +01:00
ResendLeadVerificationEmailsCommand.php feat: resend verification emails to unverified leads (#287) 2026-04-15 09:13:27 +01:00
ResendSyncCommand.php feat: Sync new users to Resend contacts (#85) 2026-01-28 21:25:58 +01:00
ResendSyncLeadsCommand.php perf(resend): default sync-leads to last 24h window (#354) 2026-05-05 09:57:25 +01:00
ResetDemoAccountCommand.php fix(budgets): remove Custom period type to fix duplicate-key crash (#355) 2026-05-05 16:07:16 +02:00
ResetUserCategories.php Refactor: Optimize category management in CreateDefaultCategories and ResetUserCategories 2025-12-01 15:37:53 +01:00
RetryLeadFailedJobsCommand.php feat: selective retry of failed lead email jobs (#286) 2026-04-15 08:00:29 +01:00
ReviewIntegrationRequestsCommand.php feat(integration-requests): markdown comments and in-progress status (#553) 2026-06-18 08:36:43 +00:00
SendAiCohortReportCommand.php feat(ai): add weekly AI-suggestions cohort report (#530) 2026-06-13 23:23:34 +02:00
SendDailyStatsReportCommand.php feat(discord): enrich Stripe event messages and dedupe deliveries (#460) 2026-05-31 12:50:37 +02:00
SendPaywallFollowUpEmailsCommand.php feat(drip): email users stuck on the paywall a day after onboarding (#562) 2026-06-19 14:11:12 +00:00
SendStuckCohortReportCommand.php feat(stats): weekly paywall stuck-cohort report to Discord (#563) 2026-06-19 14:12:51 +00:00
SendSubscriptionCancelledEmailCommand.php Add subscription cancellation email with manual trigger command (#47) 2025-12-30 20:32:44 +01:00
SendTestEmails.php chore(commands): unify command signatures 2025-12-30 07:22:18 +01:00
SendUpdateEmailCommand.php fix: delay emails to avoid reaching daily resend limit 2026-01-09 11:11:38 +01:00
SendUserLeadInvitations.php fix: keep lead invite command aliases (#406) 2026-05-20 12:26:21 +02:00
SendUserLeadReInvitations.php feat(leads): add user lead re-invite campaign (#432) 2026-05-26 08:35:31 +02:00
SetBankLogoCommand.php fix(banks:set-logo): add JPEG support test coverage and prompt for missing arguments (#214) 2026-03-07 16:33:54 +00:00
SetupMainUser.php fix(static-analysis): clear phpstan-baseline by fixing all suppressed errors (#183) 2026-03-02 12:22:30 +00:00
StripeSubscriptionStatsCommand.php feat: add Discord admin feed for daily stats and Stripe events (#458) 2026-05-30 18:14:46 +02:00
SuggestRulesCommand.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
SyncBankingConnections.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
SyncStripePricesCommand.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
TestScheduledDripEmailCommand.php Test mail drip command 2025-12-30 07:22:18 +01:00