Skip to content
This repository was archived by the owner on Jul 9, 2026. It is now read-only.

Latest commit

ย 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

GADS Backend

Backend de Gestiรณn de Asistencia / Fichadas / Novedades para empresas. Modela empleados, horarios, fichadas, novedades (vacaciones, licencias), justificativos y cierres mensuales.

Stack: FastAPI ยท SQLAlchemy 2.0 ยท Pydantic v2 ยท JWT (PyJWT) ยท bcrypt ยท SQLite (dev) / Postgres (prod) ยท Alembic.


Tabla de contenidos

  1. Setup
  2. Configuraciรณn (env vars)
  3. Bootstrap del primer admin
  4. Cรณmo correrlo
  5. Preview con frontend mรญnimo
  6. Endpoints โ€” referencia completa
  7. Modelo de dominio
  8. Tests y calidad
  9. Estructura del proyecto
  10. Limitaciones conocidas

Setup

Requiere Python 3.11+.

python3.11 -m venv venv
source venv/bin/activate            # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env                # editar valores
alembic upgrade head                # crea esquema (o se crea solo en preview)

Dependencias clave: fastapi, uvicorn, sqlalchemy, pydantic[email], pydantic-settings, bcrypt, pyjwt, python-multipart, alembic. Ver requirements.txt.


Configuraciรณn (env vars)

Todas las variables usan prefijo GADS_. Cargadas desde .env (ver .env.example).

Variable Default Descripciรณn
GADS_DATABASE_URL sqlite:///./app.db URL SQLAlchemy. En prod: postgresql+psycopg://user:pass@host/db.
GADS_JWT_SECRET dev-secret-change-me Cambiar en prod. Mรญnimo 32 bytes recomendado (HS256).
GADS_JWT_ALGORITHM HS256 Algoritmo JWT.
GADS_JWT_EXPIRE_MINUTES 60 Vida del access token.
GADS_DEFAULT_TIMEZONE America/Argentina/Buenos_Aires Zona horaria para parsear CSVs sin tzinfo.
GADS_INITIAL_ADMIN_USER โ€” Bootstrap: nombre de usuario admin si BD vacรญa.
GADS_INITIAL_ADMIN_PASSWORD โ€” Bootstrap: contraseรฑa inicial.
GADS_INITIAL_ADMIN_EMAIL โ€” Bootstrap: email del admin.
GADS_INITIAL_EMPRESA_RAZON_SOCIAL โ€” Bootstrap: razรณn social de la primera empresa.
GADS_INITIAL_EMPRESA_CUIT โ€” Bootstrap: CUIT de la primera empresa.

Bootstrap del primer admin

Dos caminos:

a) Automรกtico con env vars

Setea las 5 env vars GADS_INITIAL_* antes de arrancar. Si la BD no tiene admin, el lifespan crea Empresa + Empleado mรญnimo + Usuario admin al iniciar.

b) Manual con endpoint

Con la BD vacรญa:

curl -X POST http://localhost:8000/auth/register-first-admin \
  -H 'Content-Type: application/json' \
  -d '{
    "nombre_usuario": "admin",
    "contrasena": "admin1234",
    "email": "admin@local.dev",
    "nombre": "Admin",
    "apellido": "Inicial",
    "dni": "11111111",
    "cuil": "20-11111111-1",
    "legajo": "ADM-001",
    "empresa_razon_social": "Mi Empresa",
    "empresa_cuit": "30-12345678-9",
    "empresa_email": "contacto@miempresa.com",
    "empresa_telefono": "011-1234-5678",
    "empresa_direccion": "Av. Siempreviva 742"
  }'

Despuรฉs del primer admin, este endpoint devuelve 409.

c) Seed dev (Nero IT)

Solo contra SQLite:

python scripts/crear_usuario_admin_alejandro.py --reset-db
# Crea 1 empresa (Nero IT) + 7 empleados/usuarios. Idempotente.

Cรณmo correrlo

python scripts/iniciar_servidor.py        # arranque normal con --reload
# o:
uvicorn app.main:app --host 0.0.0.0 --port 8000

Despuรฉs: http://localhost:8000/docs para Swagger UI.


Preview con frontend mรญnimo

Hay un frontend HTML/CSS/JS plano en app/static/ para probar end-to-end sin depender del frontend definitivo (ese vive en otro repo).

