whisper-money/app/Models
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
..
Account.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
AccountBalance.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
AiConsent.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
AutomationRule.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
AutomationRuleLabel.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
Bank.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
BankingConnection.php refactor: centralize duplicated provider & locale keys into enums (#543) 2026-06-16 13:43:14 +00:00
BankingSyncLog.php fix(banking): retry failed sync connections and log every sync attempt (#251) 2026-03-31 11:34:35 +01:00
Budget.php feat: add catch-all budgets (#527) 2026-06-15 16:07:19 +00:00
BudgetPeriod.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
BudgetTransaction.php fix(static-analysis): clear phpstan-baseline by fixing all suppressed errors (#183) 2026-03-02 12:22:30 +00:00
Category.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
CategoryCorrection.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
EncryptedMessage.php fix(static-analysis): clear phpstan-baseline by fixing all suppressed errors (#183) 2026-03-02 12:22:30 +00:00
ExchangeRate.php Preload exchange rates (#362) 2026-05-06 16:35:49 +01:00
IntegrationRequest.php feat(integration-requests): add not-doable status with a public comment (#552) 2026-06-17 16:36:32 +02:00
IntegrationRequestVote.php feat(integration-requests): community board to request & vote bank integrations (#550) 2026-06-17 12:50:51 +00:00
Label.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
LabelTransaction.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
LoanDetail.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
RealEstateDetail.php refactor(api): standardize serialization via model $hidden (#492) 2026-06-05 13:57:34 +02:00
RuleSuggestion.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
SavedFilter.php feat(analysis): project-aware transaction analysis (#513) 2026-06-09 15:32:07 +02:00
StuckCohortSnapshot.php feat(stats): weekly paywall stuck-cohort report to Discord (#563) 2026-06-19 14:12:51 +00:00
SuggestionRun.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
Transaction.php feat(ai): persist AI categorization suggestions below the label bar (#547) 2026-06-17 08:24:30 +00:00
User.php feat(integration-requests): let the admin bypass limits and auto-approve (#551) 2026-06-17 14:12:51 +00:00
UserLead.php feat(leads): add user lead re-invite campaign (#432) 2026-05-26 08:35:31 +02:00
UserMailLog.php feat: Send custom emails to users (#52) 2026-01-09 09:33:19 +01:00
UserSetting.php feat(settings): let users disable bank transactions email (#472) 2026-06-02 12:24:39 +02:00