timelineRepository->logActivity([ 'client_id' => $client->id, 'actor_type' => 'user', 'actor_user_id' => auth()->id(), 'event_type' => 'client_created', 'entity_type' => 'client', 'entity_id' => $client->id, 'title' => 'Nouveau client créé', 'description' => "Le client {$client->name} a été ajouté au système.", 'created_at' => now(), ]); } catch (\Exception $e) { LaravelLog::error("Failed to log client creation activity: " . $e->getMessage()); } return $client; } /** * Get paginated clients */ public function paginate(int $perPage = 15, array $filters = []): LengthAwarePaginator { $query = $this->model->newQuery(); // Apply filters if (!empty($filters['search'])) { $query->where(function ($q) use ($filters) { $q->where('name', 'like', '%' . $filters['search'] . '%') ->orWhere('email', 'like', '%' . $filters['search'] . '%') ->orWhere('vat_number', 'like', '%' . $filters['search'] . '%') ->orWhere('siret', 'like', '%' . $filters['search'] . '%'); }); } if (isset($filters['is_active'])) { $query->where('is_active', $filters['is_active']); } if (!empty($filters['group_id'])) { $query->where('group_id', $filters['group_id']); } if (!empty($filters['client_category_id'])) { $query->where('client_category_id', $filters['client_category_id']); } // Apply sorting $sortField = $filters['sort_by'] ?? 'created_at'; $sortDirection = $filters['sort_direction'] ?? 'desc'; $query->orderBy($sortField, $sortDirection); return $query->paginate($perPage); } public function searchByName(string $name, int $perPage = 15, bool $exactMatch = false) { $query = $this->model->newQuery(); if ($exactMatch) { $query->where('name', $name); } else { $query->where('name', 'like', '%' . $name . '%'); } return $query->get(); } /** * Retrieve aggregated statistics about clients. */ public function getStatistics(): array { // 1. Clients actifs vs inactifs $statusCounts = $this->model ->selectRaw('is_active, COUNT(*) as total') ->groupBy('is_active') ->pluck('total', 'is_active'); $activeCount = (int) ($statusCounts[1] ?? 0); $inactiveCount = (int) ($statusCounts[0] ?? 0); $totalCount = $activeCount + $inactiveCount; // 2. Taux de rétention : clients ayant plus d'une intervention (dossier récurrent) $clientsWithMultipleDossiers = DB::table('interventions') ->select('client_id') ->whereNotNull('client_id') ->groupBy('client_id') ->havingRaw('COUNT(*) > 1') ->get() ->count(); $retentionRate = $totalCount > 0 ? round(($clientsWithMultipleDossiers / $totalCount) * 100, 2) : 0.0; // 3. Délai moyen (jours) entre création du client et premier dossier (première intervention) $avgDelayDays = DB::table('clients') ->joinSub( DB::table('interventions') ->selectRaw('client_id, MIN(created_at) as first_intervention_at') ->whereNotNull('client_id') ->groupBy('client_id'), 'first_interventions', 'clients.id', '=', 'first_interventions.client_id' ) ->selectRaw('AVG(DATEDIFF(first_interventions.first_intervention_at, clients.created_at)) as avg_days') ->value('avg_days'); // 4. Nombre de dossiers (interventions) par client — top 10 grands comptes $dossiersPerClientTop10 = DB::table('interventions') ->join('clients', 'interventions.client_id', '=', 'clients.id') ->select( 'clients.id', 'clients.name', DB::raw('COUNT(interventions.id) as total_dossiers') ) ->whereNotNull('interventions.client_id') ->groupBy('clients.id', 'clients.name') ->orderByDesc('total_dossiers') ->limit(10) ->get(); // 5. Répartition géographique des clients $geographicDistribution = $this->model ->selectRaw('billing_country_code, billing_city, COUNT(*) as total') ->whereNotNull('billing_country_code') ->groupBy('billing_country_code', 'billing_city') ->orderByDesc('total') ->get(); // 6. Groupes les plus représentés $groupDistribution = DB::table('clients') ->join('client_groups', 'clients.group_id', '=', 'client_groups.id') ->select('client_groups.id', 'client_groups.name', DB::raw('COUNT(clients.id) as total')) ->groupBy('client_groups.id', 'client_groups.name') ->orderByDesc('total') ->get(); // Catégories les plus représentées $categoryDistribution = DB::table('clients') ->join('client_categories', 'clients.client_category_id', '=', 'client_categories.id') ->select('client_categories.id', 'client_categories.name', DB::raw('COUNT(clients.id) as total')) ->groupBy('client_categories.id', 'client_categories.name') ->orderByDesc('total') ->get(); return [ 'active_vs_inactive' => [ 'active' => $activeCount, 'inactive' => $inactiveCount, 'total' => $totalCount, ], 'retention' => [ 'clients_with_recurring_dossiers' => $clientsWithMultipleDossiers, 'retention_rate_percentage' => $retentionRate, ], 'avg_delay_first_contact_to_first_dossier_days' => $avgDelayDays !== null ? round((float) $avgDelayDays, 1) : null, 'dossiers_per_client_top10' => $dossiersPerClientTop10, 'geographic_distribution' => $geographicDistribution, 'group_distribution' => $groupDistribution, 'category_distribution' => $categoryDistribution, ]; } }