File manager - Edit - /var/www/html/portal/app/Http/Services/IndicadoresTi/IndicadoresTiRelatorioService.php
Back
<?php namespace App\Http\Services\IndicadoresTi; use App\Models\IndicadoresTi\Assunto; use App\Models\IndicadoresTi\Chamado; use App\Models\IndicadoresTi\Subassunto; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; class IndicadoresTiRelatorioService { public function queryBase(array $filters): Builder { $user = auth()->user(); $permissoes = $user->permissoes->pluck('permissao'); $query = Chamado::with(['cliente', 'setor', 'assunto', 'subassunto', 'solicitante', 'atendente']) ->when(!empty($filters['data_inicio']), fn ($q) => $q->whereDate('data_abertura', '>=', $filters['data_inicio'])) ->when(!empty($filters['data_fim']), fn ($q) => $q->whereDate('data_abertura', '<=', $filters['data_fim'])) ->when(!empty($filters['setor_id']), fn ($q) => $q->where('setor_id', $filters['setor_id'])) ->when(!empty($filters['assunto_id']), fn ($q) => $q->where('assunto_id', $filters['assunto_id'])) ->when(!empty($filters['subassunto_id']), fn ($q) => $q->where('subassunto_id', $filters['subassunto_id'])) ->when(!empty($filters['status']), fn ($q) => $q->where('status', $filters['status'])) ->when(!empty($filters['prioridade']), fn ($q) => $q->where('prioridade', $filters['prioridade'])) ->when(!empty($filters['tipo']), fn ($q) => $q->where('tipo', $filters['tipo'])) ->when(!empty($filters['canal']), fn ($q) => $q->where('canal', $filters['canal'])) ->orderBy('data_abertura'); if (!$permissoes->contains('DIRETOR_TI')) { if ($permissoes->contains('INDICADORES_TI')) { $query->where('atendente_id', $user->id); } else { $query->whereRaw('1 = 0'); } } return $query; } public function calcularResumo(array $filters): array { $chamados = $this->queryBase($filters)->get(); $total = $chamados->count(); $dataInicio = !empty($filters['data_inicio']) ? \Carbon\Carbon::parse($filters['data_inicio']) : null; $dataFim = !empty($filters['data_fim']) ? \Carbon\Carbon::parse($filters['data_fim']) : null; // Monthly: only months within the selected range if ($dataInicio && $dataFim) { $mesLabels = []; $mesData = []; $cur = $dataInicio->copy()->startOfMonth(); while ($cur->lte($dataFim->copy()->endOfMonth())) { $ym = $cur->format('Y-m'); $mesLabels[] = $cur->translatedFormat('M/y'); $mesData[] = $chamados->filter(fn($c) => \Carbon\Carbon::parse($c->data_abertura)->format('Y-m') === $ym)->count(); $cur->addMonth(); } } else { $mesLabels = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez']; $porMes = collect(array_fill(1, 12, 0)) ->merge($chamados->groupBy(fn($c) => (int) \Carbon\Carbon::parse($c->data_abertura)->format('n'))->map->count()) ->map(fn($v) => (int) $v); $mesData = $porMes->values()->toArray(); } // Weekly: natural grouping within the filtered range $porSemana = $chamados ->groupBy(fn($c) => 'Sem ' . \Carbon\Carbon::parse($c->data_abertura)->weekOfYear) ->map->count() ->sortKeys(); // Days in range — used by JS to decide which charts are available $dias = ($dataInicio && $dataFim) ? (int) $dataInicio->diffInDays($dataFim) : null; return [ 'total' => $total, 'por_status' => $chamados->groupBy('status')->map->count(), 'por_prioridade' => $chamados->groupBy('prioridade')->map->count(), 'por_setor' => $chamados->groupBy(fn ($c) => $c->setor->nome ?? '—')->map->count(), 'tempo_medio' => $total ? round($chamados->avg('tempo_atendimento')) : 0, 'atendidos' => $chamados->where('status', 'atendido')->count(), 'por_mes_labels' => $mesLabels, 'por_mes_data' => $mesData, 'por_semana' => $porSemana, 'dias_range' => $dias, ]; } public function montarDadosRelatorio(array $filters, string $tipo): array { $chamados = $this->queryBase($filters)->get(); $total = $chamados->count(); $porSetor = $chamados->groupBy(fn ($c) => $c->setor->nome ?? '—') ->map(fn ($grupo, $setor) => [ 'nome' => $setor, 'total' => $grupo->count(), 'atendidos' => $grupo->where('status', 'atendido')->count(), 'pendentes' => $grupo->where('status', 'pendente')->count(), 'em_andamento' => $grupo->where('status', 'em_andamento')->count(), 'tempo_medio_min' => $grupo->avg('tempo_atendimento') ?? 0, ])->values(); $porAssunto = $chamados->groupBy(fn ($c) => $c->assunto->nome ?? '—') ->map(fn ($grupo, $assunto) => [ 'nome' => $assunto, 'setor' => optional($grupo->first()->setor)->nome ?? '—', 'total' => $grupo->count(), 'atendidos' => $grupo->where('status', 'atendido')->count(), 'tempo_medio_min' => $grupo->avg('tempo_atendimento') ?? 0, ])->values(); $porSubassunto = $chamados->groupBy(fn ($c) => ($c->assunto->nome ?? '—') . ' › ' . ($c->subassunto->nome ?? '—')) ->map(fn ($grupo, $chave) => [ 'chave' => $chave, 'total' => $grupo->count(), 'atendidos' => $grupo->where('status', 'atendido')->count(), 'tempo_medio_min' => $grupo->avg('tempo_atendimento') ?? 0, ])->values(); $porStatus = $chamados->groupBy('status')->map->count(); $porPrioridade = $chamados->groupBy('prioridade')->map->count(); $porTipo = $chamados->groupBy('tipo')->map->count(); $porCanal = $chamados->groupBy('canal')->map->count(); $tempoMedioMin = $total ? round($chamados->avg('tempo_atendimento')) : 0; $tempoMedioFormatado = $this->formatarMinutos((int) $tempoMedioMin); return compact( 'chamados', 'total', 'filters', 'porSetor', 'porAssunto', 'porSubassunto', 'porStatus', 'porPrioridade', 'porTipo', 'porCanal', 'tempoMedioMin', 'tempoMedioFormatado' ); } public function buildTotalMensal(Collection $chamados): array { $mensal = array_fill(1, 12, 0); foreach ($chamados as $c) { $mensal[Carbon::parse($c->data_abertura)->month]++; } return ['mensal' => $mensal, 'total' => $chamados->count()]; } public function buildMatrizSetoresSimples(Collection $chamados): array { $agrupado = $chamados->groupBy(fn ($c) => $c->setor->nome ?? 'Sem Setor'); $resultado = []; foreach ($agrupado as $setorNome => $grupo) { $mensal = array_fill(1, 12, 0); foreach ($grupo as $c) { $mensal[Carbon::parse($c->data_abertura)->month]++; } $resultado[] = ['setor' => $setorNome, 'mensal' => $mensal, 'total' => $grupo->count()]; } usort($resultado, fn ($a, $b) => strcmp($a['setor'], $b['setor'])); return $resultado; } public function gerarCsvResumido($handle, array $filters): void { $dados = $this->montarDadosRelatorio($filters, 'resumido'); fputcsv($handle, ['Status', 'Quantidade'], ';'); foreach ($dados['porStatus'] as $status => $qtd) { fputcsv($handle, [__(Chamado::STATUS[$status] ?? $status), $qtd], ';'); } fputcsv($handle, ['Prioridade', 'Quantidade'], ';'); foreach ($dados['porPrioridade'] as $prio => $qtd) { fputcsv($handle, [__(Chamado::PRIORIDADE[$prio] ?? $prio), $qtd], ';'); } fputcsv($handle, ['Setor', 'Total', 'Atendidos', 'Pendentes', 'Em Andamento'], ';'); foreach ($dados['porSetor'] as $row) { fputcsv($handle, [ $row['nome'], $row['total'], $row['atendidos'], $row['pendentes'], $row['em_andamento'], ], ';'); } fputcsv($handle, ['Assunto', 'Setor', 'Total', 'Atendidos'], ';'); foreach ($dados['porAssunto'] as $row) { fputcsv($handle, [ $row['nome'], $row['setor'], $row['total'], $row['atendidos'], ], ';'); } } public function gerarCsvDetalhado($handle, array $filters): void { $chamados = $this->queryBase($filters)->get(); fputcsv($handle, [ 'Número', 'Status', 'Prioridade', 'Tipo', 'Canal', 'Setor', 'Assunto', 'Subassunto', 'Solicitante', 'Atendente', 'Abertura', 'Fechamento', 'Tempo Atendimento', 'Descrição', ], ';'); foreach ($chamados as $c) { fputcsv($handle, [ $c->numero, __(Chamado::STATUS[$c->status] ?? $c->status), __(Chamado::PRIORIDADE[$c->prioridade] ?? $c->prioridade), __(Chamado::TIPO[$c->tipo] ?? $c->tipo), __(Chamado::CANAL[$c->canal] ?? $c->canal), $c->setor_nome ?? optional($c->setor)->nome ?? '—', $c->assunto_nome ?? optional($c->assunto)->nome ?? '—', $c->subassunto_nome ?? optional($c->subassunto)->nome ?? '—', $c->solicitante_nome, optional($c->atendente)->name ?? '—', optional($c->data_abertura)->format('d/m/Y H:i') ?? '—', optional($c->data_fechamento)->format('d/m/Y H:i') ?? '—', $c->getTempoAtendimentoFormatado(), str_replace(["\n", "\r"], ' ', $c->descricao ?? ''), ], ';'); } } public function determinarAno(array $filters): int { if (!empty($filters['data_inicio'])) { return Carbon::parse($filters['data_inicio'])->year; } if (!empty($filters['data_fim'])) { return Carbon::parse($filters['data_fim'])->year; } return now()->year; } public function formatarMinutos(int $minutos): string { if ($minutos <= 0) return '—'; $d = floor($minutos / 1440); $h = floor(($minutos % 1440) / 60); $m = $minutos % 60; $partes = []; if ($d > 0) $partes[] = $d . 'd'; if ($h > 0) $partes[] = $h . 'h'; if ($m > 0 || empty($partes)) $partes[] = $m . 'min'; return implode(' ', $partes); } public function assuntosPorSetor(int $setorId): \Illuminate\Database\Eloquent\Collection { return Assunto::where('setor_id', $setorId)->orderBy('nome')->get(['id', 'nome']); } public function subassuntosPorAssunto(int $assuntoId): \Illuminate\Database\Eloquent\Collection { return Subassunto::where('assunto_id', $assuntoId)->orderBy('nome')->get(['id', 'nome']); } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings