paginate(config('app.pagination')); return ApiResponse::OK->response([ 'data' => $packages, ]); } public function show(Packages $package) { return ApiResponse::OK->response([ 'data' => $package, ]); } public function store(PackagesStoreRequest $request) { $validated = $request->validated(); $package = Packages::create($validated); return ApiResponse::CREATED->response([ 'data' => $package, ]); } public function update(PackagesUpdateRequest $request, Packages $package) { $package->update($request->validated()); return ApiResponse::OK->response([ 'data' => $package, ]); } public function destroy(Packages $package) { $package->delete(); return ApiResponse::NO_CONTENT->response(); } /** * Obtiene los paquetes que están por vencer * */ public function expiring(Request $request) { // Parámetros de consulta $customDays = $request->query('days'); $period = $request->query('period'); $packageId = $request->query('package_id'); // NUEVO $withClientOnly = $request->boolean('with_client', false); // Obtener paquetes activos con sus relaciones $query = PackSim::with(['package', 'simCard.activeClient']) ->where('is_active', true) ->whereNotNull('activated_at'); // Filtrar por periodo si se especifica if ($period !== null) { $query->whereHas('package', function ($q) use ($period) { $q->where('period', $period); }); } // Filtrar por paquete específico si se especifica if ($packageId !== null) { $query->where('package_id', $packageId); } $activePackages = $query->get(); // Filtrar y calcular días restantes $expiringPackages = $activePackages ->map(function ($packSim) use ($customDays, $withClientOnly) { // Calcular fecha de vencimiento $activatedAt = Carbon::parse($packSim->activated_at); $expirationDate = $activatedAt->copy()->addDays($packSim->package->period); $now = Carbon::now()->startOfDay(); $expirationDateStart = $expirationDate->copy()->startOfDay(); if ($expirationDateStart->isFuture()) { $daysRemaining = (int) $now->diffInDays($expirationDateStart, false); } elseif ($expirationDateStart->isToday()) { $daysRemaining = 0; } else { // Ya expiró, no incluir return null; } // Determinar si debe incluirse $shouldInclude = $this->shouldIncludePackage( $packSim->package->period, $daysRemaining, $customDays ); if (!$shouldInclude) { return null; } // Obtener cliente $client = $packSim->simCard->activeClient()->first(); // Filtrar por cliente si se requiere if ($withClientOnly && !$client) { return null; } // Formatear respuesta return [ 'id' => $packSim->id, 'sim_card' => [ 'id' => $packSim->simCard->id, 'iccid' => $packSim->simCard->iccid, 'msisdn' => $packSim->simCard->msisdn, ], 'package' => [ 'id' => $packSim->package->id, 'name' => $packSim->package->name, 'period' => $packSim->package->period, ], 'client' => $client ? [ 'id' => $client->id, 'full_name' => $client->full_name, 'phone' => $client->phone, 'email' => $client->email, ] : null, 'activated_at' => $activatedAt->format('Y-m-d'), 'expires_at' => $expirationDate->format('Y-m-d'), 'days_remaining' => $daysRemaining, ]; }) ->filter() ->values(); return ApiResponse::OK->response([ 'packages' => $expiringPackages, ]); } /** * Determina si un paquete debe incluirse según el periodo y días restantes */ private function shouldIncludePackage(int $packagePeriod, int $daysRemaining, ?string $customDays = null): bool { // Si se especificaron días personalizados, usarlos if ($customDays !== null) { $notificationDays = array_map('intval', explode(',', $customDays)); return in_array($daysRemaining, $notificationDays); } // Lógica automática según el periodo del paquete switch ($packagePeriod) { case 30: // Paquetes de 30 días: incluir si quedan 7 días o menos return $daysRemaining <= 7 && $daysRemaining >= 0; case 15: // Paquetes de 15 días: incluir si quedan 3 días o menos return $daysRemaining <= 3 && $daysRemaining >= 0; case 7: // Paquetes de 7 días: incluir si quedan 2 días o menos return $daysRemaining <= 2 && $daysRemaining >= 0; default: // Para otros periodos, incluir si quedan 7 días o menos return $daysRemaining <= 7 && $daysRemaining >= 0; } } }