Add budget review screen and dashboard summary

This commit is contained in:
JKuijperM 2026-10-21 19:14:22 +02:00
parent 7c6777091d
commit e46a1e3668
3 changed files with 113 additions and 0 deletions

View File

@ -112,6 +112,17 @@
{% endif %}
</section>
{% if budget_overruns %}
<div class="messages">
<div class="message warning">
En el mes en curso ({{ budget_month|date:"F Y" }}, todas las cuentas, sin
importar los filtros de arriba) se pasan del reparto:
{% for row in budget_overruns %}<strong>{{ row.label }}</strong>, {{ row.real_amount|floatformat:2 }} €{% if row.progress is None %} sin nada asignado{% else %} de {{ row.defined_amount|floatformat:2 }} €{% endif %}{% if not forloop.last %}; {% endif %}{% endfor %}.
<a href="{% url 'budget_review' %}">Ver la revisión</a>
</div>
</div>
{% endif %}
<!-- ========================= -->
<!-- KPIs -->
<!-- ========================= -->

View File

@ -5,6 +5,8 @@
{% block content %}
<h1>Reparto por bloques</h1>
<p><a href="{% url 'budget_review' %}">Revisar cómo va el mes</a></p>
<p>Qué porcentaje de tus ingresos debería ir a cada bloque. Los bloques de gasto son los tipos que asignas a tus categorías; el ahorro son los traspasos a tus cuentas de ahorro.</p>
{# Arranca con el reparto guardado; el script de abajo lo recalcula al teclear. #}

View File

@ -0,0 +1,100 @@
{% extends "expenses/base.html" %}
{% load l10n %}
{% block title %}Revisión del reparto{% endblock %}
{% block content %}
<h1>Revisión del reparto</h1>
<p>Lo que definiste para cada bloque frente a lo que fue de verdad, sobre los ingresos del mes. <a href="{% url 'budget_blocks' %}">Cambiar el reparto</a></p>
<form method="get" class="filters">
<div class="filters-main">
<label class="sr-only" for="reviewYear">Año</label>
<select name="year" id="reviewYear">
{% for y in year_list %}
<option value="{{ y }}"{% if y == selected_year %} selected{% endif %}>{{ y }}</option>
{% endfor %}
</select>
<label class="sr-only" for="reviewMonth">Mes</label>
<select name="month" id="reviewMonth">
{% for m in months %}
<option value="{{ m.month }}"{% if m.month == selected_month %} selected{% endif %}>{{ m|date:"F" }}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-primary">Ver mes</button>
</div>
</form>
{% if not review %}
<p class="empty-state">{{ selected_date|date:"F Y"|capfirst }} está sin ingresos registrados, y sin ingresos no se puede repartir nada: cada bloque se mide como parte del ingreso del mes.</p>
{% else %}
<p>Ingresos de {{ selected_date|date:"F Y" }}: {{ review.income|floatformat:2 }} €.</p>
{% if not review.defined_any %}
<div class="messages">
<div class="message warning">
Aún no has asignado ningún porcentaje, así que todo el gasto cuenta como exceso.
<a href="{% url 'budget_blocks' %}">Define el reparto</a>
</div>
</div>
{% endif %}
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Bloque</th>
<th>Definido</th>
<th>Real</th>
<th>Real sobre definido</th>
</tr>
</thead>
<tbody>
{% for row in review.rows %}
<tr>
<td>{{ row.label }}</td>
<td>{{ row.defined_pct|floatformat:"-2" }} % · {{ row.defined_amount|floatformat:2 }} €</td>
<td>{{ row.real_pct|floatformat:1 }} % · {{ row.real_amount|floatformat:2 }} €</td>
<td>
<div class="progress-container">
<div class="progress-bar"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="{{ row.bar_width|unlocalize }}"
aria-label="{{ row.label }}: real sobre definido"
aria-valuetext="{% if row.progress is None %}{{ row.real_amount|floatformat:2 }} € sin nada asignado{% else %}{{ row.real_amount|floatformat:2 }} € de {{ row.defined_amount|floatformat:2 }} € ({{ row.progress|floatformat:1 }} %){% endif %}">
<div class="progress-fill {{ row.state }}"
style="width: {{ row.bar_width|unlocalize }}%"></div>
</div>
<span class="progress-label">
{% if row.progress is None %}Nada asignado{% else %}{{ row.progress|floatformat:1 }} %{% endif %}
</span>
{% if row.exceeded %}
<span class="badge badge-inactive">Se pasa</span>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p>Sin asignar en el reparto: {{ review.unassigned.pct|floatformat:"-2" }} % · {{ review.unassigned.amount|floatformat:2 }} €.</p>
{% if review.unclassified.amount %}
<div class="messages">
<div class="message warning">
Gasto sin clasificar: {{ review.unclassified.amount|floatformat:2 }} € ({{ review.unclassified.pct|floatformat:1 }} % del ingreso).
No es un bloque, así que no cuenta en ninguno, y mientras sea alto la comparación es poco fiable.
<a href="{% url 'category_list' %}">Clasifica sus categorías</a>
</div>
</div>
{% endif %}
{% endif %}
{% endblock %}