ldc-boutique-cnc-sable/app/Notifications/MissingOrderReminderNotification.php
Jérémy Munsch 4ff3daecab
refactor(Auth): improve reset URL handling and validation
refactor(Helpers): update parameter types and remove ignores

refactor(Models): streamline order assignment logic

refactor(Enums): clarify unused constant warnings

refactor(Services): encapsulate cutoff week offset logic
2026-10-28 08:53:34 +01:00

133 lines
4.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Notifications;
use App\Contracts\Repositories\OrderRepository;
use App\Dto\IcsContentDto;
use App\Jobs\SendMissingOrderReminderJob;
use App\Models\Employee;
use App\Services\IcsGeneratorService;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Domain\Services\PickupSchedulingService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Config;
class MissingOrderReminderNotification extends Notification implements ShouldQueue
{
use Queueable;
private readonly string $appName;
private readonly string $appUrl;
private readonly string $appHost;
public function __construct(
private readonly CarbonInterface $pickupDay
) {
$appUrl = Config::string('app.url');
$appHost = parse_url($appUrl, \PHP_URL_HOST);
if (empty($appHost)) {
throw new \UnexpectedValueException('Invalid app URL configuration');
}
$this->appUrl = $appUrl;
$this->appHost = $appHost;
$this->appName = Config::string('app.name', 'La Boutique de Cocotte');
}
/**
* Determine if the notification should be sent.
*/
public function shouldSend(Employee $notifiable, string $channel): bool
{
/** @var OrderRepository $orderRepository */
$orderRepository = app(OrderRepository::class);
return ! $orderRepository->hasValidatedOrderForPickupDay(
$notifiable->matricule,
$this->pickupDay
);
}
/**
* @return array<int, string>
*/
public function via(mixed $notifiable): array
{
return ['mail'];
}
public function toMail(mixed $notifiable): MailMessage
{
/** @var Employee $notifiable */
$icsGenerator = app(IcsGeneratorService::class);
$schedulingService = app(PickupSchedulingService::class);
$cutoffDate = $schedulingService->getPeriodForPickupDate($this->pickupDay)->getStartDate();
$closureTime = $cutoffDate->format('G') . 'h';
$reminderDay = $cutoffDate->translatedFormat('l');
$reminderTime = $cutoffDate->copy()->subHours(2)->format('G') . 'h';
// The reminder event starts 2 hours before the cutoff on the cutoff day
$eventStart = CarbonImmutable::instance($cutoffDate)->subHours(2);
$eventEnd = $eventStart->addMinutes(15);
$icsContent = $this->generateIcsContent(
$icsGenerator,
$eventStart,
$eventEnd,
$closureTime
);
return (new MailMessage())
->subject(trans(':time before order taking closes - :appname', [
'time' => SendMissingOrderReminderJob::HOURS_BEFORE_CUTOFF . 'h',
'appname' => $this->appName,
]))
->markdown('emails.missing-order-reminder', [
'employee' => $notifiable,
'deliveryDate' => $this->pickupDay->translatedFormat('d/m/Y'),
'closureTime' => $closureTime,
'reminderDay' => $reminderDay,
'reminderTime' => $reminderTime,
'appHost' => $this->appHost,
'appUrl' => $this->appUrl,
])
->attachData($icsContent, 'rappel-commande.ics', [
'mime' => 'text/calendar',
]);
}
private function generateIcsContent(
IcsGeneratorService $icsGenerator,
CarbonInterface $eventStart,
CarbonInterface $eventEnd,
string $closureTime
): string {
$eventDto = new IcsContentDto(
uid: 'reminder-' . $eventStart->format('Y-m-d') . '@' . $this->appHost,
summary: trans('Place my order :appname', [
'appname' => $this->appName,
]),
description: trans('Remember to validate your order before :closureTime on :url', [
'closureTime' => $closureTime,
'url' => $this->appHost,
]),
startsAt: $eventStart,
endsAt: $eventEnd,
url: $this->appUrl
);
return $icsGenerator->generate($eventDto);
}
}