Commit Graph

184 Commits

Author SHA1 Message Date
50fd58daa7 Anade tests del rango de fechas
Cubren las reglas que no se ven leyendo la vista: que los extremos son
inclusivos, que cada fecha puede ir sola, que un rango invertido o una fecha
ilegible se ignoran en vez de vaciar el listado, y que el rango tiene
precedencia sobre los selectores de ano y mes.

En el dashboard se comprueba ademas que la comparativa queda desactivada y
avisada cuando hay rango, que el eje del grafico cambia de dia a mes al pasar
el umbral, y que los huecos del eje salen a cero en lugar de desaparecer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:32:52 +02:00
836f3e8524 Aplica el rango de fechas al dashboard
Un rango que solo funciona en el listado obliga a cambiar de pantalla para
responder "cuanto gaste en ese viaje". El dashboard acepta ahora los mismos
date_from y date_to, con la misma precedencia sobre ano y mes.

Dos decisiones que no son obvias:

La comparativa se desactiva cuando hay rango. Un rango arbitrario no tiene un
"periodo anterior" evidente, y elegir uno por el usuario daria un porcentaje
que parece significativo sin serlo. Se distingue entre pedirla y poder
aplicarla (compare_requested / compare_enabled) para poder avisar de que se
ha ignorado en vez de desmarcar la casilla en silencio.

El eje del grafico lo decide la longitud del rango, no el mes: por debajo de
62 dias se agrupa por dia y por encima por mes, que es donde un grafico
diario deja de leerse. El eje se rellena con ceros en los huecos, igual que
ya hacia el resto, para que un periodo sin gastos no colapse la escala. La
agrupacion diaria usa el propio campo date y no TruncDate, porque date ya es
un DateField y TruncDate le aplicaria una conversion de zona horaria que en
SQLite revienta si no esta instalada la base de datos de husos.

La tarjeta de proyeccion a fin de mes se oculta con rango activo: proyectar a
fin de mes desde un periodo arbitrario no significa nada.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:32:43 +02:00
c395c638e9 Anade un rango de fechas libre al listado de gastos
Los selectores de ano y mes solo saben expresar periodos naturales completos.
"Del 15 de marzo al 3 de abril" o "desde que empece el trabajo nuevo" no se
pueden pedir, y son justo las preguntas que uno le hace a un gestor de gastos.

Se anaden dos parametros date_from y date_to, opcionales por separado: "desde
el 1 de marzo" sin fecha final es un filtro perfectamente valido. Si el rango
viene invertido se ignoran las dos fechas, porque devolver una lista vacia sin
explicacion es peor que descartar un filtro mal puesto.

Cuando hay rango, manda el rango y los selectores de ano y mes no se aplican:
combinar ambos criterios da intersecciones que el usuario no ha pedido y no
puede ver. La plantilla lo avisa con un mensaje y un enlace para quitarlo, en
vez de dejar los selectores mostrando un periodo que no es el que se esta
viendo.

Los campos de fecha comparten estilo con el resto de filtros, incluido el
anillo de foco, para que no desentonen con los selectores de al lado.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:32:11 +02:00
bb5ade4cc9 Quita un div de cierre sobrante en el listado de repostajes
La plantilla cerraba un div que nunca se habia abierto, asi que el
</div> del .table-wrap acababa cerrando el contenedor de la pagina y todo lo
que viniera despues quedaba fuera de su sitio. Ahora las aperturas y los
cierres cuadran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:31:31 +02:00
2c05318e0b Anade tests de los traspasos
El campo is_transfer tiene dos comportamientos opuestos y uno de ellos falla
en silencio: si el filtro se cuela en un calculo de saldo, los numeros siguen
apareciendo, solo que mal. Los tests se organizan en dos bloques que cubren
justo esa frontera.

- Analisis: el traspaso no cuenta en los KPIs de home ni del dashboard, ni en
  el total del listado, ni en la comparativa de periodos, ni en el progreso de
  los objetivos de pago y presupuesto.
- Saldo: el traspaso SI cuenta en current_balance, monthly_balance,
  balance_until, monthly_net y en el objetivo de ahorro.

