Retour RH: vue jour par date, RTT mi-semaine, récap salaire & exports, panier de nuit #21
@@ -71,6 +71,7 @@
|
||||
- FORFAIT weekend/holiday bonus: each weekend or public holiday day worked gives bonus leave (full day if morning+afternoon, 0.5 if only one). Added to acquired days, no cap. PRESENCE mode only.
|
||||
- **FORFAIT — jours de présence et N-1** : les congés posés et imputés sur le stock N-1 ne décrémentent **pas** les jours de présence affichés (`presenceDaysByMonth` et `presenceDaysToToday`). Implémenté dans `EmployeeLeaveSummaryProvider::computePresenceDaysByMonth` via un budget N-1 (= `previousYearTakenDays`) consommé chronologiquement avant comptage des absences. Pour les non-forfait, ce budget vaut toujours 0 → comportement inchangé.
|
||||
- **Récap salaire (export PDF mensuel)** : même règle appliquée dans `SalaryRecapPrintProvider` — un congé forfait imputé N-1 n'est ni affiché en colonne congés ni soustrait, et compte comme jour de présence. Le budget N-1 vient de `EmployeeLeaveSummaryProvider::resolvePreviousYearTakenDays()` (méthode publique mutualisée, qui reproduit `provide()` : phase courante + recalcul jours payés, donc **même budget que la fiche employé**). Comme l'export est mensuel, les congés sont chargés depuis le 1er janvier (`findForPrint(yearStart, to)`) et le budget consommé chronologiquement par `splitForfaitCongesByN1()`. Non-forfait ou budget N-1 = 0 → `countAbsencesByCode(['C'])` inchangé.
|
||||
- **Colonne « Heures payés » scindée 25 %/50 %** : en-tête fusionné (`colspan=2`) + deux sous-colonnes `25%`/`50%` dans le template `salary-recap/print.html.twig`. Données : `paid25Hours` = `base25Minutes`, `paid50Hours` = `base50Minutes` (bases seules, **hors bonus** — total inchangé vs l'ancienne colonne unique). `buildRttPaymentMap` renvoie `['m25','m50']` par employé. Le tableau a désormais 20 colonnes (`colspan` des lignes site/vide ajusté).
|
||||
- **Jours de présence — borne début de contrat** : `presenceDaysByMonth`/`presenceDaysToToday` sont calculés à partir de `resolveEarliestContractStartWithinRange` (début de contrat dans l'exercice), pas du début d'exercice brut. Évite de compter comme « présents » les jours ouvrés antérieurs à l'embauche pour une entrée en cours d'exercice (ex. CDD : Dylan passait de 43,5 à 246 sans la borne). Sans effet pour un employé présent depuis avant l'exercice ni pour le forfait (déjà capé au début de phase).
|
||||
|
||||
## Onglet Congés (fiche employé)
|
||||
|
||||
@@ -621,7 +621,7 @@ export const documentationSections: DocSection[] = [
|
||||
requiredLevel: 'admin',
|
||||
blocks: [
|
||||
{ type: 'paragraph', content: 'Génère un PDF A4 paysage avec le détail mensuel pour la paie.' },
|
||||
{ type: 'list', content: 'Sélecteur de mois (défaut = mois courant)\nDonnées groupées par site\nColonnes : nom, base contrat, jours de présence cadre, heures de nuit, panier de nuit, heures RTT payées, congés (nombre + dates), maladie/AT (nombre + dates), primes conducteur (PDJ, repas, nuitée, samedi), observations\nColonne « Repas » chauffeur : somme déjeuner + dîner sur le mois (un jour avec les deux compte 2 repas)' },
|
||||
{ type: 'list', content: 'Sélecteur de mois (défaut = mois courant)\nDonnées groupées par site\nColonnes : nom, base contrat, jours de présence cadre, heures de nuit, panier de nuit, heures RTT payées (en-tête fusionné scindé en deux sous-colonnes 25 % et 50 %), congés (nombre + dates), maladie/AT (nombre + dates), primes conducteur (PDJ, repas, nuitée, samedi), observations\nColonne « Repas » chauffeur : somme déjeuner + dîner sur le mois (un jour avec les deux compte 2 repas)' },
|
||||
{ type: 'note', content: 'Forfait : un congé imputé sur le stock de l\'année précédente (N-1) n\'apparaît pas dans la colonne congés et compte comme un jour de présence. Le budget N-1 est consommé dans l\'ordre chronologique depuis janvier, de façon cohérente avec la fiche employé (les jours payés réduisent le stock N-1 d\'abord). Au-delà du budget N-1, les congés s\'affichent normalement.' },
|
||||
],
|
||||
},
|
||||
|
||||
@@ -174,6 +174,9 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
/**
|
||||
* @return array<int, int>
|
||||
*/
|
||||
/**
|
||||
* @return array<int, array{m25: int, m50: int}>
|
||||
*/
|
||||
private function buildRttPaymentMap(array $rttPayments): array
|
||||
{
|
||||
$map = [];
|
||||
@@ -182,7 +185,9 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
if (!$employeeId) {
|
||||
continue;
|
||||
}
|
||||
$map[$employeeId] = ($map[$employeeId] ?? 0) + $payment->getBase25Minutes() + $payment->getBase50Minutes();
|
||||
$map[$employeeId] ??= ['m25' => 0, 'm50' => 0];
|
||||
$map[$employeeId]['m25'] += $payment->getBase25Minutes();
|
||||
$map[$employeeId]['m50'] += $payment->getBase50Minutes();
|
||||
}
|
||||
|
||||
return $map;
|
||||
@@ -295,7 +300,7 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
$driverMap[$employeeId] ?? [],
|
||||
$workHourMap[$employeeId] ?? [],
|
||||
$absenceMap[$employeeId] ?? [],
|
||||
$rttPaymentMap[$employeeId] ?? 0,
|
||||
$rttPaymentMap[$employeeId] ?? ['m25' => 0, 'm50' => 0],
|
||||
$bonusMap[$employeeId] ?? 0.0,
|
||||
$mileageMap[$employeeId] ?? 0.0,
|
||||
$observationMap[$employeeId] ?? '',
|
||||
@@ -328,7 +333,7 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
array $driverByDate,
|
||||
array $workHoursByDate,
|
||||
array $absences,
|
||||
int $rttPaidMinutes,
|
||||
array $rttPaid,
|
||||
float $bonusAmount,
|
||||
float $mileageKm,
|
||||
string $observation,
|
||||
@@ -455,7 +460,8 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
$maladie = $this->countAbsencesByCode($absences, ['M', 'AT']);
|
||||
|
||||
$nightHours = round($nightMinutesTotal / 60, 2);
|
||||
$paidHours = round($rttPaidMinutes / 60, 2);
|
||||
$paid25Hours = round(($rttPaid['m25'] ?? 0) / 60, 2);
|
||||
$paid50Hours = round(($rttPaid['m50'] ?? 0) / 60, 2);
|
||||
$sundayHours = round($sundayMinutesTotal / 60, 2);
|
||||
$holidayHours = round($holidayMinutesTotal / 60, 2);
|
||||
|
||||
@@ -467,7 +473,8 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
'mileageKm' => $mileageKm,
|
||||
'nightHours' => $nightHours,
|
||||
'nightBasketCount' => $nightBasketCount,
|
||||
'paidHours' => $paidHours,
|
||||
'paid25Hours' => $paid25Hours,
|
||||
'paid50Hours' => $paid50Hours,
|
||||
'sundayHours' => $sundayHours,
|
||||
'holidayHours' => $holidayHours,
|
||||
'bonusAmount' => $bonusAmount,
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<th rowspan="2" style="width: 8mm;">Frais<br>Kms</th>
|
||||
<th rowspan="2" style="width: 8mm;">Heures<br>de<br>nuit</th>
|
||||
<th rowspan="2" style="width: 8mm;">Panier<br>de<br>nuit</th>
|
||||
<th rowspan="2" style="width: 10mm;">Heures<br>payés</th>
|
||||
<th colspan="2" style="width: 14mm;">Heures<br>payés</th>
|
||||
<th rowspan="2" style="width: 8mm;">Heures<br>férié</th>
|
||||
<th rowspan="2" style="width: 8mm;">Heures<br>dim.</th>
|
||||
<th rowspan="2" style="width: 8mm;">Prime</th>
|
||||
@@ -127,6 +127,8 @@
|
||||
<th rowspan="2" style="width: 20mm;">Observations</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width: 7mm;">25%</th>
|
||||
<th style="width: 7mm;">50%</th>
|
||||
<th style="width: 8mm;">Nbre</th>
|
||||
<th style="width: 22mm;">Date</th>
|
||||
<th style="width: 8mm;">Nbre</th>
|
||||
@@ -141,7 +143,7 @@
|
||||
{% for siteId, group in siteGroups %}
|
||||
{% set siteColor = group.color ?? '#B3E5FC' %}
|
||||
<tr class="site-header">
|
||||
<td style="background: {{ siteColor }}; text-align: left;" colspan="19">
|
||||
<td style="background: {{ siteColor }}; text-align: left;" colspan="20">
|
||||
{{ group.name }}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -153,7 +155,8 @@
|
||||
<td class="num">{{ row.mileageKm > 0 ? row.mileageKm : '' }}</td>
|
||||
<td class="num">{{ row.nightHours > 0 ? row.nightHours : '' }}</td>
|
||||
<td class="num">{{ row.nightBasketCount > 0 ? row.nightBasketCount : '' }}</td>
|
||||
<td class="num">{{ row.paidHours > 0 ? row.paidHours : '' }}</td>
|
||||
<td class="num">{{ row.paid25Hours > 0 ? row.paid25Hours : '' }}</td>
|
||||
<td class="num">{{ row.paid50Hours > 0 ? row.paid50Hours : '' }}</td>
|
||||
<td class="num">{{ row.holidayHours > 0 ? row.holidayHours : '' }}</td>
|
||||
<td class="num">{{ row.sundayHours > 0 ? row.sundayHours : '' }}</td>
|
||||
<td class="num">{{ row.bonusAmount > 0 ? row.bonusAmount ~ ' €' : '' }}</td>
|
||||
@@ -169,7 +172,7 @@
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="19">Aucun employé.</td>
|
||||
<td colspan="20">Aucun employé.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user