feat: Regeln (Betrag/Transfer/Konto-Texte) + Münz-Icon + pastellgrünes Theme

Regeln:
- Schlagwörter mit optionaler Betragsbedingung (z. B. „SV = 5.00 → Restaurant");
  Betrags-Regeln gewinnen vor betragslosen
- Transfer-Schlagwörter (Text → Transfer) auf der Konten-Seite
- Konto-Identifikator-Art „Text": erkennt das Zielkonto eines Transfers aus dem
  Buchungstext (längster passender Text gewinnt)
- Migration 0007, Endpunkte, Tests (Betrag/Transfer/Konflikt), gesamt 30 grün

Optik:
- App-Icon: Münzhaufen (aus Nutzer-SVG) weiß auf grünem Grund, klein gehalten
- Farbschema auf pastelliges Grün (Akzent hell/dunkel + Favicon)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-19 22:41:38 +02:00
co-authored by Claude Opus 4.8
parent 30bc037579
commit 204a24d0df
16 changed files with 401 additions and 114 deletions
+9 -4
View File
@@ -7,16 +7,21 @@ from sqlalchemy.orm import Session
from ..database import get_db
from ..deps import get_current_user, get_owned
from ..models import Account, AccountIdentifier, User
from ..models import Account, AccountIdentifier, IdentifierKind, User
from ..schemas import AccountCreate, AccountIdentifierOut, AccountOut, AccountUpdate
from ..services import balances
from ..services.csv_import import normalize_text
from .settings import BASE_CURRENCY_KEY, get_user_setting
router = APIRouter(prefix="/accounts", tags=["accounts"])
def normalize_identifier(value: str) -> str:
"""Vereinheitlicht IBAN/Nummer für den Abgleich: Großbuchstaben, nur alphanumerisch."""
def normalize_identifier(value: str, kind: IdentifierKind) -> str:
"""Vereinheitlicht einen Identifikator für den Abgleich. IBAN/Nummer:
Großbuchstaben, nur alphanumerisch. Freier Text: klein, getrimmt (für die
Zielkonto-Erkennung im Buchungstext)."""
if kind == IdentifierKind.text:
return normalize_text(value)
return "".join(ch for ch in value.upper() if ch.isalnum())
@@ -49,7 +54,7 @@ def _set_identifiers(acc: Account, items) -> None:
acc.identifiers.clear()
seen: set[tuple[str, str]] = set()
for it in items:
norm = normalize_identifier(it.value)
norm = normalize_identifier(it.value, it.kind)
key = (it.kind.value, norm)
if not norm or key in seen:
continue