id; $today = Carbon::today(); $monthStart = Carbon::now()->startOfMonth(); $totalEquipment = Equipment::where('business_id', $bid)->count(); $activeEquipment = Equipment::where('business_id', $bid)->where('status', 'active')->count(); $underOverhaul = Equipment::where('business_id', $bid)->where('status', 'under_overhaul')->count(); $upcomingOverhauls = Overhaul::where('business_id', $bid) ->whereIn('status', ['planned', 'approved', 'waiting']) ->where('start_date', '>=', $today) ->where('start_date', '<=', $today->copy()->addDays(30)) ->count(); $delayedOverhauls = Overhaul::where('business_id', $bid) ->whereIn('status', ['planned', 'approved', 'waiting', 'in_progress']) ->where('end_date', '<', $today) ->count(); $todayTasks = WorkOrder::where('business_id', $bid) ->whereIn('status', ['open', 'in_progress']) ->whereDate('scheduled_at', $today) ->count(); $monthlyCosts = (int) WorkOrder::where('business_id', $bid) ->where('status', 'completed') ->where('completed_at', '>=', $monthStart) ->sum('cost') + (int) Overhaul::where('business_id', $bid) ->where('status', 'completed') ->where('updated_at', '>=', $monthStart) ->sum('actual_cost'); $avgHealth = (float) Equipment::where('business_id', $bid)->avg('health_percentage'); $mttr = $this->calculateMttr($bid); $mtbf = $this->calculateMtbf($bid); $maintenanceStats = [ 'preventive_due' => PreventiveSchedule::where('business_id', $bid) ->where('is_active', true) ->where('next_due_at', '<=', now()->addDays(7)) ->count(), 'open_work_orders' => WorkOrder::where('business_id', $bid) ->whereIn('status', ['open', 'in_progress']) ->count(), 'completed_this_month' => WorkOrder::where('business_id', $bid) ->where('status', 'completed') ->where('completed_at', '>=', $monthStart) ->count(), 'low_stock_parts' => SparePart::where('business_id', $bid) ->whereColumn('quantity', '<=', 'min_stock') ->count(), ]; return [ 'equipment' => [ 'total' => $totalEquipment, 'active' => $activeEquipment, 'under_overhaul' => $underOverhaul, 'health_percentage' => round($avgHealth, 1), ], 'overhauls' => [ 'upcoming' => $upcomingOverhauls, 'delayed' => $delayedOverhauls, ], 'tasks' => [ 'today' => $todayTasks, ], 'costs' => [ 'monthly' => $monthlyCosts, ], 'kpi' => [ 'mttr_hours' => $mttr, 'mtbf_hours' => $mtbf, ], 'maintenance' => $maintenanceStats, ]; } public function getRecentActivities(Business $business, int $limit = 10): array { if (! Schema::hasTable('maintenance_activity_logs')) { return []; } return DB::table('maintenance_activity_logs') ->where('business_id', $business->id) ->orderByDesc('created_at') ->limit($limit) ->get() ->toArray(); } public function getCalendarEvents(Business $business, string $start, string $end): array { $bid = $business->id; $events = []; $overhauls = Overhaul::where('business_id', $bid) ->whereBetween('start_date', [$start, $end]) ->with('equipment:id,name,code') ->get(); foreach ($overhauls as $o) { $events[] = [ 'id' => 'overhaul-'.$o->id, 'type' => 'overhaul', 'title' => $o->title, 'start' => $o->start_date?->toDateString(), 'end' => $o->end_date?->toDateString(), 'start_jalali' => $this->maintenanceUtil->formatDate($o->start_date), 'end_jalali' => $this->maintenanceUtil->formatDate($o->end_date), 'status' => $o->status, 'equipment' => $o->equipment?->name, ]; } $schedules = PreventiveSchedule::where('business_id', $bid) ->where('is_active', true) ->whereBetween('next_due_at', [$start, $end]) ->with('equipment:id,name,code') ->get(); foreach ($schedules as $s) { $events[] = [ 'id' => 'pm-'.$s->id, 'type' => 'preventive', 'title' => $s->title, 'start' => $s->next_due_at?->toDateString(), 'start_jalali' => $this->maintenanceUtil->formatDateTime($s->next_due_at), 'equipment' => $s->equipment?->name, ]; } $workOrders = WorkOrder::where('business_id', $bid) ->whereBetween('scheduled_at', [$start, $end]) ->with('equipment:id,name,code') ->get(); foreach ($workOrders as $w) { $events[] = [ 'id' => 'wo-'.$w->id, 'type' => 'work_order', 'title' => $w->work_order_number, 'start' => $w->scheduled_at?->toDateString(), 'start_jalali' => $this->maintenanceUtil->formatDateTime($w->scheduled_at), 'status' => $w->status, 'equipment' => $w->equipment?->name, ]; } $rebuilds = RebuildProject::where('business_id', $bid) ->whereNotIn('status', ['draft', 'cancelled']) ->where(function ($q) use ($start, $end) { $q->whereBetween('planned_start_date', [$start, $end]) ->orWhereBetween('planned_end_date', [$start, $end]); }) ->get(); foreach ($rebuilds as $r) { $events[] = [ 'id' => 'rebuild-'.$r->id, 'type' => 'rebuild', 'title' => $r->title, 'start' => $r->planned_start_date?->toDateString(), 'end' => $r->planned_end_date?->toDateString(), 'start_jalali' => $this->maintenanceUtil->formatDate($r->planned_start_date), 'end_jalali' => $this->maintenanceUtil->formatDate($r->planned_end_date), 'status' => $r->status, ]; } return $events; } public function getChartData(Business $business): array { $bid = $business->id; $months = collect(range(5, 0))->map(fn ($i) => Carbon::now()->subMonths($i)); $costByMonth = $months->map(function ($month) use ($bid) { $start = $month->copy()->startOfMonth(); $end = $month->copy()->endOfMonth(); return [ 'label' => $this->maintenanceUtil->formatMonthLabel($month), 'cost' => (int) WorkOrder::where('business_id', $bid) ->where('status', 'completed') ->whereBetween('completed_at', [$start, $end]) ->sum('cost'), ]; }); $statusDistribution = Equipment::where('business_id', $bid) ->select('status', DB::raw('count(*) as count')) ->groupBy('status') ->pluck('count', 'status') ->toArray(); return [ 'monthly_costs' => $costByMonth->values()->toArray(), 'equipment_by_status' => $statusDistribution, ]; } protected function calculateMttr(int $businessId): float { if (! Schema::hasTable('maintenance_failure_reports')) { return 0; } $avg = FailureReport::where('business_id', $businessId) ->where('repair_hours', '>', 0) ->avg('repair_hours'); return round((float) $avg, 2); } protected function calculateMtbf(int $businessId): float { if (! Schema::hasTable('maintenance_failure_reports')) { return 0; } $equipmentIds = Equipment::where('business_id', $businessId)->pluck('id'); if ($equipmentIds->isEmpty()) { return 0; } $failureCount = FailureReport::where('business_id', $businessId)->count(); if ($failureCount === 0) { return 0; } $totalHours = Equipment::where('business_id', $businessId)->sum('working_hours'); return round($totalHours / $failureCount, 2); } }