python scripts/preview.py
# abrรญ http://127.0.0.1:8000/ui/  ยท  login: admin / admin1234

El script borra app.db, setea env vars de bootstrap y levanta uvicorn.


Endpoints โ€” referencia completa

Convenciรณn de auth:

  • ๐Ÿ”“ pรบblico โ€” sin autenticaciรณn.
  • ๐Ÿ”‘ autenticado โ€” bearer JWT.
  • ๐Ÿ‘‘ admin โ€” rol == Administrador.
  • ๐Ÿงฎ admin/contador โ€” rol in {Administrador, ContadorExterno}.
  • ๐Ÿชช propio o admin โ€” current.id_usuario == :id o admin.

Todos los endpoints autenticados esperan: Authorization: Bearer <access_token>.


Health

GET /health ๐Ÿ”“

Liveness probe.

Response 200:

{"status": "ok"}

Auth

POST /auth/login ๐Ÿ”“

Body:

{"nombre_usuario": "admin", "contrasena": "admin1234"}

Response 200:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 3600
}

Errores: 401 (credenciales invรกlidas o usuario inactivo), 422 (body invรกlido).


GET /auth/me ๐Ÿ”‘

Devuelve el usuario autenticado.

Response 200:

{
  "id_usuario": 1,
  "nombre_usuario": "admin",
  "email": "admin@local.dev",
  "rol": "Administrador",
  "estado": "activo",
  "id_empleado": 1
}

Errores: 401 (sin token, token invรกlido o expirado, usuario eliminado).


POST /auth/register-first-admin ๐Ÿ”“

Bootstrap inicial. Solo permitido si no existe ningรบn admin.

Body: ver Bootstrap manual.

Response 201: mismo formato que GET /auth/me.

Errores: 409 (ya hay admin), 422 (body invรกlido โ€” email malformado, password < 8 chars, etc.).


Empresas

GET /empresas ๐Ÿ”‘

Admin: lista todas. ContadorExterno/Empleado: solo la propia.

Response 200:

[
  {
    "id_empresa": 1,
    "razon_social": "Nero IT",
    "cuit": "30-71888999-1",
    "email_contacto": "contacto@neroit.local",
    "telefono_contacto": "011-4000-0000",
    "direccion": "Buenos Aires, Argentina",
    "fecha_alta": "2026-05-10",
    "estado": "activo"
  }
]

GET /empresas/export ๐Ÿ‘‘

Devuelve un CSV (UTF-8, separador coma) con todas las empresas. Header Content-Disposition: attachment; filename="empresas.csv". Columnas: id_empresa, razon_social, cuit, email_contacto, telefono_contacto, direccion, fecha_alta, estado, creado_en, actualizado_en. Ver muestra real en exports/empresas.csv.


POST /empresas ๐Ÿ‘‘

Body:

{
  "razon_social": "ACME SA",
  "cuit": "30-99999999-1",
  "email_contacto": "rrhh@acme.com",
  "telefono_contacto": "011-5555-5555",
  "direccion": "Av. Corrientes 1234",
  "fecha_alta": "2026-05-10",
  "estado": "activo"
}

Response 201: misma forma que el item de GET /empresas con el id_empresa asignado.

Errores: 409 (CUIT duplicado), 403 (no admin), 422.


GET /empresas/{id_empresa} ๐Ÿ”‘

Admin: cualquiera. No-admin: solo la propia (current.empleado.id_empresa), sino 403.

Response 200: forma EmpresaResponse. 404 si no existe.


PATCH /empresas/{id_empresa} ๐Ÿ‘‘

Body parcial (solo los campos a cambiar):

{"estado": "inactivo", "telefono_contacto": "011-9999-9999"}

Response 200: empresa actualizada. 404 si no existe.


DELETE /empresas/{id_empresa} ๐Ÿ‘‘

Baja lรณgica: pasa estado a inactivo. La empresa sigue accesible vรญa GET.

Response 204 (sin body). 404 si no existe.


Empleados

GET /empleados ๐Ÿ”‘

Lista todos los empleados.

Response 200:

[
  {
    "id_empleado": 1,
    "id_empresa": 1,
    "legajo": "NERO-001",
    "nombre": "Alejandro",
    "apellido": "Mabbdet",
    "dni": "35111222",
    "cuil": "20-35111222-4",
    "fecha_ingreso": "2026-05-10",
    "categoria_laboral": "administracion",
    "tipo_jornada": "completa",
    "modalidad_fichada_habilitada": "habilitada",
    "estado": "activo"
  }
]

