尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Finanzas personales y familiares para Colombia: cuánto puedes gastar hoy sin comprometer tus obligaciones. Laravel 13 · PHP 8.4 · PWA
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
¿Cuánto dinero puedo gastar realmente sin comprometer mis obligaciones? Finlia es una aplicación web que ayuda a personas y familias a registrar ingresos y gastos, controlar deudas y tarjetas, crear presupuestos y metas de ahorro, y —sobre todo— calcular cuánto dinero tienen realmente disponible para gastar. Pensada para usarse a diario desde el celular. 🇨🇴 Dirigida inicialmente al mercado colombiano (COP, español). Diseñada para permitir futura expansión a otras monedas y países.查看完整 README ↗
Gestión de finanzas personales y familiares
¿Cuánto dinero puedo gastar realmente sin comprometer mis obligaciones?
finlia.online · Entrar a la app
Finlia es una aplicación web que ayuda a personas y familias a registrar ingresos y gastos, controlar deudas y tarjetas, crear presupuestos y metas de ahorro, y —sobre todo— calcular cuánto dinero tienen realmente disponible para gastar. Pensada para usarse a diario desde el celular.
🇨🇴 Dirigida inicialmente al mercado colombiano (COP, español). Diseñada para permitir futura expansión a otras monedas y países.
Las apps de finanzas personales responden bien a «¿en qué se me fue el dinero?». Casi ninguna responde a la pregunta que una familia se hace de verdad el día 12 del mes, con el mercado por hacer y la cuota de la moto pendiente:
¿Cuánto puedo gastar hoy sin quedar mal a fin de mes?
El saldo del banco no lo dice. Ese número no sabe que el arriendo sale el día 5, que el SOAT vence en marzo, ni que hay $800.000 comprometidos en la cuota de la tarjeta. Mirar solo el saldo es exactamente como se llega a fin de mes en rojo habiendo «tenido plata» todo el mes.
Finlia calcula el dinero realmente disponible con la plata que ya tienes, no con la que esperas recibir:
disponible hasta el cobro = saldo real en cuentas
− lo ya apartado en metas de ahorro
− gastos fijos y obligaciones que vencen antes del próximo pago
− cuotas de deuda que vencen antes del próximo pago
− ahorro programado de esos días
puedes gastar hoy = disponible hasta el cobro ÷ días que faltan para el pago
El sueldo que aún no llega no suma: el día de cobro solo dice hasta cuándo tiene que alcanzar lo de hoy. Por eso la cifra sirve desde el primer día, sin historial, y no miente si un pago se atrasa. Los ingresos esperados alimentan el plan del mes (la proyección del mes siguiente y un tope para que la cifra no se dispare), nunca la aumentan (ADR-0040).
Ese cálculo vive en un único servicio de dominio (BudgetCalculatorService) y es la cifra que la app pone en primer plano: «Puedes gastar hoy $81.521», no «tu saldo es $19.992.420».
Datos de demostración generados con Faker (es_CO). El repositorio nunca contiene datos financieros reales.
| Panel | Dinero disponible | Registrar gasto |
|---|---|---|
| Reportes | Deudas | Metas de ahorro |
Se regeneran con la app corriendo y sembrada. La clave es la del usuario demo de más abajo; va por entorno para no dejarla escrita en el repositorio:
SHOTS_PASSWORD=finlia123 npm run screenshots
Finlia no nació de un tutorial ni de un ejercicio: nació de un problema real de administración financiera de un hogar colombiano. Llevar las cuentas en una hoja de cálculo respondía qué había pasado, pero nunca qué se podía hacer hoy — y esa es la decisión que uno toma a diario, no una vez al mes.
Eso condicionó cada decisión técnica del proyecto:
Las decisiones que no eran obvias están escritas y fechadas en docs/DECISIONS.md — 37 ADR con contexto, alternativas descartadas y consecuencias.
El estado detallado de cada funcionalidad está en docs/ROADMAP.md.
git clone https://github.com/ronaldo-duran/Finlia.git
cd Finlia
composer install
cp .env.example .env
php artisan key:generate
Configura la base de datos en .env. Finlia soporta MySQL/MariaDB y PostgreSQL (ADR-0036); elige uno (ver docs/DEPLOYMENT.md para los valores exactos y la configuración de Colombia):
APP_NAME=Finlia
APP_TIMEZONE=America/Bogota
APP_LOCALE=es
APP_FAKER_LOCALE=es_CO
# MySQL / MariaDB
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=finlia
DB_USERNAME=tu_usuario
DB_PASSWORD=tu_password
# …o PostgreSQL: basta cambiar el driver y el puerto
# DB_CONNECTION=pgsql
# DB_PORT=5432
Luego:
php artisan migrate --seed
npm install
npm run build
php artisan serve
Abre http://localhost:8000.
🔑 Usuario de demostración (creado por el seeder con datos falsos): correo
[email protected]· contraseñafinlia123.
--seed ejecuta DatabaseSeeder, que deja la app usable de inmediato:
| Seeder | Qué crea |
|---|---|
CategorySeeder | Categorías por defecto de ingreso y gasto del hogar |
TermsVersionSeeder | Versión vigente de los términos (sin ella el login rebota a aceptarlos) |
DatabaseSeeder | Usuario demo, su hogar y movimientos, deudas y metas de ejemplo |
Todos los datos son falsos, generados con Faker (es_CO). El repositorio
nunca contiene datos financieros reales de nadie.
Para rehacer la base desde cero: php artisan migrate:fresh --seed.
composer test # PHPUnit con SQLite en memoria
php artisan test --filter=HouseholdTest
vendor/bin/pint # linter/formatter PHP (en CI: pint --test)
npm run test:e2e # Playwright (Chromium) contra Laravel + SQLite + seed
npm run test:e2e:ui # ídem, con inspector visual
Los E2E levantan su propio servidor (php artisan serve en el puerto 8890) con una
BD SQLite aislada (database/playwright.sqlite) y el seeder de datos falsos; no
tocan tu .env ni tu base local. Se necesita npx playwright install chromium la
primera vez.
En cada push/PR corre .github/workflows/ci.yml con cuatro jobs: PHP (Pint + PHPUnit sobre SQLite), BD (la misma suite contra MySQL 8 y PostgreSQL 16, en matriz), Assets (build de Vite) y E2E (Playwright con Chromium, sube el reporte como artefacto si falla).
El job de BD existe porque SQLite perdona SQL que los motores de producción rechazan: es lo que impide que una función específica de un motor llegue a producción sin que ningún test se entere.
Finlia corre en Hostinger (hosting compartido) sobre dos dominios: finlia.online sirve el sitio público y app.finlia.online la aplicación (ADR-0038).
Hostinger no tiene Node ni Composer, así que el artefacto desplegable —código + vendor/ + public/build— se construye en GitHub Actions al publicar un tag v* y se publica en un repositorio aparte del que el servidor solo hace pull:
# En el servidor, tras cada despliegue publicado
git fetch origin && git reset --hard origin/main
php artisan migrate --force
php artisan config:cache && php artisan route:cache && php artisan view:cache
El detalle está en docs/DEPLOYMENT.md.
El document root del dominio debe apuntar a public/, para que .env, storage/ y app/ queden fuera de la web.
Hosting compartido no admite procesos permanentes: todo lo periódico entra por el Scheduler, con una sola entrada de cron (Hostinger → Advanced → Cron Jobs):
* * * * * cd /home/uXXXX/domains/tudominio/finlia && /usr/local/bin/php artisan schedule:run >> /dev/null 2>&1
Esa única línea dispara todas las tareas programadas (routes/console.php):
| Tarea | Cuándo | Para qué |
|---|---|---|
finlia:process-export-requests | 02:00 | Genera el ZIP de datos del hogar y lo envía por correo (hora valle) |
finlia:purge-pending-deletions | 05:30 | Borra definitivamente las cuentas cuyo plazo de 30 días venció |
finlia:generate-recurring-payments | 06:00 | Materializa los gastos recurrentes que tocan hoy |
finlia:send-reminder-digests | 06:30 | Resumen diario de obligaciones por correo |
Comprueba que está bien con php artisan schedule:list.
| Síntoma | Causa habitual | Solución |
|---|---|---|
419 Page Expired al enviar un formulario | Falta @csrf, o la sesión caducó | Añade @csrf al formulario. CsrfTokenSweepTest detecta el caso — la suite no lo pilla sola porque Laravel desactiva CSRF en tests |
500 tras desplegar, con la web en blanco | Caché de config apuntando a valores viejos | php artisan config:clear y vuelve a cachear |
| Los estilos no cargan en producción | Falta public/build (está en .gitignore) | npm ci && npm run build y sube la carpeta |
SQLSTATE[HY000] [1045] (MySQL) o password authentication failed (PostgreSQL) al migrar | Credenciales de BD incorrectas en .env | Revisa DB_USERNAME / DB_PASSWORD |
SQLSTATE[42601] syntax error con una función de fecha | SQL específico de un motor usado contra otro | Toda función propia del motor va en un match sobre el driver (ADR-0036); el job BD del CI lo detecta |
| El login rebota siempre a «aceptar términos» | Falta la versión vigente de términos | php artisan db:seed --class=TermsVersionSeeder |
| Las invitaciones no llegan por correo | MAIL_MAILER=log (no entrega a bandejas) | Configura SMTP; mientras tanto la app ofrece el enlace manual |
| Las tareas programadas no corren | El cron de schedule:run no está puesto | Añade la línea de la sección anterior |
Permission denied en storage/ | Permisos tras subir por FTP | chmod -R 775 storage bootstrap/cache |
Este proyecto maneja información financiera sensible y es un repositorio público. La política de seguridad completa está en docs/SECURITY.md. Lo esencial:
DECIMAL para dinero (nunca FLOAT)..env, credenciales ni datos reales.Para reportar una vulnerabilidad, abre un issue privado o contacta al maintainer. No abras un issue público con detalles explotables.
Qué se resolvió con cada pieza, no solo qué se usó:
| Tecnología | Dónde se ve en este repositorio |
|---|---|
| Laravel 13 · PHP 8.3 | 22 modelos, 24 enums y 14 servicios de dominio con tipado estricto; middleware propio para términos y cuentas suspendidas |
| Arquitectura por capas | Controladores finos → servicios → Eloquent. Ningún cálculo financiero vive en un controlador o una vista (ADR-0010) |
| MySQL · PostgreSQL · SQLite | La misma suite corre contra los tres. El SQL propio de un motor se resuelve con un match sobre el driver (ADR-0036) |
| Modelado de datos | DECIMAL(15,2) para todo el dinero, FKs con onDelete explícito, índices compuestos por (household_id, …) |
| Seguridad multi-tenant | Aislamiento por hogar con policies + consultas acotadas, y un barrido que intenta el acceso cruzado sobre cada ruta |
| Autenticación y privacidad | Verificación de correo, cambio de correo en dos pasos, términos versionados con prueba de consentimiento, eliminación de cuenta con plazo y exportación de datos en ZIP |
| Blade · Bootstrap 5 | Sistema de diseño propio (glass, chips, barra inferior, FAB) documentado en docs/UI_DESIGN.md |
| JavaScript vanilla · Chart.js | Cinco gráficos, simulador de deuda en el navegador que replica el servicio PHP, e indicadores de carga. Sin framework de frontend |
| PWA | Manifest, service worker e instalación en iOS y Android |
| Testing | 559 tests PHPUnit + 37 E2E con Playwright. Barridos que fijan invariantes: aislamiento entre hogares, CSRF y N+1 |
| CI/CD | GitHub Actions con matriz de motores de base de datos, linter, build de assets y E2E con reporte como artefacto |
scrum/epics/ — Detalle de las épicas aún abiertas (las completadas se liberaron en la v0.34.1; su histórico está en CHANGELOG.md)Finlia tiene dos licencias, y la frontera es un directorio:
| Qué | Licencia | Qué puedes hacer |
|---|---|---|
| Todo el repositorio | AGPL-3.0-or-later | Usar, estudiar, modificar, redistribuir y autoalojar, incluso ofrecerlo como servicio, publicando tus cambios |
El directorio ee/ (funciones Premium) | Licencia comercial | Leer, auditar y ejecutar en local para evaluarlo o contribuir. No usarlo en producción ni ofrecerlo como servicio sin autorización escrita |
Finlia funciona completa sin ee/: borrar ese directorio deja una aplicación
de finanzas personales plenamente usable, libre y autoalojable. Las funciones de
pago se publican para que puedan leerse y auditarse —quien confía sus
finanzas a la app tiene derecho a verificar también aquello por lo que paga—, no
para que puedan operarse como servicio. El razonamiento completo, con las
alternativas descartadas, está en ADR-0042.
Si quieres contribuir, CONTRIBUTING.md explica cómo y qué implica para tus aportes.
El texto completo está en LICENSE.
Copyright (C) 2026 Ronaldo Duran
Este programa es software libre: puedes redistribuirlo y/o modificarlo bajo
los términos de la GNU Affero General Public License publicada por la Free
Software Foundation, en su versión 3 o (a tu elección) cualquier versión
posterior.
Se distribuye con la esperanza de que sea útil, pero SIN NINGUNA GARANTÍA;
ni siquiera la garantía implícita de COMERCIABILIDAD o IDONEIDAD PARA UN
PROPÓSITO PARTICULAR. Consulta la GNU Affero General Public License para
más detalles.
Sobre versiones anteriores: Finlia se publicó bajo licencia MIT hasta la versión 0.28.0. Ese código sigue disponible bajo MIT para quien lo obtuviera entonces — una relicencia no es retroactiva. El cambio aplica de esta versión en adelante.
| Despliegue en hosting compartido |
Sin procesos permanentes: cuatro tareas programadas tras una sola línea de cron; cabeceras de seguridad y HTTPS en .htaccess |