Understand your personal finances. Forget Excels, try Whisper Money.
Go to file
Víctor Falcón f2a7f955e6
fix(budgets): handle refunds correctly in budget spending calculations (#152)
## Why

### Problem

When a refund (positive transaction amount) is assigned to a budget, it
incorrectly **increases** the cumulative spending instead of
**reducing** it. This causes:

- The spending chart line to go **up** on refunds instead of down
- The "Spent" amount to be inflated
- The "Remaining" amount to be understated
- Period rollover calculations to carry over incorrect amounts

### Root Cause

`BudgetTransactionService` uses `abs($transaction->amount)` when
creating budget transactions, which forces all amounts to be positive —
including refunds. Since expenses are stored as negative in the
`transactions` table and refunds as positive, `abs()` treats both as
spending.

## What

### Changes

- Replace `abs($transaction->amount)` with `-$transaction->amount` in
both `assignTransaction()` and `assignHistoricalTransactionsToPeriod()`
— expenses (`-5000`) become positive spending (`5000`), refunds
(`+1000`) become negative spending (`-1000`)
- Remove redundant `abs()` in `BudgetPeriodService::closePeriod()`
rollover calculation
- Add data migration to fix existing `budget_transactions` rows where
the original transaction was a refund
- No frontend changes needed — the chart and budget card already sum
`t.amount` directly

## Verification

### Tests

- Updated `assignHistoricalTransactionsToPeriod stores negated
transaction amount for expenses` — verifies expense sign is preserved
- Added `assignHistoricalTransactionsToPeriod stores refund as negative
amount` — verifies refunds reduce spending
- Added `budget spending correctly reflects mix of expenses and refunds`
— verifies net spending (expense - refund)
- Added `assignTransaction stores refund as negative budget transaction
amount` — verifies real-time assignment handles refunds
2026-02-24 21:12:36 +01:00
.agents/skills chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
.claude/skills chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
.cursor chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
.github chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
.opencode/skills chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
app fix(budgets): handle refunds correctly in budget spending calculations (#152) 2026-02-24 21:12:36 +01:00
bootstrap Remove budgets feature flag (#108) 2026-02-12 09:58:01 +01:00
config Remove encryption from browser tests and demo user (#129) 2026-02-17 11:45:27 +01:00
database fix(budgets): handle refunds correctly in budget spending calculations (#152) 2026-02-24 21:12:36 +01:00
docker chore: Fix caddy container 2026-01-17 20:02:14 +01:00
lang Replace E2E encryption mentions with privacy-first messaging (#124) 2026-02-14 16:59:50 +01:00
public Redesign landing page with real feature screenshots (#125) 2026-02-16 09:24:07 +01:00
resources feat: Update facehash and enable blink 2026-02-24 15:54:49 +01:00
routes feat: Add daily granularity toggle with area visualization to net worth chart (#136) 2026-02-19 15:35:22 +01:00
screenshots fix: Add gap between filter/create button on mobile settings pages (#115) 2026-02-12 20:50:05 +01:00
src/lib/crypto E2E Encryption 2025-11-07 14:21:25 +00:00
storage feat: Enable email verification on sign up (#97) 2026-02-03 10:15:07 +01:00
templates/coolify Replace E2E encryption mentions with privacy-first messaging (#124) 2026-02-14 16:59:50 +01:00
tests fix(budgets): handle refunds correctly in budget spending calculations (#152) 2026-02-24 21:12:36 +01:00
.editorconfig Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
.env.example feat: Integrate EnableBanking as open banking provider (#106) 2026-02-12 09:09:28 +01:00
.env.production.example Add production Docker setup for easy self-hosting with the CI-built image (#42) 2025-12-30 07:22:19 +01:00
.gitattributes Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
.gitignore feat: Integrate EnableBanking as open banking provider (#106) 2026-02-12 09:09:28 +01:00
.mcp.json chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
.php-cs-fixer.dist.php Execute browser tests on CI (#10) 2025-12-03 16:26:30 +01:00
.php-version chore: ignore local .php-version 2026-02-07 18:48:04 +01:00
.prettierignore Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
.prettierrc Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
.release-it.json chore: Generate Github releases 2026-02-24 11:18:26 +01:00
AGENTS.md chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
CHANGELOG.md chore: release v0.1.12 2026-02-24 10:34:53 +01:00
CLAUDE.md chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
Caddyfile feat: Docker dev env with Caddy, PHP on host (#103) 2026-02-09 13:31:16 +01:00
Dockerfile chore: Simplify IndexedDB sync by moving to Inertia shared props (#63) 2026-01-19 19:15:26 +01:00
Dockerfile.production feat: Automated setup script for local deployment (#58) 2026-01-16 15:57:12 +01:00
LICENSE.md Add Creative Commons license 2025-11-26 17:07:20 +01:00
LOCALIZATION.md feat: Spanish localization (#74) 2026-02-08 11:58:08 +01:00
README.md fix: Discord link 2026-02-18 13:00:30 +01:00
artisan Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
boost.json chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
bun.lock feat: Update facehash and enable blink 2026-02-24 15:54:49 +01:00
components.json Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
compose.yaml feat: Docker dev env with Caddy, PHP on host (#103) 2026-02-09 13:31:16 +01:00
composer.json feat: investment benefits — show gains/losses on investment accounts (#140) 2026-02-23 13:59:10 +01:00
composer.lock chore: Update composer packages and laravel boost (#150) 2026-02-24 16:28:22 +01:00
docker-compose.production.yml Add production Docker setup for easy self-hosting with the CI-built image (#42) 2025-12-30 07:22:19 +01:00
eslint.config.js Y3:0 2025-11-26 12:01:49 +01:00
opencode.json chore: Update larevel boot package 2026-01-27 10:55:46 +01:00
package-lock.json chore: release v0.1.12 2026-02-24 10:34:53 +01:00
package.json feat: Update facehash and enable blink 2026-02-24 15:54:49 +01:00
phpunit.xml test: add performance test suite with query count ceilings (#148) 2026-02-24 10:47:51 +01:00
tsconfig.json Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
vite.config.ts feat: Docker dev env with Caddy, PHP on host (#103) 2026-02-09 13:31:16 +01:00
vitest.config.ts feat: add multiple chart view modes for net worth evolution (#37) 2025-12-30 07:22:19 +01:00
vitest.setup.ts feat: add multiple chart view modes for net worth evolution (#37) 2025-12-30 07:22:19 +01:00
whispermoney fix: Wrong whispermoney script path 2026-01-16 18:48:44 +01:00

README.md

Whisper Money

Deutsch | Español | français | 日本語 | 한국어 | Português | Русский | 中文

Whisper Money

CC BY-NC 4.0

The most secure way to understand your finances.

Whisper Money is a privacy-first personal finance application that helps you track, categorize, and understand your spending—all while keeping your financial data encrypted and secure.

🎮 Try the Demo: Experience Whisper Money with our demo account - no registration required!

💬 Join our Community: Whether you're a user looking for help or a developer wanting to contribute, we'd love to have you in our Discord server! Share feedback, ask questions, discuss new features, or just hang out with fellow privacy enthusiasts.

Features

  • 🔐 Privacy-first — Your data is never shared with third parties. You own it
  • 🏦 Bank account management — Track multiple accounts in one place
  • 📊 Transaction categorization — Automatic and manual categorization
  • 🤖 Automation rules — Set up rules to auto-categorize transactions
  • 📈 Financial insights — Understand your spending patterns

Tech Stack

  • Backend: Laravel 12, PHP 8.4
  • Frontend: React 19, Inertia.js v2, TypeScript
  • Styling: Tailwind CSS v4
  • Database: MySQL
  • Cache/Queue: Redis
  • Testing: Pest v4

Running Locally

The easiest way to get started is using our automated setup script:

bash <(curl -fsSL https://whisper.money/setup.sh)

After installation, just visit https://whisper.money.local in your browser.

Manual Setup

If you prefer to set up manually:

  1. Clone the repository:
git clone https://github.com/whisper-money/whisper-money.git
cd whisper-money
  1. Run the setup script:
whispermoney install

Available Commands

Important: You must run whispermoney install before using any other command. If you skip the install step, commands like start will not work.

Once installed, you can use the whispermoney command for common tasks:

# Start all services
whispermoney start

# Stop all services
whispermoney stop

# Upgrade to latest version
whispermoney upgrade

# Interactive menu
whispermoney

Development Server

For active development with hot reloading:

composer run dev

This will concurrently start:

  • PHP development server
  • Queue worker
  • Log viewer (Pail)
  • Vite dev server

The application will be available at https://whisper.money.local or http://localhost:8000 (direct PHP server).

Running with Docker (Production Image)

For testing the production Docker image locally:

  1. Copy the production environment file:
cp .env.production.example .env
  1. Start the services:
docker compose -f docker-compose.production.yml up -d

The application will be available at http://localhost:8080.

To use a different port, set APP_PORT:

APP_PORT=3000 docker compose -f docker-compose.production.yml up -d

Deploying to Coolify

Whisper Money can be easily deployed to Coolify using our Docker Compose template.

Quick Deploy

  1. In Coolify, create a new resource and select Docker Compose
  2. Choose Empty Compose File as the source
  3. Paste the contents from our template: 👉 whisper-money.yaml
  4. Deploy!

The template includes:

  • Whisper Money application container
  • MySQL 8.0 database with health checks
  • Persistent volumes for data and storage
  • Auto-generated database credentials

Required Environment Variables

Variable Description
RESEND_API_KEY Email service API key (for password resets, notifications)

Note: APP_KEY and APP_URL are auto-configured. The container generates an APP_KEY on first startup if not provided.

Optional Environment Variables

Variable Default Description
DRIP_EMAILS_ENABLED true Enable drip emails (welcome, onboarding, feedback)
HIDE_AUTH_BUTTONS false Hide login/register buttons on landing page
SUBSCRIPTIONS_ENABLED false Enable Stripe subscriptions
STRIPE_KEY - Stripe publishable key
STRIPE_SECRET - Stripe secret key
STRIPE_WEBHOOK_SECRET - Stripe webhook signing secret

Star History

Star History Chart

License

This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.