371 lines
12 KiB
PHP
371 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Appointment;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Carbon\Carbon;
|
|
|
|
class AppointmentNotificationService
|
|
{
|
|
/**
|
|
* Send appointment confirmation notification
|
|
*/
|
|
public function sendConfirmationNotification(Appointment $appointment): bool
|
|
{
|
|
try {
|
|
// Load relationships if not already loaded
|
|
$appointment->load(['customer', 'vehicle', 'assignedTechnician']);
|
|
|
|
// Send email notification
|
|
$this->sendConfirmationEmail($appointment);
|
|
|
|
// Log successful notification
|
|
Log::info('Appointment confirmation notification sent', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'scheduled_datetime' => $appointment->scheduled_datetime,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send appointment confirmation notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send appointment reminder notification
|
|
*/
|
|
public function sendReminderNotification(Appointment $appointment): bool
|
|
{
|
|
try {
|
|
$appointment->load(['customer', 'vehicle', 'assignedTechnician']);
|
|
|
|
// Send email reminder
|
|
$this->sendReminderEmail($appointment);
|
|
|
|
Log::info('Appointment reminder notification sent', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'scheduled_datetime' => $appointment->scheduled_datetime,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send appointment reminder notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send appointment cancellation notification
|
|
*/
|
|
public function sendCancellationNotification(Appointment $appointment, string $reason = ''): bool
|
|
{
|
|
try {
|
|
$appointment->load(['customer', 'vehicle', 'assignedTechnician']);
|
|
|
|
// Send email notification
|
|
$this->sendCancellationEmail($appointment, $reason);
|
|
|
|
Log::info('Appointment cancellation notification sent', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'reason' => $reason,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send appointment cancellation notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send appointment reschedule notification
|
|
*/
|
|
public function sendRescheduleNotification(Appointment $appointment, Carbon $oldDateTime): bool
|
|
{
|
|
try {
|
|
$appointment->load(['customer', 'vehicle', 'assignedTechnician']);
|
|
|
|
// Send email notification
|
|
$this->sendRescheduleEmail($appointment, $oldDateTime);
|
|
|
|
Log::info('Appointment reschedule notification sent', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'old_datetime' => $oldDateTime,
|
|
'new_datetime' => $appointment->scheduled_datetime,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send appointment reschedule notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send completed service notification
|
|
*/
|
|
public function sendCompletionNotification(Appointment $appointment): bool
|
|
{
|
|
try {
|
|
$appointment->load(['customer', 'vehicle', 'assignedTechnician', 'serviceOrder']);
|
|
|
|
// Send email notification
|
|
$this->sendCompletionEmail($appointment);
|
|
|
|
Log::info('Service completion notification sent', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'completed_at' => $appointment->completed_at,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send service completion notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send daily appointment reminders
|
|
*/
|
|
public function sendDailyReminders(): int
|
|
{
|
|
$tomorrow = now()->addDay();
|
|
$remindersSent = 0;
|
|
|
|
// Get appointments scheduled for tomorrow that are confirmed
|
|
$appointments = Appointment::with(['customer', 'vehicle', 'assignedTechnician'])
|
|
->whereDate('scheduled_datetime', $tomorrow->toDateString())
|
|
->where('status', 'confirmed')
|
|
->get();
|
|
|
|
foreach ($appointments as $appointment) {
|
|
if ($this->sendReminderNotification($appointment)) {
|
|
$remindersSent++;
|
|
}
|
|
}
|
|
|
|
Log::info('Daily appointment reminders sent', [
|
|
'date' => $tomorrow->toDateString(),
|
|
'reminders_sent' => $remindersSent,
|
|
'total_appointments' => $appointments->count(),
|
|
]);
|
|
|
|
return $remindersSent;
|
|
}
|
|
|
|
/**
|
|
* Send overdue appointment notifications
|
|
*/
|
|
public function sendOverdueNotifications(): int
|
|
{
|
|
$notificationsSent = 0;
|
|
|
|
// Get appointments that are overdue (scheduled but past their time)
|
|
$overdueAppointments = Appointment::with(['customer', 'vehicle', 'assignedTechnician'])
|
|
->where('status', 'scheduled')
|
|
->where('scheduled_datetime', '<', now()->subMinutes(30)) // 30 minutes past
|
|
->get();
|
|
|
|
foreach ($overdueAppointments as $appointment) {
|
|
// Mark as no-show and send notification
|
|
if ($appointment->markNoShow()) {
|
|
$this->sendNoShowNotification($appointment);
|
|
$notificationsSent++;
|
|
}
|
|
}
|
|
|
|
Log::info('Overdue appointment notifications sent', [
|
|
'notifications_sent' => $notificationsSent,
|
|
'overdue_appointments' => $overdueAppointments->count(),
|
|
]);
|
|
|
|
return $notificationsSent;
|
|
}
|
|
|
|
/**
|
|
* Send no-show notification
|
|
*/
|
|
private function sendNoShowNotification(Appointment $appointment): bool
|
|
{
|
|
try {
|
|
// This would typically send an internal notification to staff
|
|
// and possibly a follow-up email to the customer
|
|
|
|
Log::info('Customer marked as no-show', [
|
|
'appointment_id' => $appointment->id,
|
|
'customer_id' => $appointment->customer_id,
|
|
'scheduled_datetime' => $appointment->scheduled_datetime,
|
|
]);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
Log::error('Failed to send no-show notification', [
|
|
'appointment_id' => $appointment->id,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send confirmation email
|
|
*/
|
|
private function sendConfirmationEmail(Appointment $appointment): void
|
|
{
|
|
$data = [
|
|
'appointment' => $appointment,
|
|
'customer' => $appointment->customer,
|
|
'vehicle' => $appointment->vehicle,
|
|
'technician' => $appointment->assignedTechnician,
|
|
'shopName' => config('app.name', 'Auto Repair Shop'),
|
|
];
|
|
|
|
// In a real application, you would create a Mailable class
|
|
// For now, we'll simulate the email sending
|
|
$this->simulateEmail($appointment->customer->email, 'Appointment Confirmation', $data);
|
|
}
|
|
|
|
/**
|
|
* Send reminder email
|
|
*/
|
|
private function sendReminderEmail(Appointment $appointment): void
|
|
{
|
|
$data = [
|
|
'appointment' => $appointment,
|
|
'customer' => $appointment->customer,
|
|
'vehicle' => $appointment->vehicle,
|
|
'technician' => $appointment->assignedTechnician,
|
|
'shopName' => config('app.name', 'Auto Repair Shop'),
|
|
];
|
|
|
|
$this->simulateEmail($appointment->customer->email, 'Appointment Reminder', $data);
|
|
}
|
|
|
|
/**
|
|
* Send cancellation email
|
|
*/
|
|
private function sendCancellationEmail(Appointment $appointment, string $reason): void
|
|
{
|
|
$data = [
|
|
'appointment' => $appointment,
|
|
'customer' => $appointment->customer,
|
|
'vehicle' => $appointment->vehicle,
|
|
'reason' => $reason,
|
|
'shopName' => config('app.name', 'Auto Repair Shop'),
|
|
];
|
|
|
|
$this->simulateEmail($appointment->customer->email, 'Appointment Cancelled', $data);
|
|
}
|
|
|
|
/**
|
|
* Send reschedule email
|
|
*/
|
|
private function sendRescheduleEmail(Appointment $appointment, Carbon $oldDateTime): void
|
|
{
|
|
$data = [
|
|
'appointment' => $appointment,
|
|
'customer' => $appointment->customer,
|
|
'vehicle' => $appointment->vehicle,
|
|
'technician' => $appointment->assignedTechnician,
|
|
'oldDateTime' => $oldDateTime,
|
|
'shopName' => config('app.name', 'Auto Repair Shop'),
|
|
];
|
|
|
|
$this->simulateEmail($appointment->customer->email, 'Appointment Rescheduled', $data);
|
|
}
|
|
|
|
/**
|
|
* Send completion email
|
|
*/
|
|
private function sendCompletionEmail(Appointment $appointment): void
|
|
{
|
|
$data = [
|
|
'appointment' => $appointment,
|
|
'customer' => $appointment->customer,
|
|
'vehicle' => $appointment->vehicle,
|
|
'technician' => $appointment->assignedTechnician,
|
|
'serviceOrder' => $appointment->serviceOrder,
|
|
'shopName' => config('app.name', 'Auto Repair Shop'),
|
|
];
|
|
|
|
$this->simulateEmail($appointment->customer->email, 'Service Completed', $data);
|
|
}
|
|
|
|
/**
|
|
* Simulate email sending (replace with actual Mail::send in production)
|
|
*/
|
|
private function simulateEmail(string $email, string $subject, array $data): void
|
|
{
|
|
// In production, replace this with:
|
|
// Mail::send('emails.appointment.template', $data, function ($message) use ($email, $subject) {
|
|
// $message->to($email)->subject($subject);
|
|
// });
|
|
|
|
Log::info('Email notification simulated', [
|
|
'to' => $email,
|
|
'subject' => $subject,
|
|
'appointment_id' => $data['appointment']->id ?? null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get notification preferences for a customer
|
|
*/
|
|
public function getCustomerNotificationPreferences($customerId): array
|
|
{
|
|
// In a real application, this would fetch from customer preferences
|
|
// For now, return default preferences
|
|
return [
|
|
'email_confirmations' => true,
|
|
'email_reminders' => true,
|
|
'email_cancellations' => true,
|
|
'sms_reminders' => false, // Could be implemented later
|
|
'reminder_hours_before' => 24,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check if notification should be sent based on customer preferences
|
|
*/
|
|
public function shouldSendNotification($customerId, string $notificationType): bool
|
|
{
|
|
$preferences = $this->getCustomerNotificationPreferences($customerId);
|
|
|
|
return match($notificationType) {
|
|
'confirmation' => $preferences['email_confirmations'] ?? true,
|
|
'reminder' => $preferences['email_reminders'] ?? true,
|
|
'cancellation' => $preferences['email_cancellations'] ?? true,
|
|
default => true,
|
|
};
|
|
}
|
|
}
|