feat: Vorschlaege mit Rhythmus, Start und Hinweis
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,15 +88,19 @@
|
|||||||
{% if suggestions %}
|
{% if suggestions %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<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>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for s in suggestions %}
|
{% for s in suggestions %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ s.name }}</td>
|
<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.rhythm|de_label }}</td>
|
||||||
<td>{{ s.due_day }}</td>
|
<td>{{ s.due_day }}</td>
|
||||||
|
<td>{{ s.start_date.strftime('%d.%m.%Y') if s.start_date else '–' }}</td>
|
||||||
<td>
|
<td>
|
||||||
<form class="inline-form" hx-ext="json-form" hx-post="/api/recurring" hx-swap="none"
|
<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()}">
|
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="rhythm" value="{{ s.rhythm }}">
|
||||||
<input type="hidden" name="due_day" data-type="int" value="{{ s.due_day }}">
|
<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="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>
|
<button type="submit">Vorschlag übernehmen</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
@@ -113,8 +118,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
|
<p class="muted">Erkannt werden monatliche, vierteljährliche und jährliche Serien; Betrag = jeweils letzte Buchung.</p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -246,6 +246,33 @@ def test_planning_page_shows_empty_suggestions_hint(client):
|
|||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "Vorschläge aus Buchungen" in r.text
|
assert "Vorschläge aus Buchungen" in r.text
|
||||||
assert "Keine Vorschläge" 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):
|
def test_salden_page_stichtag_and_month_overview(client, db):
|
||||||
|
|||||||
Reference in New Issue
Block a user