diff --git a/backend/app/services/bank_sync.py b/backend/app/services/bank_sync.py index d8f5f66..51f3bf0 100644 --- a/backend/app/services/bank_sync.py +++ b/backend/app/services/bank_sync.py @@ -501,13 +501,14 @@ def _startdatum(db: Session, user: User, konto: Account, ein Einzelabruf das Startdatum der uebrigen Konten mit vor, und deren Umsaetze aus dem uebersprungenen Zeitraum kaemen nie an. """ + heute = date.today() if tage is not None: # Uebergeht den letzten Abruf. Noetig, wenn etwas nicht angekommen ist: # Sonst laege es fuer immer vor dem Fenster, obwohl die Bank es haette. - return date.today() - timedelta(days=tage) + return heute - timedelta(days=tage) if verknuepfung.last_sync_at: - return verknuepfung.last_sync_at.date() - timedelta(days=3) - frueheste = date.today() - timedelta(days=ERSTER_ABRUF_TAGE) + return min(heute, verknuepfung.last_sync_at.date() - timedelta(days=3)) + frueheste = heute - timedelta(days=ERSTER_ABRUF_TAGE) letzte = ( db.query(func.max(Transaction.date)) .filter(Transaction.owner_id == user.id, Transaction.account_id == konto.id) @@ -515,7 +516,12 @@ def _startdatum(db: Session, user: User, konto: Account, ) if letzte is None: return frueheste - return max(frueheste, letzte + timedelta(days=1)) + # NIE in die Zukunft: Die Schnittstelle lehnt das rundweg ab + # ("date_from can not be in the future") – und zwar erst beim Umsatz-Abruf, + # nachdem die Saldi schon gelesen sind. Dann sieht es aus, als kaemen nur + # Saldi und keine Umsaetze. Liegt die neueste Buchung auf heute, waere der + # Tag danach morgen. + return min(heute, max(frueheste, letzte + timedelta(days=1))) def hole_umsaetze(db: Session, user: User, verbindung: BankConnection, diff --git a/backend/tests/test_bank.py b/backend/tests/test_bank.py index a1b5224..ef43bba 100644 --- a/backend/tests/test_bank.py +++ b/backend/tests/test_bank.py @@ -1053,3 +1053,35 @@ def test_verschluckter_umsatz_laesst_sich_nachholen(admin, fake_bank): assert admin.post(f"/api/bank/links/{link['id']}/sync", params={"tage": 400}).status_code == 400 + + +def test_startdatum_liegt_nie_in_der_zukunft(admin, fake_bank): + """Liegt die neueste Buchung auf heute, waere der Tag danach morgen – und + die Schnittstelle lehnt das ab ("date_from can not be in the future"). + Der Fehler faellt erst beim Umsatz-Abruf an, nachdem die Saldi schon + gelesen sind: Es sieht dann aus, als kaemen nur Saldi und keine Umsaetze. + """ + _app(admin) + heute = date.today() + uid = f"acc-{secrets.token_hex(4)}" + gegenstelle = fake_bank( + konten=[{"uid": uid, "currency": "EUR"}], + umsaetze=[dict(UMSATZ, entry_reference=f"tx-{secrets.token_hex(4)}", + booking_date=heute.isoformat())]) + state = admin.post("/api/bank/connect", + json={"aspsp_name": f"Zukunft-{secrets.token_hex(3)}", + "country": "DE"}).json()["state"] + admin.post(f"/api/bank/callback?state={state}&code=guter-code") + link = [v for v in admin.get("/api/bank/links").json() if v["remote_uid"] == uid][0] + konto = _konto(admin, name=f"Zukunft {secrets.token_hex(3)}", currency="EUR") + # Eine Buchung von HEUTE – der Tag danach ist morgen. + admin.post("/api/transactions", json={ + "type": "expense", "date": heute.isoformat(), "account_id": konto["id"], + "amount_cents": 500, "payee": "von heute"}) + admin.post(f"/api/bank/links/{link['id']}/account/{konto['id']}") + + r = admin.post(f"/api/bank/links/{link['id']}/sync") + assert r.status_code == 200, r.text + assert gegenstelle.date_from <= heute.isoformat(), \ + f"date_from liegt in der Zukunft: {gegenstelle.date_from}" + assert r.json()["ab"] == heute.isoformat()