Commit Graph

18 Commits

Author SHA1 Message Date
Víctor Falcón e4243c2eaa
feat: use testcontainers for isolated MySQL in test runs (#153)
## Summary

- Add `testcontainers/testcontainers` to `require-dev` to spin up an
ephemeral MySQL 8.0 container per test process, eliminating the need for
a local database or Docker Compose stack
- Create `tests/bootstrap.php` that starts a `MySQLContainer`, sets
`DB_*` env vars dynamically, and auto-generates `APP_KEY` when missing
(supports fresh worktrees and CI)
- Update `phpunit.xml` to use the new bootstrap file and remove the
hardcoded `DB_DATABASE` env var

## How it works

Running `php artisan test` now automatically starts a short-lived MySQL
container on a random port. Each test process gets its own isolated
database, so multiple agents, worktrees, or CI jobs can run in parallel
without conflicts.

Set `TESTCONTAINERS=false` to bypass containers and use the database
configured in `.env` instead.
2026-02-25 10:14:20 +01:00
Víctor Falcón 545cc66024
chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
Víctor Falcón 8ce0adf8ae
feat: Apply automation rules to bank-synced transactions (#112)
## Summary

- Adds backend automation rule evaluation for bank-synced transactions
using `jwadhams/json-logic-php` (same engine as the frontend
`json-logic-js`) for exact rule parity
- Synchronous `ApplyAutomationRules` listener on `TransactionCreated`
runs before `AssignTransactionToBudget`, auto-assigning categories and
labels via `saveQuietly()` / `syncWithoutDetaching()`
- Skips encrypted transactions and `action_note` (encrypted, can't
handle server-side)

## Test plan

- [x] 17 new Pest tests covering all JsonLogic operators, category/label
assignment, encrypted transaction skip, priority ordering,
case-insensitive matching, user scoping, bank_name matching, amount in
dollars, no `TransactionUpdated` event dispatch, compound rules, and
end-to-end listener integration
- [x] Existing `TransactionSyncServiceTest` passes (7 tests)
- [x] Full test suite passes (546 tests, 0 failures)
- [x] Pint formatting clean
2026-02-12 14:20:55 +01:00
Víctor Falcón db7b6e4da7
feat: Integrate EnableBanking as open banking provider (#106)
## Summary

- Adds **EnableBanking** as the first open banking provider, allowing
users to connect real bank accounts and automatically sync transactions
and balances
- Uses a **`BankingProviderInterface`** contract so future providers
(Plaid, GoCardless, etc.) can be added by implementing the same
interface
- Feature-flagged behind the **`open-banking`** Pennant flag (default:
off)
- Connected accounts are **unencrypted** and transactions have `source =
'enablebanking'`

### What's included

**Backend:**
- `BankingProviderInterface` contract + `EnableBankingProvider`
implementation (JWT RS256 auth)
- `BankingConnection` model with full lifecycle (pending →
awaiting_mapping → active → expired/revoked/error)
- `TransactionSyncService` — pagination, deduplication by
`external_transaction_id`, amount/date mapping
- `BalanceSyncService` — preferred balance type selection (CLBD → ITAV
fallback)
- Authorization flow: start auth → bank redirect → callback → session
creation → account mapping → sync
- `SyncBankingConnectionJob` (unique per connection, 3 retries) +
scheduled every 6 hours
- `banking:sync` artisan command
- 5 migrations: `banking_connections` table, account fields, transaction
`external_transaction_id`, `pending_accounts_data`, `linked_at`

**Frontend:**
- Manual vs Connected account choice in the create account dialog
- Multi-step bank connection dialog (country → bank selection →
confirmation → redirect)
- Account mapping page — map discovered bank accounts to existing
accounts, create new ones, or skip
- Settings/Connections page with status badges, sync/disconnect actions
- "Connected" badge on linked accounts in settings

**Tests:**
- 49 tests covering feature flags, controllers, account mapping,
transaction sync, balance sync, deduplication, and pagination

### Feature Flags

This PR introduces **two Pennant feature flags**:

1. **`open-banking`** — Gates the entire open banking feature
(institutions endpoint, authorization flow, connections page). When
disabled, all open banking routes return 404.

2. **`account-mapping`** — Controls whether users see an intermediate
account mapping step after connecting a bank. When **enabled**, users
are redirected to a mapping page where they can choose to create new
accounts, link to existing ones, or skip each discovered bank account.
When **disabled**, all discovered accounts are automatically created
(original behavior). Linked accounts only sync transactions from their
last transaction date and only update the current balance from the
provider (no historical balance calculation or daily balance tracking).

Enable per-user:
```bash
php artisan feature:enable open-banking user@example.com
php artisan feature:enable account-mapping user@example.com
```

Enable for all users:
```bash
php artisan feature:enable open-banking all
php artisan feature:enable account-mapping all
```

## Test plan

- [x] Enable feature flag: `php artisan feature:enable open-banking`
- [x] Verify "Connected" option appears in create account dialog
- [x] Start authorization flow and verify redirect to bank
- [x] With `account-mapping` **disabled**: verify callback creates
accounts directly and dispatches sync
- [x] With `account-mapping` **enabled**: verify callback redirects to
mapping page
- [x] Test mapping page: create new, link to existing, and skip actions
- [x] Verify linked accounts sync only from last transaction date and
only update current balance
- [x] Verify connections page shows "Setup Required" badge for
awaiting_mapping status
- [x] Run `php artisan banking:sync` and verify transactions sync
- [x] Verify connections page shows status, sync, and disconnect actions
- [x] Run full test suite: `php artisan test --compact`
2026-02-12 09:09:28 +01:00
Víctor Falcón bc02bf948f chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
Víctor Falcón 9b6c30775f
Add Budgeting Feature to Track and Manage Spending (#36)
## Overview

We're excited to introduce budgeting capabilities to Whisper Money! This
feature helps you take control of your finances by setting spending
limits and tracking your progress over time.

## Screenshots
<img width="1316" height="793" alt="image"
src="https://github.com/user-attachments/assets/ac394d36-cded-4ea4-9883-120785e260f1"
/>
<img width="1315" height="907" alt="image"
src="https://github.com/user-attachments/assets/7c682474-5aa7-4388-b626-29b56f5ebbef"
/>
<img width="1315" height="992" alt="image"
src="https://github.com/user-attachments/assets/21eace45-23c6-472d-9aa0-0feb6db3fba4"
/>

## What's New

### Create Flexible Budgets
- Set budgets for specific categories or labels
- Choose from monthly, weekly, bi-weekly, or custom periods
- Set your own budget start date for better alignment with your pay
schedule

### Track Your Spending
- Visual spending charts show how much you've spent vs. your budget
- See at a glance which budgets are on track and which need attention
- View all transactions that count toward each budget

### Smart Budget Management
- **Carry Over**: Unused budget amounts automatically roll into the next
period
- **Reset**: Unused amounts return to your available money pool
- Edit or delete budgets anytime as your needs change

### Easy Access
- New Budgets section in the main navigation
- Quick overview cards showing budget status
- Detailed budget pages with spending history and transaction lists

## How It Works

1. Create a budget by selecting a category or label and setting your
spending limit
2. Your transactions are automatically matched to relevant budgets
3. Track your progress with visual charts and spending summaries
4. Adjust your budgets as needed to stay on track with your financial
goals

This feature is now available behind a feature flag and can be enabled
for users who want to start budgeting their expenses.
2026-01-21 15:25:50 +01:00
Víctor Falcón 1cec4a6421
Add Cashflow Analytics Feature (#49)
Summary
- Add new Cashflow page with income/expense analytics, Sankey chart
visualization, and trend analysis
- Add Cashflow summary widget to the dashboard
- Implement feature flag system using Laravel Pennant to control feature
visibility per user
- Add artisan commands to enable/disable features: `php artisan
feature:enable/disable [feature] [email|all]`

## New Features
- **Cashflow Page**: Period-based income/expense breakdown with category
analysis
- **Sankey Chart**: Visual flow diagram showing money movement between
categories
- **Trend Chart**: 12-month historical cashflow visualization
- **Dashboard Widget**: Quick cashflow summary card
- **Feature Flags**: Cashflow feature hidden behind feature flag,
controllable per-user

## How to enable it
This feature it's under a feature flag until we are sure it's working
fine. If you want to test it, [send us a message on the general channel
in our discord](https://discord.gg/9UQWZECDDv). If you're self hosting
just run `php artisan feature:enable CashflowFeature {your_email}`.

## New Cashflow Page
<img width="1278" height="1185" alt="image"
src="https://github.com/user-attachments/assets/83469f24-9f96-4e5f-bef9-86c076d31243"
/>
<img width="1274" height="1035" alt="image"
src="https://github.com/user-attachments/assets/8949ff9d-c370-4fc4-81c6-268307c587a5"
/>

## New Dashboard Widget
<img width="1180" height="289" alt="image"
src="https://github.com/user-attachments/assets/ad984a66-9ffd-4079-85ce-0b2d28278d76"
/>
<img width="1179" height="286" alt="image"
src="https://github.com/user-attachments/assets/bff60286-8ea2-4e9c-8fa6-22755566648f"
/>
2026-01-05 13:06:50 +01:00
Víctor Falcón cb44687216 Config bugsink on Laravel 2025-12-31 12:11:55 +01:00
Víctor Falcón 6b0ce387d7 Reapply "swap horizon -> queue:work on mysql"
This reverts commit 43d615869c.
2025-12-30 07:22:18 +01:00
Víctor Falcón 03880ca492 Revert "swap horizon -> queue:work on mysql"
This reverts commit 214ad684a8.
2025-12-30 07:22:18 +01:00
Víctor Falcón c79222870d swap horizon -> queue:work on mysql 2025-12-30 07:22:18 +01:00
Víctor Falcón 46c5b13739 feat: Implement drip email campaign system (#35)
## Summary

- Implement event-driven drip email campaign system for new user signups
- Add 5 targeted emails based on user journey (welcome, onboarding
reminder, promo code, import help, feedback)
- Configure Laravel Horizon with Redis queue for reliable job processing
- Track sent emails in `user_mail_logs` table to prevent duplicates

## Email Schedule

| Email | Timing | Condition |
|-------|--------|-----------|
| Welcome | 30 min after signup | All users |
| Onboarding Reminder | 1 day after signup | Not onboarded |
| Promo Code (FOUNDER) | 1 day after signup | Onboarded + has
transactions + not subscribed |
| Import Help | 1 day after signup | Onboarded + no transactions + not
subscribed |
| Feedback | 5 days after signup | Not subscribed |

## Architecture

```
User Registers → ScheduleDripEmailsListener
    ├─> SendWelcomeEmailJob (30 min delay)
    ├─> SendOnboardingReminderEmailJob (1 day delay)
    ├─> SendPromoCodeEmailJob (1 day delay)
    ├─> SendImportHelpEmailJob (1 day delay)
    └─> SendFeedbackEmailJob (5 days delay)
```

Each job checks conditions at execution time and either sends the email
+ logs it, or silently completes.

## Test plan

- [x] All 23 drip email tests pass
- [x] Migration creates `user_mail_logs` table
- [x] Jobs dispatch with correct delays
- [x] Emails only sent when conditions are met
- [x] Duplicate emails prevented via UserMailLog check
- [ ] Verify Horizon processes jobs in production
2025-12-30 07:22:18 +01:00
Víctor Falcón 3c22453fc6 feat: Configure Resend email integration (#34)
- Install symfony/resend-mailer package for Resend support
- Add RESEND_API_KEY to .env.example with documentation
- Create mail:test command to verify email configuration
- Users can now set MAIL_MAILER=resend in .env to use Resend
- Test command usage: php artisan mail:test email@example.com
2025-12-30 07:22:09 +01:00
Víctor Falcón 374ce0546b
Subscriptions (#15)
## Subscribe paywall
<img width="1119" height="764" alt="image"
src="https://github.com/user-attachments/assets/55367c91-f2b9-43f5-b450-75faf867bde0"
/>

## Subscribe success page
<img width="1112" height="681" alt="image"
src="https://github.com/user-attachments/assets/a923752a-d506-410e-ac66-e02b96acca20"
/>

## Manage subscription page
<img width="1221" height="705" alt="image"
src="https://github.com/user-attachments/assets/1eba1fec-7fb7-4500-8549-d8def809ddae"
/>
2025-12-06 19:09:56 +01:00
Víctor Falcón 5cfe3c81a1 Better input field UX 2025-11-26 11:24:37 +01:00
Víctor Falcón a9217b4efe Install Laravel Boost 2025-11-07 13:55:11 +00:00
Víctor Falcón 2eaad3204c Install Pest 2025-11-07 12:01:58 +00:00
Víctor Falcón fb140bbece Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00