Asistencia Geo — Documentación

White Paper Técnico — Sistema de Control de Asistencia Georeferenciado

Documento: explicación técnica de la aplicación (arquitectura, algoritmos, modelo de datos, seguridad y mapa de fuentes). Versión del producto: 0.3.1 · Fecha: 2026-09-22 · Autor: Rocky (IA DevOps) + Javier Páez

Este documento es el white sheet técnico de la aplicación. Complementa a: 07 — Manual DevOps, 08 — Versiones y 09 — Stack tecnológico. El mapa de fuentes (§11) apunta al código real en el repositorio (git).


1. Resumen ejecutivo

Sistema empresarial de control de asistencia georeferenciado para cumplimiento laboral mexicano (LFT / NOM-037 / INAI). El empleado registra su entrada/salida desde una app móvil que valida: (1) quién (biometría local + reconocimiento facial on-device), (2) dónde (GPS + geocerca PostGIS), (3) cuándo (hora firmada por el servidor, no por el dispositivo) y (4) que el dispositivo no está manipulado (anti-spoofing de mock-location / root / jailbreak).

Cada registro es un evento inmutable encadenado con HMAC-SHA256 (tamper-evidence), apto como evidencia legal. La administración (RR.HH.) opera un panel web con mapa, geocercas, empleados, turnos y aprobaciones. La salida se sincroniza con Odoo 18 (hr.attendance) mediante un outbox idempotente.


2. Alcance funcional

Área Funcionalidad Estado
Autenticación Login JWT + registro autoservicio (email + contraseña) ✅ v0.3.1
Asistencia Check-in / check-out con Entrada/Salida explícita ✅ v0.3.0
Biometría local_auth (huella/rostro) + facial on-device (ML Kit) ✅ v0.2.0
Anti-spoofing mock location + root/jailbreak + hora NTP ✅ v0.1.0
Geocercas PostGIS ST_DWithin, perfiles sitio/remoto ✅ v0.1.0
Historial GET /attendance/me + badge “pendiente” ✅ v0.2.0 / v0.3.1
Aprobaciones Flujo administrativo fuera de zona/horario ✅ v0.3.1
Panel web Mapa + zonas + empleados + asignaciones + aprobaciones ✅ v0.3.1
Nómina Horas ordinarias/extra (doble/triple), retardos (LFT) ✅ (lógica pura)
Odoo Outbox odoo_sync → hr.attendance (XML-RPC) 🟡 plan (pipeline codificado)
Offline Cola cifrada + sync idempotente 🟡 parcial

3. Arquitectura de referencia

Ver diagrama PNG docs/diagrams/01-arquitectura.png.

Capas:

┌─────────────────────────────────────────────────────────────┐
│  Clientes:  App Flutter (Android/iOS)  ·  Panel web React   │
└───────────────┬─────────────────────────┬───────────────────┘
                │ HTTPS + JWT (Bearer)    │ HTTPS + JWT
                ▼                         ▼
        ┌───────────────────────────────────────────┐
        │  Cloudflare CDN → nginx (Linode)          │
        └────────────────────┬──────────────────────┘
                             ▼
        ┌───────────────────────────────────────────┐
        │  Backend FastAPI (uvicorn, async)         │
        │   auth · attendance · sites · admin ...   │
        │   geofence · payroll · odoo_client · hash │
        └──────┬──────────────────────────┬─────────┘
               │ asyncpg (SQLAlchemy 2)   │ XML-RPC (odoorpc)
               ▼                          ▼
   ┌──────────────────────┐      ┌──────────────────────┐
   │ PostgreSQL 15+PostGIS│      │ Odoo 18 (hr.attend.) │
   └──────────────────────┘      └──────────────────────┘

Principios de diseño: 1. Backend async end-to-end (FastAPI + SQLAlchemy 2 asyncio + asyncpg). 2. Eventos append-only: la asistencia no se edita ni borra; se corrige con eventos nuevos + auditoría. 3. Autoridad de tiempo en el servidor (NTP), nunca el reloj del dispositivo. 4. PII cifrada en reposo (AES-256-GCM vía Fernet). 5. Separación por capas (api/ → services/ → models/ → core/).


4. Modelo de datos

Ver db/000_schema.sql y diagrama docs/diagrams/03-er-diagrama.png.

4.1 Entidades núcleo

Tabla Rol
companies Multi-tenant (empresa)
users Empleados/usuarios (profile_type: site / remote)
sites Centros de trabajo (geocerca: geom GEOGRAPHY(Point,4326) + radius_m)
employee_site_assignments Asignación empleado ↔︎ sitio (valid_from/valid_until)
shifts Turnos (jornada_type: diurna/nocturna/mixta)
attendance_events Núcleo, inmutable + cadena de hash
attendance_approvals Aprobaciones (mutable; refiere al evento)
attendance_audit_log Correcciones legales (auditoría)
consents Consentimientos INAI
devices / push_tokens Dispositivos y tokens push
alerts Alertas (fraude, salida faltante, fuera de geocerca)
odoo_sync Outbox de sincronización con Odoo

4.2 Enumeraciones (backend/app/models/models.py)

4.3 Inmutabilidad (db/001_immutability.sql)


