ultimatepos/Modules/Maintenance/Services/PreventiveScheduleService.php

181 lines
6.6 KiB
PHP

<?php
namespace Modules\Maintenance\Services;
use App\Business;
use Modules\Maintenance\Models\Equipment;
use Modules\Maintenance\Models\MaintenanceNotification;
use Modules\Maintenance\Models\PreventiveSchedule;
use Modules\Maintenance\Models\WorkOrder;
use Carbon\Carbon;
use Illuminate\Support\Str;
use Modules\Maintenance\Utils\MaintenanceUtil;
class PreventiveScheduleService
{
public function __construct(
protected WorkOrderService $workOrderService,
protected MaintenanceUtil $maintenanceUtil
) {}
public function calculateNextDue(PreventiveSchedule $schedule): ?Carbon
{
$base = $schedule->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();
}
}