Tul xxx Tul
User / IP
:
216.73.216.217
Host / Server
:
45.84.207.204 / aircan.me
System
:
Linux lt-bnk-web1726.main-hosting.eu 5.14.0-611.36.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Mar 3 11:23:52 EST 2026 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
u931257429
/
domains
/
aircan.me
/
public_html
/
piscina
/
app
/
models
/
Viewing: Servicio.php
<?php namespace App\Models; require_once dirname(__DIR__, 2) . '/config/database.php'; use mysqli; class Servicio { public static function all(): array { $conn = self::connection(); $sql = 'SELECT su.id, su.servicio_id, su.cliente_id, su.hora_inicio, su.hora_fin, su.monto, su.estado, s.nombre AS servicio_nombre, s.descripcion AS servicio_descripcion, s.imagen, COALESCE(s.precio_por_hora, 0) AS tarifa_hora, c.nombre AS cliente_nombre, c.apellido AS cliente_apellido, c.cedula, c.membresia FROM servicios_uso su INNER JOIN servicios s ON s.id = su.servicio_id LEFT JOIN clientes c ON c.id = su.cliente_id ORDER BY su.hora_inicio DESC'; $resultado = $conn->query($sql); if (!$resultado) { return []; } $servicios = []; while ($row = $resultado->fetch_assoc()) { $servicios[] = self::mapServicioEnUso($row); } return $servicios; } /** * Historial de servicios: solo pagados y de días anteriores al actual. * Se pueden aplicar filtros opcionales de fecha (desde/hasta) sobre la fecha de inicio. * @return array<int,array<string,mixed>> */ public static function historial(?string $desde = null, ?string $hasta = null): array { $conn = self::connection(); $sql = 'SELECT su.id, su.servicio_id, su.cliente_id, su.hora_inicio, su.hora_fin, su.monto, su.estado, s.nombre AS servicio_nombre, s.descripcion AS servicio_descripcion, s.imagen, COALESCE(s.precio_por_hora, 0) AS tarifa_hora, c.nombre AS cliente_nombre, c.apellido AS cliente_apellido, c.cedula, c.membresia FROM servicios_uso su INNER JOIN servicios s ON s.id = su.servicio_id LEFT JOIN clientes c ON c.id = su.cliente_id'; $where = ["su.estado = 'pagado'", 'DATE(su.hora_inicio) < CURDATE()']; $types = ''; $params = []; if ($desde !== null) { $where[] = 'DATE(su.hora_inicio) >= ?'; $types .= 's'; $params[] = $desde; } if ($hasta !== null) { $where[] = 'DATE(su.hora_inicio) <= ?'; $types .= 's'; $params[] = $hasta; } if (!empty($where)) { $sql .= ' WHERE ' . implode(' AND ', $where); } $sql .= ' ORDER BY su.hora_inicio DESC, su.id DESC'; $stmt = $conn->prepare($sql); if (!$stmt) { return []; } if (!empty($params)) { $stmt->bind_param($types, ...$params); } $stmt->execute(); $resultado = $stmt->get_result(); if (!$resultado) { return []; } $rows = []; while ($row = $resultado->fetch_assoc()) { $rows[] = self::mapServicioEnUso($row); } return $rows; } public static function find(int $id): ?array { $conn = self::connection(); $sql = 'SELECT su.id, su.servicio_id, su.cliente_id, su.hora_inicio, su.hora_fin, su.monto, su.estado, s.nombre AS servicio_nombre, s.descripcion AS servicio_descripcion, s.imagen, COALESCE(s.precio_por_hora, 0) AS tarifa_hora, c.nombre AS cliente_nombre, c.apellido AS cliente_apellido, c.cedula, c.membresia FROM servicios_uso su INNER JOIN servicios s ON s.id = su.servicio_id LEFT JOIN clientes c ON c.id = su.cliente_id WHERE su.id = ? LIMIT 1'; $stmt = $conn->prepare($sql); $stmt->bind_param('i', $id); $stmt->execute(); $resultado = $stmt->get_result(); $fila = $resultado?->fetch_assoc(); return $fila ? self::mapServicioEnUso($fila) : null; } public static function create(array $data): array { $conn = self::connection(); $servicioId = (int) $data['servicio_id']; $clienteId = isset($data['cliente_id']) ? (int) $data['cliente_id'] : 0; if ($clienteId <= 0) { throw new \InvalidArgumentException('Selecciona un cliente válido.'); } $duracion = (int) ($data['duracion'] ?? 60); $inicio = $data['hora_inicio'] ?? date('Y-m-d H:i:s'); $horaInicio = new \DateTime($inicio); $horaFin = (clone $horaInicio)->modify('+' . $duracion . ' minutes'); $tarifa = self::catalogoFind($servicioId); if (!$tarifa) { throw new \InvalidArgumentException('Servicio no encontrado en catálogo.'); } $monto = self::calcularMontoCatalogo($tarifa['tarifa'], $duracion); $sql = 'INSERT INTO servicios_uso (servicio_id, cliente_id, hora_inicio, hora_fin, monto, estado) VALUES (?, ?, ?, ?, ?, ?)'; $estadoInicial = 'en_uso'; $stmt = $conn->prepare($sql); $horaInicioStr = $horaInicio->format('Y-m-d H:i:s'); $horaFinStr = $horaFin->format('Y-m-d H:i:s'); $stmt->bind_param('iissds', $servicioId, $clienteId, $horaInicioStr, $horaFinStr, $monto, $estadoInicial); $stmt->execute(); $id = $stmt->insert_id; Cliente::agregarHistorial($clienteId, [ 'fecha' => $horaInicio->format('Y-m-d'), 'concepto' => 'Uso de servicio: ' . $tarifa['nombre'], 'monto' => $monto, ]); // Registrar en caja inmediatamente al crear el servicio $cliente = Cliente::find($clienteId); $clienteNombre = $cliente ? trim(($cliente['nombre'] ?? '') . ' ' . ($cliente['apellido'] ?? '')) : 'Cliente #' . $clienteId; Caja::asegurarCajaDeHoy(); Caja::registrarCobro([ 'origen' => 'Servicios', 'cliente' => $clienteNombre, 'concepto' => 'Servicio: ' . $tarifa['nombre'], 'monto' => $monto, 'metodo' => 'Efectivo', 'fecha' => $horaInicio->format('Y-m-d'), 'hora' => $horaInicio->format('H:i:s'), ]); return self::find($id) ?? []; } public static function update(int $id, array $data): bool { $registro = self::find($id); if (!$registro) { return false; } $conn = self::connection(); $servicioId = isset($data['servicio_id']) ? (int) $data['servicio_id'] : (int) $registro['servicio_id']; $clienteId = array_key_exists('cliente_id', $data) ? (int) $data['cliente_id'] : ($registro['cliente_id'] ?? 0); if ($clienteId <= 0) { throw new \InvalidArgumentException('Selecciona un cliente válido.'); } $duracion = (int) ($data['duracion'] ?? $registro['duracion']); $horaInicio = $data['hora_inicio'] ?? ($registro['fecha_inicio'] . ' ' . $registro['hora_inicio']); $inicio = new \DateTime($horaInicio); $horaFin = (clone $inicio)->modify('+' . $duracion . ' minutes'); $tarifa = self::catalogoFind($servicioId); if (!$tarifa) { throw new \InvalidArgumentException('Servicio no encontrado en catálogo.'); } $monto = self::calcularMontoCatalogo($tarifa['tarifa'], $duracion); $sql = 'UPDATE servicios_uso SET servicio_id = ?, cliente_id = ?, hora_inicio = ?, hora_fin = ?, monto = ? WHERE id = ?'; $stmt = $conn->prepare($sql); $horaInicioStr = $inicio->format('Y-m-d H:i:s'); $horaFinStr = $horaFin->format('Y-m-d H:i:s'); $stmt->bind_param('iissdi', $servicioId, $clienteId, $horaInicioStr, $horaFinStr, $monto, $id); $stmt->execute(); return $stmt->affected_rows >= 0; } public static function finalizar(int $id): bool { $conn = self::connection(); // Simplemente marcar como pagado, ya que se registró en caja al crear $sql = "UPDATE servicios_uso SET estado = 'pagado', hora_fin = ? WHERE id = ? AND estado = 'en_uso'"; $stmt = $conn->prepare($sql); $horaFin = date('Y-m-d H:i:s'); $stmt->bind_param('si', $horaFin, $id); $stmt->execute(); return $stmt->affected_rows > 0; } public static function registrarPago(int $id): bool { $servicio = self::find($id); if (!$servicio || $servicio['estado_raw'] !== 'pendiente') { return false; } $conn = self::connection(); $sql = "UPDATE servicios_uso SET estado = 'pagado' WHERE id = ?"; $stmt = $conn->prepare($sql); $stmt->bind_param('i', $id); $stmt->execute(); Caja::registrarCobro([ 'origen' => 'Servicio', 'cliente' => $servicio['cliente'], 'concepto' => 'Pago servicio: ' . $servicio['tipo_servicio'], 'monto' => $servicio['monto'], 'metodo' => 'Efectivo', ]); return true; } public static function extender(int $id, int $minutos): ?array { $servicio = self::find($id); if (!$servicio) { return null; } $nuevaDuracion = $servicio['duracion'] + $minutos; self::update($id, [ 'duracion' => $nuevaDuracion, 'hora_inicio' => $servicio['fecha_inicio'] . ' ' . $servicio['hora_inicio'], ]); return self::find($id); } public static function delete(int $id): bool { $conn = self::connection(); $stmt = $conn->prepare('DELETE FROM servicios_uso WHERE id = ?'); $stmt->bind_param('i', $id); $stmt->execute(); return $stmt->affected_rows > 0; } public static function serviciosPorEstado(string $estado): array { $conn = self::connection(); $sql = 'SELECT su.id, su.servicio_id, su.cliente_id, su.hora_inicio, su.hora_fin, su.monto, su.estado, s.nombre AS servicio_nombre, s.descripcion AS servicio_descripcion, s.imagen, COALESCE(s.precio_por_hora, 0) AS tarifa_hora, c.nombre AS cliente_nombre, c.apellido AS cliente_apellido, c.cedula, c.membresia FROM servicios_uso su INNER JOIN servicios s ON s.id = su.servicio_id LEFT JOIN clientes c ON c.id = su.cliente_id WHERE su.estado = ? ORDER BY su.hora_inicio DESC'; $stmt = $conn->prepare($sql); $stmt->bind_param('s', $estado); $stmt->execute(); $resultado = $stmt->get_result(); $servicios = []; while ($row = $resultado->fetch_assoc()) { $servicios[] = self::mapServicioEnUso($row); } return $servicios; } public static function catalogoAll(): array { $conn = self::connection(); $resultado = $conn->query('SELECT id, nombre, descripcion, COALESCE(precio_por_hora, 0) AS tarifa, COALESCE(imagen, "img/servicio-default.jpg") AS imagen, activo FROM servicios ORDER BY nombre'); return $resultado ? $resultado->fetch_all(MYSQLI_ASSOC) : []; } public static function catalogoFind(int $id): ?array { $conn = self::connection(); $stmt = $conn->prepare('SELECT id, nombre, descripcion, COALESCE(precio_por_hora, 0) AS tarifa, COALESCE(imagen, "img/servicio-default.jpg") AS imagen, activo FROM servicios WHERE id = ? LIMIT 1'); $stmt->bind_param('i', $id); $stmt->execute(); $resultado = $stmt->get_result()->fetch_assoc(); return $resultado ?: null; } public static function catalogoCreate(array $data): array { $conn = self::connection(); $sql = 'INSERT INTO servicios (nombre, descripcion, imagen, precio_por_hora, activo) VALUES (?, ?, ?, ?, 1)'; $stmt = $conn->prepare($sql); $imagen = $data['imagen'] ?? 'img/servicio-default.jpg'; $stmt->bind_param('sssd', $data['nombre'], $data['descripcion'], $imagen, $data['tarifa']); $stmt->execute(); $id = $stmt->insert_id; return self::catalogoFind($id) ?? []; } public static function catalogoUpdate(int $id, array $data): bool { $registro = self::catalogoFind($id); if (!$registro) { return false; } $conn = self::connection(); $imagen = $data['imagen'] ?? $registro['imagen']; $sql = 'UPDATE servicios SET nombre = ?, descripcion = ?, imagen = ?, precio_por_hora = ?, activo = ? WHERE id = ?'; $stmt = $conn->prepare($sql); $activo = isset($data['activo']) ? (int) $data['activo'] : (int) $registro['activo']; $stmt->bind_param('sssdii', $data['nombre'], $data['descripcion'], $imagen, $data['tarifa'], $activo, $id); $stmt->execute(); return $stmt->affected_rows >= 0; } public static function catalogoDelete(int $id): bool { $conn = self::connection(); $stmt = $conn->prepare('DELETE FROM servicios WHERE id = ?'); $stmt->bind_param('i', $id); $stmt->execute(); return $stmt->affected_rows > 0; } public static function stats(): array { $conn = self::connection(); $stats = [ 'en_uso' => self::contarPorEstado($conn, 'en_uso'), 'pendientes' => self::contarPorEstado($conn, 'pendiente'), 'finalizados' => self::finalizadosDia(), 'ingresos' => self::ingresosDia(), ]; return $stats; } public static function tarifas(): array { $catalogo = self::catalogoAll(); $tarifas = []; foreach ($catalogo as $item) { $tarifas[$item['nombre']] = (float) $item['tarifa']; } return $tarifas; } public static function tiposDisponibles(): array { return array_map(fn ($item) => $item['nombre'], self::catalogoAll()); } public static function estadoIcono(string $estado): string { return match ($estado) { 'en_uso' => '🟢', 'pendiente' => '🟠', 'finalizado' => '🔵', 'pagado' => '✅', default => '⚪', }; } private static function contarPorEstado(mysqli $conn, string $estado): int { $stmt = $conn->prepare('SELECT COUNT(*) AS total FROM servicios_uso WHERE estado = ?'); $stmt->bind_param('s', $estado); $stmt->execute(); $resultado = $stmt->get_result()->fetch_assoc(); return (int) ($resultado['total'] ?? 0); } private static function totalIngresos(mysqli $conn): float { $resultado = $conn->query("SELECT COALESCE(SUM(monto), 0) AS total FROM servicios_uso WHERE estado IN ('pendiente', 'finalizado', 'pagado')"); $fila = $resultado?->fetch_assoc(); return (float) ($fila['total'] ?? 0); } public static function ingresosDia(?string $fecha = null): float { $conn = self::connection(); $fechaConsulta = $fecha ?? date('Y-m-d'); $sql = "SELECT COALESCE(SUM(monto), 0) AS total FROM servicios_uso WHERE DATE(hora_inicio) = ? AND estado IN ('en_uso', 'pendiente', 'finalizado', 'pagado')"; $stmt = $conn->prepare($sql); $stmt->bind_param('s', $fechaConsulta); $stmt->execute(); $resultado = $stmt->get_result()->fetch_assoc(); return (float) ($resultado['total'] ?? 0); } public static function finalizadosDia(?string $fecha = null): int { $conn = self::connection(); $fechaConsulta = $fecha ?? date('Y-m-d'); $sql = "SELECT COUNT(*) AS total FROM servicios_uso WHERE hora_fin IS NOT NULL AND estado = 'pagado' AND DATE(hora_inicio) = ?"; $stmt = $conn->prepare($sql); $stmt->bind_param('s', $fechaConsulta); $stmt->execute(); $resultado = $stmt->get_result()->fetch_assoc(); return (int) ($resultado['total'] ?? 0); } private static function calcularMontoCatalogo(float $tarifaPorHora, int $minutos): float { $valorMinuto = $tarifaPorHora / 60; return round($valorMinuto * $minutos, 2); } private static function mapServicioEnUso(array $row): array { $inicio = new \DateTime($row['hora_inicio']); $finReal = $row['hora_fin'] ? new \DateTime($row['hora_fin']) : null; $clienteNombre = trim(($row['cliente_nombre'] ?? '') . ' ' . ($row['cliente_apellido'] ?? '')); if ($clienteNombre === '') { $clienteNombre = 'Cliente sin registrar'; } $tarifaHora = isset($row['tarifa_hora']) ? (float) $row['tarifa_hora'] : 0.0; $monto = isset($row['monto']) ? (float) $row['monto'] : 0.0; $duracion = 0; if ($tarifaHora > 0) { $duracion = (int) round(($monto / $tarifaHora) * 60); } elseif ($finReal instanceof \DateTime) { $duracion = max(self::diferenciaMinutos($inicio, $finReal), 0); } else { $duracion = 60; } $finEstimado = (clone $inicio)->modify('+' . $duracion . ' minutes'); return [ 'id' => (int) $row['id'], 'servicio_id' => (int) $row['servicio_id'], 'cliente_id' => $row['cliente_id'] ? (int) $row['cliente_id'] : null, 'cliente' => $clienteNombre, 'cedula' => $row['cedula'] ?? null, 'membresia' => $row['membresia'] ?? null, 'tipo_servicio' => $row['servicio_nombre'], 'descripcion' => $row['servicio_descripcion'], 'duracion' => $duracion, 'fecha_inicio' => $inicio->format('Y-m-d'), 'hora_inicio' => $inicio->format('h:i A'), 'hora_fin' => $finEstimado->format('h:i A'), 'monto' => (float) $row['monto'], 'estado_raw' => $row['estado'], 'estado' => self::formatearEstado($row['estado']), 'imagen' => $row['imagen'] ?? 'img/servicio-default.jpg', ]; } private static function diferenciaMinutos(\DateTime $inicio, \DateTime $fin): int { return (int) round(($fin->getTimestamp() - $inicio->getTimestamp()) / 60); } private static function formatearEstado(string $estado): string { return match ($estado) { 'en_uso' => 'En uso', 'pendiente' => 'Pendiente de pago', 'finalizado' => 'Finalizado', 'pagado' => 'Pagado', default => ucfirst($estado), }; } private static function connection(): mysqli { static $conn = null; if ($conn instanceof mysqli) { return $conn; } $db = new \Database(); $conn = $db->getConnection(); return $conn; } }
Coded With 💗 by
0x6ick