feat: Import-Seite mit Drag-and-Drop und Planungs-Seite
Ersetzt die Platzhalter-Routen aus Task 12 durch echte GUI-Seiten: /import mit Drop-Zone (Fetch-Upload), gepollter Importliste (Status-Badges, aufklappbarer Vorschau samt Saldo-/Duplikat-Anzeige, Uebernehmen/Verwerfen) und /planung mit den vier Abschnitten Fixposten (inkl. Vorschlaege), Einmalposten, Kredite (Tilgungsplan-Aufklapper) und Szenarien (Kredit-Zuordnung, Modifikatoren, Durchrechnen mit Ergebnisanzeige). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,15 @@ from app.auth import COOKIE, session_valid
|
||||
from app.db import get_session
|
||||
from app.engine.recurrence import occurrences
|
||||
from app.models.tables import (Account, Category, PlannedItem,
|
||||
ProjectionResult, RecurringItem)
|
||||
ProjectionResult, RecurringItem,
|
||||
ScenarioLoan, ScenarioModifier)
|
||||
from app.routers.imports import list_imports as _list_imports
|
||||
from app.routers.imports import preview as _preview_import
|
||||
from app.routers.planning import list_loans as _list_loans
|
||||
from app.routers.planning import list_planned as _list_planned
|
||||
from app.routers.planning import list_recurring as _list_recurring
|
||||
from app.routers.planning import recurring_suggestions as _recurring_suggestions
|
||||
from app.routers.scenarios import list_scenarios as _list_scenarios
|
||||
from app.routers.transactions import list_transactions
|
||||
from app.services.balances import account_balance, total_balance
|
||||
|
||||
@@ -102,12 +110,70 @@ def buchungen(
|
||||
|
||||
|
||||
@router.get("/import", dependencies=[Depends(gui_session)])
|
||||
def import_page(request: Request):
|
||||
# Platzhalter, wird in Task 13 mit Upload-/Vorschau-Funktion gefüllt.
|
||||
return templates.TemplateResponse(request, "import.html", {})
|
||||
def import_page(request: Request, session: Session = Depends(get_session)):
|
||||
return templates.TemplateResponse(request, "import.html", {
|
||||
"statements": _list_imports(session=session),
|
||||
})
|
||||
|
||||
|
||||
@router.get("/import/list", dependencies=[Depends(gui_session)])
|
||||
def import_list_fragment(request: Request, session: Session = Depends(get_session)):
|
||||
return templates.TemplateResponse(request, "_import_list.html", {
|
||||
"statements": _list_imports(session=session),
|
||||
})
|
||||
|
||||
|
||||
@router.get("/import/{statement_id}/preview-fragment", dependencies=[Depends(gui_session)])
|
||||
def import_preview_fragment(statement_id: int, request: Request,
|
||||
session: Session = Depends(get_session)):
|
||||
result = _preview_import(statement_id, session=session)
|
||||
return templates.TemplateResponse(request, "_preview_table.html", {
|
||||
"statement": result.statement,
|
||||
"transactions": result.transactions,
|
||||
"balance_ok": result.balance_ok,
|
||||
"duplicates": result.duplicates,
|
||||
})
|
||||
|
||||
|
||||
def _scenario_rows(session: Session, scenarios: list, loans: list) -> list[dict]:
|
||||
"""Baut je Szenario den Kontext fürs Template: zugeordnete Kredite,
|
||||
Modifikatoren und (falls vorhanden) das letzte Projektionsergebnis."""
|
||||
rows = []
|
||||
for sc in scenarios:
|
||||
assigned_loan_ids = {
|
||||
sl.loan_id for sl in session.execute(
|
||||
select(ScenarioLoan).where(ScenarioLoan.scenario_id == sc.id)
|
||||
).scalars()
|
||||
}
|
||||
modifiers = session.execute(
|
||||
select(ScenarioModifier).where(ScenarioModifier.scenario_id == sc.id)
|
||||
).scalars().all()
|
||||
rows.append({
|
||||
"scenario": sc,
|
||||
"assigned_loan_ids": assigned_loan_ids,
|
||||
"modifiers": modifiers,
|
||||
"result": session.get(ProjectionResult, sc.id),
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
@router.get("/planung", dependencies=[Depends(gui_session)])
|
||||
def planung_page(request: Request):
|
||||
# Platzhalter, wird in Task 13 mit Fixposten-/Szenario-Verwaltung gefüllt.
|
||||
return templates.TemplateResponse(request, "planung.html", {})
|
||||
def planung_page(request: Request, session: Session = Depends(get_session)):
|
||||
categories = session.execute(select(Category)).scalars().all()
|
||||
recurring = _list_recurring(session=session)
|
||||
planned = _list_planned(session=session)
|
||||
loans = _list_loans(session=session)
|
||||
scenarios = _list_scenarios(session=session)
|
||||
return templates.TemplateResponse(request, "planning.html", {
|
||||
"categories": categories,
|
||||
"category_names": {c.id: c.name for c in categories},
|
||||
"recurring": recurring,
|
||||
"recurring_names": {r.id: r.name for r in recurring},
|
||||
"suggestions": _recurring_suggestions(session=session),
|
||||
"planned": planned,
|
||||
"loans": loans,
|
||||
"scenario_rows": _scenario_rows(session, scenarios, loans),
|
||||
"rhythms": ["monthly", "quarterly", "yearly"],
|
||||
"repayment_types": ["annuity", "bullet"],
|
||||
"modifier_kinds": ["percent", "absolute", "remove"],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user