118 lines
4.8 KiB
PHP
118 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Priority;
|
|
use App\Models\RequestType;
|
|
use App\Models\ServiceRequest;
|
|
use App\Models\Status;
|
|
use App\Models\User;
|
|
use App\Support\DatabaseExpression;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class ReportService
|
|
{
|
|
public function requestBaseQuery(array $filters = []): Builder
|
|
{
|
|
return ServiceRequest::query()
|
|
->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('.DatabaseExpression::hoursBetween('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();
|
|
}
|
|
}
|