Spaces:
Sleeping
Sleeping
FK estudios->contactos (RESTRICT), correlativo RC en ingresos, codigos contables en saldos, endpoint /archivos (Supabase), pais_id en JWT y login; migraciones idempotentes en startup
a863251 | title: GNIT API | |
| emoji: 🐳 | |
| colorFrom: purple | |
| colorTo: gray | |
| sdk: docker | |
| app_port: 7860 | |
| # GNIT API | |
| API REST para la plataforma GNIT — gestión de miembros, contactos, estudios bíblicos, reportes, finanzas y más. | |
| - **Stack:** FastAPI + SQLAlchemy asyncio + PostgreSQL 16 (Neon) | |
| - **Documentacion interactiva:** `/docs` (Swagger UI) | `/redoc` (ReDoc) | |
| - **Health check:** `/health` | |
| --- | |
| ## Base de Datos | |
| ### Geografia | |
| #### `continentes` | |
| | Campo | Tipo | Nulleable | Default | | |
| |----------|---------------|-----------|---------| | |
| | id | Integer PK | NO | | | |
| | nombre | String(100) | NO | | | |
| #### `paises` | |
| | Campo | Tipo | Nulleable | Default | | |
| |---------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | iso | String(2) | SI | | | |
| | nombre | String(100) | NO | | | |
| | continente_id | Integer FK → continentes | SI | SET NULL | | |
| #### `ciudades` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | nombre | String(100) | NO | | | |
| | nombre_ascii | String(100) | SI | | | |
| | lat | Numeric(10,7) | SI | | | |
| | lng | Numeric(10,7) | SI | | | |
| | pais_iso2 | String(2) FK → paises | SI | SET NULL | | |
| | admin_name | String(100) | SI | | | |
| | capital | String(20) | SI | | | |
| | population | BigInteger | SI | | | |
| | worldcity_id | BigInteger | SI | | | |
| --- | |
| ### Usuarios y Roles | |
| #### `usuarios` | |
| | Campo | Tipo | Nulleable | Default | | |
| |---------------|--------------------------|-----------|------------| | |
| | id | String(30) PK | NO | | | |
| | nombre | Text | NO | | | |
| | email | Text | NO | UNIQUE | | |
| | password_hash | Text | NO | | | |
| | rol | Integer FK → roles | NO | | | |
| | fecha_registro| DateTime | NO | utcnow | | |
| | activo | Boolean | NO | false | | |
| | region | String(20) | SI | | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | ciudad_id | Integer FK → ciudades | SI | SET NULL | | |
| | miembro_id | String(30) FK → miembros | SI | SET NULL | | |
| #### `roles` | |
| | Campo | Tipo | Nulleable | Default | | |
| |------------|---------------|-----------|---------| | |
| | id | Integer PK | NO | | | |
| | nombre | String(30) | NO | '' | | |
| | descripcion| String(100) | SI | | | |
| #### `rol_permisos` | |
| | Campo | Tipo | Nulleable | Default | | |
| |------------|--------------------------|-----------|---------| | |
| | rol_id | Integer FK → roles | NO | PK | | |
| | permiso_id | Integer | NO | PK | | |
| | activo | Boolean | SI | true | | |
| #### `usuario_permisos` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-------------|----------------------------|-----------|----------| | |
| | id | Integer PK | NO | | | |
| | usuario_id | String(30) FK → usuarios | NO | CASCADE | | |
| | permiso_id | Integer | NO | | | |
| | tiene_acceso| Boolean | SI | true | | |
| --- | |
| ### Personas | |
| #### `miembros` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-------------------|--------------------------|-----------|------------| | |
| | id | String(30) PK | NO | | | |
| | nombre | Text | NO | | | |
| | identidad | String(30) | SI | | | |
| | pais | Text | SI | | | |
| | ciudad | Text | SI | | | |
| | edad | Integer | SI | | | |
| | evangelizado_por | Text | SI | | | |
| | estado_civil | Text | SI | | | |
| | profesion | Text | SI | | | |
| | comentarios | Text | SI | | | |
| | tipo_miembro | String(20) | NO | | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | ciudad_id | Integer FK → ciudades | SI | SET NULL | | |
| | cargo_funcion | Text | SI | | | |
| | ministerio_of | Text | SI | | | |
| | avance_audio | Text | SI | | | |
| #### `miembros_info_adicional` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------------|----------------------------|-----------|------------| | |
| | id | String(30) PK FK → miembros| NO | CASCADE | | |
| | nombre_padre | Text | SI | | | |
| | telefono_padre | String(30) | SI | | | |
| | nombre_madre | Text | SI | | | |
| | telefono_madre | String(30) | SI | | | |
| | tipo_sangre | String(5) | SI | | | |
| | correo_electronico | String(255) | SI | | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| | fecha_actualizacion| DateTime | NO | utcnow | | |
| #### `contactos` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-----------------------|------------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | miembro_responsable | Text | NO | | | |
| | nombre | Text | NO | | | |
| | telefono | Text | SI | | | |
| | pais | Text | SI | | | |
| | notas | Text | SI | | | |
| | profesion | Text | SI | | | |
| | fecha_creacion | DateTime | SI | utcnow | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | miembro_responsable_id| String(30) FK → miembros | SI | SET NULL | | |
| | ciudad_id | Integer FK → ciudades | SI | SET NULL | | |
| --- | |
| ### Misiones y Estudios | |
| #### `ciudades_mision` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------------------|---------------------------|-----------|---------------| | |
| | id | Integer PK | NO | | | |
| | ciudad_id | Integer FK → ciudades | NO | SET NULL | | |
| | region | String(150) | SI | | | |
| | estado_presencia | String(30) | NO | 'En proceso' | | |
| | fecha_inicio_trabajo | Date | SI | | | |
| | pastor_encargado_id | String(30) | SI | | | |
| | pastor_encargado_nombre | Text | SI | | | |
| | cantidad_miembros_activos| Integer | SI | 0 | | |
| | notas | Text | SI | | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| | fecha_actualizacion | DateTime | NO | utcnow | | |
| #### `reportes` | |
| | Campo | Tipo | Nulleable | Default | | |
| |------------------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | miembro_que_reporta | Text | NO | | | |
| | fecha | Date | NO | | | |
| | tiempo_evangelizacion | Interval | SI | | | |
| | contactos_obtenidos | Integer | SI | 0 | | |
| | contactos_estudian | Integer | SI | 0 | | |
| | numero_estudios_dados | Integer | SI | 0 | | |
| | total_estudiantes | Integer | SI | 0 | | |
| | pais | Text | SI | | | |
| | fecha_creacion | DateTime | SI | utcnow | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | miembro_id | String(30) FK → miembros | SI | SET NULL | | |
| | ciudad_id | Integer FK → ciudades | SI | SET NULL | | |
| #### `estudios_diarios` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-----------------|------------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | miembro_id | String(30) FK → miembros | NO | CASCADE | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | contacto_id | Integer FK → contactos | SI | RESTRICT | | |
| | mes | Integer | NO | | | |
| | anio | Integer | NO | | | |
| | dia | Integer | NO | | | |
| | capitulo | String(255) | SI | | | |
| | horas | Numeric(5,2) | SI | | | |
| | tipo | String(50) | SI | | | |
| | donde | String(255) | SI | | | |
| | dijeron_si | Integer | SI | 0 | | |
| | nuevos_contactos| Integer | SI | 0 | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| > `contacto_id` usa **ON DELETE RESTRICT**: no se puede eliminar un contacto que tenga estudios registrados (el `DELETE /contactos/{id}` devuelve **409**). | |
| --- | |
| ### Finanzas | |
| #### `cotizaciones` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------------|----------------------------|-----------|-------------| | |
| | id | Integer PK | NO | | | |
| | fecha | Date | NO | | | |
| | solicitante | Text | NO | | | |
| | concepto | Text | NO | | | |
| | monto | Numeric(15,2) | NO | | | |
| | moneda | String(10) | NO | 'USD' | | |
| | estado | String(20) | NO | 'pendiente' | | |
| | agregado_a_gastos | Boolean | NO | false | | |
| | mes_agregado | Integer | SI | | | |
| | anio_agregado | Integer | SI | | | |
| | notas | Text | SI | | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| | fecha_actualizacion| DateTime | NO | utcnow | | |
| | miembro_id | String(30) FK → miembros | SI | SET NULL | | |
| #### `presupuestos` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | pais | String(100) | NO | | | |
| | mes | Integer | SI | | | |
| | anio | Integer | NO | | | |
| | tipo_gasto | String(100) | NO | | | |
| | concepto | Text | SI | | | |
| | monto | Numeric(15,2) | NO | | | |
| | moneda | String(10) | NO | | | |
| | tasa_cambio | Numeric(15,6) | SI | | | |
| | notas | Text | SI | | | |
| | fecha_registro | DateTime | SI | utcnow | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| #### `ejecuciones` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------------|-----------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | pais | String(100) | NO | | | |
| | mes | Integer | NO | | | |
| | anio | Integer | NO | | | |
| | monto_recibido_usd | Numeric(15,2) | NO | | | |
| | presupuesto_id | Integer FK → presupuestos | SI | SET NULL | | |
| | notas | Text | SI | | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| | fecha_actualizacion| DateTime | NO | utcnow | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| #### `gastos_reales` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-------------|-----------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | ejecucion_id| Integer FK → ejecuciones | NO | CASCADE | | |
| | concepto | Text | NO | | | |
| | monto | Numeric(15,2) | NO | | | |
| | tipo_gasto | String(100) | NO | | | |
| | fecha_creacion | DateTime | NO | utcnow | | |
| #### `ingresos` | |
| | Campo | Tipo | Nulleable | Default | | |
| |---------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | numero | String(20) | SI | | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | mes | Integer | NO | | | |
| | anio | Integer | NO | | | |
| | tipo | String(100) | NO | | | |
| | origen | Text | SI | | | |
| | donde_ingresa | String(10) | NO | | | |
| | valor | Numeric(15,2) | NO | | | |
| | observaciones | Text | SI | | | |
| | fecha | Date | NO | | | |
| | fecha_creacion| DateTime | NO | utcnow | | |
| > `numero` es un **correlativo automático por país + año + mes** con formato `RC-001`, `RC-002`… Lo genera el `POST /ingresos` y lo devuelve en la respuesta. | |
| #### `saldos_caja_banco` | |
| | Campo | Tipo | Nulleable | Default | | |
| |-----------------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | saldo_caja | Numeric(15,2) | NO | 0 | | |
| | saldo_banco | Numeric(15,2) | NO | 0 | | |
| | codigo_contable_caja | String(20) | SI | | | |
| | codigo_contable_banco | String(20) | SI | | | |
| | updated_at | DateTime | NO | utcnow | | |
| > `codigo_contable_caja` / `codigo_contable_banco` guardan los códigos contables de cada cuenta (ej. `11050501` Caja General, `11200501` cuenta bancaria). | |
| #### `traslados` | |
| | Campo | Tipo | Nulleable | Default | | |
| |---------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| | de | String(10) | NO | | | |
| | a | String(10) | NO | | | |
| | valor | Numeric(15,2) | NO | | | |
| | observaciones | Text | SI | | | |
| | fecha | Date | NO | | | |
| | fecha_creacion| DateTime | NO | utcnow | | |
| #### `archivos` | |
| Adjuntos (PDF/imagen) asociados a un ingreso o gasto. Referencia polimórfica (`tipo` + `referencia_id`), sin FK dura porque apunta a dos tablas. | |
| | Campo | Tipo | Nulleable | Default | Notas | | |
| |----------------|--------------|-----------|---------|-----------------------------------------| | |
| | id | Integer PK | NO | | | | |
| | tipo | String(20) | NO | | `'ingreso'` \| `'gasto'` (CHECK) | | |
| | referencia_id | Integer | NO | | id del ingreso / gasto asociado | | |
| | nombre_original| Text | SI | | | | |
| | content_type | String(100) | SI | | | | |
| | tamano_bytes | BigInteger | SI | | | | |
| | storage_path | Text | SI | | ruta interna en el bucket | | |
| | url | Text | NO | | URL pública del archivo | | |
| | fecha_creacion | DateTime | NO | utcnow | | | |
| > Los archivos se almacenan en **Supabase Storage** (el disco del Space es efímero). Requiere las variables `SUPABASE_URL`, `SUPABASE_SERVICE_KEY`, `SUPABASE_BUCKET`; si faltan, `POST /archivos` responde **503**. | |
| --- | |
| ### Configuracion | |
| #### `configuracion` | |
| | Campo | Tipo | Nulleable | Default | | |
| |--------------------|---------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | clave | String(100) | NO | UNIQUE | | |
| | valor | Text | NO | | | |
| | descripcion | Text | SI | | | |
| | fecha_actualizacion| DateTime | NO | utcnow | | |
| --- | |
| ### Estadisticas | |
| #### `estadisticas_paises` | |
| | Campo | Tipo | Nulleable | Default | | |
| |---------------------|--------------------------|-----------|------------| | |
| | id | Integer PK | NO | | | |
| | nombre_pais | String(100) | NO | | | |
| | cantidad_miembros | Integer | SI | 0 | | |
| | cantidad_estudios | Integer | SI | 0 | | |
| | cantidad_reportes | Integer | SI | 0 | | |
| | color | String(20) | SI | | | |
| | mes | Integer | SI | | | |
| | anio | Integer | NO | | | |
| | fecha_creacion | DateTime | SI | utcnow | | |
| | fecha_actualizacion | DateTime | SI | utcnow | | |
| | pais_id | Integer FK → paises | SI | SET NULL | | |
| --- | |
| ## Endpoints de la API | |
| ### Auth | |
| `/auth` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|-------------------------|------------------------------------------------|--------------------------| | |
| | POST | `/auth/login` | Iniciar sesion, devuelve JWT | Body: email, password | | |
| | GET | `/auth/mis-permisos` | Permisos del usuario autenticado | Header: Authorization | | |
| > El JWT incluye `sub`, `rol` y **`pais_id`** en el payload. La respuesta del login trae el objeto `usuario` con `pais_id` y `pais_nombre`. | |
| ### Miembros | |
| `/miembros` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/miembros` | Listar miembros | `?tipo=&pais_id=` | | |
| | GET | `/miembros/{id}` | Obtener miembro por ID | | | |
| | POST | `/miembros` | Crear miembro | Body: MiembroCreate | | |
| | PATCH | `/miembros/{id}` | Actualizar miembro | Body: MiembroUpdate | | |
| | DELETE | `/miembros/{id}` | Eliminar miembro | | | |
| ### Miembros Info Adicional | |
| `/miembros-info-adicional` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|-------------------------------------------|---------------------------|--------------------------------| | |
| | GET | `/miembros-info-adicional` | Listar info adicional | `?id=` | | |
| | GET | `/miembros-info-adicional/{id}` | Obtener por ID de miembro | | | |
| | POST | `/miembros-info-adicional` | Crear info adicional | Body: MiembroInfoAdicionalCreate | | |
| | PATCH | `/miembros-info-adicional/{id}` | Actualizar info adicional | Body: MiembroInfoAdicionalUpdate | | |
| | DELETE | `/miembros-info-adicional/{id}` | Eliminar info adicional | | | |
| ### Contactos | |
| `/contactos` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|------------------------------------------------| | |
| | GET | `/contactos` | Listar contactos | `?miembro_responsable_id=&pais_id=` | | |
| | GET | `/contactos/{id}` | Obtener contacto | | | |
| | POST | `/contactos` | Crear contacto | Body: ContactoCreate | | |
| | PATCH | `/contactos/{id}` | Actualizar contacto | Body: ContactoUpdate | | |
| | DELETE | `/contactos/{id}` | Eliminar contacto (**409** si tiene estudios) | | | |
| ### Estudios Diarios | |
| `/estudios-diarios` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|------------------------------|--------------------------|--------------------------------------| | |
| | GET | `/estudios-diarios` | Listar estudios diarios | `?miembro_id=&pais_id=&anio=&mes=` | | |
| | GET | `/estudios-diarios/{id}` | Obtener estudio | | | |
| | POST | `/estudios-diarios` | Crear estudio | Body: EstudioDiarioCreate | | |
| | PATCH | `/estudios-diarios/{id}` | Actualizar estudio | Body: EstudioDiarioUpdate | | |
| | DELETE | `/estudios-diarios/{id}` | Eliminar estudio | | | |
| ### Reportes | |
| `/reportes` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/reportes` | Listar reportes | `?miembro_id=&pais_id=&anio=` | | |
| | GET | `/reportes/{id}` | Obtener reporte | | | |
| | POST | `/reportes` | Crear reporte | Body: ReporteCreate | | |
| | PATCH | `/reportes/{id}` | Actualizar reporte | Body: ReporteUpdate | | |
| | DELETE | `/reportes/{id}` | Eliminar reporte | | | |
| ### Estadisticas Generales | |
| `/estadisticas` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|------------------------------------------------------------------|------------| | |
| | GET | `/estadisticas` | Dashboard: comparacion estudios, rendimiento, evangelismo, crecimiento | `?anio=` | | |
| ### Estadisticas por Pais | |
| `/estadisticas-paises` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------------|--------------------------|--------------------------| | |
| | GET | `/estadisticas-paises` | Listar estadisticas | `?pais_id=&anio=&mes=` | | |
| | GET | `/estadisticas-paises/{id}` | Obtener estadistica | | | |
| | POST | `/estadisticas-paises` | Crear estadistica | Body: EstadisticaPaisCreate | | |
| | PATCH | `/estadisticas-paises/{id}` | Actualizar estadistica | Body: EstadisticaPaisUpdate | | |
| | DELETE | `/estadisticas-paises/{id}` | Eliminar estadistica | | | |
| ### Paises | |
| `/paises` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/paises` | Listar paises | | | |
| | GET | `/paises/{id}` | Obtener pais | | | |
| | POST | `/paises` | Crear pais | Body: PaisCreate | | |
| | PATCH | `/paises/{id}` | Actualizar pais | Body: PaisUpdate | | |
| | DELETE | `/paises/{id}` | Eliminar pais | | | |
| ### Continentes | |
| `/continentes` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/continentes` | Listar continentes | | | |
| | GET | `/continentes/{id}` | Obtener continente | | | |
| | POST | `/continentes` | Crear continente | Body: ContinenteCreate | | |
| | PATCH | `/continentes/{id}` | Actualizar continente | Body: ContinenteUpdate | | |
| | DELETE | `/continentes/{id}` | Eliminar continente | | | |
| ### Ciudades | |
| `/ciudades` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|-----------------------------------| | |
| | GET | `/ciudades` | Listar ciudades | `?pais_iso2=&limit=&offset=` | | |
| | GET | `/ciudades/{id}` | Obtener ciudad | | | |
| | POST | `/ciudades` | Crear ciudad | Body: CiudadCreate | | |
| | PATCH | `/ciudades/{id}` | Actualizar ciudad | Body: CiudadUpdate | | |
| | DELETE | `/ciudades/{id}` | Eliminar ciudad | | | |
| ### Ciudades Mision | |
| `/ciudades-mision` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------------|--------------------------|--------------------------------| | |
| | GET | `/ciudades-mision` | Listar ciudades mision | `?ciudad_id=&estado_presencia=` | | |
| | GET | `/ciudades-mision/{id}` | Obtener ciudad mision | | | |
| | POST | `/ciudades-mision` | Crear ciudad mision | Body: CiudadMisionCreate | | |
| | PATCH | `/ciudades-mision/{id}` | Actualizar ciudad mision| Body: CiudadMisionUpdate | | |
| | DELETE | `/ciudades-mision/{id}` | Eliminar ciudad mision | | | |
| ### Cotizaciones | |
| `/cotizaciones` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------------| | |
| | GET | `/cotizaciones` | Listar cotizaciones | `?estado=&miembro_id=&anio=&mes=` | | |
| | GET | `/cotizaciones/{id}` | Obtener cotizacion | | | |
| | POST | `/cotizaciones` | Crear cotizacion | Body: CotizacionCreate | | |
| | PATCH | `/cotizaciones/{id}` | Actualizar cotizacion | Body: CotizacionUpdate | | |
| | DELETE | `/cotizaciones/{id}` | Eliminar cotizacion | | | |
| ### Presupuestos | |
| `/presupuestos` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------------| | |
| | GET | `/presupuestos` | Listar presupuestos | `?pais_id=&anio=&mes=&tipo_gasto=` | | |
| | GET | `/presupuestos/{id}` | Obtener presupuesto | | | |
| | POST | `/presupuestos` | Crear presupuesto | Body: PresupuestoCreate | | |
| | PATCH | `/presupuestos/{id}` | Actualizar presupuesto | Body: PresupuestoUpdate | | |
| | DELETE | `/presupuestos/{id}` | Eliminar presupuesto | | | |
| ### Ejecuciones | |
| `/ejecuciones` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/ejecuciones` | Listar ejecuciones | `?pais_id=&anio=&mes=` | | |
| | GET | `/ejecuciones/{id}` | Obtener ejecucion | | | |
| | POST | `/ejecuciones` | Crear ejecucion | Body: EjecucionCreate | | |
| | PATCH | `/ejecuciones/{id}` | Actualizar ejecucion | Body: EjecucionUpdate | | |
| | DELETE | `/ejecuciones/{id}` | Eliminar ejecucion | | | |
| ### Gastos Reales | |
| `/gastos-reales` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/gastos-reales` | Listar gastos reales | `?ejecucion_id=` | | |
| | GET | `/gastos-reales/{id}` | Obtener gasto | | | |
| | POST | `/gastos-reales` | Crear gasto | Body: GastoRealCreate | | |
| | PATCH | `/gastos-reales/{id}` | Actualizar gasto | Body: GastoRealUpdate | | |
| | DELETE | `/gastos-reales/{id}` | Eliminar gasto | | | |
| ### Ingresos | |
| `/ingresos` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|----------------------------------------------| | |
| | GET | `/ingresos` | Listar ingresos | `?pais_id=&anio=&mes=&tipo=&donde_ingresa=` | | |
| | GET | `/ingresos/{id}` | Obtener ingreso | | | |
| | POST | `/ingresos` | Crear ingreso (genera `numero` RC-###) | Body: IngresoCreate | | |
| | PATCH | `/ingresos/{id}` | Actualizar ingreso | Body: IngresoUpdate | | |
| | DELETE | `/ingresos/{id}` | Eliminar ingreso | | | |
| ### Saldos Caja / Banco | |
| `/saldos-caja-banco` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------------|--------------------------|--------------------------| | |
| | GET | `/saldos-caja-banco` | Listar saldos | `?pais_id=` | | |
| | GET | `/saldos-caja-banco/{id}` | Obtener saldo | | | |
| | POST | `/saldos-caja-banco` | Crear saldo | Body: SaldoCajaBancoCreate | | |
| | PATCH | `/saldos-caja-banco/{id}` | Actualizar saldo | Body: SaldoCajaBancoUpdate | | |
| | DELETE | `/saldos-caja-banco/{id}` | Eliminar saldo | | | |
| ### Traslados | |
| `/traslados` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/traslados` | Listar traslados | `?pais_id=&anio=&mes=` | | |
| | GET | `/traslados/{id}` | Obtener traslado | | | |
| | POST | `/traslados` | Crear traslado | Body: TrasladoCreate | | |
| | PATCH | `/traslados/{id}` | Actualizar traslado | Body: TrasladoUpdate | | |
| | DELETE | `/traslados/{id}` | Eliminar traslado | | | |
| ### Archivos | |
| `/archivos` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|-----------------------------------------------|-----------------------------------------------------| | |
| | GET | `/archivos` | Listar adjuntos | `?tipo=&referencia_id=` | | |
| | POST | `/archivos` | Subir adjunto y asociarlo a un ingreso/gasto | multipart: `tipo`, `referencia_id`, `file` → devuelve la URL | | |
| | DELETE | `/archivos/{id}` | Eliminar adjunto | | | |
| > `POST /archivos` sube el archivo a Supabase Storage. Valida que `tipo` sea `ingreso`/`gasto` (**422**), que la referencia exista (**404**) y que el almacenamiento esté configurado (**503**). | |
| ### Usuarios | |
| `/usuarios` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|-----------------------------------------------|---------------------------|--------------------------------| | |
| | GET | `/usuarios` | Listar usuarios | | | |
| | GET | `/usuarios/{id}` | Obtener usuario | | | |
| | POST | `/usuarios` | Crear usuario | Body: UsuarioCreate | | |
| | PATCH | `/usuarios/{id}` | Actualizar usuario | Body: UsuarioUpdate | | |
| | DELETE | `/usuarios/{id}` | Eliminar usuario | | | |
| | GET | `/usuarios/{id}/permisos` | Listar permisos | | | |
| | POST | `/usuarios/{id}/permisos` | Agregar permiso | Body: permiso_id | | |
| | PATCH | `/usuarios/{id}/permisos/{permiso_id}` | Actualizar permiso | Body: tiene_acceso | | |
| | DELETE | `/usuarios/{id}/permisos/{permiso_id}` | Quitar permiso | | | |
| ### Roles y Permisos | |
| `/roles` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|---------------------------------------------|---------------------------|--------------------------------| | |
| | GET | `/roles` | Listar roles | | | |
| | GET | `/roles/{id}` | Obtener rol | | | |
| | POST | `/roles` | Crear rol | Body: RolCreate | | |
| | PATCH | `/roles/{id}` | Actualizar rol | Body: RolUpdate | | |
| | DELETE | `/roles/{id}` | Eliminar rol | | | |
| | GET | `/roles/{rol_id}/permisos` | Listar permisos del rol | | | |
| | POST | `/roles/{rol_id}/permisos` | Agregar permiso al rol | Body: permiso_id | | |
| | PATCH | `/roles/{rol_id}/permisos/{permiso_id}` | Actualizar permiso del rol| Body: activo | | |
| | DELETE | `/roles/{rol_id}/permisos/{permiso_id}` | Quitar permiso del rol | | | |
| ### Configuracion | |
| `/configuracion` | |
| | Metodo | Ruta | Descripcion | Parametros | | |
| |--------|--------------------------|--------------------------------|--------------------------------| | |
| | GET | `/configuracion` | Listar configuracion | | | |
| | GET | `/configuracion/{clave}` | Obtener valor por clave | | | |
| | POST | `/configuracion` | Crear clave de configuracion | Body: ConfiguracionCreate | | |
| | PATCH | `/configuracion/{clave}` | Actualizar valor | Body: ConfiguracionUpdate | | |
| | DELETE | `/configuracion/{clave}` | Eliminar clave | | | |
| --- | |
| ## Setup | |
| ```bash | |
| # Variables de entorno requeridas | |
| DATABASE_URL=postgresql://usuario:password@host:5432/neondb | |
| SECRET_KEY=clave-secreta-para-jwt | |
| ENVIRONMENT=development | |
| # Almacenamiento de archivos (Supabase Storage) — para POST /archivos | |
| SUPABASE_URL=https://TU_PROYECTO.supabase.co | |
| SUPABASE_SERVICE_KEY=tu_service_role_key | |
| SUPABASE_BUCKET=archivos | |
| # Ejecutar con Docker | |
| docker build -t gnit-api . | |
| docker run -p 7860:7860 gnit-api | |
| # Ejecutar localmente | |
| pip install -r requirements.txt | |
| uvicorn app.main:app --reload --port 7860 | |
| ``` | |