fix: Automatischer Abruf lief gegen die Abfragegrenze der Banken

"Weniger als 15 Minuten lassen die Banken nicht zu" stand als Hinweis im
Formular. Das war erfunden. PSD2 verpflichtet die Banken zu VIER
unbeaufsichtigten Abfragen je Konto und Tag – haeufiger duerfen sie ablehnen,
und N26 tut es: "Too many requests", und der Abruf scheitert, bis das
Zeitfenster weiterrueckt.

Bei zehn angebundenen Konten und einem Takt von 15 Minuten waren es tausende
Anfragen am Tag. Der Takt betraegt jetzt mindestens sechs Stunden, also genau
die zugesicherten vier je Tag. Bestehende, zu knappe Einstellungen werden beim
Faelligkeitspruefen mitgebremst – sonst liefe eine gespeicherte 15 weiter
dagegen.

Von Hand abrufen bleibt unbegrenzt: Dabei sitzt der Nutzer davor, und fuer
diesen Fall gilt die Grenze nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-30 13:11:35 +02:00
co-authored by Claude Opus 5
parent 6ed91ddd58
commit e0d8882851
6 changed files with 48 additions and 15 deletions
+25 -6
View File
@@ -812,29 +812,29 @@ def test_takt_null_laeuft_nie_und_sonst_erst_wenn_faellig(admin):
assert bank_sync.faellig(aus, jetzt) is False
nie_gelaufen = BankConnection(owner_id=1, aspsp_name="X", aspsp_country="DE",
poll_minutes=60)
poll_minutes=720)
assert bank_sync.faellig(nie_gelaufen, jetzt) is True
frisch = BankConnection(owner_id=1, aspsp_name="X", aspsp_country="DE",
poll_minutes=60,
poll_minutes=720,
last_sync_at=jetzt - timedelta(minutes=30))
assert bank_sync.faellig(frisch, jetzt) is False
# SQLite gibt Zeitstempel OHNE Zeitzone zurueck – das darf nicht werfen.
ohne_zone = BankConnection(owner_id=1, aspsp_name="X", aspsp_country="DE",
poll_minutes=60,
last_sync_at=(jetzt - timedelta(hours=2)).replace(tzinfo=None))
poll_minutes=720,
last_sync_at=(jetzt - timedelta(hours=13)).replace(tzinfo=None))
assert bank_sync.faellig(ohne_zone, jetzt) is True
def test_takt_wird_geprueft(admin, fake_bank):
verbindung, _, _ = _verbunden(admin, fake_bank)
assert admin.put(f"/api/bank/connections/{verbindung}/takt",
params={"minutes": 5}).status_code == 400
params={"minutes": 15}).status_code == 400, "zu haeufig"
assert admin.put(f"/api/bank/connections/{verbindung}/takt",
params={"minutes": 0}).json()["poll_minutes"] == 0
assert admin.put(f"/api/bank/connections/{verbindung}/takt",
params={"minutes": 240}).json()["poll_minutes"] == 240
params={"minutes": 720}).json()["poll_minutes"] == 720
def test_spaeter_gelieferte_gegenseite_wird_nicht_noch_einmal_gebucht(admin, fake_bank):
@@ -1288,3 +1288,22 @@ def test_saldo_kommt_erst_nach_den_umsaetzen(admin, fake_bank, monkeypatch):
aus_liste = [v for v in admin.get("/api/bank/links").json()
if v["remote_uid"] == uid][0]
assert aus_liste["balance_cents"] is None, "Saldo ohne Umsaetze geschrieben"
def test_zu_knapper_takt_wird_gebremst():
"""Eine bestehende Einstellung von 15 Minuten liefe sonst weiter gegen die
Grenze der Bank – und jeder Abruf scheiterte mit 'Too many requests'."""
from datetime import datetime, timezone
from app.models import BankConnection
from app.services import bank_sync
jetzt = datetime.now(timezone.utc)
knapp = BankConnection(owner_id=1, aspsp_name="N26", aspsp_country="DE",
poll_minutes=15,
last_sync_at=jetzt - timedelta(minutes=20))
assert bank_sync.faellig(knapp, jetzt) is False, "15 Minuten duerfen nicht greifen"
# Nach sechs Stunden schon.
knapp.last_sync_at = jetzt - timedelta(hours=7)
assert bank_sync.faellig(knapp, jetzt) is True