Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

270 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ’° Expense Manager

A modern, dockerized expense tracking application built with Flask, SQLAlchemy, and Chart.js.

Background

I was originally using expenseowl but there was some functionality I needed that was missing. Since it doesn't seem to be actively developed anymore, I decided to brush off my web-dev skills and make my own. This project is also my first try at using LLMs to assist with coding.

Features

  • ✨ Real-time Updates: Add, edit, and delete transactions without page refreshes
  • πŸ“Š Visual Analytics: Interactive charts with income/expense tracking and category/tag filtering
  • πŸ’° Income & Expense Tracking: Track both income and expenses with net balance calculation
  • 🏷️ Flexible Tagging System: Add custom tags to transactions for fine-grained organization
  • πŸ“‚ Category Management: Create, rename, merge, and delete categories from the Settings page
  • πŸ”– Tag Management: Rename, merge, and delete tags across all transactions from the Settings page
  • πŸ” Multi-select Filtering: Filter by multiple categories and/or tags simultaneously
  • πŸ“… Time-range Filtering: Filter by current month, custom date range, or all time
  • πŸ“„ Month-based Pagination: Transactions are grouped and paginated by month
  • 🐳 Dockerized: Easy deployment with Docker and Docker Compose
  • πŸ’Ύ Multiple Database Backends: SQLite, PostgreSQL, or MySQL
  • πŸ“€ CSV Import: Bulk import transactions from a CSV file
  • πŸ“₯ CSV Export: Download your full transaction history (or a filtered date range) as a CSV file
  • πŸ§ͺ Test Data Generator: Populate with sample data (debug mode only)
  • πŸ—οΈ SOLID Architecture: Clean separation of concerns across models, services, and routes
  • πŸ“± Mobile Responsive: Works on all device sizes

Tech Stack

  • Backend: Flask (Python 3.11+) with SQLAlchemy ORM
  • Frontend: Vanilla JS with fetch-based dynamic updates
  • Charts: Chart.js
  • Database: SQLite (default), PostgreSQL, or MySQL
  • Build: flit / pyproject.toml
  • Containerization: Docker & Docker Compose

Quick Start

Using Docker (Recommended)

The application supports three database backends. SQLite requires no extra services and is the default.

SQLite (default):

docker-compose up --build

PostgreSQL:

docker-compose -f docker-compose.postgres.yml up --build

MySQL:

docker-compose -f docker-compose.mysql.yml up --build

Then open http://localhost:5000 in your browser.

Local Development (Without Docker)

# Install the package in editable mode (includes all dependencies)
pip install -e .

# Initialise the database
flask --app JustAnotherExpenseManager init-db

# Run the development server
flask --app JustAnotherExpenseManager run

Or use the installed entry point directly:

JustAnotherExpenseManager

Then open http://localhost:5000.

Configuration

All configuration is done through environment variables. Copy .env.example to .flaskenv for local development and edit as needed.

Network Binding

# Bind to localhost only (default for local dev β€” more secure)
FLASK_RUN_HOST=127.0.0.1

# Bind to all interfaces (required for Docker)
FLASK_RUN_HOST=0.0.0.0

FLASK_RUN_PORT=5000

The Docker images set FLASK_RUN_HOST=0.0.0.0 automatically via ENV in the Dockerfile. You can override it in your docker-compose.yml if needed.

Database

# SQLite (default)
DATABASE_TYPE=sqlite
SQLITE_PATH=./data/expenses.db

# PostgreSQL
DATABASE_TYPE=postgresql
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=expenses
DATABASE_USER=postgres
DATABASE_PASSWORD=postgres

# MySQL
DATABASE_TYPE=mysql
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_NAME=expenses
DATABASE_USER=expenses_user
DATABASE_PASSWORD=expenses_pass

See DATABASE_GUIDE.md for full configuration details and migration guides.

Usage

Navigation

The application has three pages:

  • Summary: Financial overview, charts, and multi-select category/tag/time filtering
  • Transactions: Add, edit, delete, and filter individual transactions; CSV import
  • Settings: Manage categories and tags; export transactions as CSV; test data generator (debug mode only)

Adding Transactions

  1. Navigate to the Transactions page
  2. Fill in the form:
    • Description: What the transaction was for
    • Amount: Positive number
    • Type: Income or Expense
    • Date: When it occurred
    • Category: Choose from existing or add new ones in Settings
    • Tags: Optional comma-separated tags (e.g. recurring, business)
  3. Click Add Transaction β€” it appears immediately in the list

Filtering

Both the Summary and Transactions pages support combined filtering:

  • Category filter: Multi-select β€” choose one or more categories (or All)
  • Tag filter: Multi-select β€” choose one or more tags (or All Tags)
  • Time range: Current month, custom date range, or all time

Filters are reflected in the URL as query parameters (categories=, tags=, range=/start_date=/end_date=), so filtered views can be bookmarked or shared.

Editing Transactions

Click the Edit button on any transaction row to open an inline edit form. Changes are saved immediately without a page reload.

Category Management

