Initial commit: Sistema de comisiones y gastos personales

This commit is contained in:
2026-04-19 09:59:57 -06:00
commit dc964d6bce
103 changed files with 15859 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class AuthController extends Controller
{
/**
* Mostrar formulario de login
*/
public function showLoginForm()
{
return view('auth.login');
}
/**
* Procesar login
*/
public function login(Request $request)
{
$credentials = $request->validate([
'username' => ['required', 'string'],
'password' => ['required'],
]);
$user = User::where('username', $credentials['username'])->first();
if (!$user || !Hash::check($credentials['password'], $user->password)) {
throw ValidationException::withMessages([
'username' => ['Las credenciales no coinciden con nuestros registros.'],
]);
}
if (!$user->is_active) {
throw ValidationException::withMessages([
'username' => ['Tu cuenta está inactiva. Contacta al administrador.'],
]);
}
Auth::login($user, $request->boolean('remember'));
$request->session()->regenerate();
return redirect()->intended('/');
}
/**
* Mostrar formulario de registro
*/
public function showRegisterForm()
{
return view('auth.register');
}
/**
* Procesar registro
*/
public function register(Request $request)
{
$validated = $request->validate([
'username' => ['required', 'string', 'max:50', 'unique:users,username'],
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'confirmed', 'min:8'],
'commission_percentage' => ['required', 'numeric', 'min:0', 'max:100'],
'monthly_salary' => ['required', 'numeric', 'min:0'],
]);
$user = User::create([
'username' => $validated['username'],
'name' => $validated['name'],
'email' => $validated['email'],
'password' => $validated['password'],
'commission_percentage' => $validated['commission_percentage'],
'monthly_salary' => $validated['monthly_salary'],
'is_active' => true,
]);
Auth::login($user);
return redirect('/');
}
/**
* Cerrar sesión
*/
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/login');
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers;
use App\Models\DailySale;
use App\Models\Month;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class CalendarController extends Controller
{
/**
* Mostrar calendario interactivo
*/
public function index(Request $request)
{
$user = Auth::user();
$year = $request->get('year', now()->year);
$monthName = $request->get('month');
// Obtener meses del usuario
$months = $user->months()
->where('year', $year)
->get()
->keyBy('name');
// Si se especifica mes, mostrar ese mes
if ($monthName) {
$currentMonth = $months->get($monthName);
} else {
// Si no, buscar mes abierto o el último del año
$currentMonth = $user->months()
->where('year', $year)
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre')")
->first();
}
// Obtener ventas diarias del mes actual
$dailySales = [];
if ($currentMonth) {
$sales = $currentMonth->dailySales()->get()->keyBy('date');
foreach ($sales as $sale) {
$dailySales[$sale->date->format('Y-m-d')] = $sale;
}
}
// Obtener gastos del mes
$expenses = [];
if ($currentMonth) {
$expenseList = $currentMonth->expenses()->get();
foreach ($expenseList as $expense) {
$expenses[$expense->date->format('Y-m-d')] = $expense;
}
}
return view('calendar.index', compact('year', 'months', 'currentMonth', 'dailySales', 'expenses'));
}
/**
* Obtener datos de un día específico (para AJAX)
*/
public function day(Request $request)
{
$user = Auth::user();
$date = $request->get('date');
if (!$date) {
return response()->json(['error' => 'Fecha requerida'], 400);
}
// Buscar venta del día
$sale = DailySale::whereHas('month', function ($query) use ($user) {
$query->where('user_id', $user->id);
})
->where('date', $date)
->first();
// Buscar gastos del día
$expenses = $user->expenses()
->where('date', $date)
->get();
return response()->json([
'date' => $date,
'sale' => $sale,
'expenses' => $expenses,
]);
}
/**
* Guardar venta y gasto del día (para AJAX)
*/
public function storeDay(Request $request)
{
$user = Auth::user();
$data = $request->all();
$monthId = $request->input('month_id');
$date = $request->input('date');
$userSales = floatval($request->input('user_sales', 0));
$systemSales = floatval($request->input('system_sales', 0));
$expenseAmount = floatval($request->input('expense_amount', 0));
if (!$monthId || !$date) {
return response()->json(['success' => false, 'message' => 'Faltan datos requeridos']);
}
$month = Month::where('id', $monthId)
->where('user_id', $user->id)
->first();
if (!$month) {
return response()->json(['success' => false, 'message' => 'Mes no encontrado']);
}
// Guardar o actualizar venta solo si hay datos
if ($userSales > 0) {
DailySale::updateOrCreate(
[
'month_id' => $month->id,
'date' => $date,
],
[
'user_id' => $user->id,
'user_sales' => $userSales,
'system_sales' => $systemSales,
]
);
}
// Guardar o actualizar gasto si hay monto
$expenseType = $request->input('expense_type', 'q1');
if ($expenseAmount > 0) {
\App\Models\Expense::updateOrCreate(
[
'month_id' => $month->id,
'date' => $date,
],
[
'user_id' => $user->id,
'description' => $request->input('expense_description', 'Gasto del día'),
'amount' => $expenseAmount,
'expense_type' => $expenseType,
]
);
}
return response()->json(['success' => true, 'message' => 'Datos guardados correctamente']);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Models\DailySale;
use App\Models\Month;
use App\Services\CommissionCalculator;
use Illuminate\Support\Facades\Auth;
class DashboardController extends Controller
{
/**
* Mostrar dashboard del usuario
*/
public function index()
{
$user = Auth::user();
// Obtener mes actual o último mes
$currentMonth = $user->getCurrentMonth();
// Si no hay mes abierto, buscar el último mes
if (!$currentMonth) {
$currentMonth = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->first();
}
$data = null;
if ($currentMonth) {
$data = CommissionCalculator::calculateForMonth($user, $currentMonth);
}
// Últimos meses del usuario
$recentMonths = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->limit(6)
->get();
return view('dashboard.index', compact('currentMonth', 'data', 'recentMonths'));
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace App\Http\Controllers;
use App\Models\Expense;
use App\Models\Month;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ExpenseController extends Controller
{
/**
* Listar todos los gastos
*/
public function index(Request $request)
{
$user = Auth::user();
$monthId = $request->get('month_id');
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
$expenses = $month->expenses()
->orderBy('date', 'desc')
->paginate(30);
} else {
$month = null;
$expenses = $user->expenses()
->orderBy('date', 'desc')
->paginate(30);
}
$months = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->get();
return view('expenses.index', compact('expenses', 'month', 'months'));
}
/**
* Mostrar formulario para crear gasto
*/
public function create(Request $request)
{
$user = Auth::user();
$monthId = $request->get('month_id');
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
} else {
$month = $user->getCurrentMonth();
if (!$month) {
$month = $user->months()->latest()->first();
}
}
if (!$month) {
return redirect()->route('months.index')->with('info', 'Necesitas un mes de trabajo primero.');
}
return view('expenses.create', compact('month'));
}
/**
* Guardar nuevo gasto
*/
public function store(Request $request)
{
$user = Auth::user();
$validated = $request->validate([
'month_id' => ['required', 'exists:months,id'],
'description' => ['required', 'string', 'max:255'],
'amount' => ['required', 'numeric', 'min:0.01'],
'date' => ['required', 'date'],
'expense_type' => ['required', 'in:q1,q2,mensual'],
]);
// Verificar que el mes pertenece al usuario
$month = $user->months()->findOrFail($validated['month_id']);
$month->expenses()->create([
'user_id' => $user->id,
'description' => $validated['description'],
'amount' => $validated['amount'],
'date' => $validated['date'],
'expense_type' => $validated['expense_type'],
]);
return redirect()->route('expenses.index', ['month_id' => $month->id])
->with('success', 'Gasto registrado correctamente.');
}
/**
* Mostrar formulario de edición
*/
public function edit(Expense $expense)
{
$user = Auth::user();
if ($expense->user_id !== $user->id) {
abort(403);
}
return view('expenses.edit', compact('expense'));
}
/**
* Actualizar gasto
*/
public function update(Request $request, Expense $expense)
{
$user = Auth::user();
if ($expense->user_id !== $user->id) {
abort(403);
}
$validated = $request->validate([
'description' => ['required', 'string', 'max:255'],
'amount' => ['required', 'numeric', 'min:0.01'],
'date' => ['required', 'date'],
'expense_type' => ['required', 'in:q1,q2,mensual'],
]);
$expense->update($validated);
return redirect()->route('expenses.index', ['month_id' => $expense->month_id])
->with('success', 'Gasto actualizado correctamente.');
}
/**
* Eliminar gasto
*/
public function destroy(Expense $expense)
{
$user = Auth::user();
if ($expense->user_id !== $user->id) {
abort(403);
}
$monthId = $expense->month_id;
$expense->delete();
return redirect()->route('expenses.index', ['month_id' => $monthId])
->with('success', 'Gasto eliminado correctamente.');
}
}

View File

@@ -0,0 +1,163 @@
<?php
namespace App\Http\Controllers;
use App\Models\Month;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class MonthController extends Controller
{
/**
* Listar todos los meses
*/
public function index()
{
$user = Auth::user();
$months = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Diciembre', 'Noviembre', 'Octubre', 'Septiembre', 'Agosto', 'Julio', 'Junio', 'Mayo', 'Abril', 'Marzo', 'Febrero', 'Enero')")
->paginate(12);
return view('months.index', compact('months'));
}
/**
* Mostrar formulario para crear mes
*/
public function create()
{
return view('months.create');
}
/**
* Guardar nuevo mes
*/
public function store(Request $request)
{
$user = Auth::user();
$validated = $request->validate([
'name' => ['required', 'string', 'max:50'],
'year' => ['required', 'integer', 'min:2020', 'max:2100'],
]);
// Verificar que no exista ya el mes para el usuario
$exists = $user->months()
->where('name', $validated['name'])
->where('year', $validated['year'])
->exists();
if ($exists) {
return back()->withErrors([
'name' => 'Ya existe un mes con ese nombre y año.',
])->withInput();
}
$user->months()->create([
'name' => $validated['name'],
'year' => $validated['year'],
'status' => 'open',
]);
return redirect()->route('months.index')
->with('success', 'Mes creado correctamente.');
}
/**
* Mostrar detalles del mes
*/
public function show(Month $month)
{
$user = Auth::user();
if ($month->user_id !== $user->id) {
abort(403);
}
$month->load(['dailySales', 'expenses']);
return view('months.show', compact('month'));
}
/**
* Mostrar formulario de edición
*/
public function edit(Month $month)
{
$user = Auth::user();
if ($month->user_id !== $user->id) {
abort(403);
}
return view('months.edit', compact('month'));
}
/**
* Actualizar mes
*/
public function update(Request $request, Month $month)
{
$user = Auth::user();
if ($month->user_id !== $user->id) {
abort(403);
}
$validated = $request->validate([
'name' => ['required', 'string', 'max:50'],
'year' => ['required', 'integer', 'min:2020', 'max:2100'],
'status' => ['required', 'in:open,closed,paid'],
]);
$month->update($validated);
return redirect()->route('months.show', $month->id)
->with('success', 'Mes actualizado correctamente.');
}
/**
* Cerrar mes
*/
public function close(Month $month)
{
$user = Auth::user();
if ($month->user_id !== $user->id) {
abort(403);
}
if ($month->status !== 'open') {
return back()->with('error', 'Solo se pueden cerrar meses abiertos.');
}
$month->update(['status' => 'closed']);
return redirect()->route('months.show', $month->id)
->with('success', 'Mes cerrado correctamente.');
}
/**
* Eliminar mes
*/
public function destroy(Month $month)
{
$user = Auth::user();
if ($month->user_id !== $user->id) {
abort(403);
}
// Verificar que no tenga ventas o gastos asociados
if ($month->dailySales()->count() > 0 || $month->expenses()->count() > 0) {
return back()->with('error', 'No puedes eliminar un mes que tiene ventas o gastos asociados.');
}
$month->delete();
return redirect()->route('months.index')
->with('success', 'Mes eliminado correctamente.');
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace App\Http\Controllers;
use App\Models\Month;
use App\Services\CommissionCalculator;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class ReportController extends Controller
{
/**
* Mostrar reporte mensual
*/
public function monthly(Request $request)
{
$user = Auth::user();
$monthId = $request->get('month_id');
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
} else {
$month = $user->getCurrentMonth();
if (!$month) {
$month = $user->months()->latest()->first();
}
}
if (!$month) {
return redirect()->route('dashboard')->with('info', 'No hay meses disponibles.');
}
$report = CommissionCalculator::calculateForMonth($user, $month);
// Cargar ventas y gastos detalles
$dailySales = $month->dailySales()
->orderBy('date', 'asc')
->get();
$expenses = $month->expenses()
->orderBy('date', 'desc')
->get();
$months = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->get();
return view('reports.monthly', compact('report', 'month', 'dailySales', 'expenses', 'months'));
}
/**
* Mostrar reporte quincenal
*/
public function biweekly(Request $request)
{
$user = Auth::user();
$monthId = $request->get('month_id');
$biweekly = $request->get('biweekly', 1);
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
} else {
$month = $user->getCurrentMonth();
if (!$month) {
$month = $user->months()->latest()->first();
}
}
if (!$month) {
return redirect()->route('dashboard')->with('info', 'No hay meses disponibles.');
}
$report = CommissionCalculator::calculateBiweekly($user, $month, $biweekly);
// Obtener ventas del mes
$dailySales = $month->dailySales()
->orderBy('date', 'asc')
->get();
// Obtener gastos según la quincena seleccionada
$expenses = $month->expenses()
->where(function($query) use ($biweekly) {
if ($biweekly === 1) {
$query->where('expense_type', 'q1')
->orWhere('expense_type', 'mensual');
} else {
$query->where('expense_type', 'q2')
->orWhere('expense_type', 'mensual');
}
})
->orderBy('date', 'desc')
->get();
$months = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->get();
return view('reports.biweekly', compact('report', 'month', 'dailySales', 'expenses', 'months', 'biweekly'));
}
/**
* Resumen anual
*/
public function yearly(Request $request)
{
$user = Auth::user();
$year = $request->get('year', now()->year);
$report = CommissionCalculator::calculateYearly($user, $year);
// Obtener meses del año
$months = $user->months()
->where('year', $year)
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre')")
->get();
// Años disponibles
$years = $user->months()
->select('year')
->distinct()
->orderBy('year', 'desc')
->pluck('year');
return view('reports.yearly', compact('report', 'year', 'months', 'years'));
}
/**
* Obtener número del mes por nombre
*/
private static function getMonthNumber(string $monthName): int
{
$months = [
'Enero' => 1, 'Febrero' => 2, 'Marzo' => 3, 'Abril' => 4,
'Mayo' => 5, 'Junio' => 6, 'Julio' => 7, 'Agosto' => 8,
'Septiembre' => 9, 'Octubre' => 10, 'Noviembre' => 11, 'Diciembre' => 12
];
return $months[$monthName] ?? 1;
}
}

View File

@@ -0,0 +1,172 @@
<?php
namespace App\Http\Controllers;
use App\Models\DailySale;
use App\Models\Month;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class SaleController extends Controller
{
/**
* Listar todas las ventas del mes actual
*/
public function index(Request $request)
{
$user = Auth::user();
// Obtener mes seleccionado
$monthId = $request->get('month_id');
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
} else {
$month = $user->getCurrentMonth();
if (!$month) {
$month = $user->months()->latest()->first();
}
}
if (!$month) {
return redirect()->route('months.create')->with('info', 'Primero debes crear un mes de trabajo.');
}
$sales = $month->dailySales()
->orderBy('date', 'desc')
->paginate(31);
$months = $user->months()
->orderBy('year', 'desc')
->orderByRaw("FIELD(name, 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre') DESC")
->get();
return view('sales.index', compact('sales', 'month', 'months'));
}
/**
* Mostrar formulario para crear venta
*/
public function create(Request $request)
{
$user = Auth::user();
$monthId = $request->get('month_id');
if ($monthId) {
$month = $user->months()->findOrFail($monthId);
} else {
$month = $user->getCurrentMonth();
if (!$month) {
$month = $user->months()->latest()->first();
}
}
if (!$month) {
return redirect()->route('months.index')->with('info', 'Necesitas un mes de trabajo primero.');
}
return view('sales.create', compact('month'));
}
/**
* Guardar nueva venta
*/
public function store(Request $request)
{
$user = Auth::user();
$validated = $request->validate([
'month_id' => ['required', 'exists:months,id'],
'date' => ['required', 'date'],
'user_sales' => ['required', 'numeric', 'min:0'],
'system_sales' => ['nullable', 'numeric', 'min:0'],
]);
// Verificar que el mes pertenece al usuario
$month = $user->months()->findOrFail($validated['month_id']);
// Verificar si ya existe una venta para esa fecha
$existingSale = $month->dailySales()->where('date', $validated['date'])->first();
if ($existingSale) {
// Actualizar venta existente
$existingSale->update([
'user_sales' => $validated['user_sales'],
'system_sales' => $validated['system_sales'] ?? $existingSale->system_sales,
]);
return redirect()->route('sales.index', ['month_id' => $month->id])
->with('success', 'Venta actualizada correctamente.');
}
// Crear nueva venta
$month->dailySales()->create([
'user_id' => $user->id,
'date' => $validated['date'],
'user_sales' => $validated['user_sales'],
'system_sales' => $validated['system_sales'] ?? 0,
]);
return redirect()->route('sales.index', ['month_id' => $month->id])
->with('success', 'Venta registrada correctamente.');
}
/**
* Mostrar formulario de edición
*/
public function edit(DailySale $sale)
{
$user = Auth::user();
// Verificar que la venta pertenece al usuario
if ($sale->user_id !== $user->id) {
abort(403);
}
return view('sales.edit', compact('sale'));
}
/**
* Actualizar venta
*/
public function update(Request $request, DailySale $sale)
{
$user = Auth::user();
if ($sale->user_id !== $user->id) {
abort(403);
}
$validated = $request->validate([
'date' => ['required', 'date'],
'user_sales' => ['required', 'numeric', 'min:0'],
'system_sales' => ['nullable', 'numeric', 'min:0'],
]);
$sale->update($validated);
return redirect()->route('sales.index', ['month_id' => $sale->month_id])
->with('success', 'Venta actualizada correctamente.');
}
/**
* Eliminar venta
*/
public function destroy(DailySale $sale)
{
$user = Auth::user();
if ($sale->user_id !== $user->id) {
abort(403);
}
$monthId = $sale->month_id;
$sale->delete();
return redirect()->route('sales.index', ['month_id' => $monthId])
->with('success', 'Venta eliminada correctamente.');
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
class SettingsController extends Controller
{
/**
* Mostrar configuración del usuario
*/
public function index()
{
$user = Auth::user();
return view('settings.index', compact('user'));
}
/**
* Actualizar configuración del usuario
*/
public function update(Request $request)
{
$user = Auth::user();
foreach (['fecha_ingreso', 'razon_social', 'sueldo_integro_diario'] as $field) {
if ($request->has($field) && $request->input($field) === '') {
$request->merge([$field => null]);
}
}
if ($request->has('commission_percentage')) {
$request->validate([
'commission_percentage' => ['required', 'numeric', 'min:0', 'max:100'],
]);
$user->commission_percentage = floatval($request->input('commission_percentage', 0));
}
if ($request->has('monthly_salary')) {
$request->validate([
'monthly_salary' => ['required', 'numeric', 'min:0'],
]);
$user->monthly_salary = floatval($request->input('monthly_salary', 0));
}
if ($request->has('fecha_ingreso')) {
if ($request->filled('fecha_ingreso')) {
$user->fecha_ingreso = $request->input('fecha_ingreso');
} else {
$user->fecha_ingreso = null;
}
}
if ($request->has('razon_social')) {
if ($request->filled('razon_social')) {
$user->razon_social = $request->input('razon_social');
} else {
$user->razon_social = null;
}
}
if ($request->has('sueldo_integro_diario')) {
if ($request->filled('sueldo_integro_diario')) {
$user->sueldo_integro_diario = floatval($request->input('sueldo_integro_diario'));
} else {
$user->sueldo_integro_diario = null;
}
}
$user->save();
return redirect()->route('settings.index')->with('success', 'Configuración actualizada correctamente.');
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace App\Http\Controllers;
use App\Models\TelegramAccount;
use App\Models\User;
use App\Services\TelegramBotService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class TelegramController extends Controller
{
/**
* Mostrar página de vinculación de Telegram
*/
public function showVerifyPage()
{
$user = Auth::user();
// Obtener o crear cuenta de Telegram
$telegramAccount = $user->telegramAccount()->first();
if (!$telegramAccount) {
// Generar nuevo código de verificación
$verificationCode = TelegramBotService::generateVerificationCode();
$telegramAccount = TelegramAccount::create([
'user_id' => $user->id,
'chat_id' => null,
'verification_code' => $verificationCode,
'is_verified' => false,
]);
} elseif (!$telegramAccount->is_verified && !$telegramAccount->verification_code) {
// Regenerar código si no existe
$verificationCode = TelegramBotService::generateVerificationCode();
$telegramAccount->update(['verification_code' => $verificationCode]);
}
return view('telegram.verify', compact('telegramAccount'));
}
/**
* Regenerar código de verificación
*/
public function regenerateCode(Request $request)
{
$user = Auth::user();
$telegramAccount = $user->telegramAccount;
if (!$telegramAccount) {
return back()->with('error', 'No tienes una cuenta de Telegram vinculada.');
}
if ($telegramAccount->is_verified) {
return back()->with('error', 'Tu cuenta ya está verificada.');
}
$verificationCode = TelegramBotService::generateVerificationCode();
$telegramAccount->update(['verification_code' => $verificationCode]);
return back()->with('success', 'Nuevo código de verificación generado.');
}
/**
* desvincular cuenta de Telegram
*/
public function unlink(Request $request)
{
$user = Auth::user();
$telegramAccount = $user->telegramAccount;
if ($telegramAccount) {
$telegramAccount->delete();
// Crear nueva cuenta sin verificar
TelegramAccount::create([
'user_id' => $user->id,
'chat_id' => null,
'verification_code' => TelegramBotService::generateVerificationCode(),
'is_verified' => false,
]);
}
return back()->with('success', 'Cuenta de Telegram desvinculada.');
}
/**
* Webhook para recibir mensajes de Telegram
*/
public function webhook(Request $request)
{
try {
$update = $request->all();
Log::info('Telegram webhook received', $update);
if (empty($update)) {
return response()->json(['ok' => true, 'message' => 'No update
received']);
}
$botService = new TelegramBotService();
$result = $botService->handleUpdate($update);
return response()->json($result);
} catch (\Exception $e) {
Log::error('Telegram webhook error', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return response()->json(['ok' => false, 'error' => $e->getMessage()], 500);
}
}
/**
* Configurar webhook (ruta temporal para configurar desde el navegador)
*/
public function setupWebhook(Request $request)
{
$token = $request->get('token');
// Verificar token de seguridad simple
if ($token !== config('app.key')) {
abort(403, 'Token inválido');
}
$botService = new TelegramBotService();
$result = $botService->setWebhook();
if ($result) {
return response()->json(['success' => true, 'message' => 'Webhook configurado correctamente']);
}
return response()->json(['success' => false, 'error' => 'Error al configurar webhook'], 500);
}
}