Commit Graph

4 Commits

Author SHA1 Message Date
Víctor Falcón 17ddbbf821 test(encryption): cover trialing, past_due and grace branches for notify-removal 2026-07-17 11:08:27 +02:00
Víctor Falcón f140f5c785 fix(encryption): guard billed users independently of the subscriptions flag 2026-07-17 10:25:31 +02:00
Víctor Falcón 7d750fa1a8
fix(encryption): never email or delete users who are still being billed (#656)
## What

The `encryption:delete-accounts` and `encryption:notify-removal`
commands target users who still hold legacy client-side encrypted data.
They should **never** warn or delete an account that is still being
billed.

## Changes

- Add `excludeBilledUsers()` to the shared
`FindsUsersWithLegacyEncryption` trait. It drops any user where
`hasActiveSubscriptionOrTrial()` is true — a valid subscription outside
its grace period, or an active generic trial. This reuses the existing
domain method (whose docblock already states such users must cancel
before deletion) instead of reimplementing Cashier's subscription-status
logic in SQL.
- Both commands apply the filter right after fetching their candidates.
- Eager-load `subscriptions` in the base query to avoid an N+1 when the
filter runs.

## Tests

- `it never deletes a subscribed user` (delete command)
- `it never warns a subscribed user` (notify command)

Both new tests plus the existing suite pass (8/8). Pint clean.
2026-07-07 16:04:27 +00:00
Víctor Falcón 477e4d50e2
feat(encryption): commands to warn and remove inactive encrypted-data accounts (#633)
## What

Two artisan commands to retire the remaining legacy client-side
encrypted data in production (transactions with
`description_iv`/`notes_iv`, accounts with `name_iv`).

- **`encryption:notify-removal`** — queues a re-engagement/deletion
warning to every user who still has an encrypted transaction or account.
Reuses `SendUpdateEmailJob` (queue `emails`, dedup via `UserMailLog`,
50/day pacing). The email is framed as an **inactivity** cleanup (no
mention of encryption) and pitches recent additions (AI insights, bank
integrations, etc.).
- **`encryption:delete-accounts`** — soft-deletes and anonymizes
(`User::markAsDeleted()`) those users **only** if they have not signed
in within the grace window (`--days`, default 7, via `last_active_at`).
Anyone who came back is spared, and the demo account is always excluded.

Both support `--dry-run` and `--force`, and share the target query via
the `FindsUsersWithLegacyEncryption` concern so the warning and the
deletion always target the same set.

## Intended manual run

`notify-removal --dry-run` → run for real → wait 7 days →
`delete-accounts --dry-run` → run for real.

## Notes / scope

- Deletion is a **soft-delete + email anonymization** (accounts become
unusable); it does not hard-purge rows, and does not cancel Stripe
subscriptions or revoke bank connections (unlike `user:delete`). Legacy
inactive accounts are unlikely to have either; handle separately if
needed.
- Email copy is plain English (matches the existing `mail/updates/*`
convention), no new `__()` keys.

## Tests

`tests/Feature/Console/NotifyEncryptedDataRemovalCommandTest.php` and
`DeleteEncryptedDataAccountsCommandTest.php` — 6 passing (targeting,
queue, dry-run, grace window, demo exclusion).
2026-07-03 15:57:04 +00:00