Commit Graph
378 Commits
Author SHA1 Message Date
Lizandro Guarnizo 702323f852 fix: corrección portal enfermero — pagos y agendamiento
- fix: portal.renderAgenda → portal._renderLista() (error JS al registrar pago)
- feat: modal nueva agenda añade tipo_cliente (particular/seguro), seguro_nombre, autorizacion, valor_domicilio, valor_copago
- feat: toggleSeguro() muestra/oculta campo EPS según tipo seleccionado
- fix: abrir() resetea todos los campos nuevos en cada apertura del modal
- datos de cobro se envían al guardar y se auto-asigna la enfermera creadora
2026-03-12 23:02:48 -05:00
Lizandro Guarnizo eaebda9fe7 feat: edición completa de domicilio desde panel detalle
- Modal expandido: tipo_cliente, seguro, autorizacion, valor_domicilio, valor_copago, copago_laboratorio, indicaciones, fecha, hora, tipo_servicio, notas
- Botón Editar en header del panel detalle (aparece al seleccionar un domicilio)
- editarDomicilio(id): carga y pre-rellena todos los campos del modal
- toggleSeguro(): muestra/oculta campo seguro_nombre según tipo_cliente
- Tras guardar, el detalle se refresca automáticamente
2026-03-12 23:00:25 -05:00
Lizandro Guarnizo beb1bbe1d4 feat: sistema de pagos domicilios — cobro enfermero y estado pago admin
- database/07_add_pago_domicilio.sql: 6 columnas nuevas en lab_domicilios
  (pago_estado ENUM, pago_modo, pago_monto, pago_fecha, pago_notas, pago_registrado_por)
- Domicilio::filtrarCampos(): permite guardar campos de pago vía actualizar()
- Enfermera::agenda(): incluye valor_domicilio, valor_copago, pago_estado, pago_modo, pago_monto, pago_notas
- api/lab/registrar_pago.php: endpoint POST para registrar/actualizar pago del domicilio
- enfermero_portal.php: bloque cobro en card, modal registrar pago (efectivo/transferencia/exento), objeto pagoModal JS
- lab_domicilios.php: badge pago //🔵 en fila tabla, sección pago detallada en panel lateral
2026-03-12 22:53:24 -05:00
Lizandro Guarnizo c42e0976d7 fix: enfermero_portal - mostrar imagen de la orden aunque no esté vinculada al domicilio
Si lab_domicilios.orden_id es NULL (domicilio creado antes del fix
o desde otro flujo), usa subquery para encontrar la orden más reciente
del mismo paciente que tenga local_file.

Así el enfermero siempre ve la imagen de la orden del paciente
independientemente de cómo se agendó el domicilio.
2026-03-12 22:38:48 -05:00
Lizandro Guarnizo 8956de8eaa fix: ruta doble en local_file al mostrar imagen de orden médica
La BD guarda local_file con 'uploads/media/...' incluido.
El código le añadía otro 'uploads/media/' dando ruta doble.

- lab_ordenes.php: helper _resolveImg() detecta si el path
  ya empieza con 'uploads/' y no añade el prefijo de nuevo
- enfermero_portal.php: nueva función resImg() con misma lógica,
  usada en _cardHTML() al renderizar la imagen de la orden
2026-03-12 22:23:15 -05:00
Lizandro Guarnizo d362598a70 feat: relacionar imagen de WhatsApp con orden médica al agendar domicilio
Al abrir el modal desde una imagen en el chat:
- guardar() crea primero la OrdenMedica via crear_desde_whatsapp.php
  y pasa el orden_id resultante a save_domicilio.php
- Enfermera::agenda() hace LEFT JOIN a lab_ordenes_medicas para traer
  local_file de la orden asociada (as orden_local_file)
- enfermero_portal._cardHTML() muestra la imagen de la orden médica
  con link para ampliar
2026-03-12 22:14:04 -05:00
Lizandro Guarnizo 12e7b411d7 feat: enfermero_portal - mostrar todos los datos del paciente en tarjeta de domicilio
La tarjeta ahora muestra: documento, fecha de nacimiento, EPS,
teléfono (clickeable), correo (clickeable), exámenes solicitados
y tipo de cliente (particular/seguro con nombre del seguro).