GET /empleados/export ๐Ÿ‘‘

CSV con todos los empleados. Columnas: id_empleado, id_empresa, legajo, nombre, apellido, dni, cuil, fecha_ingreso, categoria_laboral, tipo_jornada, modalidad_fichada_habilitada, estado, creado_en, actualizado_en. Muestra: exports/empleados.csv.


POST /empleados ๐Ÿ‘‘

Body:

{
  "legajo": "EMP-100",
  "nombre": "Juan",
  "apellido": "Pรฉrez",
  "dni": "30111222",
  "cuil": "20-30111222-3",
  "fecha_ingreso": "2026-05-10",
  "categoria_laboral": "operaciones",
  "tipo_jornada": "completa",
  "modalidad_fichada_habilitada": "habilitada",
  "id_empresa": 1,
  "estado": "activo"
}

Enums vรกlidos:

  • categoria_laboral: operaciones | administracion | contaduria
  • tipo_jornada: completa | parcial | turnos
  • modalidad_fichada_habilitada: habilitada | deshabilitada
  • estado: activo | inactivo

Errores: 404 (id_empresa inexistente), 409 (dni/cuil duplicado o (id_empresa, legajo) duplicado), 422.


GET /empleados/{id_empleado} ๐Ÿ”‘

Response 200: EmpleadoResponse. 404 si no existe.


PATCH /empleados/{id_empleado} ๐Ÿ‘‘

id_empresa NO es modificable (excluido del schema). Body parcial:

{"estado": "inactivo", "categoria_laboral": "administracion"}

DELETE /empleados/{id_empleado} ๐Ÿ‘‘

Baja lรณgica. 204 sin body.


Usuarios

GET /usuarios ๐Ÿ‘‘

Response 200: lista sin contrasena_hash.

[
  {
    "id_usuario": 1,
    "nombre_usuario": "admin",
    "email": "admin@local.dev",
    "estado": "activo",
    "ultimo_acceso": "2026-05-10T20:58:54.530602",
    "rol": "Administrador",
    "id_empleado": 1
  }
]

GET /usuarios/export ๐Ÿ‘‘

CSV sin contrasena_hash. Columnas: id_usuario, nombre_usuario, email, rol, estado, ultimo_acceso, id_empleado, creado_en, actualizado_en. Muestra: exports/usuarios.csv.


POST /usuarios ๐Ÿ‘‘

Body:

{
  "nombre_usuario": "jperez",
  "contrasena": "secreta12",
  "email": "jperez@empresa.com",
  "rol": "Empleado",
  "id_empleado": 5,
  "estado": "activo"
}

rol: Administrador | ContadorExterno | Empleado. La contraseรฑa se hashea con bcrypt antes de persistir.

Errores: 409 (nombre_usuario/email duplicado), 404 (id_empleado inexistente), 403 (no admin), 422 (password < 8 chars).


GET /usuarios/{id_usuario} ๐Ÿชช

Admin: cualquiera. No-admin: solo a sรญ mismo (current.id_usuario == :id), sino 403.


PATCH /usuarios/{id_usuario} ๐Ÿชช

Reglas:

  • current.id_usuario == :id o admin โ†’ puede editar email, nombre_usuario, contrasena, estado.
  • Solo admin puede modificar rol (sino 403).
  • id_rol y id_empleado NO son modificables.

Body ejemplo:

{"email": "nuevo@empresa.com", "contrasena": "nueva-pass-123"}

DELETE /usuarios/{id_usuario} ๐Ÿ‘‘

Baja lรณgica. 204.


Fichadas

POST /fichadas/import ๐Ÿงฎ

Import masivo de fichadas desde CSV.

Multipart:

  • file: CSV UTF-8 (encoding utf-8-sig tolerado para BOM de Excel).

Query params:

  • dry_run (bool, default false) โ€” si true, valida pero hace rollback.
  • id_empresa (int, opcional) โ€” admin debe indicarla. ContadorExterno la infiere de su empleado.

Headers esperados del CSV:

Fecha, Hora, Forma Registro, Tipo Registro, Legajo, Empleado, Observaciones

