Files
SIRH/tests/Service/WorkHours/NightHoursCalculatorTest.php
T
tristan b5bd4db5f1
Auto Tag Develop / tag (push) Successful in 9s
feat(heures) : export Contingent heures de nuit (liste employés) (#28)
## Résumé
Nouvel export PDF **Contingent heures de nuit** dans le drawer Export de la liste employés.

- PDF **A4 paysage** : lignes = employés (groupés par site, triés displayOrder/nom/prénom), colonnes = 12 mois civils, chaque mois avec 2 sous-colonnes **H.nuit** et **N.jours**.
- Heures de nuit = minutes dans la fenêtre **21h→6h** via un service partagé `NightHoursCalculator` (mutualisé avec `WorkHourWeeklySummaryProvider` et `YearlyHoursExportBuilder` — duplication supprimée, sans changement de comportement).
- **Conducteurs inclus** via `WorkHour.nightHoursMinutes`. Statut conducteur résolu par date.
- **N.jours** = nb de jours où les minutes de nuit ≥ 240 (4h). Aucun crédit absence/férié.
- Périmètre via `EmployeeRepository::findScoped` (admin → tous, chef de site → ses sites), endpoint `GET /night-hours-contingent/print?year=YYYY` (`ROLE_USER`).
- Sélecteur d'année (année civile). Colonne Nom calibrée, séparateurs de mois épais.

## Composants
- Service `NightHoursCalculator`, builder `NightContingentExportBuilder`, DTO `NightContingentRow`
- Provider `NightHoursContingentPrintProvider` + opération API `NightHoursContingentPrint`
- Gabarit `templates/night-hours-contingent/print.html.twig`
- Option frontend dans `frontend/pages/employees/index.vue`
- Docs : `doc/functional-rules.md`, `CLAUDE.md`, `frontend/data/documentation-content.ts`

## Tests
- Nouveaux tests unitaires : `NightHoursCalculatorTest` (fenêtre 21h→6h, passage minuit, bornes), `NightContingentExportBuilderTest` (agrégation mensuelle, règle ≥4h=1j, conducteur, cas sans heures)
- Suite complète : **208 tests OK**
- Rendu PDF validé visuellement (Twig→Dompdf)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #28
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-11 13:02:30 +00:00

81 lines
2.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Service\WorkHours;
use App\Entity\WorkHour;
use App\Service\WorkHours\NightHoursCalculator;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
/**
* @internal
*/
final class NightHoursCalculatorTest extends TestCase
{
public function testNullRangeReturnsZero(): void
{
$calc = new NightHoursCalculator();
self::assertSame(0, $calc->nightIntervalMinutes(null, null));
self::assertSame(0, $calc->nightIntervalMinutes('08:00', null));
self::assertSame(0, $calc->nightIntervalMinutes(null, '17:00'));
}
public function testPureDayRangeHasNoNight(): void
{
$calc = new NightHoursCalculator();
// 08:00 -> 17:00 : entierement hors fenetres nuit (00:00-06:00, 21:00-24:00).
self::assertSame(0, $calc->nightIntervalMinutes('08:00', '17:00'));
}
public function testWindowBoundariesAreRightExclusive(): void
{
$calc = new NightHoursCalculator();
// 06:00 -> 21:00 : pile entre les deux fenetres de nuit, 0 min.
self::assertSame(0, $calc->nightIntervalMinutes('06:00', '21:00'));
// 22:00 -> 06:00 : 22-24 (120) + 00-06 (360) = 480, borne 06:00 exclue.
self::assertSame(480, $calc->nightIntervalMinutes('22:00', '06:00'));
}
public function testEveningWindowCounts(): void
{
$calc = new NightHoursCalculator();
// 21:00 -> 24:00 = 180 min de nuit.
self::assertSame(180, $calc->nightIntervalMinutes('21:00', '00:00'));
}
public function testShiftCrossingMidnightCountsBothWindows(): void
{
$calc = new NightHoursCalculator();
// 21:00 -> 05:00 : 21-24 (180) + 00-05 (300) = 480 min.
self::assertSame(480, $calc->nightIntervalMinutes('21:00', '05:00'));
}
public function testNightMinutesForWorkHourDriverUsesManualField(): void
{
$calc = new NightHoursCalculator();
$wh = new WorkHour();
$wh->setWorkDate(new DateTimeImmutable('2026-01-15'))
->setDayHoursMinutes(300)
->setNightHoursMinutes(250)
->setMorningFrom('08:00')->setMorningTo('12:00')
;
// Driver -> champ manuel nightHoursMinutes, plages ignorees.
self::assertSame(250, $calc->nightMinutesForWorkHour($wh, true));
}
public function testNightMinutesForWorkHourNonDriverSumsRanges(): void
{
$calc = new NightHoursCalculator();
$wh = new WorkHour();
$wh->setWorkDate(new DateTimeImmutable('2026-01-15'))
->setMorningFrom('22:00')->setMorningTo('00:00') // 120 min nuit
->setEveningFrom('04:00')->setEveningTo('06:00') // 120 min nuit
;
self::assertSame(240, $calc->nightMinutesForWorkHour($wh, false));
}
}