Gráfico ingresos vs gastos #42
@ -617,6 +617,7 @@ button[type="submit"]:hover,
|
|||||||
.comparison {
|
.comparison {
|
||||||
background: var(--color-surface-alt);
|
background: var(--color-surface-alt);
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
margin-top: 1rem;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
@ -1067,10 +1068,6 @@ tr:hover {
|
|||||||
color: var(--color-danger-accent);
|
color: var(--color-danger-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.comparison {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.movement-account {
|
.movement-account {
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@ -55,49 +55,51 @@
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<form method="get" action="{% url 'dashboard' %}" id="filtered_form">
|
<form method="get" action="{% url 'dashboard' %}" id="filtered_form">
|
||||||
{% if period %}
|
<div class="filters-main">
|
||||||
<input type="hidden" name="period" value="{{ period }}" id="hiddenPeriod">
|
{% if period %}
|
||||||
{% endif %}
|
<input type="hidden" name="period" value="{{ period }}" id="hiddenPeriod">
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<label class="sr-only" for="account">Cuenta</label>
|
<label class="sr-only" for="account">Cuenta</label>
|
||||||
<select name="account" id="account">
|
<select name="account" id="account">
|
||||||
<option value="">Todas las cuentas</option>
|
<option value="">Todas las cuentas</option>
|
||||||
{% for acc in accounts %}
|
{% for acc in accounts %}
|
||||||
<option value="{{ acc.id }}" {% if selected_account == acc.id %}selected{% endif %}>
|
<option value="{{ acc.id }}" {% if selected_account == acc.id %}selected{% endif %}>
|
||||||
{{ acc.name }}
|
{{ acc.name }}
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
{% include "expenses/_date_range.html" %}
|
|
||||||
|
|
||||||
{% if not period %}
|
|
||||||
<label class="sr-only" for="yearSelect">Año</label>
|
|
||||||
<select name="year" id="yearSelect">
|
|
||||||
{% for y in year_list %}
|
|
||||||
<option value="{{ y }}" {% if y == selected_year %}selected{% endif %}>
|
|
||||||
{{ y }}
|
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label class="sr-only" for="monthSelect">Mes</label>
|
{% include "expenses/_date_range.html" %}
|
||||||
<select name="month" id="monthSelect">
|
|
||||||
<option value="">Todo el año</option>
|
|
||||||
{% for m in months %}
|
|
||||||
<option value="{{ m }}"{% if m == selected_month %}selected{% endif %}>
|
|
||||||
{{ m }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<label class="checkbox">
|
{% if not period %}
|
||||||
<input type="checkbox" name="compare" value="1" {% if compare_enabled %}checked{% endif %}>
|
<label class="sr-only" for="yearSelect">Año</label>
|
||||||
Comparar periodo anterior
|
<select name="year" id="yearSelect">
|
||||||
</label>
|
{% for y in year_list %}
|
||||||
|
<option value="{{ y }}" {% if y == selected_year %}selected{% endif %}>
|
||||||
|
{{ y }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Aplicar Filtros</button>
|
<label class="sr-only" for="monthSelect">Mes</label>
|
||||||
|
<select name="month" id="monthSelect">
|
||||||
|
<option value="">Todo el año</option>
|
||||||
|
{% for m in months %}
|
||||||
|
<option value="{{ m }}"{% if m == selected_month %}selected{% endif %}>
|
||||||
|
{{ m }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" name="compare" value="1" {% if compare_enabled %}checked{% endif %}>
|
||||||
|
Comparar periodo anterior
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Aplicar Filtros</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if range_active %}
|
{% if range_active %}
|
||||||
|
|||||||
@ -172,6 +172,61 @@ def test_dashboard_compare_fills_difference_and_trend(auth_client, user, account
|
|||||||
assert rows[category.name]['difference'] == 10.0
|
assert rows[category.name]['difference'] == 10.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_comparison_is_ordered_by_absolute_difference(
|
||||||
|
auth_client, user, account
|
||||||
|
):
|
||||||
|
# Las categorías se juntan con un set, y el orden de un set no es estable
|
||||||
|
# entre ejecuciones: sin ordenar, la tabla sale distinta en cada recarga.
|
||||||
|
moved_little = Category.objects.create(owner=user, name="Poco")
|
||||||
|
moved_a_lot = Category.objects.create(owner=user, name="Mucho")
|
||||||
|
|
||||||
|
for cat, previous, current in (
|
||||||
|
(moved_little, "10", "15"),
|
||||||
|
(moved_a_lot, "10", "90"),
|
||||||
|
):
|
||||||
|
Expense.objects.create(
|
||||||
|
owner=user, account=account, category=cat,
|
||||||
|
amount=Decimal(previous), date=date(2024, 2, 15),
|
||||||
|
)
|
||||||
|
Expense.objects.create(
|
||||||
|
owner=user, account=account, category=cat,
|
||||||
|
amount=Decimal(current), date=date(2024, 3, 10),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = auth_client.get(
|
||||||
|
reverse('dashboard'),
|
||||||
|
{'year': 2024, 'month': 3, 'compare': '1'},
|
||||||
|
)
|
||||||
|
|
||||||
|
order = [row['category'] for row in response.context['category_comparison']]
|
||||||
|
assert order == ["Mucho", "Poco"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dashboard_comparison_breaks_ties_by_category_name(
|
||||||
|
auth_client, user, account
|
||||||
|
):
|
||||||
|
# Misma diferencia en las tres: sin desempate explícito por nombre
|
||||||
|
# seguirían bailando entre recargas aunque el orden principal sea estable.
|
||||||
|
for name in ("Zeta", "Alfa", "Media"):
|
||||||
|
cat = Category.objects.create(owner=user, name=name)
|
||||||
|
Expense.objects.create(
|
||||||
|
owner=user, account=account, category=cat,
|
||||||
|
amount=Decimal("10"), date=date(2024, 2, 15),
|
||||||
|
)
|
||||||
|
Expense.objects.create(
|
||||||
|
owner=user, account=account, category=cat,
|
||||||
|
amount=Decimal("20"), date=date(2024, 3, 10),
|
||||||
|
)
|
||||||
|
|
||||||
|
response = auth_client.get(
|
||||||
|
reverse('dashboard'),
|
||||||
|
{'year': 2024, 'month': 3, 'compare': '1'},
|
||||||
|
)
|
||||||
|
|
||||||
|
order = [row['category'] for row in response.context['category_comparison']]
|
||||||
|
assert order == ["Alfa", "Media", "Zeta"]
|
||||||
|
|
||||||
|
|
||||||
def test_dashboard_chart_type_is_day_when_a_month_is_selected(auth_client):
|
def test_dashboard_chart_type_is_day_when_a_month_is_selected(auth_client):
|
||||||
response = auth_client.get(reverse('dashboard'), {'year': 2024, 'month': 3})
|
response = auth_client.get(reverse('dashboard'), {'year': 2024, 'month': 3})
|
||||||
|
|
||||||
|
|||||||
@ -901,6 +901,14 @@ def dashboard(request):
|
|||||||
"difference_abs": abs(cur_t - prev_t)
|
"difference_abs": abs(cur_t - prev_t)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# El orden de un set de Python no es estable entre ejecuciones, así que
|
||||||
|
# la tabla salía distinta en cada recarga. Primero lo que más se ha
|
||||||
|
# movido, y el nombre como desempate explícito: sin él, dos categorías
|
||||||
|
# con la misma diferencia seguirían bailando.
|
||||||
|
category_comparison.sort(
|
||||||
|
key=lambda row: (-row["difference_abs"], row["category"])
|
||||||
|
)
|
||||||
|
|
||||||
# Anual evolution by accounts
|
# Anual evolution by accounts
|
||||||
accounts_charts = _build_account_charts(
|
accounts_charts = _build_account_charts(
|
||||||
accounts, balance_by_account, selected_year, today
|
accounts, balance_by_account, selected_year, today
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user