From a21675b12c5a977752cc78ab24f158987609bada Mon Sep 17 00:00:00 2001 From: Lizandro Date: Tue, 14 Jul 2026 15:19:28 +0000 Subject: [PATCH] Fix promotion listing: role-based filtering and correct pricing - Filter promos by TarifaPromo.rol_id (same as services) - Fix visible filter: use string 'true' not boolean - Get price from Usuario_promo or TarifaPromo instead of Promociones.precio - Get utilidad from TarifaPromo for correct margin tracking - Applied same fixes to TelegramBotService promo flows Co-Authored-By: Claude Sonnet 4.6 --- app/Http/Livewire/Chat/PublicChat.php | 98 ++++++++++++++++++++------- app/Services/TelegramBotService.php | 92 +++++++++++++++++++------ 2 files changed, 143 insertions(+), 47 deletions(-) diff --git a/app/Http/Livewire/Chat/PublicChat.php b/app/Http/Livewire/Chat/PublicChat.php index 242feba..aaef869 100644 --- a/app/Http/Livewire/Chat/PublicChat.php +++ b/app/Http/Livewire/Chat/PublicChat.php @@ -13,6 +13,8 @@ use App\Models\Historiale; use App\Models\Historial_cuenta; use App\Models\Preferencial; use App\Models\Promociones; +use App\Models\TarifaPromo; +use App\Models\Usuario_promo; use App\Models\recarga; use App\Models\Saldo; use App\Models\Servicio; @@ -743,8 +745,14 @@ class PublicChat extends Component private function listarPromociones(): void { - $promos = Promociones::where('visible', true) + $rolId = $this->userId + ? User::find($this->userId)?->rol_id + : Role::where('nombre', 'cliente')->value('id'); + + $promos = Promociones::where('visible', 'true') ->where(fn ($q) => $q->whereNull('fecha_limite')->orWhere('fecha_limite', '>=', now())) + ->whereHas('tarifaPromo', fn ($q) => $q->where('rol_id', $rolId)) + ->with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)]) ->get(); if ($promos->isEmpty()) { @@ -753,14 +761,22 @@ class PublicChat extends Component return; } - $cards = $promos->map(fn ($p) => [ - 'imagen' => $p->img_publicidad, - 'titulo' => $p->nombre ?? 'Promocion especial', - 'descripcion' => $p->descripcion ?? null, - 'precio' => $p->precio, - 'detalle' => $p->fecha_limite ? 'Hasta ' . Carbon::parse($p->fecha_limite)->format('d/m/Y') : null, - 'accion' => ['label' => 'Comprar', 'action' => 'promo.ver', 'data' => ['id' => $p->id]], - ])->values()->all(); + $cards = $promos->map(function ($p) { + $tp = $p->tarifaPromo->first(); + $upric = $this->userId + ? Usuario_promo::where('usuario_id', $this->userId)->where('promocion_id', $p->id)->first() + : null; + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $p->precio); + + return [ + 'imagen' => $p->img_publicidad, + 'titulo' => $p->nombre ?? 'Promocion especial', + 'descripcion' => $p->descripcion ?? null, + 'precio' => $precio, + 'detalle' => $p->fecha_limite ? 'Hasta ' . Carbon::parse($p->fecha_limite)->format('d/m/Y') : null, + 'accion' => ['label' => 'Comprar', 'action' => 'promo.ver', 'data' => ['id' => $p->id]], + ]; + })->values()->all(); ChatMessage::create([ 'conversation_id' => $this->convId, @@ -776,21 +792,31 @@ class PublicChat extends Component private function verPromocion(int $id): void { - $promo = Promociones::find($id); + $rolId = $this->userId + ? User::find($this->userId)?->rol_id + : Role::where('nombre', 'cliente')->value('id'); + + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($id); if (! $promo) { $this->guardarMensajeBot($this->convId, "Promocion no encontrada."); return; } - $saldoInsuficiente = ! $this->userId || ($this->saldoUsuario < $promo->precio); + $upric = $this->userId + ? Usuario_promo::where('usuario_id', $this->userId)->where('promocion_id', $id)->first() + : null; + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); + + $saldoInsuficiente = ! $this->userId || ($this->saldoUsuario < $precio); ChatMessage::create([ 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'buttons', - 'contenido' => ($promo->nombre ?? 'Promocion') . "\n$" . number_format($promo->precio) . "\n\nElige como pagar:", + 'contenido' => ($promo->nombre ?? 'Promocion') . "\n$" . number_format($precio) . "\n\nElige como pagar:", 'payload' => ['botones' => [ - ['label' => 'Saldo ($' . number_format($promo->precio) . ')', + ['label' => 'Saldo ($' . number_format($precio) . ')', 'action' => 'promo.comprar.saldo', 'data' => ['id' => $id], 'disabled' => $saldoInsuficiente], @@ -803,17 +829,29 @@ class PublicChat extends Component private function comprarPromoConSaldo(int $promoId): void { - $promo = Promociones::find($promoId); - if (! $promo || ! $this->userId) { + if (! $this->userId) { $this->guardarMensajeBot($this->convId, "No se pudo procesar la compra."); return; } $user = User::with('saldo')->find($this->userId); + $rolId = $user->rol_id; + + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($promoId); + if (! $promo) { + $this->guardarMensajeBot($this->convId, "No se pudo procesar la compra."); + return; + } + + $upric = Usuario_promo::where('usuario_id', $this->userId)->where('promocion_id', $promoId)->first(); + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); + $utilidad = $upric ? $upric->utilidad : ($tp ? $tp->utilidad : 0); + $saldo = $user->saldo; - if (! $saldo || $saldo->valor < $promo->precio) { - $this->guardarMensajeBot($this->convId, "Saldo insuficiente ($" . number_format($saldo?->valor ?? 0) . "). La promo cuesta $" . number_format($promo->precio) . "."); + if (! $saldo || $saldo->valor < $precio) { + $this->guardarMensajeBot($this->convId, "Saldo insuficiente ($" . number_format($saldo?->valor ?? 0) . "). La promo cuesta $" . number_format($precio) . "."); return; } @@ -826,8 +864,8 @@ class PublicChat extends Component $historial = Historiale::create([ 'fecha_inicio' => now(), 'fecha_final' => now()->addDays(30), - 'valor' => $promo->precio, - 'utilidad' => $promo->utilidad ?? 0, + 'valor' => $precio, + 'utilidad' => $utilidad, 'tipo_pago' => 'saldo', 'estado' => 'entregado', 'vendedor_id' => $this->userId, @@ -838,8 +876,8 @@ class PublicChat extends Component Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]); $cuenta->update(['estado' => 'ocupado']); - $saldo->update(['valor' => $saldo->valor - $promo->precio]); - $this->saldoUsuario = $saldo->valor - $promo->precio; + $saldo->update(['valor' => $saldo->valor - $precio]); + $this->saldoUsuario = $saldo->valor - $precio; ChatMessage::create([ 'conversation_id' => $this->convId, @@ -860,13 +898,23 @@ class PublicChat extends Component private function comprarPromoConMP(int $promoId): void { - $promo = Promociones::find($promoId); + $rolId = $this->userId + ? User::find($this->userId)?->rol_id + : Role::where('nombre', 'cliente')->value('id'); + + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($promoId); if (! $promo) { return; } - $ref = 'PROMO-' . strtoupper(Str::random(10)); - $url = $this->crearPreferenciaMP($promo->nombre ?? 'Promocion streaming', (int) $promo->precio, $ref); + $upric = $this->userId + ? Usuario_promo::where('usuario_id', $this->userId)->where('promocion_id', $promoId)->first() + : null; + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); + + $ref = 'PROMO-' . strtoupper(Str::random(10)); + $url = $this->crearPreferenciaMP($promo->nombre ?? 'Promocion streaming', (int) $precio, $ref); if (! $url) { $this->guardarMensajeBot($this->convId, "No se pudo generar el link de pago. Intenta de nuevo o contacta a un asesor."); @@ -877,7 +925,7 @@ class PublicChat extends Component 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'link', - 'contenido' => "Link de pago por $" . number_format($promo->precio) . ". Envia el comprobante aqui al pagar.", + 'contenido' => "Link de pago por $" . number_format($precio) . ". Envia el comprobante aqui al pagar.", 'payload' => ['url' => $url, 'label' => 'Pagar con MercadoPago', 'nota' => 'Ref: ' . $ref], 'leido' => true, ]); diff --git a/app/Services/TelegramBotService.php b/app/Services/TelegramBotService.php index 3e3cd3d..ef38254 100644 --- a/app/Services/TelegramBotService.php +++ b/app/Services/TelegramBotService.php @@ -12,6 +12,8 @@ use App\Models\Historiale; use App\Models\Historial_cuenta; use App\Models\Preferencial; use App\Models\Promociones; +use App\Models\TarifaPromo; +use App\Models\Usuario_promo; use App\Models\recarga; use App\Models\Role; use App\Models\Saldo; @@ -597,8 +599,15 @@ class TelegramBotService private function listPromos(string $chatId): void { - $promos = Promociones::where('visible', true) + $state = $this->getState($chatId); + $rolId = isset($state['user_id']) + ? (User::find($state['user_id'])?->rol_id ?? Role::where('nombre', 'cliente')->value('id')) + : Role::where('nombre', 'cliente')->value('id'); + + $promos = Promociones::where('visible', 'true') ->where(fn ($q) => $q->whereNull('fecha_limite')->orWhere('fecha_limite', '>=', now())) + ->whereHas('tarifaPromo', fn ($q) => $q->where('rol_id', $rolId)) + ->with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)]) ->get(); if ($promos->isEmpty()) { @@ -611,7 +620,13 @@ class TelegramBotService $buttons = []; foreach ($promos as $p) { - $text .= "*{$p->nombre}* — \$" . number_format($p->precio) . "\n"; + $upric = isset($state['user_id']) + ? Usuario_promo::where('usuario_id', $state['user_id'])->where('promocion_id', $p->id)->first() + : null; + $tp = $p->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $p->precio); + + $text .= "*{$p->nombre}* — \$" . number_format($precio) . "\n"; if ($p->descripcion) { $text .= "{$p->descripcion}\n"; } @@ -619,7 +634,7 @@ class TelegramBotService $text .= "Hasta: " . Carbon::parse($p->fecha_limite)->format('d/m/Y') . "\n"; } $text .= "\n"; - $buttons[] = [['text' => ($p->nombre ?? 'Promo') . ' — $' . number_format($p->precio), 'callback_data' => 'promo_view|' . $p->id]]; + $buttons[] = [['text' => ($p->nombre ?? 'Promo') . ' — $' . number_format($precio), 'callback_data' => 'promo_view|' . $p->id]]; } $buttons[] = [['text' => '🔙 Menú principal', 'callback_data' => 'menu']]; @@ -629,26 +644,36 @@ class TelegramBotService private function viewPromo(string $chatId, int $promoId): void { $state = $this->getState($chatId); - $promo = Promociones::find($promoId); + $rolId = isset($state['user_id']) + ? (User::find($state['user_id'])?->rol_id ?? Role::where('nombre', 'cliente')->value('id')) + : Role::where('nombre', 'cliente')->value('id'); + + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($promoId); if (! $promo) { $this->send($chatId, "Promoción no encontrada."); return; } - $user = User::with('saldo')->find($state['user_id']); - $saldo = $user->saldo?->valor ?? 0; + $user = isset($state['user_id']) ? User::with('saldo')->find($state['user_id']) : null; + $saldo = $user?->saldo?->valor ?? 0; + + $upric = isset($state['user_id']) + ? Usuario_promo::where('usuario_id', $state['user_id'])->where('promocion_id', $promoId)->first() + : null; + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); $buttons = []; - if ($saldo >= $promo->precio) { - $buttons[] = [['text' => "💳 Pagar con saldo (\$" . number_format($promo->precio) . ")", 'callback_data' => 'promo_saldo|' . $promoId]]; + if ($saldo >= $precio) { + $buttons[] = [['text' => "💳 Pagar con saldo (\$" . number_format($precio) . ")", 'callback_data' => 'promo_saldo|' . $promoId]]; } $buttons[] = [['text' => '🏦 MercadoPago', 'callback_data' => 'promo_mp|' . $promoId]]; $buttons[] = [['text' => '🔙 Promociones', 'callback_data' => 'promo_list']]; $this->sendWithKeyboard( $chatId, - "*{$promo->nombre}*\n\$" . number_format($promo->precio) . "\n\nElige cómo pagar:", + "*{$promo->nombre}*\n\$" . number_format($precio) . "\n\nElige cómo pagar:", $buttons ); } @@ -656,17 +681,29 @@ class TelegramBotService private function buyPromoSaldo(string $chatId, int $promoId): void { $state = $this->getState($chatId); - $promo = Promociones::find($promoId); - $user = User::with('saldo')->find($state['user_id']); + $user = isset($state['user_id']) ? User::with('saldo')->find($state['user_id']) : null; - if (! $promo || ! $user) { + if (! $user) { $this->send($chatId, "No se pudo procesar la compra."); return; } + $rolId = $user->rol_id; + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($promoId); + + if (! $promo) { + $this->send($chatId, "No se pudo procesar la compra."); + return; + } + + $upric = Usuario_promo::where('usuario_id', $user->id)->where('promocion_id', $promoId)->first(); + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); + $utilidad = $upric ? $upric->utilidad : ($tp ? $tp->utilidad : 0); + $saldo = $user->saldo; - if (! $saldo || $saldo->valor < $promo->precio) { - $this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). La promo cuesta \$" . number_format($promo->precio) . "."); + if (! $saldo || $saldo->valor < $precio) { + $this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). La promo cuesta \$" . number_format($precio) . "."); return; } @@ -679,19 +716,19 @@ class TelegramBotService $historial = Historiale::create([ 'fecha_inicio' => now(), 'fecha_final' => now()->addDays(30), - 'valor' => $promo->precio, - 'utilidad' => $promo->utilidad ?? 0, + 'valor' => $precio, + 'utilidad' => $utilidad, 'tipo_pago' => 'saldo', 'estado' => 'entregado', - 'vendedor_id' => $state['user_id'], + 'vendedor_id' => $user->id, 'promocion_id' => $promoId, - 'cliente_id' => $state['user_id'], + 'cliente_id' => $user->id, 'nombre_cliente' => $user->name, ]); Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]); $cuenta->update(['estado' => 'ocupado']); - $nuevoSaldo = $saldo->valor - $promo->precio; + $nuevoSaldo = $saldo->valor - $precio; $saldo->update(['valor' => $nuevoSaldo]); $text = "✅ *¡Promo activada!*\n\n"; @@ -707,13 +744,24 @@ class TelegramBotService private function buyPromoMP(string $chatId, int $promoId): void { - $promo = Promociones::find($promoId); + $state = $this->getState($chatId); + $rolId = isset($state['user_id']) + ? (User::find($state['user_id'])?->rol_id ?? Role::where('nombre', 'cliente')->value('id')) + : Role::where('nombre', 'cliente')->value('id'); + + $promo = Promociones::with(['tarifaPromo' => fn ($q) => $q->where('rol_id', $rolId)])->find($promoId); if (! $promo) { return; } + $upric = isset($state['user_id']) + ? Usuario_promo::where('usuario_id', $state['user_id'])->where('promocion_id', $promoId)->first() + : null; + $tp = $promo->tarifaPromo->first(); + $precio = $upric ? $upric->precio : ($tp ? $tp->precio : $promo->precio); + $ref = 'PROMO-' . strtoupper(Str::random(10)); - $url = $this->createMPPreference($promo->nombre ?? 'Promocion streaming', (int) $promo->precio, $ref); + $url = $this->createMPPreference($promo->nombre ?? 'Promocion streaming', (int) $precio, $ref); if (! $url) { $this->send($chatId, "No se pudo generar el link de pago. Intenta de nuevo o contacta a un asesor."); @@ -722,7 +770,7 @@ class TelegramBotService $this->sendWithKeyboard( $chatId, - "🏦 *Pago con MercadoPago*\n\n{$promo->nombre} — \$" . number_format($promo->precio) . "\nRef: `{$ref}`\n\n[👉 Pagar ahora]({$url})\n\nDespués de pagar, envía la foto del comprobante aquí.", + "🏦 *Pago con MercadoPago*\n\n{$promo->nombre} — \$" . number_format($precio) . "\nRef: `{$ref}`\n\n[👉 Pagar ahora]({$url})\n\nDespués de pagar, envía la foto del comprobante aquí.", [[['text' => '🔙 Menú principal', 'callback_data' => 'menu']]] ); }