553e1a4c83d2bb80377d619d1b579d4342dc67cb
finanz-cockpit
Financial tracking application for German bank exports (b4w, comdirect) with unified transaction processing and category-based budget management.
Features
- Dual-source ingestion: Import transactions from b4w and comdirect CSV exports
- Unified transaction table: All transactions are consolidated into a single
transactionstable using business-key deduplication - Category hierarchy: Support for nested category structures (e.g.,
"Lebenshaltung:Essen:Supermarkt") - Budget tracking per category: Set and manage budgets with time-bounded or unbounded validity periods
- Automatic overlap handling: Smart budget management handles partial/full overlaps automatically
Installation
uv sync
alembic upgrade head
Usage
Import b4w Export
from src.ingest.b4w import ingest_b4w
from pathlib import Path
csv_path = Path("/mnt/anbau/Software/Python/finanz-cockpit/scripts/dummy_data/b4w_export_dummy_01.csv")
ingest_b4w(csv_path)
Import comdirect Export
from src.ingest.comdirect import ingest_comdirect
from pathlib import Path
csv_path = Path("/mnt/anbau/Software/Python/finanz-cockpit/scripts/dummy_data/comdirect_giro_01.csv")
ingest_comdirect(csv_path)
Set Category Budgets
from src.models.category import Category, CategoryBudget
from datetime import date
from src.domain_utils import set_category_budget
# Create category hierarchy
cat = Category.from_str("Lebenshaltung:Essen:Supermarkt")
# Set budget for October-November 2026
budget = CategoryBudget(
category=cat,
amount_eur=150.00,
valid_from=date(2026, 10, 1),
valid_to=date(2026, 11, 30),
)
set_category_budget(db_session, budget)
# Budgets can also be unbounded (no dates specified)
budget = CategoryBudget(
category=cat,
amount_eur=200.00, # Monthly average
)
set_category_budget(db_session, budget)
Testing
# Run all tests
pytest
# Run with coverage
pytest --cov=src --cov-report=term-missing
# Run a single test file
pytest tests/test_model_b4wexport.py -v
Project Structure
src/: Application codemain.py: CLI entrypoint for CSV ingestiondb.py: Database connection managementmodels/: Pydantic models + SQLAlchemy tablesingest/: Data ingestion logic per source (b4w, comdirect)domain_utils.py: Domain-level utilities (budget overlap handling)
tests/: Tests with fixtures inconftest.pyalembic/versions/: Database migrations
Data Models
Transaction Sources
- b4w:
B4wExport,Category - comdirect:
ComdirectGiro,ComdirectCreditCard,ComdirectDepot - unified:
Transaction,TransactionDb– consolidates all sources into one table
All models inherit from common base in src/models/common.py:
hash_id(): SHA256-based deduplication key_parse_date_german(): Handles "07.09.2026" format and values like "offen", "--"_parse_decimal_german(): Converts "1,23" to Decimal
Category Budgets
Budget tracking for categories with time-bounded or unbounded periods:
from src.models.category import Category, CategoryBudget
cat = Category(name="Lebenshaltung")
budget = CategoryBudget(
category=cat,
amount_eur=150.00,
valid_from=date(2026, 10, 1), # Optional: None for unbounded start
valid_to=date(2026, 11, 30), # Optional: None for unbounded end
)
set_category_budget(db_session, budget)
The set_category_budget() function handles overlap scenarios automatically:
- No overlap: Adds new budget alongside existing ones
- Partial overlap: Splits existing budget into non-overlapping parts + adds new budget
- Full overlap: Replaces existing budget entirely
Development
Setup
# Install dependencies
uv sync
# Run migrations
alembic upgrade head
# Run all tests
pytest
# Run with coverage report
pytest --cov=src --cov-report=html -c .coverage htmlcov
Common Patterns
Database Session: Always use get_session() context manager:
from src.db import get_session
with get_session() as session:
# query operations here
session.commit()
CSV Format:
- b4w: German bank export (CSV)
- comdirect: CSV with fields like
Buchungstag,Umsatz in EUR,Vorgang
Testing Workflow
Tests automatically run migrations via conftest.py. For testing without migrations:
export DATABASE_URL="sqlite://" pytest tests/test_db.py
Common Mistakes
- Global engine state: Always use
get_session()context manager - German date formats: Use
_parse_date_german()for CSV parsing - Migration setup: Tests auto-migrate, but integration tests need explicit
alembic upgrade head - Faker locale: Use
Faker("de_DE")for realistic names/IBANs
License
MIT
Languages
Python
99.5%
Mako
0.5%