5. Flujo de check-in / check-out (end-to-end)

Ver diagrama docs/diagrams/02-flujo-checkin.png.

  1. App móvil (mobile/lib/features/attendance/) — el usuario elige Entrada o Salida.
  2. Biometría (biometric_service.dart) — local_auth (huella/rostro).
  3. Anti-spoofing (mock_location_detector.dart, security_service.dart) — mock/root/jailbreak.
  4. GPS (geolocation_service.dart) — geolocator con accuracy_m.
  5. POST /attendance/check-in (backend/app/api/attendance.py + services/attendance.py):
  6. Respuesta 200 con geofence_status, server_time, approval_status.
  7. Worker APScheduler (tasks/scheduler.py) sincroniza odoo_sync → Odoo.

5.1 Reglas de geocerca (services/geofence.py)

La consulta usa ST_DWithin sobre geography (distancias en metros), tomando el sitio primario vigente (is_primary DESC + valid_until).


6. Seguridad

6.1 Autenticación (core/security.py)

6.2 Cifrado de PII en reposo (core/crypto.py)

6.3 Autoridad de tiempo (core/ntp.py)

6.4 Cadena de hash / tamper-evidence (core/hash_chain.py)

6.5 Anti-spoofing (móvil)


7. Motor de nómina (LFT) — services/payroll_rules.py

Funciones puras (testeables, sin BD) que codifican la ley federal:

La autoridad del tipo de jornada es shifts.jornada_type; classify_jornada es heurística de respaldo. Ver backend/tests/test_payroll_rules.py.


8. Integración Odoo — patrón outbox idempotente

Ver docs/05-odoo-integration.md y PLAN_INTEGRACION_ODOO.md.


9. Modo offline (parcial)



11. Mapa de fuentes (dónde vive cada cosa)

Backend — backend/

Componente Archivo(s)
Punto de entrada / routers / CORS / panel estático app/main.py
Configuración (Settings, .env) app/core/config.py, .env.example
Conexión BD (async engine) app/db.py
Modelos / enums app/models/models.py
Esquemas Pydantic app/schemas/
Auth (login/refresh/register) app/api/auth.py
Check-in/out app/api/attendance.py, app/services/attendance.py
Geocercas app/services/geofence.py
Nómina LFT app/services/payroll_rules.py
Odoo client app/services/odoo_client.py
Alertas / notificaciones app/services/alerts.py, app/services/notifications.py
Cadena de hash app/core/hash_chain.py
Tiempo NTP app/core/ntp.py
Cifrado PII app/core/crypto.py
JWT + Argon2id app/core/security.py
Admin (asignaciones, aprobaciones, sitios, empleados) app/api/admin.py
Reportes (timesheet, eventos geo) app/api/reports.py
Scheduler / worker app/tasks/scheduler.py
Seed demo scripts/seed_demo.py, scripts/seed_attendance_month.py
Tests tests/test_hash_chain.py, tests/test_payroll_rules.py, tests/test_time_api.py

Base de datos — db/

Archivo Contenido
000_schema.sql Esquema + enums + PostGIS
001_immutability.sql Trigger append-only + REVOKE
002_seed.sql Datos demo (10 empleados, 3 sitios)
003_approvals.sql Tabla attendance_approvals + backfill

Móvil — mobile/lib/

Componente Archivo(s)
Entrypoint / tema / rutas main.dart, app.dart
Config base URL core/config.dart
Cliente HTTP + auto-refresh JWT core/network/api_client.dart
Secure storage resiliente core/storage/secure_storage.dart
Cola offline core/storage/offline_store.dart
Tiempo NTP (cliente) core/time/ntp_time.dart
Auth (login/registro) features/auth/auth_service.dart
Check-in service features/attendance/services/attendance_checkin_service.dart
Biometría features/attendance/services/biometric_service.dart
Facial features/attendance/services/face_verification_service.dart
Geolocalización features/attendance/services/geolocation_service.dart
Mock/root detector features/attendance/services/mock_location_detector.dart, security_service.dart
Historial / badge features/attendance/history_screen.dart
Sync features/sync/sync_service.dart
Mapa geocerca features/maps/geofence_view.dart
MainActivity (host Android) android/app/src/main/kotlin/.../MainActivity.kt

Panel web — web/

Componente Archivo(s)
Entrypoint src/main.tsx, src/App.tsx
Cliente API src/api.ts
Config env .env.development, .env.production

Infraestructura

Artefacto Ubicación
Compose (PostGIS/Redis local) docker-compose.yml
Diagramas docs/diagrams/*.png (+ generador _gen_diagrams.py)
Checksums APK entregas/asistencia-geo/SHA256SUMS.txt

12. Despliegue

Detalle operativo completo en docs/07-devops-manual.md y diagrama docs/diagrams/04-despliegue.png.


13. Roadmap

  1. Odoo end-to-end: habilitar outbox en producción (schema + credenciales + scheduler).
  2. Push de recordatorios de salida (FCM/APNs) — hoy notifications.py es un stub.
  3. Cierre de CORS (allow_origins=["*"] → dominios concretos).
  4. Hardening Odoo (cerrar 8069 al exterior).
  5. Build iOS completo (hoy el directorio ios/ está parcial).