Tambien se cubre que el listado de gastos siga mostrando el traspaso aunque su
total lo descuente, que es la parte que un filtro puesto de mas romperia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:31:23 +02:00
2d7cd8cb68 Permite marcar un traspaso y distinguirlo en la interfaz
Sin esto el campo is_transfer no es alcanzable: no aparece en los formularios
de gasto ni de ingreso, asi que nadie puede marcar un movimiento como
traspaso.

Ademas, un movimiento que no suma al total tiene que verse distinto o el
usuario no entendera por que sus numeros no cuadran con la tabla. Se anade un
distintivo "Traspaso" en el listado de gastos, en los ultimos movimientos de
home y en los gastos recientes del dashboard, que son los tres sitios donde
se listan movimientos sin agregarlos.

El distintivo usa una variante nueva, .badge-neutral, en vez de reutilizar
.badge-inactive: un traspaso es informacion, no un estado degradado, y
pintarlo con el color de error o de cuenta inactiva daria a entender que algo
va mal. Las variables que usa (--color-chip-bg y --color-text-muted) ya
existen en los dos temas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:31:11 +02:00
e2b756bcc7 Excluye los traspasos de los calculos de analisis
Un traspaso mueve dinero entre cuentas propias: suma al saldo de cada cuenta
pero no es gasto ni ingreso real. Las agregaciones de analisis (KPIs, graficos
de home y dashboard, comparativa de periodos, totales del listado y objetivos
de pago y presupuesto) pasan a excluirlo.

Hay dos comportamientos opuestos sobre el mismo campo y conviene no
confundirlos:

- Analisis: excluye los traspasos. De ahi el helper _analysis_expenses, que
  documenta en su docstring donde NO debe usarse.
- Saldo: los incluye. _account_balances, current_balance, monthly_balance,
  balance_until y monthly_net quedan intactos a proposito; filtrarlos ahi
  descuadraria todos los saldos de la aplicacion de forma silenciosa, que es
  mucho peor y menos evidente que inflar un KPI.

Por el mismo motivo la rama saving de Goal.progress tampoco filtra: mandar
dinero a la cuenta de ahorro es justo como se progresa en ese objetivo.

Los ultimos movimientos de home dejan de colgar de la consulta de KPIs y usan
la suya propia: son un listado, no un analisis, y un traspaso tiene que poder
verse ahi. Lo mismo en el listado de gastos, donde la tabla los sigue
mostrando y solo los totales los descuentan.

Los managers por defecto de Expense e Income no se tocan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:30:43 +02:00
8c0225a297 Anade el campo is_transfer a gastos e ingresos
Un traspaso entre cuentas propias se registra hoy como dos movimientos: un
gasto en la cuenta de origen y un ingreso en la de destino. Los saldos
cuadran, pero el gasto infla los analisis (KPIs, graficos, objetivos) con
dinero que nunca ha salido del patrimonio del usuario.

Marcar el movimiento hace falta en los dos modelos, no en uno solo. Una
"categoria de traspaso" cubriria unicamente la mitad de la operacion, porque
Income no tiene categoria; de ahi el booleano en ambos.

El campo solo se anade aqui: ninguna consulta lo usa todavia, asi que este
commit no cambia ningun numero de la aplicacion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 08:30:01 +02:00
b4508a13db Anade una escala tipografica para los encabezados
base.css nunca ha definido tamanos de encabezado, asi que los ponia el
navegador: 32px el h1, 24px el h2 y 18,7px el h3. En una vista densa de
datos como el dashboard eso compite con las propias cifras, y ademas dejaba
los tamanos a merced de que un encabezado cambiara de nivel.

Fija 1.625rem / 1.25rem / 1rem con font-weight 600. Los margenes van en rem
a proposito: los del navegador van en em, escalan con el tamano de fuente y
dejan huecos distintos entre secciones que deberian separarse igual.

Va en la zona de estilos base, antes de los componentes, para que las
reglas que ya ajustan encabezados concretos (.dashboard-context h1,
.settings-card h2, .home-section > h2, .info-panel-content h2) sigan
ganando por orden.

