fix: Anfangssaldo gilt ab dem Stichtag

Der Stichtag eines Kontos wurde gespeichert und angezeigt, von der
Saldo-Berechnung aber ignoriert: sie addierte jede Buchung des Kontos,
auch die aus der Zeit vor dem Stichtag. Damit lief jeder Saldo um die
gesamte Vorgeschichte daneben.

Der Anfangssaldo beschreibt jetzt den Stand am ENDE des Stichtags – so
steht er auf dem Auszug. Buchungen bis einschliesslich dieses Tages
zaehlen nicht mehr zum Kontostand, bleiben aber in Liste und
Auswertungen. Beim Uebertrag wird jede Seite an ihrem eigenen Stichtag
gemessen; der Vermoegensverlauf folgt derselben Regel. Ohne Stichtag
zaehlt weiterhin alles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-21 15:26:21 +02:00
co-authored by Claude Opus 5
parent d88efebfeb
commit 5dbfd6abc4
6 changed files with 116 additions and 31 deletions
+60
View File
@@ -102,3 +102,63 @@ def test_transfer_requires_other_account(admin):
"to_account_id": eur["id"], "amount_cents": 100,
})
assert r.status_code == 400
def test_stichtag_excludes_earlier_bookings(admin):
"""Der Anfangssaldo gilt am Ende des Stichtags.
Buchungen bis einschliesslich dieses Tages stecken schon darin und dürfen den
Kontostand nicht ein zweites Mal bewegen; erst spätere zählen. Ohne die
Datumsprüfung addierte die Berechnung jede Buchung des Kontos – der Stichtag
war reine Dekoration und Salden liefen um die Vorgeschichte daneben.
"""
stichtag = date(2026, 3, 31)
acc = _mk_account(admin, name="Lohn", currency="EUR",
opening_balance_cents=20000, opening_date=str(stichtag))
def saldo() -> int:
return admin.get(f"/api/accounts/{acc['id']}").json()["balance_cents"]
# Vor dem Stichtag: bleibt buchbar, ändert den Saldo aber nicht.
admin.post("/api/transactions", json={"type": "expense", "date": "2026-02-10",
"account_id": acc["id"], "amount_cents": 7000})
assert saldo() == 20000
# Am Stichtag selbst: ebenfalls schon im Anfangssaldo enthalten.
admin.post("/api/transactions", json={"type": "income", "date": str(stichtag),
"account_id": acc["id"], "amount_cents": 5000})
assert saldo() == 20000
# Erst der Tag danach bewegt den Saldo.
admin.post("/api/transactions", json={"type": "income", "date": "2026-04-01",
"account_id": acc["id"], "amount_cents": 1500})
assert saldo() == 21500
# Die alten Buchungen sind weiterhin da – nur der Saldo lässt sie aus.
liste = admin.get("/api/transactions", params={"account_id": acc["id"]}).json()
daten = {t["date"] for t in (liste["items"] if isinstance(liste, dict) else liste)}
assert {"2026-02-10", "2026-03-31", "2026-04-01"} <= daten
def test_stichtag_gilt_je_konto_beim_transfer(admin):
"""Beim Übertrag wird jede Seite an ihrem eigenen Stichtag gemessen."""
von = _mk_account(admin, name="Von", currency="EUR",
opening_balance_cents=50000, opening_date="2026-03-31")
nach = _mk_account(admin, name="Nach", currency="EUR",
opening_balance_cents=1000, opening_date="2026-01-31")
# Liegt vor dem Stichtag von "Von", aber nach dem von "Nach".
r = admin.post("/api/transactions", json={
"type": "transfer", "date": "2026-02-15", "account_id": von["id"],
"to_account_id": nach["id"], "amount_cents": 30000,
})
assert r.status_code == 201, r.text
assert admin.get(f"/api/accounts/{von['id']}").json()["balance_cents"] == 50000
assert admin.get(f"/api/accounts/{nach['id']}").json()["balance_cents"] == 31000
def test_ohne_stichtag_zaehlt_alles(admin):
acc = _mk_account(admin, name="Ohne", currency="EUR", opening_balance_cents=10000)
admin.post("/api/transactions", json={"type": "expense", "date": "2020-01-01",
"account_id": acc["id"], "amount_cents": 2500})
assert admin.get(f"/api/accounts/{acc['id']}").json()["balance_cents"] == 7500