[#322] Page horaire (#4)
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s

| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|        #322          |        Page horaire         |

## Description de la PR
[#322] Page horaire

## Modification du .env

## Check list

- [ ] Pas de régression
- [ ] TU/TI/TF rédigée
- [ ] TU/TI/TF OK
- [ ] CHANGELOG modifié

Reviewed-on: #4
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #4.
This commit is contained in:
2026-02-20 11:23:52 +00:00
committed by Autin
parent f6c1f7eead
commit ee16779777
85 changed files with 6232 additions and 242 deletions

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace App\Service\WorkHours;
use App\Entity\Absence;
use App\Enum\TrackingMode;
use DateMalformedStringException;
use DateTimeImmutable;
final class WorkedHoursCreditPolicy
{
/**
* @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();
// Les contrats suivis en "présence" ne cumulent pas d'heures en minutes.
if (TrackingMode::TIME->value !== $employee?->getContract()?->getTrackingMode()) {
return 0;
}
$weekday = (int) new DateTimeImmutable($dateYmd)->format('N');
// On applique la règle de crédit dépendante du contrat (35h / 39h / fallback).
$dayMinutes = $this->resolveContractDayMinutes($employee->getContract()?->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);
}
public function computeCreditedPresenceUnits(Absence $absence, bool $absentMorning, bool $absentAfternoon): float
{
$type = $absence->getType();
if (!$type?->getCountAsWorkedHours()) {
return 0.0;
}
$employee = $absence->getEmployee();
if (TrackingMode::PRESENCE->value !== $employee?->getContract()?->getTrackingMode()) {
return 0.0;
}
$halfUnits = ($absentMorning ? 1 : 0) + ($absentAfternoon ? 1 : 0);
return $halfUnits * 0.5;
}
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);
}
}