Cambios:
- Enfermera::agenda() amplía SELECT con email, fecha_nacimiento,
  numero_documento, tipo_documento, eps, examenes_solicitados,
  tipo_cliente, seguro_nombre, ciudad, indicaciones_dir, notas_admin
- my_agenda.php simplificado: ya no re-consulta lab_domicilios por
  los campos que ahora vienen directo en la query
- enfermero_portal.php _cardHTML() muestra todos los campos nuevos
2026-03-12 22:05:47 -05:00
Lizandro Guarnizo bce9a8c6b5 feat: modalAgendarDomicilio - campos fecha nacimiento y correo con auto-relleno 2026-03-12 19:58:47 -05:00
Lizandro Guarnizo 4bbb31dc30 fix: lab_usuarios.php — tab-content oculto por CSS custom vs Bootstrap 5
styles.css define .tab-content { display:none } para un sistema de tabs
propio. Bootstrap 5 agrega 'active' al .tab-pane, no al .tab-content,
por lo que el contenedor nunca se mostraba (display:none permanente).

Solución: style='display:block;padding:0' en el div .tab-content para
que Bootstrap controle la visibilidad de los panes internos.
2026-03-12 18:57:39 -05:00
Lizandro Guarnizo 3399d3cf41 fix: docker-compose.prod.yml usar red coolify externa (Coolify usa este archivo, no docker-compose.yml)
Coolify usa /artifacts/.../docker-compose.prod.yml en el deploy.
Ese archivo tenía 'whatsapp-network' local bridge — el contenedor nunca
se conectaba a la red 'coolify' donde está el Redis → SERVFAIL en DNS.

Cambios:
- networks: whatsapp-network → coolify (external: true)
- container_name: removido (Coolify asigna el suyo)
- healthcheck: timeout 5s→10s, start_period 30s→60s
2026-03-12 18:48:58 -05:00
Lizandro Guarnizo 729474b138 fix: PHP sessions → Redis correcto (session.save_path con host real)
php.ini NO expande variables de entorno, estaba hardcodeado 'tcp://redis:6379'.
PHP intentaba conectar al host inexistente 'redis' en cada request →
timeout 10s en session_start() → todos los scripts tardaban 10-13s.

Solución:
- entrypoint.sh reescribe session.save_path con el host/password real al inicio
- php.ini actualizado con placeholder (sobreescrito por entrypoint de todas formas)
2026-03-12 17:13:11 -05:00
Lizandro Guarnizo d6d312f72f fix: health.php respuesta instantánea — sin DB ni Redis
Versión anterior intentaba conectar a DB (g1286...) y Redis (zqmfom0...)
que colgaban esperando conexión → todos los workers PHP-FPM bloqueados →
nginx acepta TCP pero no responde HTTP → healthcheck timeout → unhealthy.
2026-03-12 17:04:40 -05:00
Lizandro Guarnizo 6807da8a2b fix: conectar app a red 'coolify' donde vive Redis de Coolify
El contenedor Redis (zqmfom0vw6nj3ae66mkwxek1) vive en la red 'coolify'.
La app estaba solo en la red nj05x5... → DNS SERVFAIL → health.php tardaba
10-12s intentando conectar → healthcheck timeout → unhealthy → 'no available server'.

Solución: declarar red coolify como external y conectar el servicio app.
Fix inmediato aplicado manualmente con: docker network connect coolify <container>
2026-03-12 16:58:44 -05:00
Lizandro Guarnizo a497dc4b31 fix: migración SQL + red Docker para Coolify
06_master_sync.sql:
- Comentario tenía ';' (La columna role_id ya existe; la FK) →
  el parser de migrate.php divide por ; y el texto 'la FK' quedaba
  como sentencia SQL → error SQLSTATE[42000]

