181 lines
6.6 KiB
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();
|
|
}
|
|
}
|