80)) $temp = null; if ($hum !== null && ($hum < 0 || $hum > 100)) $hum = null; if ($aire !== null && ($aire < 0 || $aire > 4095)) $aire = null; if ($agua !== null) $agua = $agua ? 1 : 0; // Si no se especifica el tipo (entrada/salida), lo determinamos automáticamente if (!$tipo) { $stmtUltimo = $pdo->prepare(" SELECT tipo_movimiento FROM historial_accesos WHERE codigo_qr = :qr AND cabina_id = :cabina_id AND acceso_concedido = 1 ORDER BY fecha_registro DESC LIMIT 1 "); $stmtUltimo->execute([':qr' => $qr, ':cabina_id' => $cabina_id]); $ultimo = $stmtUltimo->fetch(); // Si el último movimiento fue entrada, ahora toca salida. Si no, entrada. $tipo = ($ultimo && $ultimo['tipo_movimiento'] === 'entrada') ? 'salida' : 'entrada'; } // Iniciar transacción $pdo->beginTransaction(); try { // Buscar la reserva asociada a este QR // En PetStop, el QR está directamente en la tabla reservas (no hay tabla usuarios_qr) $stmtReserva = $pdo->prepare(" SELECT r.id, r.cabina_id, r.estado, r.qr_activo, r.codigo_qr, u.id AS usuario_id, u.nombre AS cliente_nombre, u.email AS cliente_email FROM reservas r JOIN usuarios u ON r.usuario_id = u.id WHERE r.codigo_qr = :qr AND r.qr_activo = 1 AND r.estado IN ('confirmada', 'activa') LIMIT 1 "); $stmtReserva->execute([':qr' => $qr]); $reserva = $stmtReserva->fetch(); $acceso = 0; $respuesta = "DENEGADO"; $reserva_id = null; if ($reserva) { // Verificar que el QR corresponde a la cabina correcta if ($cabina_id && $reserva['cabina_id'] != $cabina_id) { $respuesta = "CABINA_INCORRECTA"; } else { // Acceso permitido $acceso = 1; $respuesta = "PERMITIDO"; $reserva_id = $reserva['id']; // Si es salida, marcar reserva como finalizada y liberar cabina if ($tipo === 'salida') { $stmtFinalizar = $pdo->prepare(" UPDATE reservas SET estado = 'finalizada', hora_salida_real = NOW(), qr_activo = 0 WHERE id = :id "); $stmtFinalizar->execute([':id' => $reserva_id]); // Liberar la cabina $stmtCabina = $pdo->prepare(" UPDATE cabinas SET estado = 'disponible' WHERE id = :cabina_id "); $stmtCabina->execute([':cabina_id' => $reserva['cabina_id']]); } else { // Si es entrada, marcar como activa y registrar hora $stmtActivar = $pdo->prepare(" UPDATE reservas SET estado = 'activa', hora_entrada_real = NOW() WHERE id = :id "); $stmtActivar->execute([':id' => $reserva_id]); } } } else { // QR no encontrado, inactivo o reserva no válida $respuesta = "QR_NO_VALIDO"; } // Registrar en historial de accesos (siempre, aunque sea denegado) $stmtHistorial = $pdo->prepare(" INSERT INTO historial_accesos (reserva_id, codigo_qr, cabina_id, tipo_movimiento, temperatura, humedad, calidad_aire, nivel_agua, acceso_concedido) VALUES (:reserva_id, :qr, :cabina_id, :tipo, :temp, :hum, :aire, :agua, :acceso) "); $stmtHistorial->execute([ ':reserva_id' => $reserva_id, ':qr' => $qr, ':cabina_id' => $cabina_id, ':tipo' => $tipo, ':temp' => $temp, ':hum' => $hum, ':aire' => $aire, ':agua' => $agua, ':acceso' => $acceso ]); $pdo->commit(); // Devolver respuesta en texto plano (como espera el ESP32) echo $respuesta; } catch (Exception $e) { $pdo->rollBack(); throw $e; } } catch (PDOException $e) { http_response_code(500); echo "ERROR_SISTEMA"; } ?>