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
+5
View File
@@ -66,6 +66,7 @@ class IdentifierKind(str, enum.Enum):
iban = "iban"
account_no = "account_no"
card_no = "card_no"
text = "text" # freier Erkennungs-Text im Buchungstext (Zielkonto bei Transfers)
class CategoryKind(str, enum.Enum):
@@ -491,6 +492,10 @@ class CategorizationRule(Base):
category_id: Mapped[int | None] = mapped_column(
ForeignKey("categories.id", ondelete="CASCADE"), nullable=True
)
# Optionale Zusatzbedingung: Regel greift nur bei genau diesem (absoluten) Betrag.
amount_cents: Mapped[int | None] = mapped_column(Integer, nullable=True)
# Wenn True: passende Buchung wird zum Transfer (category_id bleibt leer).
is_transfer: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
source: Mapped[RuleSource] = mapped_column(_enum(RuleSource), nullable=False, default=RuleSource.learned)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
hits: Mapped[int] = mapped_column(Integer, nullable=False, default=0)