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
+70 -22
View File
@@ -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']]]
);
}