Files
SIRH/tests/Service/Rtt/RttRecoveryComputationServiceTest.php
T
tristan a41bd632cf
Auto Tag Develop / tag (push) Successful in 11s
Retour RH: vue jour par date, RTT mi-semaine, récap salaire & exports, panier de nuit (#21)
## Correctifs RH (branche fix/retour-rh)

### Vue Jour (Heures)
- Mode saisie/présence, libellé de contrat et sauvegarde résolus **à la date affichée** (et non au contrat courant). Corrige les salariés passés 39h/35h → Forfait.

### RTT — heures supplémentaires
- Proratisation du **plafond 25%/50%** pour les embauches en milieu de semaine (la bande +25% se décale au lieu de rester bloquée à 43h). Témoin Dylan : 4h à 25% + 3h à 50%.

### Récap salaire (PDF mensuel)
- Forfait : congés imputés **N-1** non affichés et comptés en présence.
- Colonne « Heures payés » **scindée 25% / 50%** (en-tête fusionné).
- **Exclusion des salariés sans contrat** sur le mois (ex. Marine, contrat terminé).

### Exports heures annuelles (par salarié + tous)
- **Tous les jours sous contrat** affichés, même vides/non saisis (corrige les lignes manquantes).
- Samedis/dimanches en **gris plus foncé**.

### Panier de nuit
- **Ne s'applique pas aux conducteurs** (vue semaine + récap salaire).

## Tests
- 11 tests ajoutés. Suite verte hors un test legacy pré-existant dépendant de la date (`EmployeeRttSummaryProviderTest::testNoQueryParamsKeepsLegacyYearDefaulting`, non modifié par cette branche).

## À noter (hors scope)
- L'export heures annuelles *tous salariés* peut dépasser `memory_limit=256M` (Dompdf) — limitation **pré-existante**, non corrigée ici.

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

Reviewed-on: #21
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-02 06:26:40 +00:00

121 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Tests\Service\Rtt;
use App\Entity\Contract;
use App\Service\Rtt\RttRecoveryComputationService;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
/**
* The service constructor takes several final-class collaborators that PHPUnit cannot
* double. Pure helpers are exercised via newInstanceWithoutConstructor + reflection.
*
* @internal
*/
final class RttRecoveryComputationServiceTest extends TestCase
{
public function testResolveWeekAnchorDateReturnsFirstContractedDayWhenWeekStartsBeforeHire(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
$contract = new Contract();
$weekDays = ['2026-03-16', '2026-03-17', '2026-03-18', '2026-03-19', '2026-03-20', '2026-03-21', '2026-03-22'];
$contractsByDate = [
'2026-03-16' => null,
'2026-03-17' => null,
'2026-03-18' => null,
'2026-03-19' => $contract,
'2026-03-20' => $contract,
'2026-03-21' => $contract,
'2026-03-22' => $contract,
];
$anchor = $this->invokePrivate($service, 'resolveWeekAnchorDate', $weekDays, $contractsByDate);
self::assertSame('2026-03-19', $anchor);
}
public function testResolveWeekAnchorDateReturnsFirstDayWhenItIsContracted(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
$contract = new Contract();
$weekDays = ['2026-03-23', '2026-03-24', '2026-03-25'];
$contractsByDate = [
'2026-03-23' => $contract,
'2026-03-24' => $contract,
'2026-03-25' => $contract,
];
$anchor = $this->invokePrivate($service, 'resolveWeekAnchorDate', $weekDays, $contractsByDate);
self::assertSame('2026-03-23', $anchor);
}
public function testResolveWeekAnchorDateFallsBackToFirstDayWhenNoContract(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
$weekDays = ['2026-03-16', '2026-03-17'];
$contractsByDate = ['2026-03-16' => null, '2026-03-17' => null];
$anchor = $this->invokePrivate($service, 'resolveWeekAnchorDate', $weekDays, $contractsByDate);
self::assertSame('2026-03-16', $anchor);
}
public function testResolveOvertime25BandWidthIs4hForH39(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
$contract = new Contract()->setWeeklyHours(39);
self::assertSame(4 * 60, $this->invokePrivate($service, 'resolveOvertime25BandWidthMinutes', $contract));
}
public function testResolveOvertime25BandWidthIs8hForH35(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
$contract = new Contract()->setWeeklyHours(35);
self::assertSame(8 * 60, $this->invokePrivate($service, 'resolveOvertime25BandWidthMinutes', $contract));
}
/**
* Dylan Chaboisson, semaine 12 : embauché le jeudi sur un contrat 39h.
* Total travaillé 22h (1320 min), départ 25 % proraté aux jours contractés = 15h (900 min),
* plafond 25 %/50 % = 15h + bande 4h = 19h (1140 min). Le plafond se décale avec
* l'embauche au lieu de rester bloqué à 43h, ouvrant la tranche 50 %.
*/
public function testMidWeekHireSplitsOvertimeAcross25And50(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
[$base25, $base50] = $this->invokePrivate($service, 'computeOvertimeBaseMinutes', 1320, 900, 1140);
self::assertSame(4 * 60, $base25);
self::assertSame(3 * 60, $base50);
}
/**
* Régression : semaine pleine 39h (départ 39h, plafond 43h), 46h travaillées →
* 4h à 25 % (39→43) et 3h à 50 % (43→46), comportement inchangé.
*/
public function testFullWeekOvertimeSplitUnchanged(): void
{
$service = new ReflectionClass(RttRecoveryComputationService::class)->newInstanceWithoutConstructor();
[$base25, $base50] = $this->invokePrivate($service, 'computeOvertimeBaseMinutes', 2760, 2340, 2580);
self::assertSame(4 * 60, $base25);
self::assertSame(3 * 60, $base50);
}
private function invokePrivate(object $obj, string $method, mixed ...$args): mixed
{
return new ReflectionClass($obj::class)->getMethod($method)->invoke($obj, ...$args);
}
}