Mapeos:

  • Forma Registro: Local โ†’ OrigenFichada.LOCAL, Manual โ†’ OrigenFichada.MANUAL. Otros valores โ†’ error de fila.
  • Tipo Registro: Entrada โ†’ TipoFichada.ENTRADA, Salida โ†’ TipoFichada.SALIDA.
  • Empleado: buscado por (id_empresa, legajo).
  • Observaciones: si matchea ^JUSTIFICACION\s+(Entra|Sale)\s*:\s*(.+)$ (case-insensitive), se crea automรกticamente una Novedad con TipoNovedad (auto-creado si no existe).

Comportamiento de transacciรณn: cada fila usa SAVEPOINT. Errores de fila se acumulan sin abortar el batch.

Curl:

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@tests/fixtures/planilla_ejemplo.csv" \
  "http://localhost:8000/fichadas/import?id_empresa=1"

Response 200:

{
  "total_filas": 247,
  "fichadas_creadas": 247,
  "novedades_creadas": 2,
  "tipos_novedad_creados": ["vacaciones"],
  "origenes_fichada_existentes": ["biometrico", "manual", "qr", "api", "excel", "local"],
  "errores": [],
  "dry_run": false
}

Errores tรญpicos en errores[] (no abortan):

{"fila": 17, "motivo": "Empleado legajo 99 no encontrado en empresa 1", "legajo": "99"}

Errores HTTP: 422 (headers faltantes, file vacรญo), 400 (admin sin id_empresa), 403 (rol Empleado plano).


Dashboards

Mรฉtricas agregadas para tableros de status. Auth: admin o contador (excepto /empleados/{id}, que tambiรฉn permite al propio empleado).

GET /dashboards/resumen ๐Ÿงฎ

Mรฉtricas globales (admin) o de la empresa propia (contador).

Query params (opcionales):

  • id_empresa (int) โ€” admin filtra; contador siempre ve la propia (se ignora si se pasa otra).

Response 200:

{
  "id_empresa": null,
  "empresas": {"total": 1, "activos": 1, "inactivos": 0},
  "empleados": {"total": 7, "activos": 7, "inactivos": 0},
  "empleados_por_categoria": {"administracion": 1, "contaduria": 2, "operaciones": 4},
  "empleados_por_jornada": {"completa": 7},
  "usuarios": {"total": 7, "por_rol": {"Administrador": 1, "ContadorExterno": 2, "Empleado": 4}},
  "fichadas": {"total": 247, "ultimos_7_dias": 0, "ultimos_30_dias": 0},
  "novedades": {"total": 2, "pendientes": 2, "aprobadas": 0, "rechazadas": 0, "anuladas": 0}
}

Errores: 403 (rol Empleado plano).


GET /dashboards/empleados/status ๐Ÿงฎ

Listado de empleados con mรฉtricas resumidas (รบltima fichada, fichadas de los รบltimos 30 dรญas, novedades pendientes).

Query params (opcionales):

  • id_empresa (int) โ€” admin filtra; contador forzado a la propia.
  • estado โ€” activo | inactivo.

Response 200:

{
  "total": 7,
  "items": [
    {
      "id_empleado": 1,
      "id_empresa": 1,
      "legajo": "NERO-001",
      "nombre": "Alejandro",
      "apellido": "Mabbdet",
      "estado": "activo",
      "categoria_laboral": "administracion",
      "tipo_jornada": "completa",
      "ultima_fichada": "2026-04-17T17:43:00-03:00",
      "fichadas_ultimos_30_dias": 12,
      "novedades_pendientes": 0
    }
  ]
}

GET /dashboards/empleados/{id_empleado} ๐Ÿชช

Detalle completo del empleado: fichadas por mes, novedades agrupadas por tipo, primera/รบltima fichada.

Auth: admin (cualquier empleado), contador (solo empleados de su empresa), el propio empleado (current.id_empleado == :id).

Response 200:

{
  "id_empleado": 1,
  "id_empresa": 1,
  "legajo": "NERO-001",
  "nombre": "Alejandro",
  "apellido": "Mabbdet",
  "estado": "activo",
  "categoria_laboral": "administracion",
  "tipo_jornada": "completa",
  "modalidad_fichada_habilitada": "habilitada",
  "ultima_fichada": "2026-04-17T17:43:00-03:00",
  "primera_fichada": "2026-04-01T08:00:00-03:00",
  "fichadas_total": 22,
  "fichadas_ultimos_30_dias": 22,
  "fichadas_por_mes": {"2026-04": 22},
  "novedades_total": 2,
  "novedades_pendientes": 2,
  "novedades_por_tipo": {"vacaciones": 2}
}

