Repository: mrdavis-dev/clisys
Author: mrdavis-dev
## Objetivo
Transformar el sistema en un SaaS multi-especialidad: el odontograma pasa a ser un módulo opcional activable por clínica, se añade historia clínica genérica para otras especialidades, y se implementa el ciclo de vida de suscripción.
## Precondición
Fase 2 (multi-tenancy con `clinic_id`) debe estar mergeada.
## Tareas
### Módulos por especialidad
- [ ] Crear tabla `specialties` (`id`, `name`, `slug`) — ej. dental, medicina_general, pediatria
- [ ] Añadir `clinics.specialty_id FK → specialties.id`
- [ ] Crear tabla `modules` (`id`, `name`, `slug`) — ej. odontogram, clinical_notes, lab_orders
- [ ] Crear tabla `clinic_modules` (`clinic_id`, `module_id`) — join table para feature flags
- [ ] Implementar `Tenant::hasModule(string $slug): bool` en `admin/core/Tenant.php`
- [ ] Envolver el odontograma en `if (Tenant::hasModule('odontogram'))` en `menu.php` y en `odontograma.php`
- [ ] Ocultar el link de Odontograma en `menu.php` para clínicas sin el módulo
### Historia clínica genérica
- [ ] Crear tabla `clinical_notes` (`id`, `clinic_id`, `patient_id`, `author_id`, `content` TEXT, `created_at`) — reemplaza / complementa `consulta` para clínicas no-dentales
- [ ] Crear `admin/notas.php` + `admin/insert_nota.php` — formulario y handler para notas clínicas libres
- [ ] La tabla `pacientes` tiene campos muy específicos de odontología (hábitos higiénicos, etc.) — migrar a historia genérica + campos extendidos en JSON o tabla EAV `patient_attributes`
### Planes y billing
- [ ] Crear tabla `plans` (`id`, `name`, `max_patients`, `max_users`, `modules` JSON, `price_monthly`)
- [ ] Añadir `clinics.plan_id FK → plans.id` y `clinics.plan_expires_at`
- [ ] Implementar `Tenant::withinLimit(string $resource): bool` — bloquear inserción si se excede el límite del plan
- [ ] Página de upgrade visible cuando se alcanza el límite
### Onboarding público
- [ ] Crear página pública `register.php` (en raíz, no en `/admin`) — formulario de registro de nueva clínica: nombre, especialidad, subdominio deseado, email, contraseña del primer admin
- [ ] Handler `register_clinic.php` — crea registro en `clinics` + usuario admin + email de bienvenida
- [ ] Validación de disponibilidad de subdominio (AJAX)
### Almacenamiento de imágenes (odontograma)
- [ ] Las imágenes del odontograma están como BLOBs en `consulta.imageData` — migrar a almacenamiento en disco por clínica: `storage/{clinic_id}/{patient_cedula}/{filename}`
- [ ] Actualizar `insert_odo.php` para guardar en disco y almacenar solo la ruta en BD
- [ ] Actualizar `consulta_odo.php` para servir desde ruta, no desde BLOB en base64
### Audit log
- [ ] Crear tabla `audit_log` (`id`, `clinic_id`, `user_id`, `action`, `table_name`, `record_id`, `created_at`)
- [ ] Registrar inserciones y borrados críticos (pacientes, pagos, usuarios)
### Email por clínica
- [ ] Añadir a tabla `clinics`: `smtp_host`, `smtp_user`, `smtp_password`, `smtp_port`, `smtp_from`
- [ ] Modificar `insert_pagos_send.php` para leer configuración SMTP de la clínica activa en lugar de `.env` global (el `.env` se usa como fallback)
## Verificación
- Clínica dental: ve odontograma en menú y puede guardar tratamientos
- Clínica de medicina general: no ve odontograma, ve notas clínicas genéricas
- Registro público: crear nueva clínica desde cero, recibir email de bienvenida, login inmediato
- Plan gratuito con límite de 50 pacientes: al agregar el 51 se muestra mensaje de upgrade
- Imágenes de odontograma se sirven desde disco, no desde BD
Fuente: GITHUB