Go to Settings β†’ Category Management to:

  • Add new categories
  • Rename a category (updates all transactions that use it)
  • Merge one category into another (all transactions from the source are re-assigned to the target)
  • Delete a category (removes it from all transactions)

Tag Management

Go to Settings β†’ Tag Management to:

  • Rename a tag across all transactions
  • Merge one tag into another
  • Delete a tag from all transactions

CSV Export

Go to Settings β†’ Export Transactions to download your transaction history as a CSV file.

  • Leave both date fields blank to export all transactions
  • Fill in Start Date and/or End Date to export a specific date range
  • The exported file uses the same column format as CSV import, so it can be edited and re-imported

The export endpoint (GET /api/transactions/export) also accepts categories, tags, range, start_date, and end_date query parameters, matching the filter interface on the Transactions and Summary pages.

CSV Import

  1. Prepare a CSV file with these columns:
Column Description
description What the transaction was for
amount Positive dollar amount
type income or expense
category Category name (no category: prefix)
date YYYY-MM-DD format
tags Optional, comma-separated
  1. Go to the Transactions page and use the Import from CSV section
  2. Invalid rows are reported individually; valid rows are still imported

Example:

description,amount,type,category,date,tags
Monthly Salary,5000.00,income,salary,2024-01-15,recurring
Grocery Store,125.50,expense,food,2024-01-16,
Freelance Project,1500.00,income,freelance,2024-01-20,one-time

Project Structure

JustAnotherExpenseManager/         # Main Python package
β”œβ”€β”€ __init__.py                    # App factory (create_app) and CLI commands
β”œβ”€β”€ models/
β”‚   └── __init__.py                # SQLAlchemy models: Transaction, Tag
β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ transactions.py            # Transaction CRUD, CSV import, and CSV export endpoints
β”‚   β”œβ”€β”€ stats.py                   # Summary page and /api/stats, /api/chart-data
β”‚   β”œβ”€β”€ categories.py              # Category and tag management endpoints
β”‚   └── settings.py                # Settings page and test data endpoints
β”œβ”€β”€ utils/
β”‚   β”œβ”€β”€ database.py                # DatabaseManager β€” connections and init
β”‚   └── services.py                # Business logic: TransactionService, StatsService, etc.
β”œβ”€β”€ templates/
β”‚   β”œβ”€β”€ base.html                  # Base layout with navigation
β”‚   β”œβ”€β”€ summary.html               # Summary page
β”‚   β”œβ”€β”€ transactions.html          # Transactions page
β”‚   β”œβ”€β”€ settings.html              # Settings page
β”‚   β”œβ”€β”€ stats.html                 # Stats partial (rendered by /api/stats)
β”‚   └── transactions_list.html     # Transactions list partial with pagination
└── static/
    β”œβ”€β”€ css/styles.css
    └── js/
        β”œβ”€β”€ filter_component.js    # Multi-select category/tag filter UI
        β”œβ”€β”€ stats.js               # Chart initialisation and stats refresh
        β”œβ”€β”€ transactions.js        # Transaction form, edit modal, delete
        └── settings.js            # Category/tag management and CSV export UI

pyproject.toml                     # Package metadata and dependencies (flit)
.flaskenv                          # Local dev environment variables
.env.example                       # Environment variable reference
Dockerfile                         # Container image definition
docker-compose.yml                 # SQLite deployment
docker-compose.postgres.yml        # PostgreSQL deployment
docker-compose.mysql.yml           # MySQL deployment
DATABASE_GUIDE.md                  # Detailed database configuration guide

Architecture

Models (models/) β€” SQLAlchemy table definitions and ORM relationships.

Services (utils/services.py) β€” All business logic: transaction CRUD, statistics calculations, category/tag operations, test data generation. Routes call services; services never touch HTTP.

Routes (routes/) β€” Flask blueprints that handle HTTP requests, validate input, call services, and return responses. No business logic lives here.

Database (utils/database.py) β€” DatabaseManager wraps engine creation, session factory, and init_database / reset_database lifecycle methods.

Testing

Unit and Integration Tests (pytest)

# Install with test dependencies
pip install -e ".[test]"

# Run all tests
pytest

# With coverage report
pytest --cov=JustAnotherExpenseManager --cov-report=term-missing

End-to-End Tests (Playwright)

# Install Node dependencies and browsers
npm ci
npx playwright install --with-deps chromium

# Run all E2E tests (starts the dev server automatically)
npx playwright test

# Run a specific browser only
npx playwright test --project=chromium

# Open the interactive UI runner
npx playwright test --ui

# View the last HTML report
npx playwright show-report

E2E tests require the application to be runnable via JustAnotherExpenseManager (the installed entry point). Playwright's webServer config in playwright.config.js handles starting and stopping it automatically.

Database CLI Commands

# Initialise tables (run once after install)
flask --app JustAnotherExpenseManager init-db

# Drop and recreate all tables
flask --app JustAnotherExpenseManager init-db --drop

# Load sample data after init
flask --app JustAnotherExpenseManager init-db --sample-data

# Check database connectivity
flask --app JustAnotherExpenseManager db-health

# Show database info
flask --app JustAnotherExpenseManager db-info

License

MIT β€” see LICENSE for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages