last_performed_at ?? now(); return match ($schedule->frequency_type) { 'daily' => $base->copy()->addDays($schedule->frequency_value), 'weekly' => $base->copy()->addWeeks($schedule->frequency_value), 'monthly' => $base->copy()->addMonths($schedule->frequency_value), 'quarterly' => $base->copy()->addMonths(3 * $schedule->frequency_value), 'semi_annual' => $base->copy()->addMonths(6 * $schedule->frequency_value), 'annual' => $base->copy()->addYears($schedule->frequency_value), default => null, }; } public function updateNextDue(PreventiveSchedule $schedule): void { $next = $this->calculateNextDue($schedule); if ($next) { $schedule->update(['next_due_at' => $next]); } } public function generateEquipmentQrCode(Business $business, Equipment $equipment): string { $code = $equipment->qr_code ?: 'EQ-'.$business->id.'-'.$equipment->id.'-'.Str::upper(Str::random(6)); if (! $equipment->qr_code) { $equipment->update(['qr_code' => $code]); } return $code; } public function checkDueReminders(Business $business): int { $count = 0; $settings = $this->maintenanceUtil->getMaintenanceSettings($business->id); if (! $this->maintenanceUtil->pmRemindersEnabled($business->id)) { return 0; } $schedules = PreventiveSchedule::where('business_id', $business->id) ->where('is_active', true) ->whereNotNull('next_due_at') ->whereNotNull('assigned_user_id') ->with('assignedUser', 'equipment') ->get(); foreach ($schedules as $schedule) { $daysUntil = now()->startOfDay()->diffInDays($schedule->next_due_at->copy()->startOfDay(), false); if ($daysUntil > $schedule->reminder_days_before || ! $schedule->assignedUser) { continue; } if ($this->reminderSentToday($business->id, $schedule->assigned_user_id, $schedule->id)) { continue; } $dueLabel = $daysUntil < 0 ? abs($daysUntil).' روز گذشته' : ($daysUntil === 0 ? 'امروز' : $daysUntil.' روز دیگر'); MaintenanceNotificationService::notify( $business, $schedule->assignedUser, 'preventive_due', 'یادآوری نگهداری پیشگیرانه', "نگهداری «{$schedule->title}» برای تجهیز «{$schedule->equipment?->name}» — سررسید: {$dueLabel}.", ['schedule_id' => $schedule->id, 'equipment_id' => $schedule->equipment_id] ); $count++; } return $count; } public function processBusinessSchedules(Business $business): array { return [ 'reminders' => $this->checkDueReminders($business), 'work_orders' => $this->createDueWorkOrders($business), ]; } public function createDueWorkOrders(Business $business): int { if (! $this->maintenanceUtil->pmAutoWorkOrderEnabled($business->id)) { return 0; } $count = 0; $schedules = PreventiveSchedule::where('business_id', $business->id) ->where('is_active', true) ->whereNotNull('next_due_at') ->where('next_due_at', '<=', now()) ->with(['equipment', 'assignedUser']) ->get(); foreach ($schedules as $schedule) { if ($this->createWorkOrderFromSchedule($business, $schedule)) { $count++; } } return $count; } public function createWorkOrderFromSchedule(Business $business, PreventiveSchedule $schedule): ?WorkOrder { $openExists = WorkOrder::where('business_id', $business->id) ->where('preventive_schedule_id', $schedule->id) ->whereIn('status', ['draft', 'open', 'in_progress', 'on_hold']) ->exists(); if ($openExists || ! $schedule->equipment_id) { return null; } $equipment = $schedule->equipment ?? Equipment::find($schedule->equipment_id); if (! $equipment) { return null; } $workOrder = WorkOrder::create([ 'business_id' => $business->id, 'equipment_id' => $equipment->id, 'project_id' => $schedule->project_id ?? $equipment->project_id, 'production_line_id' => $schedule->production_line_id ?? $equipment->production_line_id, 'preventive_schedule_id' => $schedule->id, 'work_order_number' => $this->workOrderService->generateNumber($business), 'assigned_technician_id' => $schedule->assigned_user_id, 'priority' => 'medium', 'description' => 'ایجاد خودکار از برنامه نگهداری پیشگیرانه: '.$schedule->title, 'status' => 'open', 'scheduled_at' => $schedule->next_due_at ?? now(), ]); if ($schedule->assignedUser) { MaintenanceNotificationService::notify( $business, $schedule->assignedUser, 'pm_work_order_created', 'دستور کار پیشگیرانه', "دستور کار «{$workOrder->work_order_number}» برای «{$schedule->title}» ایجاد شد.", ['work_order_id' => $workOrder->id, 'schedule_id' => $schedule->id] ); } return $workOrder; } protected function reminderSentToday(int $businessId, int $userId, int $scheduleId): bool { return MaintenanceNotification::where('business_id', $businessId) ->where('user_id', $userId) ->where('type', 'preventive_due') ->whereDate('created_at', today()) ->where('data->schedule_id', $scheduleId) ->exists(); } }