a41bd632cf
Auto Tag Develop / tag (push) Successful in 11s
## Correctifs RH (branche fix/retour-rh) ### Vue Jour (Heures) - Mode saisie/présence, libellé de contrat et sauvegarde résolus **à la date affichée** (et non au contrat courant). Corrige les salariés passés 39h/35h → Forfait. ### RTT — heures supplémentaires - Proratisation du **plafond 25%/50%** pour les embauches en milieu de semaine (la bande +25% se décale au lieu de rester bloquée à 43h). Témoin Dylan : 4h à 25% + 3h à 50%. ### Récap salaire (PDF mensuel) - Forfait : congés imputés **N-1** non affichés et comptés en présence. - Colonne « Heures payés » **scindée 25% / 50%** (en-tête fusionné). - **Exclusion des salariés sans contrat** sur le mois (ex. Marine, contrat terminé). ### Exports heures annuelles (par salarié + tous) - **Tous les jours sous contrat** affichés, même vides/non saisis (corrige les lignes manquantes). - Samedis/dimanches en **gris plus foncé**. ### Panier de nuit - **Ne s'applique pas aux conducteurs** (vue semaine + récap salaire). ## Tests - 11 tests ajoutés. Suite verte hors un test legacy pré-existant dépendant de la date (`EmployeeRttSummaryProviderTest::testNoQueryParamsKeepsLegacyYearDefaulting`, non modifié par cette branche). ## À noter (hors scope) - L'export heures annuelles *tous salariés* peut dépasser `memory_limit=256M` (Dompdf) — limitation **pré-existante**, non corrigée ici. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #21 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
133 lines
4.7 KiB
PHP
133 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Dto\WorkHours;
|
|
|
|
final class DayContextRow
|
|
{
|
|
public function __construct(
|
|
public int $employeeId,
|
|
public bool $hasContractAtDate = true,
|
|
public ?string $absenceLabel = null,
|
|
public ?string $absenceColor = null,
|
|
public ?string $absenceHalf = null,
|
|
public bool $absentMorning = false,
|
|
public bool $absentAfternoon = false,
|
|
public int $creditedMinutes = 0,
|
|
public float $creditedPresenceUnits = 0.0,
|
|
public bool $isDriverContract = false,
|
|
public bool $hasFormation = false,
|
|
public ?string $formationLabel = null,
|
|
public int $virtualHolidayMinutes = 0,
|
|
public ?string $contractNature = null,
|
|
public ?string $trackingMode = null,
|
|
public ?int $weeklyHours = null,
|
|
public ?string $contractType = null,
|
|
public ?string $contractName = null,
|
|
) {}
|
|
|
|
public function setFormation(string $label): void
|
|
{
|
|
$this->hasFormation = true;
|
|
$this->formationLabel = $label;
|
|
}
|
|
|
|
public function addAbsence(
|
|
?string $label,
|
|
?string $color,
|
|
bool $morning,
|
|
bool $afternoon,
|
|
int $creditedMinutes,
|
|
float $creditedPresenceUnits
|
|
): void {
|
|
// Fusionne plusieurs absences du même jour sur la ligne salarié.
|
|
$this->absentMorning = $this->absentMorning || $morning;
|
|
$this->absentAfternoon = $this->absentAfternoon || $afternoon;
|
|
|
|
// Garde un libellé lisible: unique si possible, sinon "Absences multiples".
|
|
if (null === $this->absenceLabel) {
|
|
$this->absenceLabel = $label;
|
|
} elseif ($label !== $this->absenceLabel) {
|
|
$this->absenceLabel = 'Absences multiples';
|
|
}
|
|
|
|
// Si plusieurs types d'absence différents sont fusionnés sur la même journée,
|
|
// on retire la couleur métier spécifique.
|
|
if (null === $this->absenceColor) {
|
|
$this->absenceColor = $color;
|
|
} elseif ($color !== $this->absenceColor) {
|
|
$this->absenceColor = null;
|
|
}
|
|
|
|
// AM/PM seulement pour les demi-journées, null pour journée complète.
|
|
$this->absenceHalf = $this->resolveHalfLabel($this->absentMorning, $this->absentAfternoon);
|
|
// Cumule les minutes créditées par les absences "comptées comme travaillées".
|
|
$this->creditedMinutes += $creditedMinutes;
|
|
// Cumule les unités de présence créditées (0.5 par demi-journée).
|
|
$this->creditedPresenceUnits += $creditedPresenceUnits;
|
|
}
|
|
|
|
/**
|
|
* @return array{
|
|
* employeeId:int,
|
|
* hasContractAtDate:bool,
|
|
* absenceLabel:?string,
|
|
* absenceColor:?string,
|
|
* absenceHalf:?string,
|
|
* absentMorning:bool,
|
|
* absentAfternoon:bool,
|
|
* creditedMinutes:int,
|
|
* creditedPresenceUnits:float,
|
|
* isDriverContract:bool,
|
|
* hasFormation:bool,
|
|
* formationLabel:?string,
|
|
* virtualHolidayMinutes:int,
|
|
* contractNature:?string,
|
|
* trackingMode:?string,
|
|
* weeklyHours:?int,
|
|
* contractType:?string,
|
|
* contractName:?string
|
|
* }
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
return [
|
|
'employeeId' => $this->employeeId,
|
|
'hasContractAtDate' => $this->hasContractAtDate,
|
|
'absenceLabel' => $this->absenceLabel,
|
|
'absenceColor' => $this->absenceColor,
|
|
'absenceHalf' => $this->absenceHalf,
|
|
'absentMorning' => $this->absentMorning,
|
|
'absentAfternoon' => $this->absentAfternoon,
|
|
'creditedMinutes' => $this->creditedMinutes,
|
|
'creditedPresenceUnits' => $this->creditedPresenceUnits,
|
|
'isDriverContract' => $this->isDriverContract,
|
|
'hasFormation' => $this->hasFormation,
|
|
'formationLabel' => $this->formationLabel,
|
|
'virtualHolidayMinutes' => $this->virtualHolidayMinutes,
|
|
'contractNature' => $this->contractNature,
|
|
'trackingMode' => $this->trackingMode,
|
|
'weeklyHours' => $this->weeklyHours,
|
|
'contractType' => $this->contractType,
|
|
'contractName' => $this->contractName,
|
|
];
|
|
}
|
|
|
|
private function resolveHalfLabel(bool $morning, bool $afternoon): ?string
|
|
{
|
|
// Matin + après-midi => journée complète, pas de libellé AM/PM.
|
|
if ($morning && $afternoon) {
|
|
return null;
|
|
}
|
|
if ($morning) {
|
|
return 'AM';
|
|
}
|
|
if ($afternoon) {
|
|
return 'PM';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|