Afecta a toda la aplicacion. Lo que mas se nota es el titulo de pagina:
pierde 21px de aire por arriba, porque el margen superior del h1 pasa a 0 y
el padding de .content impedia que colapsara. Entre secciones de la home no
cambia nada, ahi manda el margin-bottom de 2.5rem de .home-section. Ningun
media query toca tamanos de fuente, asi que en movil la escala es la misma,
y el bloque no declara colores, asi que el tema oscuro no se entera.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 20:13:49 +02:00
3d47f8bd90 Usa un unico h1 por pagina en el resto de vistas
Cuatro paginas tenian su titulo en <h2> sin ningun <h1> encima: el
formulario de categoria, ajustes, el login y la ayuda de contrasena. Y el
listado de categorias si tenia <h1>, pero colgaba de el dos <h3>, saltandose
el nivel intermedio.

Sube los titulos de pagina a <h1> y los subtitulos al nivel que les toca:
las dos secciones del listado de categorias a <h2> y las tres tarjetas de
ajustes a <h2>. El selector .settings-card h3 pasa a h2 para seguirlas y
mantener su margin: 0 0 0.25rem, que es lo que evita que el titulo abra
hueco dentro de la tarjeta.

En login y ayuda de contrasena el <h1> ademas importa para el layout:
.auth-container tiene padding: 2rem, y el padding impide que colapse el
margen superior del primer hijo. Con la escala que viene despues, un <h2>
ahi sumaria su margen a ese padding y dejaria el titulo a 56px del borde;
el <h1> no lleva margen superior.

Verificado renderizando las 36 vistas: todas con un solo h1 y sin saltos
de nivel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 20:13:28 +02:00
beeda95d5d Corrige la jerarquia de encabezados del dashboard
El dashboard empezaba en <h2> y bajaba hasta <h4>, sin ningun <h1>. La
pagina no tenia titulo de documento y las secciones colgaban de un nivel
que no existia, asi que un lector de pantalla no podia recorrerla por
encabezados.

Sube cada nivel uno: el nombre de la cuenta (o "Todas las cuentas") pasa a
<h1>, las secciones a <h2> y el nombre de cada cuenta dentro de su tarjeta
de grafica a <h3>. El selector .dashboard-context h2 pasa a h1 para seguir
al elemento.

Solo cambian los niveles. Como base.css no definia tamanos de encabezado,
el aspecto lo daban los valores por defecto del navegador y esto si mueve
los tamanos; el commit de la escala tipografica los fija.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 20:13:03 +02:00
1cc13bae57 Borra JS muerto y la seccion comparativa duplicada del dashboard
El bloque <script> del formulario de periodo definia clearPeriodPreset y
tres consts, y no lo llamaba nadie: no hay ningun onchange ni listener que
lo use. Debajo habia ademas un segundo <script> entero comentado, de una
version anterior de los presets. Los ids hiddenPeriod, yearSelect y
monthSelect siguen en el formulario, que los usa para las etiquetas.

La seccion "Comparativa" del final mostraba la misma diferencia y el mismo
porcentaje que "Resumen comparativo" ya ensena arriba, y ambas salen bajo
el mismo {% if compare_enabled %}. Es un subconjunto estricto: la de arriba
anade los totales de los dos periodos. La de abajo ademas imprimia
kpi_difference_abs con un "+" condicional, asi que una bajada se veia como
"12,00 € (-13,3%)", sin signo en la cifra y con el porcentaje en negativo.
Se queda la de arriba, que imprime "-12,00 €".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 20:12:38 +02:00
f074005a7f Extrae helpers del dashboard y elimina el N+1 de saldos
La vista pasaba de 226 lineas y mezclaba tres cosas distintas: traducir los
parametros de la URL a un periodo, calcular los KPIs y montar las series de
las graficas por cuenta. Saca las dos que son autonomas a _resolve_period y
_build_account_charts y deja dashboard en 170.

De paso quita el N+1: la vista llamaba a current_balance() una vez por
cuenta para el KPI y otra vez por cuenta dentro del bucle de graficas, y
cada llamada son dos queries. Ahora los saldos salen de _account_balances,
que los calcula con dos agregados, y se reparten por un dict indexado por
id. Medido con CaptureQueriesContext: con 1/3/6 cuentas se pasa de 25/53/95
queries a 21/37/61.

