From 75c3438ea9ef529f6110360029712f25755eb349 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 24 Jun 2026 15:25:09 +0200 Subject: [PATCH] feat : add contract end notification planner --- .../ContractEndNotificationPlanner.php | 70 +++++++++++ .../ContractEndNotificationPlannerTest.php | 119 ++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 src/Service/Notification/ContractEndNotificationPlanner.php create mode 100644 tests/Service/Notification/ContractEndNotificationPlannerTest.php diff --git a/src/Service/Notification/ContractEndNotificationPlanner.php b/src/Service/Notification/ContractEndNotificationPlanner.php new file mode 100644 index 0000000..39a237c --- /dev/null +++ b/src/Service/Notification/ContractEndNotificationPlanner.php @@ -0,0 +1,70 @@ +setTime(0, 0, 0); + if (!$this->calculator->isWorkingDay($today)) { + return []; + } + + $upperBound = $this->calculator->nextWorkingDay($today); + + $notices = []; + foreach ($latestPeriods as $period) { + $endDate = $period->getEndDate(); + if (null === $endDate) { + continue; + } + + $endDate = $endDate->setTime(0, 0, 0); + if ($endDate <= $today || $endDate > $upperBound) { + continue; + } + + $employee = $period->getEmployee(); + if (null === $employee) { + continue; + } + + $message = sprintf( + 'Fin de %s de %s %s le %s', + $this->natureLabel($period->getContractNatureEnum()), + $employee->getFirstName(), + $employee->getLastName(), + $endDate->format('d/m/Y'), + ); + + $notices[] = new ContractEndNotice($employee->getId(), $message); + } + + return $notices; + } + + private function natureLabel(ContractNature $nature): string + { + return match ($nature) { + ContractNature::CDI => 'CDI', + ContractNature::CDD => 'CDD', + ContractNature::INTERIM => 'Intérim', + }; + } +} diff --git a/tests/Service/Notification/ContractEndNotificationPlannerTest.php b/tests/Service/Notification/ContractEndNotificationPlannerTest.php new file mode 100644 index 0000000..250266c --- /dev/null +++ b/tests/Service/Notification/ContractEndNotificationPlannerTest.php @@ -0,0 +1,119 @@ + fin mercredi 09/07 + $notices = $this->planner()->plan( + [$this->period('Jean', 'Dupont', '2025-07-09')], + new DateTimeImmutable('2025-07-08'), + ); + + self::assertCount(1, $notices); + self::assertSame('Fin de CDD de Jean Dupont le 09/07/2025', $notices[0]->message); + } + + public function testFridayNotifiesContractsEndingOverTheWeekendAndMonday(): void + { + // Vendredi 11/07 ; lundi 14/07 férié -> prochain ouvré = mardi 15/07. + // Fenêtre ]11/07 ; 15/07] -> samedi 12, dimanche 13, lundi 14, mardi 15. + $notices = $this->planner()->plan( + [ + $this->period('A', 'Sat', '2025-07-12'), // samedi -> inclus + $this->period('B', 'Mon', '2025-07-14'), // lundi férié -> inclus + $this->period('C', 'Tue', '2025-07-15'), // mardi (= borne haute) -> inclus + $this->period('D', 'Wed', '2025-07-16'), // mercredi -> hors fenêtre + ], + new DateTimeImmutable('2025-07-11'), + ); + + self::assertCount(3, $notices); + } + + public function testIgnoresOpenEndedContract(): void + { + $notices = $this->planner()->plan( + [$this->period('Jean', 'Dupont', null, ContractNature::CDI)], + new DateTimeImmutable('2025-07-08'), + ); + + self::assertSame([], $notices); + } + + public function testIgnoresContractEndingToday(): void + { + // fin = today -> trop tard, pas de notif (on notifie la veille) + $notices = $this->planner()->plan( + [$this->period('Jean', 'Dupont', '2025-07-08')], + new DateTimeImmutable('2025-07-08'), + ); + + self::assertSame([], $notices); + } + + public function testReturnsNothingWhenTodayIsNotAWorkingDay(): void + { + // Samedi 12/07 -> aucun jour chômé ne génère de notif + $notices = $this->planner()->plan( + [$this->period('Jean', 'Dupont', '2025-07-14')], + new DateTimeImmutable('2025-07-12'), + ); + + self::assertSame([], $notices); + } + + public function testInterimNatureLabel(): void + { + $notices = $this->planner()->plan( + [$this->period('Marie', 'Martin', '2025-07-09', ContractNature::INTERIM)], + new DateTimeImmutable('2025-07-08'), + ); + + self::assertSame('Fin de Intérim de Marie Martin le 09/07/2025', $notices[0]->message); + } + + private function planner(): ContractEndNotificationPlanner + { + $holidays = $this->createStub(PublicHolidayServiceInterface::class); + $holidays->method('getHolidaysDayByYears')->willReturn([ + '2025-07-14' => 'Fête nationale', // lundi 14/07 férié + ]); + + return new ContractEndNotificationPlanner(new WorkingDayCalculator($holidays)); + } + + private function period( + string $firstName, + string $lastName, + ?string $endDate, + ContractNature $nature = ContractNature::CDD, + ): EmployeeContractPeriod { + $employee = new Employee(); + $employee->setFirstName($firstName)->setLastName($lastName); + + $period = new EmployeeContractPeriod(); + $period->setEmployee($employee) + ->setContractNature($nature) + ->setEndDate(null === $endDate ? null : new DateTimeImmutable($endDate)) + ; + + return $period; + } +}