From 9d2e70f81e36b150a30b9bee20f42f44188a9af9 Mon Sep 17 00:00:00 2001 From: tristan Date: Thu, 11 Jun 2026 10:45:33 +0200 Subject: [PATCH] feat(recap-conges) : cutoff S-1 au lieu de S-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le récap des congés se fige désormais à la fin de la semaine précédente (S-1) au lieu de l'avant-dernière (S-2), incluant ainsi une semaine de plus. Demande métier. - LeaveRecapCutoff : -14j -> -7j - Test unitaire figeant la règle S-1 - Doc fonctionnelle, doc in-app et CLAUDE.md mis à jour Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 2 +- doc/functional-rules.md | 6 ++-- doc/leave-recap-screen.md | 6 ++-- frontend/data/documentation-content.ts | 2 +- src/Util/LeaveRecapCutoff.php | 6 ++-- tests/Util/LeaveRecapCutoffTest.php | 50 ++++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 tests/Util/LeaveRecapCutoffTest.php diff --git a/CLAUDE.md b/CLAUDE.md index f53bf72..07f119d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,7 +126,7 @@ ## Récap. congés (écran) - Accès via sidebar `Récap. congés`, conditionné au flag `User.hasLeaveRecapAccess` (défaut `false`) — activé au create/edit user. Le flag s'applique à tous les profils, y compris admin. - Scope : `ROLE_ADMIN` → tous les employés, `ROLE_USER` (chef de site) → employés de ses sites, `ROLE_SELF` → sa ligne -- Cutoff temporel : fin de la semaine S-2 (dimanche 23:59:59). Formule : `dimanche(lundi_semaine_courante − 14j)`. Pas de gate `isValid`. +- Cutoff temporel : fin de la semaine S-1 (dimanche 23:59:59). Formule : `dimanche(lundi_semaine_courante − 7j)`. Pas de gate `isValid`. - Helper : `App\Util\LeaveRecapCutoff::resolveCutoff()` - Colonnes : Nom, Prénom, Contrat, CP N-1 restant, CP N, Samedis, RTT — identiques au PDF - Service partagé : `LeaveRecapRowBuilder` consommé par `LeaveRecapPrintProvider` (as-of today) et `EmployeeLeaveRecapProvider` (as-of cutoff) diff --git a/doc/functional-rules.md b/doc/functional-rules.md index 160f37b..c6c649e 100644 --- a/doc/functional-rules.md +++ b/doc/functional-rules.md @@ -374,9 +374,9 @@ Seuls les employés dont au moins une période de contrat intersecte la période - `ROLE_ADMIN` : tous les employés - `ROLE_USER` (chef de site) : employés des sites autorisés (`UserSiteRole`) - `ROLE_SELF` : uniquement son employé lié -- **Cutoff temporel** : le récap est figé à la fin de la semaine S-2 (dimanche 23:59:59) - - Formule : `cutoffDate = dimanche(lundi_semaine_courante − 14 jours)` - - Exemple : mardi 14/04/2026 (S16) → dimanche 05/04/2026 (fin S14) +- **Cutoff temporel** : le récap est figé à la fin de la semaine S-1 (dimanche 23:59:59) + - Formule : `cutoffDate = dimanche(lundi_semaine_courante − 7 jours)` + - Exemple : mardi 14/04/2026 (S16) → dimanche 12/04/2026 (fin S15) - `isValid` n'entre PAS en compte : cutoff purement temporel - Les heures et absences postérieures au cutoff sont ignorées dans les calculs - Colonnes identiques au PDF (voir §10) diff --git a/doc/leave-recap-screen.md b/doc/leave-recap-screen.md index 24ee7ab..b3ea3e3 100644 --- a/doc/leave-recap-screen.md +++ b/doc/leave-recap-screen.md @@ -2,14 +2,14 @@ ## Objet -Vue tableau des soldes de congés par employé, figée à un cutoff temporel (fin de semaine S-2). +Vue tableau des soldes de congés par employé, figée à un cutoff temporel (fin de semaine S-1). Complémentaire à l'export PDF admin : mêmes colonnes, accès étendu aux employés et chefs de site. ## Cutoff -La formule est : `cutoffDate = dimanche de (lundi de la semaine courante − 14 jours)`. +La formule est : `cutoffDate = dimanche de (lundi de la semaine courante − 7 jours)`. -Exemple : mardi 14/04/2026 (S16) → **dimanche 05/04/2026 23:59:59** (fin S14). +Exemple : mardi 14/04/2026 (S16) → **dimanche 12/04/2026 23:59:59** (fin S15). Le cutoff est purement temporel : l'état `isValid` des heures n'entre pas en compte. Les heures et absences postérieures au cutoff sont ignorées dans le calcul des soldes. diff --git a/frontend/data/documentation-content.ts b/frontend/data/documentation-content.ts index ceca4da..f425efa 100644 --- a/frontend/data/documentation-content.ts +++ b/frontend/data/documentation-content.ts @@ -505,7 +505,7 @@ export const documentationSections: DocSection[] = [ blocks: [ { type: 'paragraph', content: 'L\'écran "Récap. congés" affiche un tableau figé des soldes de congés et RTT par employé. Il est accessible via la sidebar lorsque l\'accès a été activé sur le compte utilisateur.' }, { type: 'list', content: 'Employé : voit uniquement sa propre ligne\nChef de site : voit les employés des sites qui lui sont rattachés\nAdmin : voit tous les employés, groupés par site' }, - { type: 'note', content: 'Le récap est arrêté à la fin de la semaine S-2 (dimanche). Exemple : un mardi en S16, les soldes sont calculés jusqu\'au dimanche de la S14 inclus. Les heures et absences postérieures ne sont pas comptées.' }, + { type: 'note', content: 'Le récap est arrêté à la fin de la semaine S-1 (dimanche). Exemple : un mardi en S16, les soldes sont calculés jusqu\'au dimanche de la S15 inclus. Les heures et absences postérieures ne sont pas comptées.' }, { type: 'paragraph', content: 'Les colonnes affichées sont identiques à l\'export PDF admin (Nom, Prénom, Contrat, CP N-1 restant, CP N, Samedis, RTT). L\'accès à cet écran est géré par un flag sur l\'utilisateur, activé depuis le drawer de création/édition d\'un utilisateur par un admin.' }, ], }, diff --git a/src/Util/LeaveRecapCutoff.php b/src/Util/LeaveRecapCutoff.php index 0464715..de2394a 100644 --- a/src/Util/LeaveRecapCutoff.php +++ b/src/Util/LeaveRecapCutoff.php @@ -7,16 +7,16 @@ namespace App\Util; use DateTimeImmutable; /** - * Leave recap cutoff rule: as-of end of ISO week S-2 (Sunday 23:59:59). + * Leave recap cutoff rule: as-of end of ISO week S-1 (Sunday 23:59:59). * - * Example: Tuesday 2026-04-14 (S16) → Sunday 2026-04-05 23:59:59 (end of S14). + * Example: Tuesday 2026-04-14 (S16) → Sunday 2026-04-12 23:59:59 (end of S15). */ final class LeaveRecapCutoff { public static function resolveCutoff(DateTimeImmutable $today): DateTimeImmutable { $currentWeekMonday = $today->modify('monday this week')->setTime(0, 0); - $cutoffWeekMonday = $currentWeekMonday->modify('-14 days'); + $cutoffWeekMonday = $currentWeekMonday->modify('-7 days'); return $cutoffWeekMonday->modify('+6 days')->setTime(23, 59, 59); } diff --git a/tests/Util/LeaveRecapCutoffTest.php b/tests/Util/LeaveRecapCutoffTest.php new file mode 100644 index 0000000..555b2c8 --- /dev/null +++ b/tests/Util/LeaveRecapCutoffTest.php @@ -0,0 +1,50 @@ +format('Y-m-d H:i:s')); + } + + public function testCutoffFromMondayPointsToPreviousSunday(): void + { + // Monday 2026-06-08 → previous Sunday 2026-06-07 23:59:59. + $cutoff = LeaveRecapCutoff::resolveCutoff(new DateTimeImmutable('2026-06-08')); + + self::assertSame('2026-06-07 23:59:59', $cutoff->format('Y-m-d H:i:s')); + } + + public function testCutoffFromSundayPointsToPreviousSunday(): void + { + // Sunday 2026-06-14 (still in current ISO week) → previous Sunday 2026-06-07. + $cutoff = LeaveRecapCutoff::resolveCutoff(new DateTimeImmutable('2026-06-14')); + + self::assertSame('2026-06-07 23:59:59', $cutoff->format('Y-m-d H:i:s')); + } + + public function testCutoffIsAlwaysASundayExactlyOneWeekBeforeCurrentWeek(): void + { + // Today 2026-06-11 (Thursday) → end of S-1 = Sunday 2026-06-07. + $cutoff = LeaveRecapCutoff::resolveCutoff(new DateTimeImmutable('2026-06-11')); + + self::assertSame('Sunday', $cutoff->format('l')); + self::assertSame('2026-06-07 23:59:59', $cutoff->format('Y-m-d H:i:s')); + } +}