Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 49ad6306ea | |||
| 9d2e70f81e |
@@ -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)
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '0.1.112'
|
||||
app.version: '0.1.113'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Util;
|
||||
|
||||
use App\Util\LeaveRecapCutoff;
|
||||
use DateTimeImmutable;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* Cutoff rule: end of ISO week S-1 (previous week's Sunday 23:59:59).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class LeaveRecapCutoffTest extends TestCase
|
||||
{
|
||||
public function testCutoffIsPreviousWeekSunday(): void
|
||||
{
|
||||
// Tuesday 2026-04-14 (S16) → Sunday 2026-04-12 23:59:59 (end of S15).
|
||||
$cutoff = LeaveRecapCutoff::resolveCutoff(new DateTimeImmutable('2026-04-14'));
|
||||
|
||||
self::assertSame('2026-04-12 23:59:59', $cutoff->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'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user