fix: Eine berechnete Budget-Zeile ueberlebt jetzt das Kopieren

Beide Kopierwege uebernahmen nur planned_cents. Ein Eintrag der Betriebsart
"berechnet" fiel dabei still auf "fest" zurueck - mit der Zahl, die zufaellig
zuletzt in planned_cents stand. Der Bezug zu den Vertraegen war danach weg,
ohne dass irgendwo stand, dass er weg ist.

Vorlage -> Monat (copy_template_to_month) zieht jetzt nach, was
ensure_customized nebenan laengst richtig macht. Monat -> Vorlage
(copy_month_to_template) nimmt die Betriebsart mit, statt die gerade
herauskommende Zahl einzufrieren: Eine berechnete Zeile soll auch in der
Vorlage berechnet bleiben.

Dabei aufgefallen: Eine berechnete Zeile gehoert auch dann in die Vorlage,
wenn planned_cents null ist - ihre Zahl steht in den Vertraegen. Eine feste
Null bleibt dagegen draussen, sonst fuellte sich die Vorlage mit Nullzeilen
fuer jede je beruehrte Kategorie.

Das war Backend-Ergaenzung D und damit die letzte offene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-10-21 21:04:46 +02:00
co-authored by Claude Opus 5
parent 37b29934ef
commit 4c61a02c53
2 changed files with 90 additions and 7 deletions
+63
View File
@@ -399,3 +399,66 @@ def test_vorlage_nennt_betriebsart_und_zuschlag(admin):
assert zeile["planned_cents"] == 12000
assert zeile["modus"] == "berechnet"
assert zeile["zuschlag_cents"] == 2500
def _frische_kategorie(admin, name):
import secrets
gruppe = next(g for g in admin.get("/api/categories").json()
if g["kind"] == "expense")
return admin.post("/api/categories", json={
"group_id": gruppe["id"], "name": f"{name} {secrets.token_hex(3)}"}).json()["id"]
def test_berechnete_zeile_ueberlebt_den_weg_in_den_monat(admin):
"""Vorlage -> Monat darf die Betriebsart nicht verlieren.
Fiel die Zeile auf "fest" zurueck, stand als Soll die Zahl, die zufaellig
zuletzt in planned_cents lag - und der Bezug zu den Vertraegen war weg,
ohne dass irgendwo stand, dass er weg ist.
"""
today = date.today()
label = f"{today.year:04d}-{today.month:02d}"
kat = _frische_kategorie(admin, "Berechnet-Monat")
admin.put(f"/api/budget/template/category/{kat}",
params={"planned_cents": 0, "modus": "berechnet", "zuschlag_cents": 1500})
r = admin.post(f"/api/budget/{label}/from-template")
assert r.status_code == 200, r.text
zeile = next(c for g in r.json()["groups"] for c in g["categories"]
if c["category_id"] == kat)
assert zeile["modus"] == "berechnet"
assert zeile["zuschlag_cents"] == 1500
def test_berechnete_zeile_ueberlebt_den_weg_in_die_vorlage(admin):
"""Monat -> Vorlage ebenso: "diesen Monat als Standard festlegen" soll die
Betriebsart uebernehmen, nicht die gerade herauskommende Zahl einfrieren."""
today = date.today()
label = f"{today.year:04d}-{today.month:02d}"
kat = _frische_kategorie(admin, "Berechnet-Vorlage")
admin.put(f"/api/budget/{label}/category/{kat}",
params={"planned_cents": 0, "modus": "berechnet", "zuschlag_cents": 2500})
r = admin.post(f"/api/budget/template/from-month/{label}")
assert r.status_code == 200, r.text
zeile = next(l for l in r.json()["lines"] if l["category_id"] == kat)
assert zeile["modus"] == "berechnet"
assert zeile["zuschlag_cents"] == 2500
def test_eine_feste_null_landet_nicht_in_der_vorlage(admin):
"""Nichts Geplantes bleibt nichts Geplantes - sonst fuellte sich die
Vorlage mit Nullzeilen fuer jede je beruehrte Kategorie."""
today = date.today()
label = f"{today.year:04d}-{today.month:02d}"
kat = _frische_kategorie(admin, "Nullzeile")
admin.put(f"/api/budget/{label}/category/{kat}", params={"planned_cents": 0})
r = admin.post(f"/api/budget/template/from-month/{label}")
assert all(l["category_id"] != kat for l in r.json()["lines"])