docker-compose.yml:
- Eliminar whatsapp-network (red bridge propia)
- Con 1 solo servicio no se necesita red interna
- Coolify añade automáticamente su red nj05x5... (donde está Traefik)
- La red bridge extra podría interferir con el routing de Traefik
2026-03-12 16:42:14 -05:00
Lizandro Guarnizo 2537bc8cfc fix: healthcheck nunca retorna 503 — evita 'no available server'
health.php:
- DB fallo → 'warning' (no 'unhealthy', no cierra tráfico Traefik)
- Redis ya era warning desde commit anterior

docker-compose.yml:
- timeout: 5s → 10s (DB externa puede tardar más)
- start_period: 40s → 60s (tiempo suficiente para that migrations corran)
- fallback: si health.php falla, intenta / raíz antes de marcar unhealthy
2026-03-12 16:35:37 -05:00
Lizandro Guarnizo 3b85c4ac93 fix: docker-compose.yml limpiar volúmenes y container_name para Coolify
- Eliminar container_name (Coolify asigna su propio nombre, conflicto evitado)
- Eliminar ./:/var/www/html (bind mount sobreescribía código del contenedor en prod)
- Eliminar ./logs y ./uploads bind mounts (reemplazados por named volumes)
- Eliminar php-socket volume (ya no se usa)
- Usar named volumes app-logs/app-uploads (persistencia correcta en Coolify)
2026-03-12 16:32:46 -05:00
Lizandro Guarnizo 85fb463708 fix: 06_master_sync.sql corregir errores SQL para MariaDB
1. ADD CONSTRAINT IF NOT EXISTS no es estándar en MariaDB < 10.5 —
   eliminado el bloque FK de admin_users.role_id (la integridad la
   gestiona la aplicación).
2. AFTER `pdf_path` fallaba porque la columna no existe en lab_form_envios —
   eliminado el AFTER, la columna se agrega al final de la tabla.

Esto evita que la migración falle en cada deploy.
2026-03-12 16:30:03 -05:00
Lizandro Guarnizo ec2135442d fix: eliminar wait de Redis en entrypoint.sh
Redis es externo (Coolify managed) — esperar con redis-cli causaba
30 segundos de delay en cada arranque porque:
1. redis-cli no pasaba -a $REDIS_PASSWORD (auth fallaba)
2. Redis no es local, no tiene sentido hacer ping desde dentro del contenedor
La app conecta a Redis cuando necesita (RedisQueue, ConversationState, etc.)
2026-03-12 16:26:46 -05:00
Lizandro Guarnizo 9571b2677a fix: health.php Redis fallo no marca app como unhealthy
Redis es infraestructura opcional — si falla la conexión, retorna 'warning'
en lugar de 'unhealthy', evitando que Traefik muestre 'no available server'.
Solo DB down debe marcar el app como unhealthy (es crítico).
2026-03-12 16:22:43 -05:00
Lizandro Guarnizo 2a50a5e1d4 fix: crear .installation_completed (con punto) en Dockerfile
isInstallationCompleted() en config.php busca '.installation_completed' (con punto)
pero el Dockerfile solo creaba 'installation_completed' (sin punto).
Ahora se crean ambas variantes para cubrir todos los checks del código.
2026-03-12 16:09:23 -05:00
Lizandro Guarnizo 30e38720e2 fix: eliminar REDIS_HOST hardcodeado 'redis' en todos los archivos
- docker-compose.prod.yml: REDIS_HOST=redis → ${REDIS_HOST:-127.0.0.1}
  eliminados servicios redis/worker/maintenance (usan Coolify/Supervisor)
- .env, .env.docker: REDIS_HOST=redis → 127.0.0.1 (fallback seguro)
- .env.example: actualizado con comentario de Coolify
- health.php: fallback 'redis' → '127.0.0.1'
- PHP files (RedisQueue, ConversationState, etc.) ya usaban getenv() correctamente
2026-03-12 13:48:01 -05:00
Lizandro Guarnizo d50abb3ff3 fix: eliminar Redis y worker locales del docker-compose.yml
- Redis ahora es un Database Resource externo en Coolify
  Configurar: REDIS_HOST, REDIS_PORT, REDIS_PASSWORD en env vars
