feat : add contract end notification service and command
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Service\Notification\ContractEndNotificationService;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
use function is_string;
|
||||||
|
|
||||||
|
#[AsCommand(
|
||||||
|
name: 'app:contract:end-notifications',
|
||||||
|
description: 'Notify admins on the last working day before a contract ends.'
|
||||||
|
)]
|
||||||
|
final class ContractEndNotificationCommand extends Command
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ContractEndNotificationService $service,
|
||||||
|
#[Autowire(service: 'monolog.logger.cron')]
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->addOption(
|
||||||
|
'date',
|
||||||
|
null,
|
||||||
|
InputOption::VALUE_REQUIRED,
|
||||||
|
'Override the reference day (YYYY-MM-DD) for testing or manual catch-up.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
|
||||||
|
$dateOption = $input->getOption('date');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$today = is_string($dateOption) && '' !== $dateOption
|
||||||
|
? new DateTimeImmutable($dateOption)
|
||||||
|
: new DateTimeImmutable('today');
|
||||||
|
} catch (Throwable $exception) {
|
||||||
|
$io->error(sprintf('Invalid --date value: %s', $exception->getMessage()));
|
||||||
|
|
||||||
|
return Command::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->service->run($today);
|
||||||
|
|
||||||
|
$this->logger->info('Contract end notifications generated.', [
|
||||||
|
'date' => $today->format('Y-m-d'),
|
||||||
|
'contractsMatched' => $result->contractsMatched,
|
||||||
|
'notificationsCreated' => $result->notificationsCreated,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$io->success(sprintf(
|
||||||
|
'%d notification(s) créée(s) pour %d fin(s) de contrat (%s).',
|
||||||
|
$result->notificationsCreated,
|
||||||
|
$result->contractsMatched,
|
||||||
|
$today->format('Y-m-d'),
|
||||||
|
));
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Notification;
|
||||||
|
|
||||||
|
final readonly class ContractEndNotificationResult
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public int $notificationsCreated,
|
||||||
|
public int $contractsMatched,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Notification;
|
||||||
|
|
||||||
|
use App\Entity\Notification;
|
||||||
|
use App\Repository\EmployeeContractPeriodRepository;
|
||||||
|
use App\Repository\NotificationRepository;
|
||||||
|
use App\Repository\UserRepository;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
use function count;
|
||||||
|
|
||||||
|
final readonly class ContractEndNotificationService
|
||||||
|
{
|
||||||
|
private const CATEGORY = 'Contrat';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EmployeeContractPeriodRepository $periodRepository,
|
||||||
|
private NotificationRepository $notificationRepository,
|
||||||
|
private UserRepository $userRepository,
|
||||||
|
private ContractEndNotificationPlanner $planner,
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function run(DateTimeImmutable $today): ContractEndNotificationResult
|
||||||
|
{
|
||||||
|
$latestPeriods = $this->periodRepository->findLatestPeriodsForAllEmployees();
|
||||||
|
$notices = $this->planner->plan($latestPeriods, $today);
|
||||||
|
|
||||||
|
if ([] === $notices) {
|
||||||
|
return new ContractEndNotificationResult(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$admins = $this->userRepository->findAllAdmins();
|
||||||
|
$created = 0;
|
||||||
|
|
||||||
|
foreach ($notices as $notice) {
|
||||||
|
if (null === $notice->employeeId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = '/employees/'.$notice->employeeId;
|
||||||
|
|
||||||
|
foreach ($admins as $admin) {
|
||||||
|
if ($this->notificationRepository->existsForRecipientCategoryTargetMessage(
|
||||||
|
$admin,
|
||||||
|
self::CATEGORY,
|
||||||
|
$target,
|
||||||
|
$notice->message,
|
||||||
|
)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notification = new Notification();
|
||||||
|
$notification->setRecipient($admin)
|
||||||
|
->setMessage($notice->message)
|
||||||
|
->setCategory(self::CATEGORY)
|
||||||
|
->setTarget($target)
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager->persist($notification);
|
||||||
|
++$created;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
return new ContractEndNotificationResult($created, count($notices));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user