import csv import io from decimal import Decimal from fastapi import APIRouter from fastapi.responses import StreamingResponse from database import connect from models import CATEGORIES router = APIRouter() def safe_cell(value): text = str(value or '') return "'" + text if text.lstrip().startswith(('=', '+', '-', '@')) or text.startswith(('\t', '\r', '\n')) else text def csv_rows(): stream = io.StringIO(newline='') writer = csv.writer(stream, delimiter=';', lineterminator='\r\n') yield '\ufeff' writer.writerow(['Datum', 'Position', 'Kategorie', 'Betrag', 'Notiz', 'Erwartet', 'Erhalten']) yield stream.getvalue() stream.seek(0); stream.truncate(0) with connect() as db: for row in db.execute('SELECT i.*, a.name FROM income_entries i JOIN assets a ON a.id=i.asset_id ORDER BY date DESC, i.id DESC'): writer.writerow([row['date'], safe_cell(row['name']), CATEGORIES[row['category']], f"{Decimal(row['amount'])/100:.2f}".replace('.', ','), safe_cell(row['note']), 'Ja' if row['expected'] else 'Nein', 'Ja' if row['received'] else 'Nein']) yield stream.getvalue() stream.seek(0); stream.truncate(0) @router.get('/export/income.csv') def export(): return StreamingResponse(csv_rows(), media_type='text/csv; charset=utf-8', headers={'Content-Disposition': 'attachment; filename="income.csv"'})