Errores: 403 (otro empleado sin permiso), 404 (id inexistente).


Frontend preview

GET / ๐Ÿ”“

Redirige 307 โ†’ /ui/.

GET /ui/ ๐Ÿ”“

Sirve index.html (login). Despuรฉs del login, dashboard estรกtico en /ui/dashboard.html.


Modelo de dominio

Diagrama relacional (resumen)

Empresa 1โ”€โ”€โ”€* Empleado 1โ”€โ”€โ”€? Usuario
              โ”‚                โ”‚
              โ”œโ”€โ”€โ”€* Asignacion โ”€โ”€โ”€ Horario
              โ”œโ”€โ”€โ”€* Fichada โ”€โ”€โ”€โ”€ OrigenFichada
              โ””โ”€โ”€โ”€* Novedad โ”€โ”€โ”€โ”€ TipoNovedad
                       โ”‚
                       โ””โ”€โ”€โ”€? Justificativo

CierreMensual โ”€โ”€โ”€ Empresa
              โ”œโ”€โ”€ ResumenMensualEmpleado *โ”€โ”€ Empleado
              โ””โ”€โ”€ Exportacion

DiasEspeciales (independiente)
Auditoria      (genรฉrica, registra cualquier entidad)

Tablas (14)

Tabla Notas
usuarios nombre_usuario รบnico, rol SAEnum, estado SAEnum, hash bcrypt.
empresas cuit รบnico, baja lรณgica.
empleados dni y cuil รบnicos globales, (id_empresa, legajo) รบnico compuesto.
horarios Banda, tolerancias, dรญas descanso.
asignaciones_horario Validaciรณn servicio: rango sin solapamientos por empleado.
fichadas fecha_hora aware, tipo_fichada enum (entrada/salida).
origenes_fichada Catรกlogo: biometrico, manual, qr, api, excel, local.
tipos_novedad nombre_tipo รบnico.
novedades id_fichada nullable (vacaciones planificadas no tienen fichada).
justificativos 1:1 con novedad, archivo asociado.
cierres_mensuales CHECK mes BETWEEN 1 AND 12, CHECK anio BETWEEN 2000 AND 2100.
resumenes_mensuales_empleado Mรฉtricas por empleado-cierre.
dias_especiales UniqueConstraint (fecha, tipo_dia_especial).
auditorias Genรฉrica. Index compuesto (entidad_afectada, id_registro_afectado).

Casi todas las tablas heredan TimestampMixin (creado_en / actualizado_en).

Enums (Python)

Rol, OrigenFichada, TipoFichada, EstadoEntidad, EstadoNovedad, OrigenNovedad, EstadoJustificativo, EstadoExportacion, TipoJornada, ModalidadFichada, CategoriaLaboral, TipoHorario, TipoJustificativo, UnidadMedidaTipoNovedad, EstadoCierreMensual, TipoFormatoExportacion, TipoDiaEspecial, AccionAuditoria. Ver app/models/enums.py.


Tests y calidad

pytest                       # 124 tests verdes
pytest --cov=app             # con cobertura
ruff check .                 # lint
mypy app                     # types strict

Cobertura por archivo:

Archivo Tests
test_health.py 4
test_auth.py 13
test_bootstrap.py 5
test_usuarios.py 28
test_empleados.py 16
test_empresas.py 15
test_exports.py 14
test_dashboards.py 13
test_fichadas_import.py 11
test_horarios.py 6
test_modelos_constraints.py 8
test_seed.py 4
Total 137

Estructura del proyecto

