From 2f72d87c3b59bbd628b0ed21d10001120d2c747d Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 27 May 2026 20:40:17 -0500 Subject: [PATCH] cambios --- api/lab/my_agenda.php | 7 +++++- api/lab/save_domicilio.php | 23 +++++++++++++++++- api/lab/save_paciente.php | 32 +++++++++++++++++++++++++ classes/lab/Domicilio.php | 36 ++++++++++++++++++++++++++-- classes/lab/Enfermera.php | 3 ++- conversations.php | 27 ++++++++++++++++++++- enfermero_portal.php | 48 ++++++++++++++++++++++++++++++++++++-- lab_domicilios.php | 15 +++++++++++- lab_pacientes.php | 26 +++++++++++++++++++++ 9 files changed, 208 insertions(+), 9 deletions(-) diff --git a/api/lab/my_agenda.php b/api/lab/my_agenda.php index 9a9de5d..90ecf78 100644 --- a/api/lab/my_agenda.php +++ b/api/lab/my_agenda.php @@ -48,7 +48,12 @@ try { } unset($item); - // Resumen rápido + // Marcar si el domicilio fue creado por el propio usuario (para control de edición en portal enfermero) + $adminUserId = adminId(); + foreach ($agenda as &$item) { + $item['es_propio'] = (!empty($item['creado_por']) && (int)$item['creado_por'] === $adminUserId); + } + unset($item); $totales = array_count_values(array_column($agenda, 'domicilio_estado')); jsonOk([ diff --git a/api/lab/save_domicilio.php b/api/lab/save_domicilio.php index 59aefe4..9bb55ee 100644 --- a/api/lab/save_domicilio.php +++ b/api/lab/save_domicilio.php @@ -19,7 +19,15 @@ try { // ── Reprogramar ── if (!empty($datos['id']) && !empty($datos['reprogramar'])) { - $id = (int)$datos['id']; + $id = (int)$datos['id']; + // Enfermero solo puede reprogramar domicilios que él mismo creó + if (userRole() === 'enfermero') { + $dbCheck = Database::getInstance(); + $chk = $dbCheck->fetchOne("SELECT creado_por FROM lab_domicilios WHERE id = ?", [$id]); + if (!$chk || (int)($chk['creado_por'] ?? 0) !== adminId()) { + jsonError('Solo puedes reprogramar domicilios que tú mismo agendaste.'); + } + } $nuevaFecha = trim($datos['nueva_fecha'] ?? ''); $nuevaHora = trim($datos['nueva_hora'] ?? ''); $motivo = trim($datos['motivo'] ?? ''); @@ -47,6 +55,14 @@ try { if (!empty($datos['id'])) { // Actualización general $id = (int)$datos['id']; + // Enfermero solo puede editar domicilios que él mismo creó + if (userRole() === 'enfermero') { + $dbCheck = Database::getInstance(); + $chk = $dbCheck->fetchOne("SELECT creado_por FROM lab_domicilios WHERE id = ?", [$id]); + if (!$chk || (int)($chk['creado_por'] ?? 0) !== adminId()) { + jsonError('Solo puedes editar domicilios que tú mismo agendaste.'); + } + } unset($datos['id'], $datos['solo_estado']); // Normalizar orden_id: vacío o 0 → null if (isset($datos['orden_id']) && ($datos['orden_id'] === '' || (int)$datos['orden_id'] === 0)) { @@ -61,6 +77,11 @@ try { jsonError("El campo $req es obligatorio"); } } + // Validar formato mínimo de dirección + $dirVal = trim($datos['direccion'] ?? ''); + if (strlen($dirVal) < 5) { + jsonError('La dirección es muy corta. Ingresa una dirección completa. Ej: Cra 15 # 32-10.'); + } // Normalizar orden_id: vacío o 0 → null (evita FK constraint) if (isset($datos['orden_id']) && ($datos['orden_id'] === '' || (int)$datos['orden_id'] === 0)) { $datos['orden_id'] = null; diff --git a/api/lab/save_paciente.php b/api/lab/save_paciente.php index e3d7012..7b3cd4a 100644 --- a/api/lab/save_paciente.php +++ b/api/lab/save_paciente.php @@ -12,6 +12,38 @@ requireMethod('POST'); try { $datos = inputJson(); + + // ── Validación de formato de campos del paciente ──────────────────────── + $nombreVal = trim($datos['nombre_completo'] ?? ''); + if ($nombreVal !== '') { + if (!preg_match('/^[\p{L}\s\'\-\.]+$/u', $nombreVal)) { + jsonError('El nombre solo debe contener letras, tildes y espacios (sin números ni símbolos).'); + } + if (count(preg_split('/\s+/', $nombreVal, -1, PREG_SPLIT_NO_EMPTY)) < 2) { + jsonError('Ingresa el nombre completo: nombre y apellido (mínimo 2 palabras).'); + } + } + $telVal = trim($datos['telefono'] ?? ''); + if ($telVal !== '') { + $telDigits = preg_replace('/[^0-9]/', '', $telVal); + if (!preg_match('/^(57)?3\d{9}$/', $telDigits)) { + jsonError('El celular debe comenzar por 3 y tener 10 dígitos. Ej: 3001234567.'); + } + } + $docVal = trim($datos['numero_documento'] ?? ''); + $tipoDoc = trim($datos['tipo_documento'] ?? 'CC'); + if ($docVal !== '' && in_array($tipoDoc, ['CC', 'TI', 'RC', 'CE'], true)) { + $docDigits = preg_replace('/[^0-9]/', '', $docVal); + if (strlen($docDigits) < 4 || strlen($docDigits) > 12) { + jsonError('La cédula/TI/RC/CE debe tener entre 4 y 12 dígitos numéricos.'); + } + } + $emailVal = trim($datos['email'] ?? ''); + if ($emailVal !== '' && !filter_var($emailVal, FILTER_VALIDATE_EMAIL)) { + jsonError('El correo electrónico no es válido. Ej: nombre@dominio.com.'); + } + // ──────────────────────────────────────────────────────────────────────── + $pac = new Paciente(); $admin = adminId(); diff --git a/classes/lab/Domicilio.php b/classes/lab/Domicilio.php index d132273..9f8b683 100644 --- a/classes/lab/Domicilio.php +++ b/classes/lab/Domicilio.php @@ -88,7 +88,23 @@ class Domicilio { e.telefono AS enfermera_telefono, a.enfermera_id AS enfermera_lab_id, a.id AS asignacion_id, - a.estado AS asignacion_estado + a.estado AS asignacion_estado, + CASE + WHEN a.enfermera_id IS NULL OR d.hora_llegada IS NULL THEN NULL + ELSE TIMESTAMPDIFF( + MINUTE, + ( + SELECT MAX(CONCAT(d2.fecha_programada, ' ', d2.hora_salida)) + FROM lab_domicilios d2 + JOIN lab_asignaciones a2 ON a2.domicilio_id = d2.id + WHERE a2.enfermera_id = a.enfermera_id + AND d2.hora_salida IS NOT NULL + AND d2.id <> d.id + AND CONCAT(d2.fecha_programada, ' ', d2.hora_salida) < CONCAT(d.fecha_programada, ' ', d.hora_llegada) + ), + CONCAT(d.fecha_programada, ' ', d.hora_llegada) + ) + END AS tiempo_recorrido_min FROM lab_domicilios d JOIN lab_pacientes p ON p.id = d.paciente_id LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id @@ -129,7 +145,23 @@ class Domicilio { a.enfermera_id AS enfermera_lab_id, a.id AS asignacion_id, a.estado AS asignacion_estado, - adm.full_name AS creado_por_nombre + adm.full_name AS creado_por_nombre, + CASE + WHEN a.enfermera_id IS NULL OR d.hora_llegada IS NULL THEN NULL + ELSE TIMESTAMPDIFF( + MINUTE, + ( + SELECT MAX(CONCAT(d2.fecha_programada, ' ', d2.hora_salida)) + FROM lab_domicilios d2 + JOIN lab_asignaciones a2 ON a2.domicilio_id = d2.id + WHERE a2.enfermera_id = a.enfermera_id + AND d2.hora_salida IS NOT NULL + AND d2.id <> d.id + AND CONCAT(d2.fecha_programada, ' ', d2.hora_salida) < CONCAT(d.fecha_programada, ' ', d.hora_llegada) + ), + CONCAT(d.fecha_programada, ' ', d.hora_llegada) + ) + END AS tiempo_recorrido_min FROM lab_domicilios d JOIN lab_pacientes p ON p.id = d.paciente_id LEFT JOIN lab_ordenes_medicas o ON o.id = d.orden_id diff --git a/classes/lab/Enfermera.php b/classes/lab/Enfermera.php index 7487b11..122cd14 100644 --- a/classes/lab/Enfermera.php +++ b/classes/lab/Enfermera.php @@ -146,7 +146,8 @@ class Enfermera { LIMIT 1) ) AS orden_id_display, a.estado AS asignacion_estado, - a.id AS asignacion_id + a.id AS asignacion_id, + d.creado_por FROM lab_asignaciones a JOIN lab_domicilios d ON d.id = a.domicilio_id JOIN lab_pacientes p ON p.id = d.paciente_id diff --git a/conversations.php b/conversations.php index 5941ec3..b812ed4 100644 --- a/conversations.php +++ b/conversations.php @@ -6989,7 +6989,9 @@ const labDomicilio = (() => { } function limpiarValidacion() { - ['labdom-fecha','labdom-direccion'].forEach(id => $(id).classList.remove('is-invalid')); + ['labdom-fecha','labdom-direccion','labdom-pac-nombre','labdom-pac-cedula','labdom-pac-correo'].forEach(id => { + const el = $(id); if (el) el.classList.remove('is-invalid'); + }); } function validar() { @@ -6997,11 +6999,34 @@ const labDomicilio = (() => { limpiarValidacion(); if (!fv('labdom-fecha')) { $('labdom-fecha').classList.add('is-invalid'); ok = false; } if (!fv('labdom-direccion')){ $('labdom-direccion').classList.add('is-invalid'); ok = false; } + // Validar formato de dirección + const dirVal = fv('labdom-direccion') || ''; + if (dirVal && dirVal.trim().length < 5) { + $('labdom-direccion').classList.add('is-invalid'); ok = false; + } const esSeguro = document.getElementById('tc-seguro')?.checked; if (esSeguro && !fv('labdom-seguro-nombre')) { $('labdom-seguro-nombre').classList.add('is-invalid'); ok = false; } + // Validaciones de paciente nuevo + if (_isNewPaciente) { + const nom = fv('labdom-pac-nombre') || ''; + if (!nom) { $('labdom-pac-nombre').classList.add('is-invalid'); ok = false; } + else if (!/^[\p{L}\s'\-\.]+$/u.test(nom)) { $('labdom-pac-nombre').classList.add('is-invalid'); ok = false; } + else if (nom.trim().split(/\s+/).length < 2) { $('labdom-pac-nombre').classList.add('is-invalid'); ok = false; } + + const ced = fv('labdom-pac-cedula') || ''; + if (ced) { + const tipodoc = $('labdom-pac-tipodoc')?.value || 'CC'; + if (['CC','TI','RC','CE'].includes(tipodoc)) { + const digits = ced.replace(/[^0-9]/g, ''); + if (digits.length < 4 || digits.length > 12) { $('labdom-pac-cedula').classList.add('is-invalid'); ok = false; } + } + } + const cor = fv('labdom-pac-correo') || ''; + if (cor && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(cor)) { $('labdom-pac-correo').classList.add('is-invalid'); ok = false; } + } return ok; } diff --git a/enfermero_portal.php b/enfermero_portal.php index cc465d3..2dada98 100644 --- a/enfermero_portal.php +++ b/enfermero_portal.php @@ -633,14 +633,14 @@ const portal = { ` ).join(''); - const btnCancelar = !['completado','cancelado','reprogramado'].includes(est) + const btnCancelar = !['completado','cancelado','reprogramado'].includes(est) && item.es_propio ? `` : ''; - const btnReprogramar = !['completado','reprogramado'].includes(est) + const btnReprogramar = !['completado','reprogramado'].includes(est) && item.es_propio ? `