accounts deja de ser un QuerySet y pasa a lista, porque ahora se recorre
mas de una vez, y la cuenta seleccionada se busca en memoria en vez de con
otra query.

Lo que sigue costando una query por cuenta es monthly_balance(), que no se
toca aqui.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 20:12:03 +02:00
b4c9f8507b Anade tests de regresion del dashboard
La vista dashboard concentra los presets de periodo, el filtro por cuenta,
la comparativa y las series de las graficas, y solo tenia cubierto el
filtro por ano. Antes de tocarla conviene fijar por escrito lo que hace
hoy, para que el refactor que viene no pueda cambiarlo sin que salte algo.

Cubre los tres presets (this_month, last_month, this_year), el filtro por
cuenta con su KPI de saldo, los valores por defecto de la comparativa
cuando no esta activada, la diferencia y la tendencia cuando si lo esta,
el chart_type segun se mire un mes o el ano entero, y el recorte a diez
categorias de la grafica de distribucion.

El test de la grafica de categorias comprueba tambien que by_category
sigue trayendo las doce: el recorte es solo de la grafica, no del listado.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:58:31 +02:00
57d44dad8e Pasa chart_type al contexto del dashboard
La vista calculaba chart_type ("day" o "month") pero nunca lo metia en el
contexto, asi que la plantilla lo leia siempre vacio y el
{% if chart_type == 'day' %} del titulo de la grafica no se cumplia nunca.
El encabezado decia "Por Meses" tambien al mirar un mes suelto, cuando la
serie que se dibuja debajo es por dias.

Solo anade la clave que faltaba; el calculo ya estaba bien.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:58:22 +02:00
e5aa66369a Merge branch 'main' into dev 2026-09-09 14:29:19 +00:00
4eb120927b Anade CLAUDE.md y el informe de analisis del proyecto
CLAUDE.md documenta la estructura de directorios (que tiene tres niveles
llamados expenses_manager y despista), los comandos habituales, las
variables de entorno y las decisiones de auth, para no tener que deducirlo
de settings.py cada vez.

ANALISIS_code.md es un informe de solo lectura sobre UX, deuda tecnica y
mantenibilidad, ordenado por impacto. Sirve de lista de trabajo pendiente;
varios de sus puntos ya estan resueltos en los commits anteriores.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:22:08 +02:00
814487a390 Anade columna con el numero de gastos de cada categoria
El listado de categorias no daba ninguna pista de cuales estan en uso.
Anade la columna con el mismo criterio que el de etiquetas: annotate en la
vista para no disparar una query por fila.

Cuenta solo los gastos directos de cada categoria, no los de sus
descendientes. Si el numero incluyera a los hijos, un padre sin gastos
propios mostraria una cifra que no corresponde a ninguna fila suya y que no
cuadraria con lo que se ve al filtrar por esa categoria.

Con Category.Meta.ordering = [Lower("name")], Django anade la expresion al
GROUP BY del annotate. Verificado que no multiplica filas ni fusiona
categorias cuyo nombre solo difiere en mayusculas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:21:35 +02:00
f154127f9e Anade panel de ayuda en el formulario de objetivos
El formulario tiene tres tipos de objetivo y campos que solo aplican a
algunos, sin nada en pantalla que lo explique: no habia forma de saber que
"Account" solo cuenta para ahorro o que "Period" solo cuenta para
presupuesto.

Anade un desplegable, cerrado por defecto, que describe para que sirve cada
tipo y que hace cada campo. Usa <details>/<summary> nativo en vez de
JavaScript, que es accesible por teclado de serie y ya es el patron de los
filtros avanzados de gastos.

