feat: Vorschlaege mit Rhythmus, Start und Hinweis

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:26:48 +02:00
parent 28d1267f0c
commit bf3f56118e
2 changed files with 36 additions and 3 deletions

View File

@@ -88,15 +88,19 @@
{% if suggestions %}
<table>
<thead>
<tr><th>Name</th><th>Betrag</th><th>Rhythmus</th><th>Fälligkeitstag</th><th></th></tr>
<tr><th>Name</th><th>Betrag</th><th>Rhythmus</th><th>Fälligkeitstag</th><th>Start</th><th></th></tr>
</thead>
<tbody>
{% for s in suggestions %}
<tr>
<td>{{ s.name }}</td>
<td class="{{ 'neg' if s.amount < 0 else '' }}">{{ s.amount|eur }} €</td>
<td class="{{ 'neg' if s.amount < 0 else '' }}">
{{ s.amount|eur }} €
{% if s.hinweis %}<span class="muted">{{ s.hinweis }}</span>{% endif %}
</td>
<td>{{ s.rhythm|de_label }}</td>
<td>{{ s.due_day }}</td>
<td>{{ s.start_date.strftime('%d.%m.%Y') if s.start_date else '' }}</td>
<td>
<form class="inline-form" hx-ext="json-form" hx-post="/api/recurring" hx-swap="none"
hx-on::after-request="if(event.detail.successful){window.location.reload()}">
@@ -105,6 +109,7 @@
<input type="hidden" name="rhythm" value="{{ s.rhythm }}">
<input type="hidden" name="due_day" data-type="int" value="{{ s.due_day }}">
<input type="hidden" name="category_id" data-type="int" value="{{ s.category_id if s.category_id is not none else '' }}">
<input type="hidden" name="start_date" value="{{ s.start_date.isoformat() if s.start_date else '' }}">
<button type="submit">Vorschlag übernehmen</button>
</form>
</td>
@@ -113,8 +118,9 @@
</tbody>
</table>
{% else %}
<p class="muted">Keine Vorschläge — erkannt werden Serien aus mindestens 3 Monaten gleichartiger Buchungen.</p>
<p class="muted">Keine Vorschläge.</p>
{% endif %}
<p class="muted">Erkannt werden monatliche, vierteljährliche und jährliche Serien; Betrag = jeweils letzte Buchung.</p>
</fieldset>
</section>

View File

@@ -246,6 +246,33 @@ def test_planning_page_shows_empty_suggestions_hint(client):
assert r.status_code == 200
assert "Vorschläge aus Buchungen" in r.text
assert "Keine Vorschläge" in r.text
assert ("Erkannt werden monatliche, vierteljährliche und jährliche "
"Serien; Betrag = jeweils letzte Buchung.") in r.text
def test_vorschlaege_zeigen_rhythmus_und_start(client, db):
# Synthetische vierteljaehrliche Serie relativ zu date.today(), da die
# Route suggest_recurring() ohne today-Injektion aufruft (echter
# Aktiv-Check gegen date.today()). Schrittweite ~91 Tage rueckwaerts,
# letzte Buchung 30 Tage vor heute (innerhalb des Aktiv-Fensters).
from app.models.tables import Transaction
client.post("/login", data={"username": "admin", "password": "geheim"})
acc = Account(bank="dkb", iban="DE-SUG-1", name="S", type="giro")
db.add(acc)
db.flush()
today = date.today()
booking_dates = [today - timedelta(days=d) for d in (303, 212, 121, 30)]
for d in booking_dates:
db.add(Transaction(account_id=acc.id, booking_date=d, amount=Decimal("-55.08"),
purpose="p", counterparty="Rundfunk Synth", status="confirmed",
dedup_hash=f"sug-{d.isoformat()}"))
db.commit()
r = client.get("/planung").text
letzte_buchung = booking_dates[-1]
assert "vierteljährlich" in r
assert letzte_buchung.strftime("%d.%m.%Y") in r
assert 'name="start_date"' in r
def test_salden_page_stichtag_and_month_overview(client, db):