Browse Source

fix: validate report boundaries and navigation

master
Codex 2 months ago
parent
commit
fc3aa51782
  1. 1
      core/context.py
  2. 5
      core/forms.py
  3. 2
      finance/reports.py
  4. 1
      tests/finance/test_reports.py
  5. 17
      tests/test_report_views.py

1
core/context.py

@ -5,4 +5,5 @@ def navigation(_request):
("transaction-list", "Transactions"),
("category-list", "Categories"),
("payee-list", "Payees"),
("monthly-report", "Reports"),
)}

5
core/forms.py

@ -110,6 +110,9 @@ class ReportSelectionForm(forms.Form):
if match is None:
raise forms.ValidationError("invalid_month", code="invalid_month")
try:
return date(int(match["year"]), int(match["month"]), 1)
month = date(int(match["year"]), int(match["month"]), 1)
except ValueError as exc:
raise forms.ValidationError("invalid_month", code="invalid_month") from exc
if month.year == date.max.year and month.month == 12:
raise forms.ValidationError("invalid_month", code="invalid_month")
return month

2
finance/reports.py

@ -148,6 +148,8 @@ def _validated_period(month: date, currency: str) -> tuple[date, str, date]:
errors["month"] = "invalid_date"
elif month.day != 1:
errors["month"] = "must_be_first_day"
elif month.year == date.max.year and month.month == 12:
errors["month"] = "invalid_month"
if not isinstance(currency, str) or _CURRENCY_RE.fullmatch(currency) is None:
errors["currency"] = "invalid_currency"
if errors:

1
tests/finance/test_reports.py

@ -110,6 +110,7 @@ def test_available_currencies_are_distinct_and_sorted():
("month", "currency", "errors"),
[
(date(2026, 1, 2), "BRL", {"month": "must_be_first_day"}),
(date(9999, 12, 1), "BRL", {"month": "invalid_month"}),
(date(2026, 1, 1), "brl", {"currency": "invalid_currency"}),
("2026-01", "BRL", {"month": "invalid_date"}),
],

17
tests/test_report_views.py

@ -91,6 +91,23 @@ def test_home_uses_brl_when_no_account_currency_exists(client, monkeypatch):
assert '<option value="BRL" selected>BRL</option>' in response.content.decode()
def test_monthly_report_rejects_month_without_representable_next_month(client):
response = client.get(
reverse("monthly-report"),
{"month": "9999-12", "currency": "BRL"},
)
assert response.status_code == 400
assert "invalid_month" in response.content.decode()
def test_reports_are_linked_from_non_dashboard_pages(client):
response = client.get(reverse("transaction-list"))
assert response.status_code == 200
assert f'href="{reverse("monthly-report")}">Reports</a>' in response.content.decode()
def test_negative_spending_and_balance_keep_their_sign_and_are_labeled(client):
bank = make_account()
post(bank, "-100.00", Transaction.Kind.OPENING, date(2025, 12, 31))

Loading…
Cancel
Save