feat: Erneut verbinden statt loeschen und neu anlegen
Um eine Verbindung aufzufrischen – etwa nachdem man im Control Panel weitere Konten verknuepft hat – haette man sie bisher erst loeschen muessen. Dabei gehen die Kontozuordnungen mit verloren, also genau das, was man behalten will; ein zweiter Verbindungsversuch haette sonst einen doppelten Eintrag erzeugt. Jetzt frischt das Verbinden derselben Bank den bestehenden Eintrag auf: neue Sitzung, neue Konten, bestehende Zuordnungen bleiben. Der Warntext beim Trennen sagt ausserdem, dass Trennen dafuer gar nicht noetig ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -146,11 +146,24 @@ def starte_verbindung(db: Session, user: User, aspsp_name: str, land: str,
|
|||||||
raise BankFehler("Es ist noch keine Bank-Anwendung hinterlegt.")
|
raise BankFehler("Es ist noch keine Bank-Anwendung hinterlegt.")
|
||||||
|
|
||||||
state = secrets.token_urlsafe(24)
|
state = secrets.token_urlsafe(24)
|
||||||
|
# Dieselbe Bank erneut verbinden frischt den bestehenden Eintrag auf, statt
|
||||||
|
# einen zweiten anzulegen. Sonst müsste man vorher löschen – und dabei
|
||||||
|
# gingen die Kontozuordnungen mit verloren, obwohl man sie behalten will.
|
||||||
|
verbindung = (
|
||||||
|
db.query(BankConnection)
|
||||||
|
.filter(BankConnection.owner_id == user.id,
|
||||||
|
BankConnection.aspsp_name == aspsp_name,
|
||||||
|
BankConnection.aspsp_country == land.upper())
|
||||||
|
.order_by(BankConnection.id.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if verbindung is None:
|
||||||
verbindung = BankConnection(
|
verbindung = BankConnection(
|
||||||
owner_id=user.id, aspsp_name=aspsp_name, aspsp_country=land.upper(),
|
owner_id=user.id, aspsp_name=aspsp_name, aspsp_country=land.upper(),
|
||||||
pending_state=state,
|
|
||||||
)
|
)
|
||||||
db.add(verbindung)
|
db.add(verbindung)
|
||||||
|
verbindung.pending_state = state
|
||||||
|
verbindung.last_error = None
|
||||||
db.flush()
|
db.flush()
|
||||||
|
|
||||||
antwort = _client(db, user).starte_autorisierung(
|
antwort = _client(db, user).starte_autorisierung(
|
||||||
|
|||||||
@@ -250,8 +250,12 @@ def _verbunden(admin, fake_bank_fn, **kw):
|
|||||||
"currency": "EUR", "account_id": {"iban": "DE02120300000000202051"},
|
"currency": "EUR", "account_id": {"iban": "DE02120300000000202051"},
|
||||||
}])
|
}])
|
||||||
gegenstelle = fake_bank_fn(**kw)
|
gegenstelle = fake_bank_fn(**kw)
|
||||||
|
# Eigener Bankname je Aufruf: Erneutes Verbinden derselben Bank frischt
|
||||||
|
# bewusst den bestehenden Eintrag auf, sonst erbte jeder Test die
|
||||||
|
# Verbindung des vorigen.
|
||||||
|
bank = f"Testbank-{secrets.token_hex(3)}"
|
||||||
state = admin.post("/api/bank/connect",
|
state = admin.post("/api/bank/connect",
|
||||||
json={"aspsp_name": "N26", "country": "DE"}).json()["state"]
|
json={"aspsp_name": bank, "country": "DE"}).json()["state"]
|
||||||
antwort = admin.post(f"/api/bank/callback?state={state}&code=guter-code").json()
|
antwort = admin.post(f"/api/bank/callback?state={state}&code=guter-code").json()
|
||||||
link = [v for v in admin.get("/api/bank/links").json()
|
link = [v for v in admin.get("/api/bank/links").json()
|
||||||
if v["connection_id"] == antwort["connection_id"]][0]
|
if v["connection_id"] == antwort["connection_id"]][0]
|
||||||
@@ -519,3 +523,35 @@ def test_konten_ohne_kennung_werden_gemeldet_statt_verworfen(admin, fake_bank):
|
|||||||
assert antwort["uebermittelt"] == 1
|
assert antwort["uebermittelt"] == 1
|
||||||
assert antwort["ohne_kennung"] == 1
|
assert antwort["ohne_kennung"] == 1
|
||||||
assert antwort["konten"] == 0
|
assert antwort["konten"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_erneutes_verbinden_behaelt_die_kontozuordnung(admin, fake_bank):
|
||||||
|
"""Ohne das müsste man vor dem Neuverbinden löschen – und verlöre dabei
|
||||||
|
die Zuordnungen, die man gerade behalten will."""
|
||||||
|
_app(admin)
|
||||||
|
uid = f"acc-{secrets.token_hex(4)}"
|
||||||
|
konten = [{"uid": uid, "name": "Girokonto", "currency": "EUR",
|
||||||
|
"account_id": {"iban": "DE02120300000000202051"}}]
|
||||||
|
fake_bank(konten=konten)
|
||||||
|
|
||||||
|
def verbinde():
|
||||||
|
state = admin.post("/api/bank/connect",
|
||||||
|
json={"aspsp_name": "Testbank", "country": "DE"}).json()["state"]
|
||||||
|
return admin.post(f"/api/bank/callback?state={state}&code=guter-code").json()
|
||||||
|
|
||||||
|
erste = verbinde()
|
||||||
|
link = [v for v in admin.get("/api/bank/links").json() if v["remote_uid"] == uid][0]
|
||||||
|
konto = _konto(admin, name="Zuordnung bleibt", currency="EUR")
|
||||||
|
admin.post(f"/api/bank/links/{link['id']}/account/{konto['id']}")
|
||||||
|
|
||||||
|
zweite = verbinde()
|
||||||
|
assert zweite["connection_id"] == erste["connection_id"], "kein zweiter Eintrag"
|
||||||
|
|
||||||
|
danach = [v for v in admin.get("/api/bank/links").json() if v["remote_uid"] == uid]
|
||||||
|
assert len(danach) == 1
|
||||||
|
assert danach[0]["account_id"] == konto["id"], "die Zuordnung muss bleiben"
|
||||||
|
|
||||||
|
# Und es steht genau eine Verbindung zu dieser Bank da.
|
||||||
|
passende = [c for c in admin.get("/api/bank/connections").json()
|
||||||
|
if c["aspsp_name"] == "Testbank"]
|
||||||
|
assert len(passende) == 1
|
||||||
|
|||||||
@@ -597,7 +597,7 @@
|
|||||||
"validUntil": "Zustimmung gültig bis {{datum}}",
|
"validUntil": "Zustimmung gültig bis {{datum}}",
|
||||||
"expiresSoon": "Zustimmung läuft am {{datum}} ab – danach musst du neu autorisieren.",
|
"expiresSoon": "Zustimmung läuft am {{datum}} ab – danach musst du neu autorisieren.",
|
||||||
"disconnect": "Verbindung trennen",
|
"disconnect": "Verbindung trennen",
|
||||||
"disconnectConfirm": "Verbindung zu „{{name}}“ trennen? Bereits abgerufene Buchungen bleiben.",
|
"disconnectConfirm": "Verbindung zu „{{name}}“ trennen? Dabei gehen die Kontozuordnungen verloren. Zum Auffrischen musst du NICHT trennen – such die Bank einfach erneut und verbinde nochmal, die Zuordnungen bleiben dann.",
|
||||||
"search": "Bank suchen",
|
"search": "Bank suchen",
|
||||||
"searchPlaceholder": "z. B. revolut oder n26",
|
"searchPlaceholder": "z. B. revolut oder n26",
|
||||||
"searchHint": "Ohne Land wird überall gesucht. Das ist meist richtig: Das Sitzland einer Bank steht nicht in der IBAN – Revolut vergibt deutsche IBANs, sitzt aber in Litauen.",
|
"searchHint": "Ohne Land wird überall gesucht. Das ist meist richtig: Das Sitzland einer Bank steht nicht in der IBAN – Revolut vergibt deutsche IBANs, sitzt aber in Litauen.",
|
||||||
|
|||||||
@@ -597,7 +597,7 @@
|
|||||||
"validUntil": "Consent valid until {{datum}}",
|
"validUntil": "Consent valid until {{datum}}",
|
||||||
"expiresSoon": "Consent expires on {{datum}} – you will need to authorise again.",
|
"expiresSoon": "Consent expires on {{datum}} – you will need to authorise again.",
|
||||||
"disconnect": "Disconnect",
|
"disconnect": "Disconnect",
|
||||||
"disconnectConfirm": "Disconnect from “{{name}}”? Transactions already fetched stay.",
|
"disconnectConfirm": "Disconnect from “{{name}}”? The account mappings are lost. You do NOT need to disconnect to refresh – just search the bank and connect again, the mappings are kept.",
|
||||||
"search": "Search for a bank",
|
"search": "Search for a bank",
|
||||||
"searchPlaceholder": "e.g. revolut or n26",
|
"searchPlaceholder": "e.g. revolut or n26",
|
||||||
"searchHint": "Without a country it searches everywhere, which is usually right: a bank's home country is not in the IBAN – Revolut issues German IBANs but is based in Lithuania.",
|
"searchHint": "Without a country it searches everywhere, which is usually right: a bank's home country is not in the IBAN – Revolut issues German IBANs but is based in Lithuania.",
|
||||||
|
|||||||
Reference in New Issue
Block a user