El texto esta contrastado con Goal._period_start(), Goal.progress y
Goal.progress_state(), y los nombres de campo son los que el formulario
muestra de verdad. Esas etiquetas salen hoy en ingles porque GoalForm no
define labels y no hay i18n configurada; el panel usa las reales para que
coincida con lo que se ve, no las traducidas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:19:30 +02:00
f135797d31 Ordena etiquetas, categorias, cuentas y objetivos ignorando mayusculas
Tag era el unico de los tres modelos con nombre sin ordering en su Meta, asi
que las etiquetas salian en orden de insercion. Al anadirlo aparecio el
problema de fondo: con ordering = ["name"], SQLite ordena por valor binario y
en ASCII todas las mayusculas van antes que todas las minusculas, de modo que
salia AA, MK, ZZ, mk, ms en vez de AA, MK, mk, ms, ZZ.

Importa mas de lo que parece porque en produccion la base de datos es
PostgreSQL, que ordena segun la configuracion regional. El orden pasa a
calcularse en la consulta con Lower("name") en vez de depender de la colacion
del motor, asi que local y NAS coinciden.

Los tres Meta usan ahora ordering = [Lower("name")]. Django admite
expresiones ahi, pero no se propaga solo a todos los sitios: habia tres
sitios con orden explicito que lo pisaban y tambien se corrigen.

- tag_list y account_list tenian .order_by("name")
- goal_list ordenaba igual
- _category_tree reordena los hermanos en memoria, y Python compara por code
  point igual que SQLite, asi que el arbol tampoco quedaba bien

Las dos migraciones son AlterModelOptions, sin cambios de esquema, pero hay
que desplegarlas.

Lo que no resuelve: los acentos. Lower() normaliza mayusculas y minusculas,
pero como se ordenan "N", "a" o "u" sigue dependiendo de la colacion de cada
motor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:18:52 +02:00
e814bc2d03 Anade filtro por tipo en objetivos y unifica la barra de progreso
El listado de objetivos mezclaba pagos, presupuestos y ahorros sin forma de
separarlos. Anade un filtro por tipo, con el mismo patron que los filtros de
gastos, y ordena los objetivos por nombre. Un valor de "kind" que no este en
KIND_CHOICES se ignora en vez de romper.

De paso, la barra de progreso pasa a usar goal.progress_state en lugar de
decidir el color en la plantilla con un if sobre el porcentaje. La logica ya
vivia en el modelo, que ademas distingue presupuestos (donde acercarse al
100% es un aviso) del resto; la plantilla del dashboard seguia con las clases
antiguas low/medium/high, que se retiran del CSS.

Incluye estado vacio y thead/tbody en la tabla, que le faltaban.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:16:58 +02:00
22563df48a Muestra las etiquetas en tabla con su numero de gastos
El listado de etiquetas era una lista sin mas, sin forma de saber cuales
estaban en uso. Pasa a tabla, en linea con el resto de listados, y anade
una columna con el numero de gastos que usa cada etiqueta.

El contador se calcula con annotate(Count("expenses")) en la vista, en vez
de contar desde la plantilla, para no provocar una query por fila.

Incluye tambien el estado vacio, que la lista no tenia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 15:36:02 +02:00
e65b15ccf8 Extrae helpers de vistas y rehace los listados de cuentas y categorias
Saca de home() dos ayudantes reutilizables:

- _account_balances(): calcula el saldo de cada cuenta con dos queries
  agregadas en lugar de llamar a account.current_balance() en bucle,
  que disparaba un N+1.
- _category_tree(): aplana las categorias en preorden con su profundidad,
  para poder indentarlas en la plantilla.

Con ellos, account_list pasa a mostrar el saldo calculado de cada cuenta
(antes usaba current_balance() desde la plantilla) y category_list muestra
un arbol indentado en vez de una columna "Categoria padre" que obligaba a
reconstruir la jerarquia mentalmente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 15:34:37 +02:00
b04a8db080 Amplia el dashboard de Home: KPIs, saldos, comparativa y movimientos
home() (views.py) y expenses/home.html pasan de mostrar tres numeros
sueltos y un listado plano de ultimos gastos a un panel mas completo:

- Saldo total y desglose por cuenta (account_balances/total_balance),
  calculado con dos queries agregadas (values().annotate(Sum(...)))
  en vez de N+1 llamadas a account.current_balance() en un bucle.
