From d201489bcbf5764958f9bec8546fc57f0142a619 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 24 Jun 2026 15:48:04 +0200 Subject: [PATCH] =?UTF-8?q?docs=20:=20note=20Lundi=20de=20Pentec=C3=B4te?= =?UTF-8?q?=20trait=C3=A9=20comme=20jour=20ouvr=C3=A9=20(choix=20d=C3=A9li?= =?UTF-8?q?b=C3=A9r=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 2 +- doc/contract-end-notifications.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index dad7bff..b035a86 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -214,7 +214,7 @@ ## Notifications - Système : entité `Notification` (table `notifications`, `recipient`/`actor`/`message`/`category`/`target`/`isRead`), cloche **admin-only** dans `AppTopNav.vue`, providers `/notifications/{unread,today,history}` + `POST /notifications/mark-all-read`. Création historique : `WorkHourSiteValidationProcessor` (1 notif/admin via `UserRepository::findAllAdmins`). -- **Fin de contrat (J-1 ouvré)** : commande cron quotidienne `app:contract:end-notifications` (crontab prod, ~6h ; option `--date`). Notifie les admins sur le **dernier jour ouvré avant** `endDate` (inclusif) de la **dernière** période d'un employé (changement de contrat enchaîné exclu). Week-ends + fériés sautés (`WorkingDayCalculator`). Fenêtre couverte un jour J = `]J ; prochain_jour_ouvré(J)]`. Message « Fin de {nature} de {Nom} le {date} », catégorie `Contrat`, target `/employees/{id}`, acteur null. Idempotent (`NotificationRepository::existsForRecipientCategoryTargetMessage`). Logique pure testée : `ContractEndNotificationPlanner` + `WorkingDayCalculator`. Front : `AppTopNav.vue` masque le span acteur si `actorName` vide. Doc : `doc/contract-end-notifications.md`. +- **Fin de contrat (J-1 ouvré)** : commande cron quotidienne `app:contract:end-notifications` (crontab prod, ~6h ; option `--date`). Notifie les admins sur le **dernier jour ouvré avant** `endDate` (inclusif) de la **dernière** période d'un employé (changement de contrat enchaîné exclu). Week-ends + fériés sautés (`WorkingDayCalculator`, via `getHolidaysDayByYears` → applique `EXCLUDED_PUBLIC_HOLIDAYS`, donc **Lundi de Pentecôte traité comme jour ouvré**, cohérent avec le reste de l'app). Fenêtre couverte un jour J = `]J ; prochain_jour_ouvré(J)]`. Message « Fin de {nature} de {Nom} le {date} », catégorie `Contrat`, target `/employees/{id}`, acteur null. Idempotent (`NotificationRepository::existsForRecipientCategoryTargetMessage`). Logique pure testée : `ContractEndNotificationPlanner` + `WorkingDayCalculator`. Front : `AppTopNav.vue` masque le span acteur si `actorName` vide. Doc : `doc/contract-end-notifications.md`. ## Backend Conventions - Prefer explicit DTOs over associative arrays diff --git a/doc/contract-end-notifications.md b/doc/contract-end-notifications.md index 1dfa67a..77752df 100644 --- a/doc/contract-end-notifications.md +++ b/doc/contract-end-notifications.md @@ -16,6 +16,13 @@ Commande `app:contract:end-notifications`, lancée chaque jour par le crontab de `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é). +- **Jour de solidarité (Lundi de Pentecôte)** : traité comme un **jour ouvré** (choix + délibéré). Le calcul s'appuie sur `getHolidaysDayByYears`, qui applique + `EXCLUDED_PUBLIC_HOLIDAYS` (défaut = `"Lundi de Pentecôte"`) — la même liste de fériés que + le reste de l'app (heures, congés, RTT). On évite ainsi une définition de « férié » + divergente pour ce seul calcul ; et le jour de solidarité est, par nature, un jour travaillé + (admins présents → la cloche est vue). Une fin de contrat le mardi après Pentecôte est donc + notifiée le Lundi de Pentecôte, pas le vendredi précédent. - **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.