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> |
||
|---|---|---|
| expenses_manager | ||
| .gitignore | ||
| ANALISIS_code.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| README.md | ||
expenses_manager
Aplicación web de gestión de finanzas personales (gastos, ingresos, cuentas, categorías, objetivos y repostajes de vehículo), construida con Django y desplegada en un NAS Synology mediante Docker + PostgreSQL.
Características
- Registro de gastos e ingresos, con categorías jerárquicas y etiquetas.
- Cuentas con cálculo de saldo (soft-delete para preservar históricos).
- Dashboard con gráficos por categoría, evolución de saldo y comparativas de periodo.
- Objetivos de tres tipos: pago/deuda, presupuesto (mensual/anual) y ahorro.
- Módulo de repostajes con cálculo de consumo, precio por litro y km recorridos.
Stack
- Python 3.11 · Django 5.2
- PostgreSQL (producción) / SQLite (desarrollo local por defecto)
- Gunicorn + WhiteNoise
- Docker (despliegue) · Jenkins (CI)
Desarrollo local
Por defecto, en local la aplicación usa SQLite, así que no necesitas levantar ninguna base de datos para empezar.
Requisitos
- Python 3.11 (estos pasos asumen un entorno conda, pero sirve cualquier entorno virtual).
Pasos
-
Clona el repositorio:
git clone https://gitea.kuijper.es/jkuijperm/expenses_manager.git cd expenses_manager -
Crea y activa el entorno conda:
conda create -n gastos python=3.11 conda activate gastos -
Instala las dependencias:
pip install -r expenses_manager/requirements.txt -
Crea el archivo
.env(ver Variables de entorno). Para desarrollo basta con:DEBUG=True SECRET_KEY=cualquier-clave-para-desarrolloCon
DEBUG=Truey sin variablesDB_*, se usa SQLite automáticamente. -
Aplica las migraciones y arranca el servidor:
cd expenses_manager python manage.py migrate python manage.py runserver -
Abre
http://127.0.0.1:8000/en el navegador.
Datos de demostración (opcional): con
DEBUG=Truepuedes poblar la base de datos con un usuario y datos de ejemplo:python manage.py seed_demoEl comando está protegido para no ejecutarse si
DEBUG=False.
Variables de entorno
La configuración se lee de un archivo .env (mediante python-dotenv) situado
en expenses_manager/ (junto a manage.py). Este archivo no se versiona.
| Variable | Obligatoria | Por defecto | Descripción |
|---|---|---|---|
SECRET_KEY |
Sí en producción | — | Clave secreta de Django. Con DEBUG=False es obligatoria (si falta, la app no arranca). |
DEBUG |
No | False |
True en desarrollo. Activa SQLite por defecto y desactiva el hardening. |
ALLOWED_HOSTS |
No | localhost,127.0.0.1 |
Lista separada por comas. |
CSRF_TRUSTED_ORIGINS |
No | (vacío) | Lista separada por comas (p. ej. https://midominio.com). |
DB_ENGINE |
No | sqlite3 |
postgresql para usar Postgres; cualquier otro valor usa SQLite. |
DB_NAME |
Con Postgres | — | Nombre de la base de datos. |
DB_USER |
Con Postgres | — | Usuario. |
DB_PASSWORD |
Con Postgres | — | Contraseña. |
DB_HOST |
No | localhost |
Host de la base de datos (db en Docker Compose). |
DB_PORT |
No | 5432 |
Puerto. |
En el repositorio hay un archivo .env.example con estas variables como
plantilla. Cópialo a .env y rellena los valores.
Despliegue con Docker
El despliegue en producción usa Docker con dos servicios: la aplicación Django (servida por Gunicorn) y una base de datos PostgreSQL.
Dockerfile
El Dockerfile está en el repositorio (app/Dockerfile en el árbol de
despliegue) y hace lo esencial: parte de python:3.11-slim, instala las
dependencias de requirements.txt y copia el código. A grandes rasgos:
FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
WORKDIR /app/expenses_manager
COPY expenses_manager/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
docker-compose (no versionado)
El docker-compose.yml no está en el repositorio: es específico de la
infraestructura del NAS y vive fuera del proyecto. A continuación se documenta
qué debe contener, para poder reproducirlo. Ningún secreto va escrito en el
archivo: todos los valores sensibles se leen del .env.
services:
db:
image: postgres:15
restart: always
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./postgres:/var/lib/postgresql/data
web:
build: ./app
container_name: finanzas_web
command: gunicorn expenses_manager.wsgi:application --bind 0.0.0.0:8000
volumes:
- ./app:/app
ports:
- "8001:8000"
depends_on:
- db
env_file:
- ./app/expenses_manager/.env
Notas:
- Las credenciales de Postgres (
DB_NAME,DB_USER,DB_PASSWORD) las toma Docker Compose de un.envsituado junto aldocker-compose.yml. Es un archivo distinto del.envde Django (que va dentro del proyecto y lo carga la propia app víaenv_file). - El servicio
webno repite las variables de base de datos en un bloqueenvironment: las hereda del.envde Django a través deenv_file.
Puesta en marcha y actualizaciones
Desde el directorio que contiene el docker-compose.yml:
git -C app pull # traer los últimos cambios al repo montado
docker-compose down
docker-compose up -d --build
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py collectstatic --noinput
migratesolo es necesario si hay migraciones nuevas.collectstaticsolo si han cambiado archivos estáticos (CSS/JS).- No ejecutes
makemigrationsen producción: las migraciones se generan en desarrollo y llegan versionadas con elgit pull.
Tests
La suite usa pytest + pytest-django:
cd expenses_manager
pytest
La integración continua (Jenkins) ejecuta los tests en cada cambio de la rama
main.
Ramas
main— solo recibe cambios ya probados (vía merge desdedev).dev— rama de trabajo activa.