GADS/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ api/
โ”‚   โ”‚   โ”œโ”€โ”€ deps.py                # get_db, get_current_user, require_rol
โ”‚   โ”‚   โ””โ”€โ”€ routers/
โ”‚   โ”‚       โ”œโ”€โ”€ auth.py            # /auth/*
โ”‚   โ”‚       โ”œโ”€โ”€ empresas.py        # /empresas/* (CRUD + /export)
โ”‚   โ”‚       โ”œโ”€โ”€ empleados.py       # /empleados/* (CRUD + /export)
โ”‚   โ”‚       โ”œโ”€โ”€ usuarios.py        # /usuarios/* (CRUD + /export)
โ”‚   โ”‚       โ”œโ”€โ”€ fichadas.py        # /fichadas/import
โ”‚   โ”‚       โ””โ”€โ”€ dashboards.py      # /dashboards/resumen, /empleados/status, /empleados/{id}
โ”‚   โ”œโ”€โ”€ daos/                      # acceso a datos (sin commit)
โ”‚   โ”œโ”€โ”€ services/                  # lรณgica + commit + manejo de IntegrityError
โ”‚   โ”‚   โ”œโ”€โ”€ auth_service.py        # login, JWT, bootstrap
โ”‚   โ”‚   โ”œโ”€โ”€ usuario_service.py
โ”‚   โ”‚   โ”œโ”€โ”€ empleado_service.py
โ”‚   โ”‚   โ”œโ”€โ”€ empresa_service.py
โ”‚   โ”‚   โ”œโ”€โ”€ horario_service.py     # validaciรณn de no-solapamiento
โ”‚   โ”‚   โ”œโ”€โ”€ fichada_import_service.py  # parser CSV + auto-novedades
โ”‚   โ”‚   โ”œโ”€โ”€ export_service.py      # CSVs (empresas/empleados/usuarios)
โ”‚   โ”‚   โ””โ”€โ”€ dashboard_service.py   # mรฉtricas agregadas
โ”‚   โ”œโ”€โ”€ models/                    # ORM SQLAlchemy 2.0
โ”‚   โ”œโ”€โ”€ schemas/                   # Pydantic v2
โ”‚   โ”œโ”€โ”€ static/                    # frontend preview HTML/CSS/JS
โ”‚   โ”œโ”€โ”€ config.py                  # Settings (pydantic-settings)
โ”‚   โ”œโ”€โ”€ database.py                # engine, SessionLocal, get_db, init_db
โ”‚   โ””โ”€โ”€ main.py                    # FastAPI app + lifespan + bootstrap
โ”œโ”€โ”€ exports/                       # muestras de export CSV
โ”‚   โ”œโ”€โ”€ empresas.csv
โ”‚   โ”œโ”€โ”€ empleados.csv
โ”‚   โ””โ”€โ”€ usuarios.csv
โ”œโ”€โ”€ migrations/                    # Alembic
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ iniciar_servidor.py
โ”‚   โ”œโ”€โ”€ preview.py                 # uvicorn + bootstrap env vars
โ”‚   โ””โ”€โ”€ crear_usuario_admin_alejandro.py  # seed dev
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ fixtures/planilla_ejemplo.csv  # 247 filas, 4 empleados, 2 vacaciones
โ”‚   โ””โ”€โ”€ test_*.py                  # 124 tests
โ”œโ”€โ”€ pyproject.toml                 # ruff + mypy
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ alembic.ini
โ””โ”€โ”€ .env.example

Limitaciones conocidas

Limitaciรณn Estado / workaround
Sin CRUD de horarios desde API (servicio existe). Pendiente router.
Sin CRUD de novedades (solo se crean por import CSV). Pendiente router.
Sin endpoints de cierre mensual / resumen. Modelo listo, lรณgica no implementada.
Sin upload de adjuntos de justificativos. โ€”
Sin reportes / exportaciรณn de fichadas (solo empresas/empleados/usuarios). Extender export_service.
Sin recuperaciรณn de contraseรฑa. Admin puede resetear via PATCH /usuarios/{id}.
Sin CORS configurado. Sumar CORSMiddleware cuando frontend definitivo viva en otro dominio.
Sin CSP / HSTS / headers de seguridad. Configurar en proxy reverso (nginx/Caddy).
JWT en localStorage del preview. Vulnerable a XSS โ€” para prod usar cookie httpOnly + CSRF.
Alembic baseline simplificada (Base.metadata.create_all). Regenerar con alembic revision --autogenerate al primer schema change.
Frontend preview sin paginaciรณn / filtros / bรบsqueda. UI mรญnima de demostraciรณn.
HTTP_422_UNPROCESSABLE_ENTITY deprecation warning (Starlette). Cambiar a HTTP_422_UNPROCESSABLE_CONTENT.

Licencia

Ver LICENSE.

About

Source code of the practical work Backend repository for the Applied Management in Software Development I (GADS) subject at the National University of La Matanza (@unlam).

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages