Modificaciones en filtrado y creada opción para marcar que un gasto/ingreso es un traspaso de cuentas #34

Merged
jkuijperm merged 18 commits from dev into main 2026-09-16 07:50:18 +00:00
4 changed files with 80 additions and 6 deletions
Showing only changes of commit c395c638e9 - Show all commits

View File

@ -396,7 +396,8 @@ button[type="submit"]:hover,
gap: 0.5rem; gap: 0.5rem;
} }
.filters-main select { .filters-main select,
.filters-main input[type="date"] {
padding: 0.5rem 0.7rem; padding: 0.5rem 0.7rem;
font-family: inherit; font-family: inherit;
font-size: 0.95rem; font-size: 0.95rem;
@ -406,11 +407,18 @@ button[type="submit"]:hover,
border-radius: 6px; border-radius: 6px;
} }
.filters-main select:focus { .filters-main select:focus,
.filters-main input[type="date"]:focus {
outline: 2px solid var(--color-focus-ring); outline: 2px solid var(--color-focus-ring);
outline-offset: 1px; outline-offset: 1px;
} }
.range-notice {
margin-top: 0.5rem;
font-size: 0.9rem;
color: var(--color-text-muted);
}
.filters-advanced { .filters-advanced {
margin-top: 0.5rem; margin-top: 0.5rem;
} }

View File

@ -0,0 +1,12 @@
{% comment %}
Campos del rango de fechas libre. Se incluye dentro de un <form method="get">.
Espera date_from y date_to en el contexto.
{% endcomment %}
<label class="sr-only" for="dateFrom">Desde</label>
<input type="date" name="date_from" id="dateFrom"
value="{% if date_from %}{{ date_from|date:'Y-m-d' }}{% endif %}">
<label class="sr-only" for="dateTo">Hasta</label>
<input type="date" name="date_to" id="dateTo"
value="{% if date_to %}{{ date_to|date:'Y-m-d' }}{% endif %}">

View File

@ -47,10 +47,20 @@
{% endfor %} {% endfor %}
</select> </select>
{% include "expenses/_date_range.html" %}
<button type="submit" class="btn btn-primary">Filtrar</button> <button type="submit" class="btn btn-primary">Filtrar</button>
<a href="{% url 'expense_list' %}" class="btn btn-secondary">Limpiar</a> <a href="{% url 'expense_list' %}" class="btn btn-secondary">Limpiar</a>
</div> </div>
{% if range_active %}
<p class="range-notice">
Mostrando el rango de fechas seleccionado; los filtros de año y mes no
se aplican.
<a href="{% url 'expense_list' %}">Quitar el rango</a>
</p>
{% endif %}
<br> <br>
<details class="filters-advanced" <details class="filters-advanced"

View File

@ -31,6 +31,37 @@ def _get_int(value):
return None return None
# Umbral del eje del gráfico con rango libre: dos meses es donde un gráfico
# diario deja de leerse, así que por encima se agrupa por mes.
RANGE_DAY_CHART_MAX_DAYS = 62
def _get_date(value):
"""Parsea una fecha AAAA-MM-DD de la URL. None si falta o no es válida."""
try:
return datetime.strptime(value, "%Y-%m-%d").date()
except (TypeError, ValueError):
return None
def _resolve_date_range(request):
"""Lee el rango de fechas libre de la URL.
Devuelve (date_from, date_to); cualquiera de los dos puede ser None, porque
"desde el 1 de marzo" sin fecha final es un filtro válido.
Si el rango está invertido (desde > hasta) se ignoran LAS DOS fechas: devolver
una lista vacía sin explicación es peor que ignorar un filtro mal puesto.
"""
date_from = _get_date(request.GET.get("date_from"))
date_to = _get_date(request.GET.get("date_to"))
if date_from and date_to and date_from > date_to:
return None, None
return date_from, date_to
def _safe_next(request): def _safe_next(request):
next_url = request.POST.get("next") or request.GET.get("next") next_url = request.POST.get("next") or request.GET.get("next")
@ -336,9 +367,19 @@ def expense_list(request):
if (t_id:= _get_int(t)) is not None if (t_id:= _get_int(t)) is not None
] ]
date_from, date_to = _resolve_date_range(request)
range_active = bool(date_from or date_to)
# Un solo criterio de periodo a la vez: si hay rango, manda el rango y los
# selectores de año y mes no se aplican (la plantilla lo avisa).
if range_active:
if date_from:
expenses = expenses.filter(date__gte=date_from)
if date_to:
expenses = expenses.filter(date__lte=date_to)
else:
if year: if year:
expenses = expenses.filter(date__year=year) expenses = expenses.filter(date__year=year)
if month: if month:
expenses = expenses.filter(date__month=month) expenses = expenses.filter(date__month=month)
@ -407,6 +448,9 @@ def expense_list(request):
"page_obj": page_obj, "page_obj": page_obj,
"selected_year": year, "selected_year": year,
"selected_month": month, "selected_month": month,
"date_from": date_from,
"date_to": date_to,
"range_active": range_active,
"selected_category": category, "selected_category": category,
"categories": categories, "categories": categories,
"year_list": [y.year for y in year_list], "year_list": [y.year for y in year_list],