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 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-14 15:19:28 +00:00
co-authored by Claude Sonnet 4.6
parent 758b365b7f
commit a21675b12c
2 changed files with 143 additions and 47 deletions
+67 -19
View File
@@ -13,6 +13,8 @@ use App\Models\Historiale;
use App\Models\Historial_cuenta; use App\Models\Historial_cuenta;
use App\Models\Preferencial; use App\Models\Preferencial;
use App\Models\Promociones; use App\Models\Promociones;
use App\Models\TarifaPromo;
use App\Models\Usuario_promo;
use App\Models\recarga; use App\Models\recarga;
use App\Models\Saldo; use App\Models\Saldo;
use App\Models\Servicio; use App\Models\Servicio;
@@ -743,8 +745,14 @@ class PublicChat extends Component
private function listarPromociones(): void 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())) ->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(); ->get();
if ($promos->isEmpty()) { if ($promos->isEmpty()) {
@@ -753,14 +761,22 @@ class PublicChat extends Component
return; return;
} }
$cards = $promos->map(fn ($p) => [ $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, 'imagen' => $p->img_publicidad,
'titulo' => $p->nombre ?? 'Promocion especial', 'titulo' => $p->nombre ?? 'Promocion especial',
'descripcion' => $p->descripcion ?? null, 'descripcion' => $p->descripcion ?? null,
'precio' => $p->precio, 'precio' => $precio,
'detalle' => $p->fecha_limite ? 'Hasta ' . Carbon::parse($p->fecha_limite)->format('d/m/Y') : null, '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]], 'accion' => ['label' => 'Comprar', 'action' => 'promo.ver', 'data' => ['id' => $p->id]],
])->values()->all(); ];
})->values()->all();
ChatMessage::create([ ChatMessage::create([
'conversation_id' => $this->convId, 'conversation_id' => $this->convId,
@@ -776,21 +792,31 @@ class PublicChat extends Component
private function verPromocion(int $id): void 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) { if (! $promo) {
$this->guardarMensajeBot($this->convId, "Promocion no encontrada."); $this->guardarMensajeBot($this->convId, "Promocion no encontrada.");
return; 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([ ChatMessage::create([
'conversation_id' => $this->convId, 'conversation_id' => $this->convId,
'tipo' => 'bot', 'tipo' => 'bot',
'tipo_ui' => 'buttons', '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' => [ 'payload' => ['botones' => [
['label' => 'Saldo ($' . number_format($promo->precio) . ')', ['label' => 'Saldo ($' . number_format($precio) . ')',
'action' => 'promo.comprar.saldo', 'action' => 'promo.comprar.saldo',
'data' => ['id' => $id], 'data' => ['id' => $id],
'disabled' => $saldoInsuficiente], 'disabled' => $saldoInsuficiente],
@@ -803,17 +829,29 @@ class PublicChat extends Component
private function comprarPromoConSaldo(int $promoId): void private function comprarPromoConSaldo(int $promoId): void
{ {
$promo = Promociones::find($promoId); if (! $this->userId) {
if (! $promo || ! $this->userId) {
$this->guardarMensajeBot($this->convId, "No se pudo procesar la compra."); $this->guardarMensajeBot($this->convId, "No se pudo procesar la compra.");
return; return;
} }
$user = User::with('saldo')->find($this->userId); $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; $saldo = $user->saldo;
if (! $saldo || $saldo->valor < $promo->precio) { if (! $saldo || $saldo->valor < $precio) {
$this->guardarMensajeBot($this->convId, "Saldo insuficiente ($" . number_format($saldo?->valor ?? 0) . "). La promo cuesta $" . number_format($promo->precio) . "."); $this->guardarMensajeBot($this->convId, "Saldo insuficiente ($" . number_format($saldo?->valor ?? 0) . "). La promo cuesta $" . number_format($precio) . ".");
return; return;
} }
@@ -826,8 +864,8 @@ class PublicChat extends Component
$historial = Historiale::create([ $historial = Historiale::create([
'fecha_inicio' => now(), 'fecha_inicio' => now(),
'fecha_final' => now()->addDays(30), 'fecha_final' => now()->addDays(30),
'valor' => $promo->precio, 'valor' => $precio,
'utilidad' => $promo->utilidad ?? 0, 'utilidad' => $utilidad,
'tipo_pago' => 'saldo', 'tipo_pago' => 'saldo',
'estado' => 'entregado', 'estado' => 'entregado',
'vendedor_id' => $this->userId, 'vendedor_id' => $this->userId,
@@ -838,8 +876,8 @@ class PublicChat extends Component
Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]); Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]);
$cuenta->update(['estado' => 'ocupado']); $cuenta->update(['estado' => 'ocupado']);
$saldo->update(['valor' => $saldo->valor - $promo->precio]); $saldo->update(['valor' => $saldo->valor - $precio]);
$this->saldoUsuario = $saldo->valor - $promo->precio; $this->saldoUsuario = $saldo->valor - $precio;
ChatMessage::create([ ChatMessage::create([
'conversation_id' => $this->convId, 'conversation_id' => $this->convId,
@@ -860,13 +898,23 @@ class PublicChat extends Component
private function comprarPromoConMP(int $promoId): void 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) { if (! $promo) {
return; return;
} }
$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)); $ref = 'PROMO-' . strtoupper(Str::random(10));
$url = $this->crearPreferenciaMP($promo->nombre ?? 'Promocion streaming', (int) $promo->precio, $ref); $url = $this->crearPreferenciaMP($promo->nombre ?? 'Promocion streaming', (int) $precio, $ref);
if (! $url) { if (! $url) {
$this->guardarMensajeBot($this->convId, "No se pudo generar el link de pago. Intenta de nuevo o contacta a un asesor."); $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, 'conversation_id' => $this->convId,
'tipo' => 'bot', 'tipo' => 'bot',
'tipo_ui' => 'link', '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], 'payload' => ['url' => $url, 'label' => 'Pagar con MercadoPago', 'nota' => 'Ref: ' . $ref],
'leido' => true, 'leido' => true,
]); ]);
+70 -22
View File
@@ -12,6 +12,8 @@ use App\Models\Historiale;
use App\Models\Historial_cuenta; use App\Models\Historial_cuenta;
use App\Models\Preferencial; use App\Models\Preferencial;
use App\Models\Promociones; use App\Models\Promociones;
use App\Models\TarifaPromo;
use App\Models\Usuario_promo;
use App\Models\recarga; use App\Models\recarga;
use App\Models\Role; use App\Models\Role;
use App\Models\Saldo; use App\Models\Saldo;
@@ -597,8 +599,15 @@ class TelegramBotService
private function listPromos(string $chatId): void 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())) ->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(); ->get();
if ($promos->isEmpty()) { if ($promos->isEmpty()) {
@@ -611,7 +620,13 @@ class TelegramBotService
$buttons = []; $buttons = [];
foreach ($promos as $p) { 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) { if ($p->descripcion) {
$text .= "{$p->descripcion}\n"; $text .= "{$p->descripcion}\n";
} }
@@ -619,7 +634,7 @@ class TelegramBotService
$text .= "Hasta: " . Carbon::parse($p->fecha_limite)->format('d/m/Y') . "\n"; $text .= "Hasta: " . Carbon::parse($p->fecha_limite)->format('d/m/Y') . "\n";
} }
$text .= "\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']]; $buttons[] = [['text' => '🔙 Menú principal', 'callback_data' => 'menu']];
@@ -629,26 +644,36 @@ class TelegramBotService
private function viewPromo(string $chatId, int $promoId): void private function viewPromo(string $chatId, int $promoId): void
{ {
$state = $this->getState($chatId); $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) { if (! $promo) {
$this->send($chatId, "Promoción no encontrada."); $this->send($chatId, "Promoción no encontrada.");
return; return;
} }
$user = User::with('saldo')->find($state['user_id']); $user = isset($state['user_id']) ? User::with('saldo')->find($state['user_id']) : null;
$saldo = $user->saldo?->valor ?? 0; $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 = []; $buttons = [];
if ($saldo >= $promo->precio) { if ($saldo >= $precio) {
$buttons[] = [['text' => "💳 Pagar con saldo (\$" . number_format($promo->precio) . ")", 'callback_data' => 'promo_saldo|' . $promoId]]; $buttons[] = [['text' => "💳 Pagar con saldo (\$" . number_format($precio) . ")", 'callback_data' => 'promo_saldo|' . $promoId]];
} }
$buttons[] = [['text' => '🏦 MercadoPago', 'callback_data' => 'promo_mp|' . $promoId]]; $buttons[] = [['text' => '🏦 MercadoPago', 'callback_data' => 'promo_mp|' . $promoId]];
$buttons[] = [['text' => '🔙 Promociones', 'callback_data' => 'promo_list']]; $buttons[] = [['text' => '🔙 Promociones', 'callback_data' => 'promo_list']];
$this->sendWithKeyboard( $this->sendWithKeyboard(
$chatId, $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 $buttons
); );
} }
@@ -656,17 +681,29 @@ class TelegramBotService
private function buyPromoSaldo(string $chatId, int $promoId): void private function buyPromoSaldo(string $chatId, int $promoId): void
{ {
$state = $this->getState($chatId); $state = $this->getState($chatId);
$promo = Promociones::find($promoId); $user = isset($state['user_id']) ? User::with('saldo')->find($state['user_id']) : null;
$user = User::with('saldo')->find($state['user_id']);
if (! $promo || ! $user) { if (! $user) {
$this->send($chatId, "No se pudo procesar la compra."); $this->send($chatId, "No se pudo procesar la compra.");
return; 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; $saldo = $user->saldo;
if (! $saldo || $saldo->valor < $promo->precio) { if (! $saldo || $saldo->valor < $precio) {
$this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). La promo cuesta \$" . number_format($promo->precio) . "."); $this->send($chatId, "Saldo insuficiente (\$" . number_format($saldo?->valor ?? 0) . "). La promo cuesta \$" . number_format($precio) . ".");
return; return;
} }
@@ -679,19 +716,19 @@ class TelegramBotService
$historial = Historiale::create([ $historial = Historiale::create([
'fecha_inicio' => now(), 'fecha_inicio' => now(),
'fecha_final' => now()->addDays(30), 'fecha_final' => now()->addDays(30),
'valor' => $promo->precio, 'valor' => $precio,
'utilidad' => $promo->utilidad ?? 0, 'utilidad' => $utilidad,
'tipo_pago' => 'saldo', 'tipo_pago' => 'saldo',
'estado' => 'entregado', 'estado' => 'entregado',
'vendedor_id' => $state['user_id'], 'vendedor_id' => $user->id,
'promocion_id' => $promoId, 'promocion_id' => $promoId,
'cliente_id' => $state['user_id'], 'cliente_id' => $user->id,
'nombre_cliente' => $user->name, 'nombre_cliente' => $user->name,
]); ]);
Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]); Historial_cuenta::create(['historial_id' => $historial->id, 'cuenta_id' => $cuenta->id]);
$cuenta->update(['estado' => 'ocupado']); $cuenta->update(['estado' => 'ocupado']);
$nuevoSaldo = $saldo->valor - $promo->precio; $nuevoSaldo = $saldo->valor - $precio;
$saldo->update(['valor' => $nuevoSaldo]); $saldo->update(['valor' => $nuevoSaldo]);
$text = "✅ *¡Promo activada!*\n\n"; $text = "✅ *¡Promo activada!*\n\n";
@@ -707,13 +744,24 @@ class TelegramBotService
private function buyPromoMP(string $chatId, int $promoId): void 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) { if (! $promo) {
return; 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)); $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) { if (! $url) {
$this->send($chatId, "No se pudo generar el link de pago. Intenta de nuevo o contacta a un asesor."); $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( $this->sendWithKeyboard(
$chatId, $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']]] [[['text' => '🔙 Menú principal', 'callback_data' => 'menu']]]
); );
} }