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'); // On applique la règle de crédit dépendante du contrat (35h / 39h / fallback). $dayMinutes = $this->resolveContractDayMinutes($contract?->getWeeklyHours(), $weekday); 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: // - demi-journée d'absence => 0.5 travaillé // - journée complète d'absence => 0 travaillé if ($absentMorning xor $absentAfternoon) { return 0.5; } return 0.0; } public function resolveContractDayMinutes(?int $weeklyHours, int $isoWeekDay): int { // Week-end non travaillé dans cette politique. if ($isoWeekDay >= 6) { return 0; } // Règle fixe: 35h => 7h/jour. if (35 === $weeklyHours) { return 7 * 60; } // Règle fixe: 39h => 8h lundi-jeudi, 7h le vendredi. if (39 === $weeklyHours) { return 5 === $isoWeekDay ? 7 * 60 : 8 * 60; } // Cas spécifique métier: contrat 4h/semaine réparti sur 2 jours => 2h/jour. if (4 === $weeklyHours) { return 2 * 60; } // Contrat non renseigné/invalide: aucun crédit. if (null === $weeklyHours || $weeklyHours <= 0) { return 0; } // Fallback générique: répartition homogène sur 5 jours ouvrés. return (int) round(($weeklyHours * 60) / 5); } }