feat : notification de fin de contrat (veille ouvrée du dernier jour) #35

Merged
tristan merged 10 commits from feature/SIRH-43-ajouter-une-notif-la-veille-d-un-contrat-qui-se-te into develop 2026-06-24 14:04:54 +00:00
4 changed files with 47 additions and 0 deletions
Showing only changes of commit 5e2c6c219b - Show all commits
+4
View File
@@ -212,6 +212,10 @@
- **Écran Journal refondu** (`frontend/pages/audit-logs.vue` + `useAuditLogsList`) : tableau en `MalioDataTable` (1er usage SIRH), **drawer de filtre** façon STARSEED (`MalioDrawer` + `MalioAccordion`, état brouillon/appliqué, badge compteur, Réinitialiser/Appliquer), **drawer de détail** au clic ligne. Filtres backend : `employee` (LIKE nom/prénom de l'employé affecté, via join), `username`/`ip`/`device` (LIKE insensible casse), `entityType[]`/`action[]` (IN), `perPage` (10/25/50/100, défaut 10). Filtres du drawer = champs texte (recherche libre), période en `MalioDateRange`, type/action en cases à cocher. Logique dans `useAuditLogsList` ; libellés FR en dur ; filtres hors URL. Provider/`AuditLogReadRepositoryInterface`/repository portent les nouveaux critères.
- Documentation: `doc/audit-logging.md`
## 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`.
## Backend Conventions
- Prefer explicit DTOs over associative arrays
- Business rules in backend (providers/processors/services), frontend is display/interaction only
+35
View File
@@ -0,0 +1,35 @@
# 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`.
+7
View File
@@ -486,6 +486,13 @@ Seuls les employés dont au moins une période de contrat intersecte la période
- condition: plus aucune ligne `work_hours` du site à la date concernée avec `isSiteValid = false`
- destinataires: utilisateurs `ROLE_ADMIN`
- **Fin de contrat (J-1 ouvré)** : une commande quotidienne (`app:contract:end-notifications`)
notifie tous les admins, sur le dernier jour ouvré précédant la fin d'un contrat, qu'un
salarié arrive au terme de son emploi. Cible = **dernière** période de l'employé (un
changement de contrat enchaîné ne notifie pas). Week-ends et fériés sautés. Message
« Fin de {nature} de {Nom} le {date} », catégorie `Contrat`, lien vers la fiche employé,
sans acteur. Idempotente. Détail : `doc/contract-end-notifications.md`.
## 16) Export PDF des heures annuelles
- Accessible depuis la fiche employé (bouton imprimante à droite du nom)
+1
View File
@@ -268,6 +268,7 @@ export const documentationSections: DocSection[] = [
{ type: 'paragraph', content: 'Deux tâches automatiques s\'exécutent quotidiennement pour gérer le report des compteurs.' },
{ type: 'list', content: 'Report congés (02h10) : déclenche le report des congés payés le 1er juin (CDI/CDD) et le 1er janvier (forfait)\nReport RTT (02h15) : déclenche le report du solde RTT le 1er juin' },
{ type: 'note', content: 'Ces tâches sont idempotentes : si elles s\'exécutent plusieurs fois, aucun doublon n\'est créé.' },
{ type: 'paragraph', content: 'Notification fin de contrat : chaque jour ouvré, les administrateurs sont prévenus (cloche en haut à droite) lorsqu\'un salarié atteint le dernier jour ouvré avant la fin de son contrat. Le message indique la nature du contrat, le nom du salarié et la date de fin, et renvoie vers sa fiche. Les week-ends et jours fériés sont pris en compte : une fin de contrat le lundi est signalée dès le vendredi.' },
],
},
],