Files
SIRH/src/Service/WorkHours/WorkedHoursCreditPolicy.php
tristan a8fe244b5c
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
feat : modification de la gestion des jours fériés
2026-04-16 15:52:19 +02:00

91 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service\WorkHours;
use App\Entity\Absence;
use App\Enum\TrackingMode;
use App\Service\Contracts\EmployeeContractResolver;
use DateMalformedStringException;
use DateTimeImmutable;
final readonly class WorkedHoursCreditPolicy
{
public function __construct(
private EmployeeContractResolver $contractResolver,
private DailyReferenceMinutesResolver $dailyReferenceResolver,
) {}
/**
* @throws DateMalformedStringException
*/
public function computeCreditedMinutes(Absence $absence, string $dateYmd, bool $absentMorning, bool $absentAfternoon): int
{
$type = $absence->getType();
// Certaines absences ne doivent jamais générer d'heures créditées.
if (!$type?->getCountAsWorkedHours()) {
return 0;
}
$employee = $absence->getEmployee();
if (null === $employee) {
return 0;
}
$workDate = new DateTimeImmutable($dateYmd);
$contract = $this->contractResolver->resolveForEmployeeAndDate($employee, $workDate);
// Les contrats suivis en "présence" ne cumulent pas d'heures en minutes.
if (TrackingMode::TIME->value !== $contract?->getTrackingMode()) {
return 0;
}
$weekday = (int) $workDate->format('N');
$workDaysMinutes = $this->contractResolver->resolveWorkDaysMinutesForEmployeeAndDate($employee, $workDate);
// Quand un planning est configuré sur la période (contrats non-standards),
// il prime : jour non programmé = 0 crédit, sinon on utilise les minutes prévues.
$dayMinutes = $this->resolveContractDayMinutes($contract?->getWeeklyHours(), $weekday, $workDaysMinutes);
if ($dayMinutes <= 0) {
return 0;
}
// Crédit en demi-journées: matin = 0.5, après-midi = 0.5.
$halfUnits = ($absentMorning ? 1 : 0) + ($absentAfternoon ? 1 : 0);
return (int) round(($dayMinutes / 2) * $halfUnits);
}
/**
* @throws DateMalformedStringException
*/
public function computeCreditedPresenceUnits(
Absence $absence,
string $dateYmd,
bool $absentMorning,
bool $absentAfternoon
): float {
$employee = $absence->getEmployee();
if (null === $employee) {
return 0.0;
}
$contract = $this->contractResolver->resolveForEmployeeAndDate($employee, new DateTimeImmutable($dateYmd));
if (TrackingMode::PRESENCE->value !== $contract?->getTrackingMode()) {
return 0.0;
}
// Règle forfait: les absences ne créditent jamais de présence.
// Seules les checkboxes cochées par l'employé comptent.
return 0.0;
}
/**
* Single source of truth = {@see DailyReferenceMinutesResolver}. Weekend=0,
* schedule precedence, 35h/39h fixed rules, fallback = weeklyHours/5.
*
* @param null|array<int, int> $workDaysMinutes planning iso-day → minutes (priorité absolue si fourni)
*/
public function resolveContractDayMinutes(?int $weeklyHours, int $isoWeekDay, ?array $workDaysMinutes = null): int
{
return $this->dailyReferenceResolver->resolve($weeklyHours, $isoWeekDay, $workDaysMinutes);
}
}