| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- <?php
- namespace App\Services;
- use App\Enums\ReceivableStatus;
- use Illuminate\Support\Carbon;
- use Illuminate\Support\Facades\DB;
- /**
- * Agrega os indicadores da tela inicial (Dashboard) da visão do Franqueador.
- *
- * Premissas de cálculo:
- * - "Matrículas por Período" = quantidade de alunos cadastrados por mês (created_at),
- * nos últimos 6 meses.
- * - "Aniversariantes do Mês" = alunos cujo mês de nascimento é o mês corrente.
- * - "Tickets Abertos" = chamados de suporte com status 'in_progress'.
- * - "Tarefas Pendentes" = cartões do Kanban que ainda não foram concluídos.
- * - "Receita Geral" = total cobrado das franquias (franchisee_account_receives),
- * pago + em aberto; a legenda traz a quantidade de pagamentos pendentes.
- * - "Estoque Geral de Produtos" = soma das quantidades em estoque dos produtos.
- *
- * Todas as métricas por unidade respeitam o filtro `unitIds` (vazio = rede inteira);
- * o estoque de produtos é do catálogo do franqueador (não filtra por unidade).
- */
- class FranchisorDashboardService
- {
- private const SETTLED = [
- ReceivableStatus::PAID->value,
- ReceivableStatus::CANCELLED->value,
- ];
- public function overview(array $unitIds = []): array
- {
- return [
- 'enrollments' => $this->enrollments($unitIds),
- 'birthdays' => $this->birthdays($unitIds),
- 'open_tickets' => $this->openTickets($unitIds),
- 'pending_tasks' => $this->pendingTasks($unitIds),
- 'general_revenue' => $this->generalRevenue($unitIds),
- 'product_stock' => $this->productStock(),
- ];
- }
- /**
- * Matrículas (alunos cadastrados) por mês nos últimos 6 meses.
- */
- private function enrollments(array $unitIds): array
- {
- $start = Carbon::now()->startOfMonth()->subMonthsNoOverflow(5);
- $counts = DB::table('students')
- ->whereNull('deleted_at')
- ->where('created_at', '>=', $start)
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
- ->selectRaw("DATE_FORMAT(created_at, '%Y-%m') AS ym, COUNT(*) AS total")
- ->groupBy('ym')
- ->pluck('total', 'ym');
- $labels = [];
- $data = [];
- $months = ['JAN', 'FEV', 'MAR', 'ABR', 'MAI', 'JUN', 'JUL', 'AGO', 'SET', 'OUT', 'NOV', 'DEZ'];
- for ($i = 0; $i < 6; $i++) {
- $month = $start->copy()->addMonthsNoOverflow($i);
- $labels[] = $months[$month->month - 1];
- $data[] = (int) ($counts[$month->format('Y-m')] ?? 0);
- }
- return ['labels' => $labels, 'data' => $data];
- }
- /**
- * Aniversariantes do mês corrente (dia + nome).
- */
- private function birthdays(array $unitIds): array
- {
- return DB::table('students')
- ->whereNull('deleted_at')
- ->whereMonth('birth_date', Carbon::now()->month)
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
- ->orderByRaw('DAYOFMONTH(birth_date)')
- ->get(['name', 'birth_date'])
- ->map(fn ($s) => [
- 'day' => (int) Carbon::parse($s->birth_date)->day,
- 'name' => $s->name,
- ])
- ->all();
- }
- private function openTickets(array $unitIds): int
- {
- return (int) DB::table('support_tickets')
- ->whereNull('deleted_at')
- ->where('status', 'in_progress')
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
- ->count();
- }
- private function pendingTasks(array $unitIds): int
- {
- return (int) DB::table('kanbans')
- ->whereNull('deleted_at')
- ->where('phase', '!=', 'concluido')
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
- ->count();
- }
- /**
- * Receita Geral = total das cobranças às franquias (pago + em aberto),
- * com a contagem das que ainda estão pendentes.
- */
- private function generalRevenue(array $unitIds): array
- {
- $base = DB::table('franchisee_account_receives')
- ->whereNull('deleted_at')
- ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds));
- $value = (float) (clone $base)->sum('value');
- $pending = (int) (clone $base)
- ->whereNotIn('status', self::SETTLED)
- ->count();
- return ['value' => $value, 'pending_count' => $pending];
- }
- private function productStock(): int
- {
- return (int) DB::table('products')
- ->whereNull('deleted_at')
- ->sum('quantity');
- }
- }
|