Quellcode durchsuchen

feat: enhance overview method to support date filtering and update service for revenue calculations

ebagabee vor 1 Woche
Ursprung
Commit
1282bcb7f1

+ 15 - 2
app/Http/Controllers/FranchisorDashboardController.php

@@ -4,6 +4,7 @@
 
 use App\Services\FranchisorDashboardService;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Carbon;
 
 class FranchisorDashboardController extends Controller
 {
@@ -13,7 +14,19 @@ public function __construct(
 
     public function overview(): JsonResponse
     {
-        $unitIds = array_filter((array) request()->input('unit_ids', []));
-        return $this->successResponse(payload: $this->service->overview($unitIds));
+        $data = request()->validate([
+            'unit_ids'   => ['sometimes', 'array'],
+            'unit_ids.*' => ['integer', 'exists:units,id'],
+            'start_date' => ['nullable', 'date'],
+            'end_date'   => ['nullable', 'date', 'after_or_equal:start_date'],
+        ]);
+
+        $unitIds   = array_values(array_filter((array) ($data['unit_ids'] ?? [])));
+        $startDate = isset($data['start_date']) ? Carbon::parse($data['start_date'])->startOfDay() : null;
+        $endDate   = isset($data['end_date']) ? Carbon::parse($data['end_date'])->endOfDay() : null;
+
+        return $this->successResponse(
+            payload: $this->service->overview($unitIds, $startDate, $endDate),
+        );
     }
 }

+ 48 - 1
app/Services/FranchisorDashboardService.php

@@ -29,8 +29,15 @@ class FranchisorDashboardService
         ReceivableStatus::CANCELLED->value,
     ];
 
-    public function overview(array $unitIds = []): array
+    public function __construct(
+        private readonly TbrCalculationService $tbrCalculationService,
+    ) {}
+
+    public function overview(array $unitIds = [], ?Carbon $startDate = null, ?Carbon $endDate = null): array
     {
+        $startDate ??= Carbon::now()->startOfMonth();
+        $endDate   ??= Carbon::now()->endOfMonth();
+
         return [
             'enrollments'     => $this->enrollments($unitIds),
             'birthdays'       => $this->birthdays($unitIds),
@@ -38,6 +45,46 @@ public function overview(array $unitIds = []): array
             'pending_tasks'   => $this->pendingTasks($unitIds),
             'general_revenue' => $this->generalRevenue($unitIds),
             'product_stock'   => $this->productStock(),
+            'units_summary'   => $this->unitsSummary($unitIds, $startDate, $endDate),
+        ];
+    }
+
+    /**
+     * Receita usa a mesma base do cálculo do TBR. O status é derivado da
+     * existência de contrato de franquia vigente na data atual.
+     */
+    private function unitsSummary(array $unitIds, Carbon $startDate, Carbon $endDate): array
+    {
+        $units = DB::table('units')
+            ->whereNull('deleted_at')
+            ->when(!empty($unitIds), fn ($q) => $q->whereIn('id', $unitIds));
+
+        $filteredUnitIds = (clone $units)->pluck('id')->map(fn ($id) => (int) $id)->all();
+        $total           = count($filteredUnitIds);
+        $today           = Carbon::today()->toDateString();
+
+        $activeContracts = $total === 0
+            ? 0
+            : (int) DB::table('franchisee_contracts')
+                ->whereNull('deleted_at')
+                ->whereIn('unit_id', $filteredUnitIds)
+                ->whereNotNull('start_date')
+                ->where('start_date', '<=', $today)
+                ->where(function ($q) use ($today) {
+                    $q->whereNull('end_date')->orWhere('end_date', '>=', $today);
+                })
+                ->distinct()
+                ->count('unit_id');
+
+        $revenue = $total === 0
+            ? 0.0
+            : $this->tbrCalculationService->resolveRevenueBetween($filteredUnitIds, $startDate, $endDate);
+
+        return [
+            'total'                   => $total,
+            'revenue'                 => round($revenue, 2),
+            'active_contracts'        => $activeContracts,
+            'without_active_contract' => max(0, $total - $activeContracts),
         ];
     }
 

+ 20 - 6
app/Services/TbrCalculationService.php

@@ -43,21 +43,35 @@ public function resolveRevenue(int $unitId, int $referenceYear, int $referenceMo
             $base = $base->subMonthNoOverflow();
         }
 
-        $startDate = $base->copy()->startOfMonth()->toDateString();
-        $endDate   = $base->copy()->endOfMonth()->toDateString();
+        return $this->resolveRevenueBetween(
+            [$unitId],
+            $base->copy()->startOfMonth(),
+            $base->copy()->endOfMonth(),
+        );
+    }
+
+    /**
+     * Faturamento-base do TBR em um intervalo. Mantém dashboard e geração do TBR
+     * usando a mesma composição: parcelas de alunos + recebíveis avulsos.
+     * Um array vazio de unidades representa toda a rede.
+     */
+    public function resolveRevenueBetween(array $unitIds, Carbon $startDate, Carbon $endDate): float
+    {
+        $start = $startDate->toDateString();
+        $end   = $endDate->toDateString();
 
         // Base de faturamento = parcelas de aluno + recebíveis avulsos (manuais)
         // do mês de referência (decisão da franqueadora: avulsos compõem o royalty).
         $installments = (float) StudentContractInstallment::whereNull('deleted_at')
-            ->where('unit_id', $unitId)
+            ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
             ->where('status', '!=', 'cancelled')
-            ->whereBetween('due_date', [$startDate, $endDate])
+            ->whereBetween('due_date', [$start, $end])
             ->sum('value');
 
         $manual = (float) UnitAccountReceivable::whereNull('deleted_at')
-            ->where('unit_id', $unitId)
+            ->when(!empty($unitIds), fn ($q) => $q->whereIn('unit_id', $unitIds))
             ->where('status', '!=', 'cancelled')
-            ->whereBetween('due_date', [$startDate, $endDate])
+            ->whereBetween('due_date', [$start, $end])
             ->sum('value');
 
         return $installments + $manual;