diff --git a/expenses_manager/expenses/static/expenses/css/base.css b/expenses_manager/expenses/static/expenses/css/base.css
index 6593c5a..196266f 100644
--- a/expenses_manager/expenses/static/expenses/css/base.css
+++ b/expenses_manager/expenses/static/expenses/css/base.css
@@ -617,6 +617,7 @@ button[type="submit"]:hover,
.comparison {
background: var(--color-surface-alt);
padding: 1rem;
+ margin-top: 1rem;
margin-bottom: 2rem;
border-radius: 8px;
}
@@ -1067,10 +1068,6 @@ tr:hover {
color: var(--color-danger-accent);
}
-.comparison {
- margin-top: 1rem;
-}
-
@media (max-width: 480px) {
.movement-account {
display: none;
diff --git a/expenses_manager/expenses/templates/expenses/dashboard.html b/expenses_manager/expenses/templates/expenses/dashboard.html
index f1fdb72..6b7e5f7 100644
--- a/expenses_manager/expenses/templates/expenses/dashboard.html
+++ b/expenses_manager/expenses/templates/expenses/dashboard.html
@@ -55,49 +55,51 @@
{% if range_active %}
diff --git a/expenses_manager/expenses/tests/test_dashboard.py b/expenses_manager/expenses/tests/test_dashboard.py
index 469db7f..effc428 100644
--- a/expenses_manager/expenses/tests/test_dashboard.py
+++ b/expenses_manager/expenses/tests/test_dashboard.py
@@ -172,6 +172,61 @@ def test_dashboard_compare_fills_difference_and_trend(auth_client, user, account
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):
response = auth_client.get(reverse('dashboard'), {'year': 2024, 'month': 3})
diff --git a/expenses_manager/expenses/views.py b/expenses_manager/expenses/views.py
index 30fad4e..6d79a78 100644
--- a/expenses_manager/expenses/views.py
+++ b/expenses_manager/expenses/views.py
@@ -900,6 +900,14 @@ def dashboard(request):
"difference": 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
accounts_charts = _build_account_charts(