diff --git a/app/Http/Livewire/Chat/PublicChat.php b/app/Http/Livewire/Chat/PublicChat.php index 0b2f49e..91e47c3 100755 --- a/app/Http/Livewire/Chat/PublicChat.php +++ b/app/Http/Livewire/Chat/PublicChat.php @@ -13,6 +13,7 @@ use App\Models\Historiale; use App\Models\Historial_cuenta; use App\Models\Preferencial; use App\Models\Promociones; +use App\Services\CuentasHelper; use App\Models\TarifaPromo; use App\Models\Usuario_promo; use App\Models\recarga; @@ -1411,107 +1412,17 @@ class PublicChat extends Component private function contarDisponibles(Tarifas $tarifa): int { - $servicio = $tarifa->servicio; - $fechaBase = now()->subDays(8)->format('Y-m-d'); - $fechaMin = now()->addDays($tarifa->dias - 15)->format('Y-m-d'); - $fechaMax = now()->addDays($tarifa->dias + 15)->format('Y-m-d'); - - if ((int) $tarifa->pantallas === (int) $servicio->completa) { - return Cuentas::where('servicio_id', $servicio->id) - ->whereDate('vencimiento', '>=', $fechaMin) - ->whereDate('vencimiento', '<=', $fechaMax) - ->where('estado', 'pendiente') - ->doesntHave('perfil') - ->count(); - } - - $suma = $servicio->pantallas - $tarifa->pantallas; - $baseQ = Cuentas::select('*') - ->where('servicio_id', $servicio->id) - ->whereDate('inicio', '>=', $fechaBase) - ->whereDate('vencimiento', '>=', $fechaMin) - ->whereDate('vencimiento', '<=', $fechaMax) - ->where('estado', 'activo') - ->withCount('historiales'); - - $cuentas = Cuentas::fromSub($baseQ, 'alias') - ->where('historiales_count', '<=', $suma) - ->get(); - - if ($cuentas->isEmpty()) { - return 0; - } - - $tomadas = Historial_cuenta::whereIn('cuenta_id', $cuentas->pluck('id')) - ->whereNotNull('historial_id') - ->count(); - - return (int) floor((($cuentas->count() * $servicio->pantallas) - $tomadas) / $tarifa->pantallas); + return CuentasHelper::contar($tarifa); } private function buscarCuentaDisponible(Tarifas $tarifa): ?Cuentas { - $servicio = $tarifa->servicio; - $fechaBase = now()->subDays(8)->format('Y-m-d'); - $fechaMin = now()->addDays($tarifa->dias - 15)->format('Y-m-d'); - $fechaMax = now()->addDays($tarifa->dias + 15)->format('Y-m-d'); - - if ((int) $tarifa->pantallas === (int) $servicio->completa) { - return Cuentas::where('servicio_id', $servicio->id) - ->whereDate('vencimiento', '>=', $fechaMin) - ->whereDate('vencimiento', '<=', $fechaMax) - ->where('estado', 'pendiente') - ->doesntHave('perfil') - ->orderBy('inicio', 'ASC') - ->first(); - } - - $suma = $servicio->pantallas - $tarifa->pantallas; - $baseQ = Cuentas::select('*') - ->where('servicio_id', $servicio->id) - ->whereDate('inicio', '>=', $fechaBase) - ->whereDate('vencimiento', '>=', $fechaMin) - ->whereDate('vencimiento', '<=', $fechaMax) - ->where('estado', 'activo') - ->withCount('historiales') - ->orderBy('inicio', 'ASC'); - - $cuenta = Cuentas::fromSub($baseQ, 'alias') - ->where('historiales_count', '<=', $suma) - ->orderBy('inicio', 'ASC') - ->lockForUpdate() - ->first(); - - if (! $cuenta) { - $suma2 = $servicio->pantallas - $tarifa->pantallas; - $baseQ2 = Cuentas::select('*') - ->where('servicio_id', $servicio->id) - ->whereDate('inicio', '>=', $fechaBase) - ->whereDate('vencimiento', '>=', $fechaMin) - ->whereDate('vencimiento', '<=', $fechaMax) - ->withCount('historiales') - ->where('estado', 'activo') - ->orderBy('inicio', 'ASC'); - - $cuenta = Cuentas::fromSub($baseQ2, 'alias') - ->where('historiales_count', '<=', $suma2) - ->orderBy('inicio', 'ASC') - ->lockForUpdate() - ->first(); - } - - return $cuenta; + return CuentasHelper::buscar($tarifa); } private function efectivePrice(int $tarifaId, ?int $userId, float $base): float { - if ($userId) { - $pref = Preferencial::where('usuario_id', $userId)->where('tarifa_id', $tarifaId)->first(); - if ($pref) { - return (float) $pref->valor; - } - } - return (float) $base; + return CuentasHelper::precio($tarifaId, $userId, $base); } private function volver(string $action): void diff --git a/app/Services/CuentasHelper.php b/app/Services/CuentasHelper.php new file mode 100644 index 0000000..ec2d07b --- /dev/null +++ b/app/Services/CuentasHelper.php @@ -0,0 +1,136 @@ +servicio; + $fechaBase = now()->subDays(8)->format('Y-m-d'); + $fechaMin = now()->addDays($tarifa->dias - 15)->format('Y-m-d'); + $fechaMax = now()->addDays($tarifa->dias + 15)->format('Y-m-d'); + + // Cuenta completa + if ((int) $tarifa->pantallas === (int) $servicio->completa) { + return Cuentas::where('servicio_id', $servicio->id) + ->whereDate('vencimiento', '>=', $fechaMin) + ->whereDate('vencimiento', '<=', $fechaMax) + ->where('estado', 'pendiente') + ->doesntHave('perfil') + ->count(); + } + + // Cuenta por pantalla + $suma = $servicio->pantallas - $tarifa->pantallas; + $baseQ = Cuentas::select('*') + ->where('servicio_id', $servicio->id) + ->whereDate('inicio', '>=', $fechaBase) + ->whereDate('vencimiento', '>=', $fechaMin) + ->whereDate('vencimiento', '<=', $fechaMax) + ->where('estado', 'activo') + ->withCount('historiales'); + + $cuentas = Cuentas::fromSub($baseQ, 'alias') + ->where('historiales_count', '<=', $suma) + ->get(); + + if ($cuentas->isEmpty()) { + return 0; + } + + $tomadas = Historial_cuenta::whereIn('cuenta_id', $cuentas->pluck('id')) + ->whereNotNull('historial_id') + ->count(); + + return (int) floor((($cuentas->count() * $servicio->pantallas) - $tomadas) / $tarifa->pantallas); + } + + /** + * Busca la primera cuenta disponible para una tarifa. + */ + public static function buscar(Tarifas $tarifa): ?Cuentas + { + $servicio = $tarifa->servicio; + $fechaBase = now()->subDays(8)->format('Y-m-d'); + $fechaMin = now()->addDays($tarifa->dias - 15)->format('Y-m-d'); + $fechaMax = now()->addDays($tarifa->dias + 15)->format('Y-m-d'); + + // Cuenta completa + if ((int) $tarifa->pantallas === (int) $servicio->completa) { + return Cuentas::where('servicio_id', $servicio->id) + ->whereDate('vencimiento', '>=', $fechaMin) + ->whereDate('vencimiento', '<=', $fechaMax) + ->where('estado', 'pendiente') + ->doesntHave('perfil') + ->orderBy('inicio', 'ASC') + ->first(); + } + + // Cuenta por pantalla — intento normal + $suma = $servicio->pantallas - $tarifa->pantallas; + $baseQ = Cuentas::select('*') + ->where('servicio_id', $servicio->id) + ->whereDate('inicio', '>=', $fechaBase) + ->whereDate('vencimiento', '>=', $fechaMin) + ->whereDate('vencimiento', '<=', $fechaMax) + ->where('estado', 'activo') + ->withCount('historiales') + ->orderBy('inicio', 'ASC'); + + $cuenta = Cuentas::fromSub($baseQ, 'alias') + ->where('historiales_count', '<=', $suma) + ->orderBy('inicio', 'ASC') + ->lockForUpdate() + ->first(); + + // Segundo intento sin filtro de inicio + if (! $cuenta) { + $baseQ2 = Cuentas::select('*') + ->where('servicio_id', $servicio->id) + ->whereDate('vencimiento', '>=', $fechaMin) + ->whereDate('vencimiento', '<=', $fechaMax) + ->where('estado', 'activo') + ->withCount('historiales') + ->orderBy('inicio', 'ASC'); + + $cuenta = Cuentas::fromSub($baseQ2, 'alias') + ->where('historiales_count', '<=', $suma) + ->orderBy('inicio', 'ASC') + ->lockForUpdate() + ->first(); + } + + return $cuenta; + } + + /** + * Precio efectivo para un usuario y tarifa. + * Prioridad: Usuario_tarifa (web) → Preferencial (legacy) → precio base. + */ + public static function precio(int $tarifaId, ?int $userId, float $base): float + { + if ($userId) { + $ut = Usuario_tarifa::where('usuario_id', $userId)->where('tarifa_id', $tarifaId)->first(); + if ($ut) { + return (float) $ut->precio; + } + + $pref = Preferencial::where('usuario_id', $userId)->where('tarifa_id', $tarifaId)->first(); + if ($pref) { + return (float) $pref->valor; + } + } + + return (float) $base; + } +} diff --git a/app/Services/TelegramBotService.php b/app/Services/TelegramBotService.php index e0ce88c..5c4d5c4 100755 --- a/app/Services/TelegramBotService.php +++ b/app/Services/TelegramBotService.php @@ -13,6 +13,7 @@ use App\Models\Historiale; use App\Models\Historial_cuenta; use App\Models\Preferencial; use App\Models\Promociones; +use App\Services\CuentasHelper; use App\Models\TarifaPromo; use App\Models\Usuario_promo; use App\Models\recarga; @@ -649,7 +650,7 @@ class TelegramBotService $buttons = []; foreach ($tarifas as $t) { - $precio = $this->efectivePrice($t->id, $state['user_id'], $t->valor); + $precio = CuentasHelper::precio($t->id, $state['user_id'], $t->valor); $label = $servicio->por_tiempo ? "{$t->dias} días - \$" . number_format($precio) : "{$t->pantallas} pantalla(s) / {$t->dias} días - \$" . number_format($precio); @@ -672,8 +673,7 @@ class TelegramBotService return; } - $disponibles = Cuentas::where('tarifa_id', $tarifaId)->where('estado', 'activo')->count(); - if ($disponibles < 1) { + if (CuentasHelper::contar($tarifa) < 1) { $this->send($chatId, "Sin cuentas disponibles para este plan. Intenta con otro."); $this->listServices($chatId); return; @@ -681,7 +681,7 @@ class TelegramBotService $user = User::with('saldo')->find($state['user_id']); $saldo = $user->saldo?->valor ?? 0; - $valor = $this->efectivePrice($tarifaId, $state['user_id'], $tarifa->valor); + $valor = CuentasHelper::precio($tarifaId, $state['user_id'], $tarifa->valor); $state['data'] = ['flow' => 'compra', 'tarifa_id' => $tarifaId, 'valor' => $valor, 'servicio' => $tarifa->servicio->nombre, 'servicio_id' => $tarifa->servicio_id]; $this->setState($chatId, $state); @@ -722,13 +722,15 @@ class TelegramBotService return; } - $saldo = $user->saldo; - if (! $saldo || $saldo->valor < $tarifa->valor) { - $this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). El plan cuesta \$" . number_format($tarifa->valor) . "."); + $precio = (float) ($data['valor'] ?? CuentasHelper::precio($tarifa->id, $state['user_id'], $tarifa->valor)); + $saldo = $user->saldo; + + if (! $saldo || $saldo->valor < $precio) { + $this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). El plan cuesta \$" . number_format($precio) . "."); return; } - $cuenta = Cuentas::where('tarifa_id', $tarifa->id)->where('estado', 'activo')->first(); + $cuenta = CuentasHelper::buscar($tarifa); if (! $cuenta) { $this->send($chatId, "No hay cuentas disponibles en este momento. Intenta más tarde."); return; @@ -737,7 +739,7 @@ class TelegramBotService $historial = Historiale::create([ 'fecha_inicio' => now(), 'fecha_final' => now()->addDays($tarifa->dias), - 'valor' => $tarifa->valor, + 'valor' => $precio, 'utilidad' => $tarifa->utilidad ?? 0, 'tipo_pago' => 'saldo', 'estado' => 'entregado', @@ -749,7 +751,7 @@ class TelegramBotService Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]); $cuenta->update(['estado' => 'ocupado']); - $nuevoSaldo = $saldo->valor - $tarifa->valor; + $nuevoSaldo = $saldo->valor - $precio; $saldo->update(['valor' => $nuevoSaldo]); $text = "✅ *¡Compra exitosa!*\n\n"; @@ -760,6 +762,7 @@ class TelegramBotService $text .= "📺 Perfil: {$tarifa->pantallas}\n"; } $text .= "📅 Vence: " . now()->addDays($tarifa->dias)->format('d/m/Y') . "\n"; + $text .= "💰 Pagado: *\$" . number_format($precio) . "*\n"; $text .= "\nSaldo restante: *\$" . number_format($nuevoSaldo) . "*"; $this->send($chatId, $text); @@ -1395,16 +1398,6 @@ class TelegramBotService // ─── Helpers ────────────────────────────────────────────── - private function efectivePrice(int $tarifaId, ?int $userId, float $base): float - { - if ($userId) { - $pref = Preferencial::where('usuario_id', $userId)->where('tarifa_id', $tarifaId)->first(); - if ($pref) { - return (float) $pref->valor; - } - } - return (float) $base; - } private function maskEmail(string $email): string {