with(['assignee', 'type', 'status', 'priority']) ->when($filters['date_from'] ?? null, fn (Builder $q, $v) => $q->whereDate('created_at', '>=', $v)) ->when($filters['date_to'] ?? null, fn (Builder $q, $v) => $q->whereDate('created_at', '<=', $v)) ->when($filters['technician_id'] ?? null, fn (Builder $q, $v) => $q->where('assigned_to', $v)) ->when($filters['type_id'] ?? null, fn (Builder $q, $v) => $q->where('type_id', $v)) ->when($filters['status_id'] ?? null, fn (Builder $q, $v) => $q->where('status_id', $v)); } public function requestsByStatus(array $filters = []): Collection { return $this->requestBaseQuery($filters) ->selectRaw('status_id, COUNT(*) as total') ->groupBy('status_id') ->get() ->map(function ($item) { $status = Status::query()->find($item->status_id); return [ 'status' => $status?->name ?? 'Неизвестно', 'color' => $status?->color_hex ?? '#2563eb', 'total' => (int) $item->total, ]; }); } public function requestsByPriority(array $filters = []): Collection { return $this->requestBaseQuery($filters) ->selectRaw('priority_id, COUNT(*) as total') ->groupBy('priority_id') ->get() ->map(fn ($item) => [ 'priority' => Priority::query()->find($item->priority_id)?->name, 'total' => (int) $item->total, ]); } public function technicianPerformance(array $filters = []): Collection { $technicians = User::query()->where('role', 'technician')->get(); return $technicians->map(function (User $tech) use ($filters) { $query = $this->requestBaseQuery($filters)->where('assigned_to', $tech->id); $assigned = (clone $query)->count(); $resolved = (clone $query)->whereHas('status', fn (Builder $q) => $q->whereIn('slug', ['resolved', 'closed']))->count(); $overdue = (clone $query)->overdue()->count(); $avgResolution = (clone $query) ->whereNotNull('completed_at') ->selectRaw('AVG(TIMESTAMPDIFF(HOUR, created_at, completed_at)) as avg_hours') ->value('avg_hours'); $rating = (clone $query)->whereNotNull('client_rating')->avg('client_rating'); return [ 'technician' => $tech->name, 'assigned' => $assigned, 'resolved' => $resolved, 'avg_resolution_hours' => $avgResolution ? round((float) $avgResolution, 1) : 0, 'rating' => $rating ? round((float) $rating, 2) : 0, 'overdue' => $overdue, ]; }); } public function slaCompliance(array $filters = []): Collection { $rows = $this->requestBaseQuery($filters)->with('type')->get()->groupBy('type_id'); return $rows->map(function (Collection $requests, $typeId) { $type = RequestType::query()->find($typeId); $total = $requests->count(); $withinSla = $requests ->filter(fn (ServiceRequest $request) => $request->completed_at && $request->deadline_at && $request->completed_at->lte($request->deadline_at)) ->count(); return [ 'type' => $type?->name ?? 'Неизвестно', 'total' => $total, 'within_sla' => $withinSla, 'percent' => $total > 0 ? round(($withinSla / $total) * 100, 2) : 0, ]; })->values(); } public function equipmentFailureFrequency(array $filters = []): Collection { return $this->requestBaseQuery($filters) ->whereNotNull('equipment_id') ->with('equipment') ->get() ->groupBy('equipment_id') ->map(fn (Collection $requests, $equipmentId) => [ 'equipment' => $requests->first()?->equipment?->name ?? 'Неизвестно', 'total_requests' => $requests->count(), 'open' => $requests->filter(fn (ServiceRequest $r) => !$r->status?->is_final)->count(), 'closed' => $requests->filter(fn (ServiceRequest $r) => $r->status?->is_final)->count(), ]) ->values(); } }