222 lines
7.6 KiB
PHP
222 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Modules\Maintenance\Services;
|
|
|
|
use App\Business;
|
|
use App\Modules\Maintenance\Models\Equipment;
|
|
use App\Modules\Maintenance\Models\FailureReport;
|
|
use App\Modules\Maintenance\Models\Overhaul;
|
|
use App\Modules\Maintenance\Models\PreventiveSchedule;
|
|
use App\Modules\Maintenance\Models\SparePart;
|
|
use App\Modules\Maintenance\Models\WorkOrder;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class MaintenanceDashboardService
|
|
{
|
|
public function getStats(Business $business): array
|
|
{
|
|
$bid = $business->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
|
|
{
|
|
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(),
|
|
'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(),
|
|
'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(),
|
|
'status' => $w->status,
|
|
'equipment' => $w->equipment?->name,
|
|
];
|
|
}
|
|
|
|
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' => $month->format('Y-m'),
|
|
'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
|
|
{
|
|
$avg = FailureReport::where('business_id', $businessId)
|
|
->where('repair_hours', '>', 0)
|
|
->avg('repair_hours');
|
|
|
|
return round((float) $avg, 2);
|
|
}
|
|
|
|
protected function calculateMtbf(int $businessId): float
|
|
{
|
|
$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);
|
|
}
|
|
}
|