generateReport(); } public function updatedSelectedBranch() { $this->generateReport(); } public function updatedDateRange() { $this->generateReport(); } public function generateReport() { $startDate = Carbon::now()->subDays($this->dateRange); $endDate = Carbon::now(); $this->reportData = [ 'revenue_by_branch' => $this->getRevenueByBranch($startDate, $endDate), 'labor_utilization' => $this->getLaborUtilization($startDate, $endDate), 'parts_usage' => $this->getPartsUsage($startDate, $endDate), 'customer_approval_trends' => $this->getCustomerApprovalTrends($startDate, $endDate), 'turnaround_times' => $this->getTurnaroundTimes($startDate, $endDate), 'workflow_bottlenecks' => $this->getWorkflowBottlenecks($startDate, $endDate), 'quality_metrics' => $this->getQualityMetrics($startDate, $endDate), ]; } private function getRevenueByBranch($startDate, $endDate): array { $query = JobCard::with('estimates') ->whereBetween('completion_datetime', [$startDate, $endDate]) ->where('status', 'delivered'); if ($this->selectedBranch) { $query->where('branch_code', $this->selectedBranch); } return $query->get() ->groupBy('branch_code') ->map(function ($jobs, $branchCode) { $totalRevenue = $jobs->sum(function ($job) { return $job->estimates->where('status', 'approved')->sum('total_amount'); }); return [ 'branch' => $branchCode, 'jobs_completed' => $jobs->count(), 'total_revenue' => $totalRevenue, 'average_job_value' => $jobs->count() > 0 ? $totalRevenue / $jobs->count() : 0, ]; }) ->values() ->toArray(); } private function getLaborUtilization($startDate, $endDate): array { $query = Timesheet::with('technician') ->whereBetween('date', [$startDate, $endDate]); if ($this->selectedBranch) { $query->whereHas('technician', function ($q) { $q->where('branch_code', $this->selectedBranch); }); } $timesheets = $query->get(); return $timesheets->groupBy('technician.name') ->map(function ($entries, $technicianName) { $totalHours = $entries->sum('hours_worked'); $billableHours = $entries->sum('billable_hours'); return [ 'technician' => $technicianName, 'total_hours' => $totalHours, 'billable_hours' => $billableHours, 'utilization_rate' => $totalHours > 0 ? ($billableHours / $totalHours) * 100 : 0, ]; }) ->values() ->toArray(); } private function getPartsUsage($startDate, $endDate): array { return DB::table('estimate_line_items') ->join('estimates', 'estimate_line_items.estimate_id', '=', 'estimates.id') ->join('job_cards', 'estimates.job_card_id', '=', 'job_cards.id') ->join('parts', 'estimate_line_items.part_id', '=', 'parts.id') ->whereBetween('job_cards.completion_datetime', [$startDate, $endDate]) ->where('estimates.status', 'approved') ->where('estimate_line_items.type', 'part') ->when($this->selectedBranch, function ($query) { return $query->where('job_cards.branch_code', $this->selectedBranch); }) ->select( 'parts.part_number', 'parts.name', DB::raw('SUM(estimate_line_items.quantity) as total_used'), DB::raw('SUM(estimate_line_items.total_price) as total_value'), 'parts.current_stock' ) ->groupBy('parts.id', 'parts.part_number', 'parts.name', 'parts.current_stock') ->orderBy('total_used', 'desc') ->limit(20) ->get() ->toArray(); } private function getCustomerApprovalTrends($startDate, $endDate): array { $estimates = DB::table('estimates') ->join('job_cards', 'estimates.job_card_id', '=', 'job_cards.id') ->whereBetween('estimates.created_at', [$startDate, $endDate]) ->when($this->selectedBranch, function ($query) { return $query->where('job_cards.branch_code', $this->selectedBranch); }) ->select('estimates.status', DB::raw('COUNT(*) as count')) ->groupBy('estimates.status') ->get(); $total = $estimates->sum('count'); return [ 'total_estimates' => $total, 'approval_rate' => $total > 0 ? ($estimates->where('status', 'approved')->first()?->count ?? 0) / $total * 100 : 0, 'rejection_rate' => $total > 0 ? ($estimates->where('status', 'rejected')->first()?->count ?? 0) / $total * 100 : 0, 'pending_rate' => $total > 0 ? ($estimates->where('status', 'sent')->first()?->count ?? 0) / $total * 100 : 0, ]; } private function getTurnaroundTimes($startDate, $endDate): array { $jobs = JobCard::whereBetween('completion_datetime', [$startDate, $endDate]) ->where('status', 'delivered') ->when($this->selectedBranch, function ($query) { return $query->where('branch_code', $this->selectedBranch); }) ->get(); $turnaroundTimes = $jobs->map(function ($job) { return $job->completion_datetime->diffInHours($job->arrival_datetime); }); return [ 'average_turnaround' => $turnaroundTimes->avg(), 'median_turnaround' => $turnaroundTimes->median(), 'min_turnaround' => $turnaroundTimes->min(), 'max_turnaround' => $turnaroundTimes->max(), 'total_jobs' => $jobs->count(), ]; } private function getWorkflowBottlenecks($startDate, $endDate): array { $statusCounts = JobCard::whereBetween('created_at', [$startDate, $endDate]) ->when($this->selectedBranch, function ($query) { return $query->where('branch_code', $this->selectedBranch); }) ->select('status', DB::raw('COUNT(*) as count')) ->groupBy('status') ->get() ->pluck('count', 'status') ->toArray(); // Calculate average time in each status $avgTimeInStatus = []; foreach (JobCard::getStatusOptions() as $status => $label) { $avgTimeInStatus[$status] = $this->getAverageTimeInStatus($status, $startDate, $endDate); } return [ 'status_counts' => $statusCounts, 'average_time_in_status' => $avgTimeInStatus, ]; } private function getAverageTimeInStatus($status, $startDate, $endDate): float { // This would require status change tracking - simplified for now return JobCard::where('status', $status) ->whereBetween('updated_at', [$startDate, $endDate]) ->when($this->selectedBranch, function ($query) { return $query->where('branch_code', $this->selectedBranch); }) ->avg(DB::raw('TIMESTAMPDIFF(HOUR, created_at, updated_at)')) ?? 0; } private function getQualityMetrics($startDate, $endDate): array { $inspections = DB::table('vehicle_inspections') ->join('job_cards', 'vehicle_inspections.job_card_id', '=', 'job_cards.id') ->whereBetween('vehicle_inspections.inspection_date', [$startDate, $endDate]) ->when($this->selectedBranch, function ($query) { return $query->where('job_cards.branch_code', $this->selectedBranch); }) ->get(); $totalInspections = $inspections->count(); $discrepancyCount = $inspections->where('follow_up_required', true)->count(); return [ 'total_inspections' => $totalInspections, 'discrepancy_rate' => $totalInspections > 0 ? ($discrepancyCount / $totalInspections) * 100 : 0, 'quality_score' => $totalInspections > 0 ? (($totalInspections - $discrepancyCount) / $totalInspections) * 100 : 100, ]; } public function render() { $branches = Branch::active()->get(); return view('livewire.reports.workflow-analytics', [ 'branches' => $branches, 'reportData' => $this->reportData, ]); } }