# Notification de fin de contrat (veille du dernier jour) ## Objectif Prévenir les administrateurs, sur le dernier jour ouvré précédant la fin d'un contrat, qu'un salarié arrive au terme de son emploi. ## Déclenchement Commande `app:contract:end-notifications`, lancée chaque jour par le crontab de production (ex. `0 6 * * *`). Option `--date=YYYY-MM-DD` pour test/rattrapage. Logger `cron`. ## Règle métier - **Cible** : la **dernière** période de contrat d'un employé (aucune période ne lui succède). Un changement de contrat enchaîné (ex. CDD → CDI) ne notifie pas. - **Quand** : sur le **dernier jour ouvré strictement avant** `endDate` (`endDate` est inclusif = dernier jour travaillé). Les week-ends ET jours fériés (`PublicHolidayService`, zone `metropole`) sont sautés. Concrètement, le jour J ouvré couvre les fins de contrat dans l'intervalle `]J ; prochain_jour_ouvré(J)]` — un vendredi notifie ainsi les fins du samedi, dimanche et lundi (mardi si lundi férié). - **Destinataires** : tous les `ROLE_ADMIN`. - **Message** : `Fin de {CDI|CDD|Intérim} de {Prénom Nom} le {dd/mm/yyyy}`, catégorie `Contrat`, cible `/employees/{id}`, sans acteur. ## Idempotence Avant création, on vérifie l'absence d'une notif identique `(recipient, category='Contrat', target, message)`. Le message étant unique par (employé + date + nature), relancer la commande le même jour ne crée aucun doublon. ## Implémentation - `App\Service\Notification\WorkingDayCalculator` — jour ouvré / prochain jour ouvré. - `App\Service\Notification\ContractEndNotificationPlanner` — sélection + message (pur, testé). - `App\Service\Notification\ContractEndNotificationService` — persistance (1 notif/admin). - `App\Command\ContractEndNotificationCommand` — `app:contract:end-notifications`. - `EmployeeContractPeriodRepository::findLatestPeriodsForAllEmployees`, `NotificationRepository::existsForRecipientCategoryTargetMessage`. - Pas de migration : réutilise la table `notifications`.