Files
Lesstime/src/Service/AbsenceDayCalculator.php
Matthieu de98924fd3 feat(absences) : fondation backend du module de gestion des absences
Module type Payfit (étapes 1+2 de la spec V1) : demande d'absence, validation
admin, soldes à jour.

- Enums : AbsenceType, AbsenceStatus, HalfDay, ContractType, FamilySituation
- Entités : AbsencePolicy, AbsenceBalance, AbsenceRequest + champs RH sur User
- Services : PublicHolidayProvider (fériés FR métropole en PHP pur, Computus),
  AbsenceDayCalculator (décompte jours ouvrés/ouvrables + demi-journées, TDD),
  AbsenceBalanceService (périodes + pending/taken/recrédit)
- API Platform : providers/processors (création, approve/reject/cancel) + RBAC
  me/admin, contrôleurs preview (dry-run), upload/download justificatif, calendrier
- Migrations : une par table + colonnes RH user (DEFAULT puis DROP DEFAULT)
- Fixtures : 5 policies par défaut, salariés démo, soldes et demandes
- Tests unitaires : PublicHolidayProvider, AbsenceDayCalculator (12 tests)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:45:14 +02:00

74 lines
1.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
use App\Enum\HalfDay;
use DateInterval;
use DatePeriod;
use DateTimeImmutable;
/**
* Computes the number of days deducted for an absence request, following the
* business rules of the spec (§5.1): weekends and public holidays are skipped,
* and half-days on the boundaries subtract 0.5 each.
*/
final readonly class AbsenceDayCalculator
{
public function __construct(
private PublicHolidayProvider $holidayProvider,
) {}
/**
* @param bool $workingDaysOnly true => "jours ouvrés" (Mon-Fri),
* false => "jours ouvrables" (Mon-Sat, Sunday excluded)
*/
public function countWorkingDays(
DateTimeImmutable $start,
DateTimeImmutable $end,
?HalfDay $startHalfDay = null,
?HalfDay $endHalfDay = null,
bool $workingDaysOnly = true,
): float {
$start = $start->setTime(0, 0);
$end = $end->setTime(0, 0);
if ($end < $start) {
return 0.0;
}
$days = 0.0;
$period = new DatePeriod($start, new DateInterval('P1D'), $end->modify('+1 day'));
foreach ($period as $day) {
$weekday = (int) $day->format('N'); // 1 (Mon) .. 7 (Sun)
if (7 === $weekday) {
continue; // Sunday: never counted
}
if (6 === $weekday && $workingDaysOnly) {
continue; // Saturday: only counted for "jours ouvrables"
}
if ($this->holidayProvider->isHoliday($day)) {
continue;
}
++$days;
}
if ($days <= 0.0) {
return 0.0;
}
if (null !== $startHalfDay) {
$days -= 0.5;
}
if (null !== $endHalfDay) {
$days -= 0.5;
}
return max(0.0, $days);
}
}