- Worker corre dentro del contenedor app via Supervisor (no servicio aparte)
- Eliminados: servicio redis, worker, redis-commander, volumenes db-data/redis-data
- Solo queda: servicio app + volumen php-socket
2026-03-12 13:44:41 -05:00
Lizandro Guarnizo c8a4a399fc fix: eliminar bloqueo infinito en espera de Redis al arrancar
- entrypoint.sh: Redis wait tenía loop infinito sin timeout → el contenedor
  se quedaba colgado para siempre si Redis tardaba, causando 504 Gateway Timeout
  Ahora tiene MAX_RETRIES=15 (30s) y continúa aunque Redis no esté disponible
- entrypoint.sh: removido set -e para que errores no-críticos (permisos,
  SSL, etc.) no maten el arranque del contenedor
- docker-compose.yml: removido depends_on redis con condition:service_healthy
  Docker bloqueaba el inicio de app hasta que Redis estuviera healthy,
  lo que podía exceder el timeout de Traefik
2026-03-12 13:31:57 -05:00
Lizandro Guarnizo 937a02e5ac debug: agregar try/catch y console.log en lab_usuarios.php para diagnosticar render 2026-03-12 13:24:41 -05:00
Lizandro Guarnizo 8a862e914a fix: crear installation_completed en build → activa modo producción PHP
Sin este archivo, config.php ejecuta con display_errors=1 (modo dev).
Los PHP warnings se inyectaban dentro del bloque <script> de las páginas,
rompiendo el JavaScript y causando que el contenido no se renderizara.

El archivo se crea en el RUN del Dockerfile así sobrevive cada deploy.
2026-03-12 10:40:37 -05:00
Lizandro Guarnizo 81bdda2ca3 feat(migrate): agregar migración de datos seed para roles y módulos
20260304_roles_seed_data.sql — inicializa datos en roles/role_modules
y vincula admin_users.role_id. Todos los statements son idempotentes
(UPDATE + INSERT IGNORE), por lo que se pueden re-ejecutar sin problemas.

Esto asegura que en cualquier deploy los módulos de rol y los colores
estén correctos, aunque la migración DDL ya haya sido registrada.
2026-03-12 10:24:52 -05:00
Lizandro Guarnizo 33dfc0cf71 fix(migrate): detectar tablas faltantes aunque migración esté registrada
Si una migración está en la tabla migrations pero la tabla que crea ya
no existe en la BD (ej. Coolify recreó la DB), el sistema la re-ejecuta
automáticamente. Esto evita el error anterior donde 10 lab migrations
estaban registradas como ejecutadas pero las tablas no existían.
2026-03-12 09:46:07 -05:00
Lizandro Guarnizo 01ea867229 fix: usar expose en lugar de ports en docker-compose.yml para Coolify/Traefik
El archivo docker-compose.yml es el que usa Coolify por defecto.
Con 'ports: 8080:80' Traefik no sabía a qué puerto rutear en cada
redeploy. Con 'expose: 80' Traefik detecta el puerto 80 del contenedor
directamente (igual que docker-compose.prod.yml).