- Comparativa del gasto del mes en curso contra el mes anterior
  (prev_total/diff_amount/diff_pct), con aviso de que el mes en curso
  esta incompleto y la comparacion no es equivalente.
- Avisos (has_alerts) cuando hay objetivos excedidos o cuentas en
  negativo, con enlace directo a objetivos/cuentas.
- "Ultimos movimientos" mezcla gastos e ingresos (antes solo se veian
  los ultimos gastos) ordenados por fecha, con icono/color segun tipo.
- Se reutiliza una unica query de Goal tanto para el widget de
  objetivos (show_on_home) como para las alertas de excedidos
  (is_exceeded()), en vez de dos queries separadas.

base.css: nuevo bloque "/* Home widgets */" con las clases que usa la
plantilla (.home-section, .kpi-grid/.kpi-card, .balance-*,
.movements-list/.movement-*, .amount-positive/.amount-negative,
.comparison), incluyendo un ajuste responsive que oculta la cuenta en
movil (<480px).

No relacionado con los formularios de los commits anteriores; se
incluye aqui porque estaba pendiente de subir en la misma rama.
2026-09-03 18:01:01 +02:00
5852941136 Mueve 'expenses' antes que 'django.contrib.admin' en INSTALLED_APPS
Sintoma: /accounts/password_change/ renderizaba la pantalla del admin
de Django (cabecera "Administracion de Django", breadcrumb "Inicio >
Cambio de contrasena") en vez de la plantilla de la app.

Causa: con APP_DIRS=True (settings.py) y sin DIRS explicito, Django
busca plantillas recorriendo INSTALLED_APPS en orden y usa la PRIMERA
coincidencia por ruta relativa. django.contrib.admin trae sus propias
registration/password_change_form.html y
registration/password_change_done.html. Como 'django.contrib.admin'
aparecia antes que 'expenses' en la lista, sus plantillas ganaban
siempre a las de nuestra app, que ni siquiera llegaban a evaluarse.

Verificado con el shell de Django (get_template(...).origin.name):

  Antes del cambio:
    registration/password_change_form.html -> .../django/contrib/admin/templates/registration/password_change_form.html
    registration/password_change_done.html -> .../django/contrib/admin/templates/registration/password_change_done.html

  Despues del cambio:
    registration/password_change_form.html -> .../expenses/templates/registration/password_change_form.html
    registration/password_change_done.html -> .../expenses/templates/registration/password_change_done.html

Esto tambien explica por que el bug de las plantillas rotas (extendian
"base.html", que no existe; ver commit anterior) llevaba tanto tiempo
sin dar error: al estar sombreadas, esas plantillas nunca se
renderizaban, asi que su TemplateDoesNotExist nunca saltaba. Y explica
por que el login SI funcionaba pese a la colision de app: el admin
publica su login en "admin/login.html", no en "registration/login.html",
asi que ahi no habia conflicto.

Efecto secundario esperado y comprobado (no es una regresion, pero hay
que saberlo): /admin/password_change/, que Django resuelve tambien por
la ruta "registration/password_change_form.html" cuando un admin
cambia su propia contrasena, ahora renderiza con el layout completo de
la app (menu de navegacion, etc.) en vez del look propio del admin.
Probado con un superusuario contra /admin/, /admin/login/,
/admin/password_change/ y /admin/password_change/done/: los cuatro
responden 200 y el formulario funciona (AdminPasswordChangeForm hereda
los mismos campos old_password/new_password1/new_password2 de
PasswordChangeForm, asi que el parcial generico de campos los pinta
sin problema). El resto del admin (listados, cambio de contrasena de
otros usuarios, etc.) no usa ninguna plantilla bajo registration/ ni
admin/ que la app sobrescriba, asi que no le afecta.

No se reordena alfabeticamente esta lista en el futuro sin tener en
cuenta esto: hay un comentario en el propio settings.py explicandolo.
2026-09-03 18:00:22 +02:00
94308cbfe8 Corrige registration/password_change_*.html: extendian "base.html", que no existe
Ambas plantillas hacian {% extends "base.html" %}, pero en este
proyecto no existe ninguna plantilla llamada asi a nivel raiz: el
layout de la app se llama "expenses/base.html" (esta dentro de
expenses/templates/expenses/, no de expenses/templates/). Con
APP_DIRS=True y DIRS=[] en settings.py, Django busca "base.html" tal
cual en la carpeta templates/ de cada app instalada y no lo encuentra
en ninguna, asi que un {% extends %} a esa ruta lanza
TemplateDoesNotExist en cuanto se intenta renderizar la plantilla.

Esto llevaba tiempo sin detectarse porque, hasta el commit de
INSTALLED_APPS de esta misma tanda, django.contrib.admin sombreaba
estas dos plantillas con las suyas propias (ver ese commit): la
plantilla rota de la app nunca llegaba a cargarse, asi que el error
nunca saltaba. Al arreglar el orden de INSTALLED_APPS estas plantillas
pasan a usarse de verdad, así que había que arreglarlas ahora sí en el
mismo movimiento.

Cambios:
- Ambas extienden "expenses/base.html".
- Se migran al parcial expenses/_form_fields.html y a class="app-form",
  igual que el resto de formularios (ver commit anterior).
- Se anade un enlace de "Volver" a {% url 'home' %} en las dos, y un
  {% block title %} (heredaban el titulo generico "Expenses manager").
2026-09-03 17:59:53 +02:00
581eb202c7 Migra formularios de gastos/ingresos/cuentas/etc al parcial comun
Sustituye {{ form.as_p }} (o el bucle for field in form repetido a
mano en expense_form.html) por {% include "expenses/_form_fields.html" %}
y anade class="app-form" al <form> en:

  - categories/form.html, categories/list.html (alta de categoria)
  - expenses/account_form.html
  - expenses/expense_form.html
  - expenses/income_form.html
  - expenses/tag_form.html
  - fuel/create.html
  - goals/form.html
  - registration/login.html

De paso:
- categories/list.html: se corrige indentacion de la tabla y las
  etiquetas <a> de "Editar"/"Eliminar" (estaban dentro de una celda
  sin problema funcional, solo desalineadas) y se anade una <th> vacia
  para la columna de acciones.
- goals/form.html: el bloque title y el <h1> tenian "Nuevo objetivo"
  fijo aunque la vista goal_edit ya pasa title="Editar objetivo" en el
  contexto; ahora la plantilla usa {{ title }}, asi que el formulario
  de edicion deja de decir "Nuevo objetivo" por error.

No se toca la logica de las vistas ni los formularios en forms.py.
2026-09-03 17:59:28 +02:00
d70d0e4239 Anade sistema de estilos de formulario reutilizable
Los formularios usaban form.as_p (salida generica de Django) o bucles
manuales for field in form repetidos en cada plantilla, sin estilos
propios: inputs/selects/textareas sin padding, borde ni foco visibles,
y cada plantilla resolvia los errores/ayuda de campo a su manera.

Cambios:
- base.css: nuevas clases .app-form, .form-field (label + control en
  columna), estilos de input/select/textarea (incluye estado :focus
  con el color de foco del tema), .form-field-checkbox para campos
  booleanos y .form-help para el help_text de los campos.
- Nuevo parcial expenses/_form_fields.html: itera los campos del
  formulario y renderiza label, control, help_text y errores de forma
  consistente. Incluye un caso especial para el campo "tags" de gastos
  (CheckboxSelectMultiple), que se pinta como lista de chips en vez de
  checkboxes sueltos.

Este commit solo anade el estilo y el parcial; las plantillas que lo
adoptan van en el siguiente commit.
2026-09-03 17:59:01 +02:00
31f6d31c9b Merge pull request 'dev' (#32) from dev into main
Reviewed-on: #32
2026-08-28 11:46:52 +00:00
e29c2bc5f2 Reordena el menu de navegacion y limpia enlaces comentados
Mueve el enlace de Cuentas junto al de Repostajes y elimina los
enlaces comentados a Etiquetas y Categorias del menu principal (ya
estan accesibles desde el desplegable de Configuraciones).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 13:31:57 +02:00
c371e4cdc3 Mejoras de accesibilidad en formularios, progreso y navegacion
- Anade la utilidad .sr-only en base.css para labels ocultos
  visualmente pero accesibles a lectores de pantalla.
- dashboard.html: labels sr-only en los selects de cuenta/ano/mes del
  formulario de filtros; ARIA completo (role=progressbar,
  aria-valuemin/max/now/label/valuetext) en la barra de progreso del
  widget de objetivos; corrige el bug real de esa barra, que usaba
  goal.percentage en vez de goal.bar_width y podia desbordar el 100%;
  corrige colspan="7" a colspan="5" en la fila vacia de gastos
  recientes (la tabla tiene 5 columnas).
- expense_list.html: id + label sr-only en los selects de ano/mes/
  cuenta; label explicito para el select de categoria; role="group"
  y aria-label en el bloque de filtros por etiquetas (con
  aria-hidden en el texto "Tags:" para evitar doble lectura);
  paginacion pasada de <div> a <nav aria-label="Paginacion de
  gastos">; corrige colspan="4" a colspan="6" en el estado vacio de
  la tabla (6 columnas).
- goals/list.html: mismo ARIA de progreso que en dashboard.html;
  <h2>Objetivos</h2> a <h1>, para que la pagina empiece en h1 como
  el resto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 13:29:35 +02:00
d88276556a Merge pull request 'Updated the web' (#31) from dev into main
Reviewed-on: #31
2026-08-28 11:28:25 +00:00
161ea36ff5 Theming de graficos Chart.js para modo claro/oscuro
- Anade charts.js con getChartColors, applyCartesianColors y
  registerThemedChart para centralizar los colores de los graficos segun
  el tema activo y refrescarlos en vivo al cambiar de tema (via
  MutationObserver sobre data-theme).
- Corrige el bug de renderizado en graficos de tipo 'line'
  (TypeError: t.startsWith is not a function): los colores de tema ahora
  se fijan en las opciones al crear cada grafico en lugar de mutarlas
  despues de new Chart(...).
- Anade variables --chart-grid-color y --chart-fill-color en base.css
  (light/dark) e incluye charts.js en base.html.
- Aplica el theming a categoryChart, mainChart y accountChart{id}
  (dashboard.html), miniChart (home.html) y monthlyChart/kmChart
  (fuel/list.html).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 17:43:59 +02:00
2fec314f4c Visual improvements 2026-08-05 12:44:56 +02:00
0135421655 Added other test to the navigation 2026-07-31 11:03:30 +02:00
255c035797 Unified the confirm/delete/back buttons 2026-07-31 11:03:06 +02:00
07b9b7c878 Added a night vision and modified the theme of the application 2026-07-30 08:26:48 +02:00
176f9d075c Improvements in the mobile view 2026-07-29 16:41:35 +02:00
bc1a957292 Make responsive 2026-07-29 15:33:10 +02:00
4ad2b59a1c Fixed minor typos 2026-07-29 15:14:14 +02:00
4e03c65c07 Created the .env.example 2026-07-29 13:29:28 +02:00
3e7e1f0aec Merge pull request 'main' (#30) from main into dev
Reviewed-on: #30
2026-07-29 11:21:49 +00:00
e18b1f48d9 Merge branch 'dev' into main 2026-07-29 11:21:12 +00:00
6eb9097908 Actualizar README.md 2026-07-29 11:20:39 +00:00
2d484aa40a Modified the model Goal to use cache with the progress 2026-07-29 08:56:53 +02:00
e5d6b37290 Merge pull request 'Fixed typo' (#28) from dev into main
Reviewed-on: #28
2026-07-28 13:23:17 +00:00
181f1cc98a Merge branch 'dev' of https://gitea.kuijper.es/jkuijperm/expenses_manager into dev 2026-07-28 15:21:16 +02:00
b8fa1ddf72 Fixed typo 2026-07-28 15:21:02 +02:00
956841bb02 Merge pull request 'dev' (#17) from dev into main
Reviewed-on: #17
2026-07-28 11:01:03 +00:00
360e398712 Merge branch 'main' into dev 2026-07-28 11:00:39 +00:00