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>
74 lines
1.9 KiB
PHP
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);
|
|
}
|
|
}
|