max(0, (float)($pd['efectivo'] ?? 0)), 'transferencia' => max(0, (float)($pd['transferencia'] ?? 0)), 'tarjeta' => max(0, (float)($pd['tarjeta'] ?? 0)), ]); } if ($numOrden === '') $numOrden = null; // ── Validaciones ────────────────────────────────────────────── if ($turnoId <= 0) jsonError('turno_id inválido.'); if ($pacienteId <= 0) jsonError('paciente_id inválido.'); if ($lugarId <= 0) jsonError('lugar_id inválido.'); if (empty($examIds) && !$soloMuestras) jsonError('Debe seleccionar al menos un examen.'); $metodosValidos = ['efectivo', 'transferencia', 'tarjeta', 'eps', 'cortesia', 'combinado', '']; if ($metodoPago && !in_array($metodoPago, $metodosValidos, true)) { jsonError('metodo_pago inválido.'); } $pdo = db(); $pdo->beginTransaction(); try { // Verificar que el turno existe y está en recepción $stmt = $pdo->prepare( "SELECT id, estado FROM turnero_turnos WHERE id = ? FOR UPDATE" ); $stmt->execute([$turnoId]); $turno = $stmt->fetch(PDO::FETCH_ASSOC); if (!$turno) { $pdo->rollBack(); jsonError('Turno no encontrado.', 404); } if (!in_array($turno['estado'], ['en_recepcion', 'espera'], true)) { $pdo->rollBack(); jsonError("El turno está en estado '{$turno['estado']}' y no se puede editar su solicitud.", 422); } // Si el turno fue seleccionado sin llamarlo formalmente, promoverlo a en_recepcion ahora if ($turno['estado'] === 'espera') { $pdo->prepare( "UPDATE turnero_turnos SET estado = 'en_recepcion', llamado_recepcion_at = COALESCE(llamado_recepcion_at, NOW()), inicio_recepcion_at = COALESCE(inicio_recepcion_at, NOW()), atendido_recepcion_por = COALESCE(atendido_recepcion_por, ?) WHERE id = ?" )->execute([adminId(), $turnoId]); } // Verificar que el lugar existe $stmt = $pdo->prepare("SELECT id FROM turnero_lugares WHERE id = ? AND activo = 1"); $stmt->execute([$lugarId]); if (!$stmt->fetch()) { $pdo->rollBack(); jsonError('Lugar destino no encontrado o inactivo.', 404); } // Verificar que todos los exam_tipo_ids existen (se omite si solo_muestras y sin exámenes) if (!empty($examIds)) { $in = implode(',', array_fill(0, count($examIds), '?')); $stmt = $pdo->prepare("SELECT COUNT(*) FROM exam_tipos WHERE id IN ({$in}) AND activo = 1"); $stmt->execute($examIds); if ((int)$stmt->fetchColumn() !== count($examIds)) { $pdo->rollBack(); jsonError('Uno o más exámenes no existen o están inactivos.', 422); } } // ── Insertar o reemplazar solicitud (DELETE + INSERT para poder re-guardar) ── $stmt = $pdo->prepare("SELECT id FROM turnero_solicitudes WHERE turno_id = ?"); $stmt->execute([$turnoId]); $solAnterior = $stmt->fetch(PDO::FETCH_ASSOC); if ($solAnterior) { // Eliminar ítemes anteriores (CASCADE borrará los turnero_examen_items) $pdo->prepare("DELETE FROM turnero_solicitudes WHERE turno_id = ?")->execute([$turnoId]); } $stmt = $pdo->prepare( "INSERT INTO turnero_solicitudes (turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, pagos_detalle, observaciones, embarazada, solo_muestras, medico_id, creado_por) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); $stmt->execute([ $turnoId, $pacienteId, $lugarId, $numOrden, $total, $metodoPago ?: null, $pagosDetalle, $obs ?: null, $embarazada, $soloMuestras, $medicoId, adminId(), ]); $solicitudId = (int) $pdo->lastInsertId(); // ── Insertar ítemes de examen ────────────────────────────── $stmtItem = $pdo->prepare( "INSERT INTO turnero_examen_items (solicitud_id, exam_tipo_id) VALUES (?, ?)" ); foreach ($examIds as $examId) { $stmtItem->execute([$solicitudId, $examId]); } // ── Consentimientos: solo los configurados para este lugar ── $stmt = $pdo->prepare( "SELECT f.id AS formulario_id, f.nombre AS formulario_nombre FROM lab_formularios f INNER JOIN turnero_lugar_consentimientos tlc ON tlc.formulario_id = f.id WHERE tlc.lugar_id = ? AND f.is_active = 1 ORDER BY f.nombre ASC" ); $stmt->execute([$lugarId]); $consentimientosRequeridos = $stmt->fetchAll(PDO::FETCH_ASSOC); // Crear o recuperar estado de cada consentimiento requerido $stmtCreateConsent = $pdo->prepare( "INSERT IGNORE INTO turnero_consentimientos (turno_id, formulario_id, token, estado) VALUES (?, ?, UUID(), 'pendiente')" ); foreach ($consentimientosRequeridos as &$c) { $stmtCreateConsent->execute([$turnoId, $c['formulario_id']]); $stC = $pdo->prepare( "SELECT id, token, estado, enviado_at, firmado_at FROM turnero_consentimientos WHERE turno_id = ? AND formulario_id = ?" ); $stC->execute([$turnoId, $c['formulario_id']]); $row = $stC->fetch(PDO::FETCH_ASSOC); $c['id'] = $row['id'] ?? null; $c['token'] = $row['token'] ?? null; $c['estado'] = $row['estado'] ?? 'pendiente'; $c['turno_id'] = $turnoId; } unset($c); $pdo->commit(); notificarSSE(obtenerOCrearSesionHoy()); // Recuperar solicitud completa $stmt = $pdo->prepare( "SELECT s.id, s.turno_id, s.paciente_id, s.lugar_id, s.total_cobrado, s.metodo_pago, s.observaciones, s.creado_at, p.nombre_completo AS paciente_nombre, p.numero_documento AS paciente_documento, l.nombre AS lugar_nombre FROM turnero_solicitudes s LEFT JOIN lab_pacientes p ON p.id = s.paciente_id LEFT JOIN turnero_lugares l ON l.id = s.lugar_id WHERE s.id = ?" ); $stmt->execute([$solicitudId]); $solicitud = $stmt->fetch(PDO::FETCH_ASSOC); jsonOk([ 'solicitud' => $solicitud, 'consentimientos_requeridos' => $consentimientosRequeridos, ], 'Solicitud guardada correctamente'); } catch (\Throwable $e) { if ($pdo->inTransaction()) $pdo->rollBack(); jsonError('Error al crear solicitud: ' . $e->getMessage(), 500); }