Spring Boot · MyBatis · TailwindCSS · CSV Import · PDF Export
Finance Tracker is a full end-to-end web application for managing personal financial transactions. Users can import CSV files from apps like Mony or MoneyTracker, preview and analyze their financial insights, and optionally save them to the database for long-term tracking.
The application is built using Spring Boot 3, MyBatis, SQLite, and Thymeleaf, with a clean and modern UI powered by TailwindCSS.
- Supports Mony and MoneyTracker CSV formats
- CSV files are validated and parsed into a unified
TransactionRowDtostructure - Preview screen before saving to the database
- Supports guest mode via session storage
- Income, spending, and balance summary
- Category and subcategory breakdown
- Daily & monthly charts
- Modern bento-style insights layout
- Forecast future expenses based on historical transactions
- Uses Exponential Smoothing (via
ForecastService) on time-series data - Useful for planning upcoming months and understanding spending trends
- Append mode → saves only new transactions (automatic dedup)
- Replace mode → wipes existing user data and replaces with new import
- Deduplication uses business key:
date + amount + category + note + type
- Categories and subcategories auto-create if they don't exist
- Managed via MyBatis mappers
- Export insights directly into a clean, formatted PDF file
- User registration & login
- Password hashing
- Protected endpoints for saving data:
/import/save/**
- TailwindCSS
- Dark mode toggle
- Custom modal & confirmation dialog components
- Fully responsive
Backend
- Spring Boot 3.5.x
- MyBatis
- SQLite (easily switchable to PostgreSQL)
- Java 21
- Spring Security
Frontend
- Thymeleaf
- TailwindCSS
- Chart.js
Tools
- Maven
- Lombok
- Java Time API
src/
├─ main/
│ ├─ java/com/example/finance_tracker/
│ │ ├─ controller/ # Web & auth controllers
│ │ ├─ service/ # Business logic
│ │ ├─ mapper/ # MyBatis XML mappers
│ │ ├─ dto/ # CSV & view DTOs
│ │ ├─ model/ # Database models
│ │ ├─ util/ # Utilities (CSV, modal, formatters)
│ │ └─ config/ # Security & MyBatis configuration
│ │
│ └─ resources/
│ ├─ templates/ # Thymeleaf pages & fragments
│ ├─ static/ # CSS, JS
│ ├─ mapper/ # MyBatis XML
│ ├─ schema.sql # DB bootstrap
│ └─ application.properties
│
└─ test/
├─ service/ # Integration tests
└─ FinanceTrackerApplicationTests.java
git clone https://github.com/username/finance-tracker.git
cd finance-tracker
./mvnw spring-boot:run
http://localhost:8080
example-dummy-data/mony-format-dummy.csv
Existing integration tests include:
UserServiceImplTestCategoryServiceImplTestTransactionServiceImplTest
Run all tests:
./mvnw test
All imported CSVs are normalized into a single DTO:
public class TransactionRowDto {
private LocalDate date;
private double amount;
private String category;
private String subCategory;
private TransactionType type;
private String note;
}
The application includes a built‑in forecasting module powered by the ForecastService.
It uses daily expense time‑series data and applies:
- Holt’s Linear Trend (Double Exponential Smoothing) when there is enough history (≥ 3 days)
- Single Exponential Smoothing (SES) as a fallback for very short series
Used when there are at least 3 daily points. Two components are updated:
- Level (s) — the smoothed value
- Trend (b) — the estimated slope over time
Implementation (simplified):
// initialization
s = x[0];
b = x[1] - x[0];
// update for each t
s = α * x[t] + (1 − α) * (s + b);
b = β * (s − prevS) + (1 − β) * b;
// next‑day forecast
Fₜ₊₁ = s + b;
With:
ALPHA = 0.3BETA = 0.1
Used as a fallback when the daily series is very short:
s = x[0];
for each t > 0:
s = α * x[t] + (1 − α) * s;
Fₜ₊₁ = s;
With:
ALPHA = 0.3
- Build a daily expense series for the current month (or at least the last N days, e.g. 7 days).
- Sum actual expenses so far in the current month.
- Use Holt or SES to forecast next‑day expense.
- Multiply that by the remaining days in the month.
- Final forecast = actualSoFar + forecastRemaining → projected total expenses for the current month.
This allows the user to anticipate end‑of‑month spending based on their current trajectory.
Set<String> existingKeys = existingTransactions.stream()
.map(this::businessKey)
.collect(Collectors.toSet());
List<TransactionRowDto> filtered = imported.stream()
.filter(t -> existingKeys.add(businessKey(t)))
.toList();
Set<String> existingKeys = existingTransactions.stream()
.map(this::businessKey)
.collect(Collectors.toSet());
List<TransactionRowDto> filtered = imported.stream()
.filter(t -> existingKeys.add(businessKey(t)))
.toList();
```
# 🧭 Roadmap (Optional Enhancements)
- Budgeting per category
- Multi-account support (Cash, BCA, Dana, etc.)
- Transaction tagging
- Manual transaction input
- PostgreSQL + Docker deployment
- Advanced analytics module
Created by Felix