Para desarrollo local usar: docker-compose -f docker-compose.dev.yml up
2026-03-12 09:36:27 -05:00
Lizandro Guarnizo 7000464d36 fix(migrate): robustecer parser SQL y no registrar migración si hubo errores
- Eliminar comentarios de bloque /* */ antes de parsear
- Limpiar líneas vacías y comentarios -- sentencia por sentencia
- Agregar 'Duplicate key name' y 'Multiple primary key defined' a errores ignorables
- Solo registrar en tabla migrations si no hubo errores reales
- Si una sentencia falla, continuar con las demás pero marcar had_error=true
2026-03-12 09:27:46 -05:00
Lizandro Guarnizo 28307f44d1 fix: remove labels from compose (conflicts with Coolify), EXPOSE only port 80 2026-03-11 22:38:36 -05:00
Lizandro Guarnizo 0b3b499c3f fix: migrate.php also scans migrations/ folder for lab SQL files 2026-03-11 22:29:00 -05:00
Lizandro Guarnizo ef6c4277a6 fix: add explicit Traefik loadbalancer port label (port 80) 2026-03-11 22:01:58 -05:00
Lizandro Guarnizo 7586954e53 fix: Predis ping() returns Status object, cast to string before compare 2026-03-11 21:41:58 -05:00
Lizandro Guarnizo 7cca8342b5 refactor: unify env vars — no hardcoded credentials, LOG_LEVEL/SESSION from env 2026-03-11 21:16:52 -05:00
Lizandro Guarnizo 7769e5d4f0 Update .env 2026-03-11 20:53:44 -05:00
Lizandro Guarnizo 6e03169c39 feat: remove mysql service from compose, use external Coolify DB resource 2026-03-11 20:31:11 -05:00
Lizandro Guarnizo 1b9942442a fix: disable MariaDB SSL for internal Docker networking + worker MySQL readiness wait 2026-03-11 20:22:19 -05:00
Lizandro Guarnizo 06e4ad6fd5 fix: regenerate vendor/ if Coolify mounts empty volume over /var/www/html 2026-03-10 23:05:02 -05:00
Lizandro Guarnizo b045d9db3a fix: use named volumes, reduce nginx connections to 1024, 1 worker process 2026-03-10 22:47:42 -05:00
Lizandro Guarnizo 7a17fefb3e fix: pin redis extension version 6.0.2 for reproducible builds 2026-03-10 22:39:33 -05:00
Lizandro Guarnizo 9080444daa fix: remove port bindings for Coolify Traefik proxy 2026-03-10 22:29:32 -05:00
Lizandro Guarnizo 1fb4361f96 fix: worker logs permission denied + migration AFTER password 2026-03-10 22:23:30 -05:00
Lizandro Guarnizo db469b2134 up 2026-03-10 18:42:51 -05:00
Lizandro Guarnizo b57fc16ab3 fix: archivos >200KB usan URL publica, evita uploadMedia() con OpenSSL 3.x
El contenedor falla al subir a Facebook archivos >~200KB por renegociacion
TLS de OpenSSL 3.x (errno 55). Solucion sin rebuild:
- Archivos <=200KB: uploadMedia() como antes (funciona OK)
- Archivos >200KB: enviar URL publica - WhatsApp descarga desde el servidor
  directamente, sin pasar por el contenedor ni por OpenSSL

Aplica a imagenes, documentos, videos y audios.
2026-02-21 12:30:56 -05:00
Lizandro Guarnizo edcefbe67c fix: comprimir imagen pegada hasta <250KB con calidad progresiva
OpenSSL 3.x en contenedor hace renegociacion TLS con Facebook >~300KB
causando errno 55. Forzar imagen a quedar bajo 250KB:
- Escalar a max 1920px si es más grande
- Intentar JPEG q=0.85, 0.70, 0.55, 0.40, 0.25 hasta pasar el umbral
- JPEG/WebP ya bajo 250KB se pasan sin modificar
2026-02-21 12:26:36 -05:00
Lizandro Guarnizo dd6dd2e1f4 fix: comprimir imagen pegada a JPEG antes de subir a WhatsApp
Imagen PNG del portapapeles puede ser 1-5MB sin comprimir.
Facebook resetea TCP (errno 55) al recibir payloads grandes desde el
contenedor con OpenSSL 3.x. Solución: convertir PNG→JPEG 0.85 vía Canvas
en el navegador antes de subir → reduce tamaño ~10x → upload exitoso.
JPEG/WebP ya comprimidos se pasan sin modificar.
2026-02-21 12:20:02 -05:00
Lizandro Guarnizo a716cd187a Update WhatsAppService.php 2026-02-21 12:02:15 -05:00
Lizandro Guarnizo 51f1b04293 up 2026-02-21 11:45:32 -05:00
Lizandro Guarnizo e00c96c614 Update WhatsAppService.php 2026-02-21 11:39:25 -05:00
Lizandro Guarnizo 0aa450be67 up 2026-02-21 11:33:56 -05:00