plantelesDelUsuario()->pluck('id'); $promociones = Promocion::with('planteles') ->whereHas('planteles', fn($q) => $q->whereIn('plantels.id', $plantelesIds)) ->latest() ->paginate(15); return view('promocion.index', compact('promociones')); } public function create() { $planteles = $this->plantelesDelUsuario(); return view('promocion.create', compact('planteles')); } public function store(Request $request) { $data = $request->validate([ 'titulo' => 'required|string|max:200', 'descripcion' => 'nullable|string|max:3000', 'imagen' => 'nullable|image|max:10240', 'fecha_inicio' => 'required|date', 'fecha_fin' => 'required|date|after_or_equal:fecha_inicio', 'status' => 'required|in:activo,inactivo', 'plantels' => 'required|array|min:1', 'plantels.*' => 'exists:plantels,id', ]); $this->validarAccesoPlanteles($request->plantels ?? []); if ($request->hasFile('imagen')) { $data['imagen'] = $request->file('imagen')->store('promociones', 'public'); } unset($data['plantels']); $promocion = Promocion::create($data); $promocion->planteles()->sync($request->plantels); return redirect()->route('promocion.index')->with('success', 'Promoción creada correctamente.'); } public function edit(Promocion $promocion) { $this->autorizarPlantel($promocion); $planteles = $this->plantelesDelUsuario(); $plantelesSeleccionados = $promocion->planteles->pluck('id')->toArray(); return view('promocion.edit', compact('promocion', 'planteles', 'plantelesSeleccionados')); } public function update(Request $request, Promocion $promocion) { $this->autorizarPlantel($promocion); $data = $request->validate([ 'titulo' => 'required|string|max:200', 'descripcion' => 'nullable|string|max:3000', 'imagen' => 'nullable|image|max:10240', 'fecha_inicio' => 'required|date', 'fecha_fin' => 'required|date|after_or_equal:fecha_inicio', 'status' => 'required|in:activo,inactivo', 'plantels' => 'required|array|min:1', 'plantels.*' => 'exists:plantels,id', ]); $this->validarAccesoPlanteles($request->plantels ?? []); if ($request->hasFile('imagen')) { if ($promocion->imagen) { Storage::disk('public')->delete($promocion->imagen); } $data['imagen'] = $request->file('imagen')->store('promociones', 'public'); } elseif ($request->boolean('remove_imagen') && $promocion->imagen) { Storage::disk('public')->delete($promocion->imagen); $data['imagen'] = null; } unset($data['plantels']); $promocion->update($data); $promocion->planteles()->sync($request->plantels); return redirect()->route('promocion.index')->with('success', 'Promoción actualizada.'); } public function destroy(Promocion $promocion) { $this->autorizarPlantel($promocion); if ($promocion->imagen) { Storage::disk('public')->delete($promocion->imagen); } $promocion->delete(); return back()->with('success', 'Promoción eliminada.'); } public function toggleStatus(Promocion $promocion) { $this->autorizarPlantel($promocion); $nuevoStatus = $promocion->status === 'activo' ? 'inactivo' : 'activo'; $promocion->update(['status' => $nuevoStatus]); return back()->with('success', $nuevoStatus === 'activo' ? 'Promoción activada.' : 'Promoción desactivada.'); } public function enviar(Promocion $promocion) { $this->autorizarPlantel($promocion); $plantelesIds = $promocion->planteles->pluck('id'); $prospectos = Alumno::where('status', 1) ->where('opt_out', false) ->whereHas('alumnos.plantelUsuarios', function ($q) use ($plantelesIds) { $q->whereIn('plantels.id', $plantelesIds); }) ->get(); $despachados = 0; foreach ($prospectos as $i => $alumno) { if (!$promocion->alumnos()->where('alumno_id', $alumno->id)->exists()) { $promocion->alumnos()->attach($alumno->id, ['status_envio' => 'pendiente']); } EnviarPromocionJob::dispatch($promocion, $alumno) ->delay(now()->addSeconds($i * 2)); $despachados++; } if ($despachados === 0) { return back()->with('swal', [ 'icon' => 'warning', 'title' => 'Sin destinatarios', 'text' => 'No se encontraron prospectos activos para los planteles de esta promoción.', ]); } return back()->with('swal', [ 'icon' => 'success', 'title' => '¡Enviado!', 'text' => "Se programó el envío para {$despachados} prospecto(s).", ]); } private function plantelesDelUsuario() { return Auth::user()->plantelUsuarios; } private function autorizarPlantel(Promocion $promocion): void { $ids = $this->plantelesDelUsuario()->pluck('id'); $tieneAcceso = $promocion->planteles->pluck('id')->intersect($ids)->isNotEmpty(); if (!$tieneAcceso) { abort(403); } } private function validarAccesoPlanteles(array $plantelIds): void { $permitidos = $this->plantelesDelUsuario()->pluck('id'); foreach ($plantelIds as $id) { if (!$permitidos->contains($id)) { abort(403, 'No tienes acceso a uno de los planteles seleccionados.'); } } } }