feat: Zeilenfilter für Auszüge mit mehreren Pockets + Gebühren-Richtung

Der Revolut-Auszug enthält ALLE Produkte in einer Datei – im CHF-Export stehen
533 Giro-Zeilen und 255 Zeilen des Ersparten, jedes mit eigenem Kontostand-
Verlauf. Ohne Trennung landeten beide auf demselben Konto und die Pocket-
Bewegungen zählten doppelt.

- Import-Vorlage kennt einen Zeilenfilter („nur Zeilen aus Spalte X mit Wert Y"),
  Migration 0012; damit bekommt jedes Pocket sein eigenes Konto
- Gebührenzeilen (Betrag 0.00) können Belastung ODER Erstattung sein – beide
  sehen identisch aus. Die Richtung kommt jetzt aus der Kontostand-Spalte;
  ohne sie bleibt die Belastung der Normalfall
- Tests für beides; 90 grün

Nachgerechnet an den echten Auszügen: Raiffeisen 223.37, Revolut CHF 61.88,
Erspartes 6.71, Revolut EUR 0.68 – alle vier stimmen zeilengenau mit der
Kontostand-Spalte überein.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-21 09:01:07 +02:00
co-authored by Claude Opus 4.8
parent 595662e515
commit 4a6f7a0814
9 changed files with 190 additions and 7 deletions
@@ -0,0 +1,29 @@
"""Import-Vorlagen: Zeilenfilter je Spaltenwert.
Revolut exportiert alle Pockets („Giro", „Erspartes", „Anlagekonto" …) in EINE
Datei – jedes mit eigenem Saldo-Verlauf. Ohne Filter landeten alle Zeilen auf
demselben Konto und die Pocket-Bewegungen zählten doppelt.
Revision ID: 0012
Revises: 0011
Create Date: 2026-08-21
"""
from alembic import op
import sqlalchemy as sa
revision = "0012"
down_revision = "0011"
branch_labels = None
depends_on = None
def upgrade() -> None:
with op.batch_alter_table("import_templates") as batch:
batch.add_column(sa.Column("filter_column", sa.Integer(), nullable=True))
batch.add_column(sa.Column("filter_value", sa.String(length=120), nullable=True))
def downgrade() -> None:
with op.batch_alter_table("import_templates") as batch:
batch.drop_column("filter_value")
batch.drop_column("filter_column")