Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed9df4e178 | ||
| 625b4af5ba | |||
|
|
2ec3044cb3 | ||
| f024a6a8de | |||
|
|
a60294a8f7 | ||
| dd7f9ef8a0 | |||
| cfa7d25521 | |||
|
|
5faa0facca | ||
| 04f90afc58 |
4
.env
4
.env
@@ -36,6 +36,10 @@ DEFAULT_URI=http://localhost
|
||||
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
###> app ###
|
||||
RTT_START_DATE=2026-02-23
|
||||
###< app ###
|
||||
|
||||
###> nelmio/cors-bundle ###
|
||||
CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$'
|
||||
###< nelmio/cors-bundle ###
|
||||
|
||||
@@ -26,6 +26,14 @@ services:
|
||||
arguments:
|
||||
$holidayUrl: '%env(HOLIDAY_URL)%'
|
||||
|
||||
App\Service\Rtt\RttRecoveryComputationService:
|
||||
arguments:
|
||||
$rttStartDate: '%env(RTT_START_DATE)%'
|
||||
|
||||
App\State\EmployeeRttSummaryProvider:
|
||||
arguments:
|
||||
$rttStartDate: '%env(RTT_START_DATE)%'
|
||||
|
||||
App\Repository\Contract\AbsenceReadRepositoryInterface: '@App\Repository\AbsenceRepository'
|
||||
App\Repository\Contract\EmployeeContractPeriodReadRepositoryInterface: '@App\Repository\EmployeeContractPeriodRepository'
|
||||
App\Repository\Contract\EmployeeScopedRepositoryInterface: '@App\Repository\EmployeeRepository'
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
parameters:
|
||||
app.version: '0.1.43'
|
||||
app.version: '0.1.47'
|
||||
|
||||
@@ -116,6 +116,12 @@ Documents complementaires:
|
||||
- contrats >= 39h: de 39h à 43h
|
||||
- Tranche 50%:
|
||||
- au-delà de 43h
|
||||
- Date de début RTT (`RTT_START_DATE` dans `.env`):
|
||||
- les semaines dont la fin est antérieure à cette date sont ignorées dans le calcul de récupération
|
||||
- permet d'éviter les déficits fictifs avant la mise en service du logiciel
|
||||
- Semaine en déficit (heures travaillées < heures contrat):
|
||||
- le déficit est déduit du cumul RTT : d'abord des heures à 50%, puis des heures à 25%
|
||||
- si aucun solde 50% ni 25%, les heures à 25% deviennent négatives
|
||||
- Nature `INTERIM`:
|
||||
- pas de bonus 25%
|
||||
- pas de bonus 50%
|
||||
@@ -142,7 +148,7 @@ Documents complementaires:
|
||||
- Validation: même logique que les heures classiques (`isValid`, `isSiteValid`, bulk)
|
||||
- Vue semaine:
|
||||
- jour/nuit/atelier par jour + indicateurs repas/dîner/nuitée
|
||||
- panier de nuit (PN): affiché par jour si nightMinutes > dayMinutes, et total hebdo dans la colonne Jour/Nuit sem.
|
||||
- panier de nuit (PN): affiché par jour si (nightMinutes > dayMinutes) OU (nightMinutes >= 240, soit au moins 4h de travail entre 21h et 6h), et total hebdo dans la colonne Jour/Nuit sem.
|
||||
- totaux hebdo: jour, nuit, atelier, total, compteurs petit déj/déjeuner/dîner/nuitée
|
||||
- pas de calcul d'heures supplémentaires pour les conducteurs
|
||||
- Le flag `isDriver` est sur `EmployeeContractPeriod` (un employé peut changer de statut chauffeur selon la période)
|
||||
@@ -278,6 +284,10 @@ Tous les filtres checkbox sont cochés par défaut à l'ouverture du drawer.
|
||||
- `rate`: taux de majoration, valeurs `25` ou `50`
|
||||
- les heures payées sont soustraites du disponible RTT (`availableMinutes -= totalPaidMinutes`)
|
||||
- affichage: 2 lignes par mois dans le tableau (25% et 50%)
|
||||
- colonnes Total 25% et Total 50%: somme base + bonus de chaque tranche
|
||||
- ligne Report N-1 (carry rollover): affichée en juin uniquement si carry > 0
|
||||
- ligne Report mois précédent: solde cumulé (carry N-1 + semaines antérieures − paiements antérieurs), affichée à partir de juillet (masquée si nul)
|
||||
- Reste = Report cumulé + Total du mois − Payé du mois (balance courante en fin de mois)
|
||||
- affichage:
|
||||
- le compteur global RTT est affiché en **heures** (format `Xh00`)
|
||||
|
||||
@@ -295,8 +305,9 @@ Tous les filtres checkbox sont cochés par défaut à l'ouverture du drawer.
|
||||
|---------|---------|
|
||||
| Nom | lastName + firstName |
|
||||
| Contrat | Contract.name |
|
||||
| CP Acquis (N-1) | Report de l'exercice précédent (acquiredDays du computeYearSummary) |
|
||||
| Samedi acquis | Report N-1 samedis. Forfait: `-` |
|
||||
| CP N-1 restant | CDI/CDD: acquis N-1 − pris sur N-1. Forfait: report N-1 restant |
|
||||
| Samedi restant | CDI/CDD: samedis acquis N-1 − pris. Forfait: `-` |
|
||||
| CP N | Forfait: jours acquis année civile. Non-forfait: en cours d'acquisition |
|
||||
| RTT | Minutes disponibles (report N-1 + acquis N - payés). Format `X h Y m`. Forfait et INTERIM: `-` |
|
||||
|
||||
## 11) Récapitulatif Salaire (PDF mensuel)
|
||||
@@ -314,7 +325,7 @@ Tous les filtres checkbox sont cochés par défaut à l'ouverture du drawer.
|
||||
| Base | Contract.name | Via EmployeeContractResolver pour le mois |
|
||||
| Jour de présence Cadre | WorkHour | Uniquement FORFAIT (PRESENCE). Somme isPresentMorning (0.5) + isPresentAfternoon (0.5) |
|
||||
| Heures de nuit | WorkHour | Non-chauffeurs: calcul intervalles nuit (00:00-06:00, 21:00-24:00). Chauffeurs: somme nightHoursMinutes |
|
||||
| Panier de nuit | WorkHour | Nombre de jours où nightMinutes > dayMinutes |
|
||||
| Panier de nuit | WorkHour | Nombre de jours où (nightMinutes > dayMinutes) OU (nightMinutes >= 240, soit 4h entre 21h-6h) |
|
||||
| Heures payés | EmployeeRttPayment | Somme base25Minutes + base50Minutes du mois, convertie en heures |
|
||||
| Congés - Nombre | Absence code 'C' | Jours (demi-journées = 0.5) |
|
||||
| Congés - Date | Absence code 'C' | Dates formatées dd/mm |
|
||||
|
||||
@@ -40,36 +40,55 @@
|
||||
<table class="w-full table-fixed border-collapse text-[18px]">
|
||||
<colgroup>
|
||||
<col />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[14%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
<col class="w-[11%]" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-5 py-[10px] text-left font-bold text-primary-500 border border-primary-500">Semaine</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">Heure</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">Base</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">25%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">25%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">Total 25%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">Base</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">50%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">50%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">Total 50%</th>
|
||||
<th class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Report row (only on June when carry > 0) -->
|
||||
<tr v-if="showReportRow">
|
||||
<!-- Report N-1 row (RTT rollover carry, June only) -->
|
||||
<tr v-if="showCarryRow" class="bg-tertiary-500">
|
||||
<td class="px-5 py-[10px] font-bold text-primary-500 border border-primary-500">Report</td>
|
||||
<td class="px-4 py-[10px] text-center text-neutral-500 border border-primary-500 border-r-2">-</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(summary!.carryBase25Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(summary!.carryBonus25Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(summary!.carryBonus25Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(summary!.carryBase25Minutes + summary!.carryBonus25Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(summary!.carryBase50Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(summary!.carryBonus50Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(summary!.carryBonus50Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(summary!.carryBase50Minutes + summary!.carryBonus50Minutes) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(summary!.carryFromPreviousYearMinutes) }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Report mois précédent (cumulated balance from previous months, July+) -->
|
||||
<tr v-if="showMonthReportRow" class="bg-tertiary-500">
|
||||
<td class="px-5 py-[10px] font-bold text-primary-500 border border-primary-500">Report</td>
|
||||
<td class="px-4 py-[10px] text-center text-neutral-500 border border-primary-500 border-r-2">-</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.base25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.bonus25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(monthReport.total25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.base50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.bonus50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(monthReport.total50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.total) }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Week rows (always 5) -->
|
||||
<tr
|
||||
v-for="(week, idx) in paddedWeeks"
|
||||
@@ -87,18 +106,26 @@
|
||||
<span v-if="week">{{ formatMinutes(week.base25Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">
|
||||
<span v-if="week">{{ formatMinutes(week.bonus25Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">
|
||||
<span v-if="week">{{ formatMinutes(week.base25Minutes + week.bonus25Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">
|
||||
<span v-if="week">{{ formatMinutes(week.base50Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">
|
||||
<span v-if="week">{{ formatMinutes(week.bonus50Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">
|
||||
<span v-if="week">{{ formatMinutes(week.base50Minutes + week.bonus50Minutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">
|
||||
<span v-if="week">{{ formatMinutes(week.totalMinutes) }}</span>
|
||||
<span v-else>0 h</span>
|
||||
@@ -110,9 +137,11 @@
|
||||
<td class="px-5 py-[10px] font-bold text-primary-500 border border-primary-500 border-t-2">Total</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2 border-t-2">{{ formatMinutes(totals.overtime) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-t-2">{{ formatMinutes(totals.base25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2 border-t-2">{{ formatMinutes(totals.bonus25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-t-2">{{ formatMinutes(totals.bonus25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2 border-t-2">{{ formatMinutes(totals.total25) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-t-2">{{ formatMinutes(totals.base50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2 border-t-2">{{ formatMinutes(totals.bonus50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-t-2">{{ formatMinutes(totals.bonus50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2 border-t-2">{{ formatMinutes(totals.total50) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-t-2">{{ formatMinutes(totals.total) }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -121,9 +150,11 @@
|
||||
<td class="px-5 py-[10px] font-bold text-primary-500 border border-primary-500">Payé</td>
|
||||
<td class="px-4 py-[10px] text-center text-neutral-500 border border-primary-500 border-r-2">-</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ currentPayment ? formatMinutes(-currentPayment.paidBase25Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ currentPayment ? formatMinutes(-currentPayment.paidBonus25Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ currentPayment ? formatMinutes(-currentPayment.paidBonus25Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ currentPayment ? formatMinutes(-(currentPayment.paidBase25Minutes + currentPayment.paidBonus25Minutes)) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ currentPayment ? formatMinutes(-currentPayment.paidBase50Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ currentPayment ? formatMinutes(-currentPayment.paidBonus50Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ currentPayment ? formatMinutes(-currentPayment.paidBonus50Minutes) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ currentPayment ? formatMinutes(-(currentPayment.paidBase50Minutes + currentPayment.paidBonus50Minutes)) : '0 h' }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(paidTotal) }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -131,10 +162,12 @@
|
||||
<tr>
|
||||
<td class="px-5 py-[10px] font-bold text-primary-500 border border-primary-500">Reste</td>
|
||||
<td class="px-4 py-[10px] text-center text-neutral-500 border border-primary-500 border-r-2">-</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(totals.base25 - (currentPayment?.paidBase25Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(totals.bonus25 - (currentPayment?.paidBonus25Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(totals.base50 - (currentPayment?.paidBase50Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(totals.bonus50 - (currentPayment?.paidBonus50Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.base25 + totals.base25 - (currentPayment?.paidBase25Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.bonus25 + totals.bonus25 - (currentPayment?.paidBonus25Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(monthReport.total25 + totals.total25 - (currentPayment?.paidBase25Minutes ?? 0) - (currentPayment?.paidBonus25Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.base50 + totals.base50 - (currentPayment?.paidBase50Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(monthReport.bonus50 + totals.bonus50 - (currentPayment?.paidBonus50Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500 border-r-2">{{ formatMinutes(monthReport.total50 + totals.total50 - (currentPayment?.paidBase50Minutes ?? 0) - (currentPayment?.paidBonus50Minutes ?? 0)) }}</td>
|
||||
<td class="px-4 py-[10px] text-center font-bold text-primary-500 border border-primary-500">{{ formatMinutes(resteTotal) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -290,44 +323,112 @@ const paddedWeeks = computed((): (EmployeeRttWeekSummary | null)[] => {
|
||||
return padded
|
||||
})
|
||||
|
||||
// --- Report row ---
|
||||
// --- Carry row (RTT rollover from previous year, June only) ---
|
||||
|
||||
const reportMonth = computed(() => {
|
||||
const carryMonth = computed(() => {
|
||||
if (!props.summary) return 6
|
||||
const carryMonth = props.summary.carryMonth
|
||||
// Report appears in the month AFTER carryMonth (wrapping 12 -> 1)
|
||||
return carryMonth >= 12 ? 1 : carryMonth + 1
|
||||
const cm = props.summary.carryMonth
|
||||
return cm >= 12 ? 1 : cm + 1
|
||||
})
|
||||
|
||||
const showReportRow = computed(() => {
|
||||
return (
|
||||
currentMonth.value === reportMonth.value &&
|
||||
(props.summary?.carryFromPreviousYearMinutes ?? 0) > 0
|
||||
)
|
||||
const showCarryRow = computed(() => {
|
||||
if (currentMonth.value !== carryMonth.value) return false
|
||||
if ((props.summary?.carryFromPreviousYearMinutes ?? 0) === 0) return false
|
||||
|
||||
// On the first exercise, hide carry if carry month is before rttStartDate
|
||||
if (props.summary?.rttStartDate) {
|
||||
const startDate = new Date(props.summary.rttStartDate)
|
||||
const viewYear = currentMonth.value >= 6 ? props.summary.year - 1 : props.summary.year
|
||||
const viewDate = new Date(viewYear, currentMonth.value - 1, 1)
|
||||
const startMonthDate = new Date(startDate.getFullYear(), startDate.getMonth(), 1)
|
||||
if (viewDate < startMonthDate) return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
// --- Totals ---
|
||||
// --- Month report row (cumulated balance from previous months) ---
|
||||
|
||||
// Months of the exercise in order, starting from the carry month
|
||||
const exerciseMonths = computed((): number[] => {
|
||||
const start = carryMonth.value
|
||||
const startIdx = orderedMonths.indexOf(start as (typeof orderedMonths)[number])
|
||||
if (startIdx === -1) return [...orderedMonths]
|
||||
return [...orderedMonths.slice(startIdx), ...orderedMonths.slice(0, startIdx)]
|
||||
})
|
||||
|
||||
const monthReport = computed(() => {
|
||||
if (!props.summary) return { base25: 0, bonus25: 0, total25: 0, base50: 0, bonus50: 0, total50: 0, total: 0 }
|
||||
|
||||
const cm = currentMonth.value
|
||||
const cmIdx = exerciseMonths.value.indexOf(cm)
|
||||
const previousMonths = exerciseMonths.value.slice(0, cmIdx)
|
||||
|
||||
// Start from carry (included in the cumulation)
|
||||
let base25 = props.summary.carryBase25Minutes
|
||||
let bonus25 = props.summary.carryBonus25Minutes
|
||||
let base50 = props.summary.carryBase50Minutes
|
||||
let bonus50 = props.summary.carryBonus50Minutes
|
||||
let total = props.summary.carryFromPreviousYearMinutes
|
||||
|
||||
// Add weeks from previous months
|
||||
for (const w of props.summary.weeks) {
|
||||
if (previousMonths.includes(w.month)) {
|
||||
base25 += w.base25Minutes
|
||||
bonus25 += w.bonus25Minutes
|
||||
base50 += w.base50Minutes
|
||||
bonus50 += w.bonus50Minutes
|
||||
total += w.totalMinutes
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract payments from previous months
|
||||
for (const p of props.summary.monthPayments) {
|
||||
if (previousMonths.includes(p.month)) {
|
||||
base25 -= p.paidBase25Minutes
|
||||
bonus25 -= p.paidBonus25Minutes
|
||||
base50 -= p.paidBase50Minutes
|
||||
bonus50 -= p.paidBonus50Minutes
|
||||
total -= (p.paidBase25Minutes + p.paidBonus25Minutes + p.paidBase50Minutes + p.paidBonus50Minutes)
|
||||
}
|
||||
}
|
||||
|
||||
return { base25, bonus25, total25: base25 + bonus25, base50, bonus50, total50: base50 + bonus50, total }
|
||||
})
|
||||
|
||||
const showMonthReportRow = computed(() => {
|
||||
// Not on the carry month — carry row handles that
|
||||
if (currentMonth.value === carryMonth.value) return false
|
||||
|
||||
// On the first exercise (containing rttStartDate), hide report for months before the start date
|
||||
if (props.summary?.rttStartDate) {
|
||||
const startDate = new Date(props.summary.rttStartDate)
|
||||
const startYear = startDate.getFullYear()
|
||||
const startMonth = startDate.getMonth() + 1
|
||||
const viewYear = currentMonth.value >= 6 ? props.summary.year - 1 : props.summary.year
|
||||
const viewDate = new Date(viewYear, currentMonth.value - 1, 1)
|
||||
const startMonthDate = new Date(startYear, startMonth - 1, 1)
|
||||
if (viewDate < startMonthDate) return false
|
||||
}
|
||||
|
||||
const r = monthReport.value
|
||||
return r.total !== 0
|
||||
})
|
||||
|
||||
// --- Totals (current month weeks only) ---
|
||||
|
||||
const totals = computed(() => {
|
||||
const weeks = weeksForCurrentMonth.value
|
||||
const base = {
|
||||
return {
|
||||
overtime: weeks.reduce((s, w) => s + w.overtimeMinutes, 0),
|
||||
base25: weeks.reduce((s, w) => s + w.base25Minutes, 0),
|
||||
bonus25: weeks.reduce((s, w) => s + w.bonus25Minutes, 0),
|
||||
total25: weeks.reduce((s, w) => s + w.base25Minutes + w.bonus25Minutes, 0),
|
||||
base50: weeks.reduce((s, w) => s + w.base50Minutes, 0),
|
||||
bonus50: weeks.reduce((s, w) => s + w.bonus50Minutes, 0),
|
||||
total50: weeks.reduce((s, w) => s + w.base50Minutes + w.bonus50Minutes, 0),
|
||||
total: weeks.reduce((s, w) => s + w.totalMinutes, 0),
|
||||
}
|
||||
|
||||
if (showReportRow.value && props.summary) {
|
||||
base.base25 += props.summary.carryBase25Minutes
|
||||
base.bonus25 += props.summary.carryBonus25Minutes
|
||||
base.base50 += props.summary.carryBase50Minutes
|
||||
base.bonus50 += props.summary.carryBonus50Minutes
|
||||
base.total += props.summary.carryFromPreviousYearMinutes
|
||||
}
|
||||
|
||||
return base
|
||||
})
|
||||
|
||||
const currentPayment = computed(() => {
|
||||
@@ -342,7 +443,7 @@ const paidTotal = computed(() => {
|
||||
})
|
||||
|
||||
const resteTotal = computed(() => {
|
||||
return totals.value.total + paidTotal.value
|
||||
return monthReport.value.total + totals.value.total + paidTotal.value
|
||||
})
|
||||
|
||||
// --- Format ---
|
||||
|
||||
@@ -32,4 +32,5 @@ export type EmployeeRttSummary = {
|
||||
availableMinutes: number
|
||||
weeks: EmployeeRttWeekSummary[]
|
||||
monthPayments: RttMonthPayment[]
|
||||
rttStartDate: string | null
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ final class EmployeeRttSummary
|
||||
public int $currentYearRecoveryMinutes = 0;
|
||||
public int $availableMinutes = 0;
|
||||
public int $totalPaidMinutes = 0;
|
||||
public ?string $rttStartDate = null;
|
||||
|
||||
/** @var list<RttMonthPayment> */
|
||||
public array $monthPayments = [];
|
||||
|
||||
@@ -21,13 +21,18 @@ use DateTimeImmutable;
|
||||
|
||||
final readonly class RttRecoveryComputationService
|
||||
{
|
||||
private ?DateTimeImmutable $rttStartDate;
|
||||
|
||||
public function __construct(
|
||||
private WorkHourRepository $workHourRepository,
|
||||
private AbsenceRepository $absenceRepository,
|
||||
private AbsenceSegmentsResolver $absenceSegmentsResolver,
|
||||
private WorkedHoursCreditPolicy $workedHoursCreditPolicy,
|
||||
private EmployeeContractResolver $contractResolver,
|
||||
) {}
|
||||
string $rttStartDate = '',
|
||||
) {
|
||||
$this->rttStartDate = '' !== $rttStartDate ? new DateTimeImmutable($rttStartDate) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{DateTimeImmutable, DateTimeImmutable}
|
||||
@@ -71,7 +76,7 @@ final readonly class RttRecoveryComputationService
|
||||
return $weeks;
|
||||
}
|
||||
|
||||
public function computeTotalRecoveryForExercise(Employee $employee, int $exerciseYear): WeekRecoveryDetail
|
||||
public function computeTotalRecoveryForExercise(Employee $employee, int $exerciseYear, ?DateTimeImmutable $limitDate = null): WeekRecoveryDetail
|
||||
{
|
||||
[$from, $to] = $this->resolveExerciseBounds($exerciseYear);
|
||||
$weeks = $this->buildWeeksForExercise($from, $to);
|
||||
@@ -85,7 +90,7 @@ final readonly class RttRecoveryComputationService
|
||||
$weeks
|
||||
);
|
||||
|
||||
$byWeek = $this->computeRecoveryByWeek($employee, $weekRanges, $from, $to, null);
|
||||
$byWeek = $this->computeRecoveryByWeek($employee, $weekRanges, $from, $to, $limitDate);
|
||||
|
||||
$total = new WeekRecoveryDetail();
|
||||
foreach ($byWeek as $detail) {
|
||||
@@ -172,6 +177,12 @@ final readonly class RttRecoveryComputationService
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->rttStartDate instanceof DateTimeImmutable && $effectiveEnd < $this->rttStartDate) {
|
||||
$results[$weekKey] = new WeekRecoveryDetail();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$weekDays = [];
|
||||
for ($cursor = $effectiveStart; $cursor <= $effectiveEnd; $cursor = $cursor->modify('+1 day')) {
|
||||
$weekDays[] = $cursor->format('Y-m-d');
|
||||
@@ -203,7 +214,7 @@ final readonly class RttRecoveryComputationService
|
||||
$overtime25StartMinutes = $this->computeWeeklyOvertime25StartMinutes($weekDays, $employeeContractsByDate);
|
||||
$weeklyOvertimeTotalMinutes = $isWeekPresenceTracking
|
||||
? 0
|
||||
: max(0, $weeklyTotalMinutes - $overtimeReferenceMinutes);
|
||||
: $weeklyTotalMinutes - $overtimeReferenceMinutes;
|
||||
|
||||
$base25 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : max(0, min($weeklyTotalMinutes, 43 * 60) - $overtime25StartMinutes);
|
||||
$bonus25 = ($isWeekPresenceTracking || $disableOvertimeBonuses) ? 0 : (int) round($base25 * 0.25);
|
||||
|
||||
@@ -128,7 +128,7 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
|
||||
*/
|
||||
public function computeYearSummary(Employee $employee, int $targetYear): ?array
|
||||
{
|
||||
$firstYear = $this->resolveFirstComputationYear($employee);
|
||||
$firstYear = max($this->resolveFirstComputationYear($employee), $targetYear - 1);
|
||||
if ($targetYear < $firstYear) {
|
||||
$targetYear = $firstYear;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
final readonly class EmployeeRttSummaryProvider implements ProviderInterface
|
||||
{
|
||||
private ?string $rttStartDate;
|
||||
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private RequestStack $requestStack,
|
||||
@@ -34,7 +36,10 @@ final readonly class EmployeeRttSummaryProvider implements ProviderInterface
|
||||
private EmployeeRttBalanceRepository $rttBalanceRepository,
|
||||
private EmployeeRttPaymentRepository $rttPaymentRepository,
|
||||
private RttRecoveryComputationService $rttRecoveryService,
|
||||
) {}
|
||||
string $rttStartDate = '',
|
||||
) {
|
||||
$this->rttStartDate = '' !== $rttStartDate ? $rttStartDate : null;
|
||||
}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): EmployeeRttSummary
|
||||
{
|
||||
@@ -72,9 +77,12 @@ final readonly class EmployeeRttSummaryProvider implements ProviderInterface
|
||||
$weeks
|
||||
);
|
||||
|
||||
$limitDate = null;
|
||||
if ($year > $currentExerciseYear) {
|
||||
$limitDate = $periodFrom->modify('-1 day');
|
||||
} else {
|
||||
// Exclude the current (incomplete) week: limit to last Sunday
|
||||
$isoDay = (int) $today->format('N'); // 1=Monday .. 7=Sunday
|
||||
$limitDate = 7 === $isoDay ? $today : $today->modify('last sunday');
|
||||
}
|
||||
|
||||
$currentByWeekStart = $this->rttRecoveryService->computeRecoveryByWeek($employee, $weekRanges, $periodFrom, $periodTo, $limitDate);
|
||||
@@ -90,7 +98,15 @@ final readonly class EmployeeRttSummaryProvider implements ProviderInterface
|
||||
$summary->carryBonus50Minutes = $carry->bonus50Minutes;
|
||||
$summary->currentYearRecoveryMinutes = array_sum(array_map(static fn ($d) => $d->totalMinutes, $currentByWeekStart));
|
||||
$summary->availableMinutes = $summary->carryFromPreviousYearMinutes + $summary->currentYearRecoveryMinutes;
|
||||
$summary->weeks = array_map(
|
||||
|
||||
// Pass rttStartDate only if it falls within this exercise
|
||||
if (null !== $this->rttStartDate) {
|
||||
$startDate = new DateTimeImmutable($this->rttStartDate);
|
||||
if ($startDate >= $periodFrom && $startDate <= $periodTo) {
|
||||
$summary->rttStartDate = $this->rttStartDate;
|
||||
}
|
||||
}
|
||||
$summary->weeks = array_map(
|
||||
static function (array $week) use ($currentByWeekStart) {
|
||||
$detail = $currentByWeekStart[$week['start']->format('Y-m-d')] ?? new WeekRecoveryDetail();
|
||||
|
||||
@@ -110,6 +126,37 @@ final readonly class EmployeeRttSummaryProvider implements ProviderInterface
|
||||
$weekRanges
|
||||
);
|
||||
|
||||
// Post-process: distribute deficit weeks across cumulative balance (50% first, then 25%)
|
||||
$cumulative50 = $carry->base50Minutes + $carry->bonus50Minutes;
|
||||
$cumulative25 = $carry->base25Minutes + $carry->bonus25Minutes;
|
||||
|
||||
foreach ($summary->weeks as $i => $week) {
|
||||
if ($week->totalMinutes >= 0) {
|
||||
$cumulative50 += $week->base50Minutes + $week->bonus50Minutes;
|
||||
$cumulative25 += $week->base25Minutes + $week->bonus25Minutes;
|
||||
} else {
|
||||
$deficit = -$week->totalMinutes;
|
||||
$from50 = min($deficit, max(0, $cumulative50));
|
||||
$from25 = $deficit - $from50;
|
||||
|
||||
$cumulative50 -= $from50;
|
||||
$cumulative25 -= $from25;
|
||||
|
||||
$summary->weeks[$i] = new EmployeeRttWeekSummary(
|
||||
month: $week->month,
|
||||
weekNumber: $week->weekNumber,
|
||||
weekStart: $week->weekStart,
|
||||
weekEnd: $week->weekEnd,
|
||||
overtimeMinutes: $week->overtimeMinutes,
|
||||
base25Minutes: $from25 > 0 ? -$from25 : 0,
|
||||
bonus25Minutes: 0,
|
||||
base50Minutes: $from50 > 0 ? -$from50 : 0,
|
||||
bonus50Minutes: 0,
|
||||
totalMinutes: $week->totalMinutes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$payments = $this->rttPaymentRepository->findByEmployeeAndYear($employee, $year);
|
||||
$monthBuckets = [];
|
||||
|
||||
|
||||
@@ -9,13 +9,10 @@ use ApiPlatform\State\ProviderInterface;
|
||||
use App\Entity\Employee;
|
||||
use App\Enum\ContractNature;
|
||||
use App\Enum\ContractType;
|
||||
use App\Enum\LeaveRuleCode;
|
||||
use App\Enum\TrackingMode;
|
||||
use App\Repository\EmployeeLeaveBalanceRepository;
|
||||
use App\Repository\EmployeeRepository;
|
||||
use App\Repository\EmployeeRttBalanceRepository;
|
||||
use App\Repository\EmployeeRttPaymentRepository;
|
||||
use App\Service\PublicHolidayServiceInterface;
|
||||
use App\Service\Rtt\RttRecoveryComputationService;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -30,8 +27,7 @@ class LeaveRecapPrintProvider implements ProviderInterface
|
||||
public function __construct(
|
||||
private Environment $twig,
|
||||
private EmployeeRepository $employeeRepository,
|
||||
private EmployeeLeaveBalanceRepository $leaveBalanceRepository,
|
||||
private PublicHolidayServiceInterface $publicHolidayService,
|
||||
private EmployeeLeaveSummaryProvider $leaveSummaryProvider,
|
||||
private RttRecoveryComputationService $rttRecoveryService,
|
||||
private EmployeeRttBalanceRepository $rttBalanceRepository,
|
||||
private EmployeeRttPaymentRepository $rttPaymentRepository,
|
||||
@@ -95,26 +91,24 @@ class LeaveRecapPrintProvider implements ProviderInterface
|
||||
$nature = ContractNature::tryFrom($employee->getCurrentContractNature());
|
||||
$isInterim = ContractNature::INTERIM === $nature;
|
||||
|
||||
$acquiredDays = 0.0;
|
||||
$cpN1Remaining = 0.0;
|
||||
$cpN = '-';
|
||||
$acquiredSaturdays = '-';
|
||||
$rtt = '-';
|
||||
|
||||
if (!$isInterim) {
|
||||
$leaveYear = $this->resolveLeaveYear($employee, $today);
|
||||
$ruleCode = $isForfait ? LeaveRuleCode::FORFAIT_218 : LeaveRuleCode::CDI_CDD_NON_FORFAIT;
|
||||
$balance = $this->leaveBalanceRepository->findOneByEmployeeRuleAndYear($employee, $ruleCode, $leaveYear);
|
||||
$leaveYear = $this->leaveSummaryProvider->resolveLeaveYearForToday($employee);
|
||||
$yearSummary = $this->leaveSummaryProvider->computeYearSummary($employee, $leaveYear);
|
||||
|
||||
if (null !== $balance) {
|
||||
$acquiredDays = $balance->getOpeningDays();
|
||||
$acquiredSaturdays = $isForfait ? '-' : (string) $balance->getOpeningSaturdays();
|
||||
}
|
||||
|
||||
if ($isForfait) {
|
||||
try {
|
||||
$cpN = (string) $this->computeForfaitAcquiredDays($employee, $today);
|
||||
} catch (Throwable) {
|
||||
$cpN = '-';
|
||||
if (null !== $yearSummary) {
|
||||
if ($isForfait) {
|
||||
$cpN1Remaining = round($yearSummary['previousYearRemainingDays'], 2);
|
||||
$cpN = (string) round($yearSummary['acquiredDays'], 2);
|
||||
$acquiredSaturdays = '-';
|
||||
} else {
|
||||
$cpN1Remaining = round($yearSummary['remainingDays'], 2);
|
||||
$cpN = (string) round($yearSummary['accruingDays'], 2);
|
||||
$acquiredSaturdays = (string) round($yearSummary['remainingSaturdays'], 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,31 +125,23 @@ class LeaveRecapPrintProvider implements ProviderInterface
|
||||
'lastName' => $employee->getLastName(),
|
||||
'firstName' => $employee->getFirstName(),
|
||||
'contractName' => $contractName,
|
||||
'acquiredDays' => $acquiredDays,
|
||||
'cpN1Remaining' => $cpN1Remaining,
|
||||
'cpN' => $cpN,
|
||||
'acquiredSaturdays' => $acquiredSaturdays,
|
||||
'rtt' => $rtt,
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveLeaveYear(Employee $employee, DateTimeImmutable $today): int
|
||||
{
|
||||
if (ContractType::FORFAIT === $employee->getContract()?->getType()) {
|
||||
return (int) $today->format('Y');
|
||||
}
|
||||
|
||||
$month = (int) $today->format('n');
|
||||
$year = (int) $today->format('Y');
|
||||
|
||||
return $month >= 6 ? $year + 1 : $year;
|
||||
}
|
||||
|
||||
private function computeAvailableRttMinutes(Employee $employee, DateTimeImmutable $today): int
|
||||
{
|
||||
$month = (int) $today->format('n');
|
||||
$year = (int) $today->format('Y');
|
||||
$exerciseYear = $month >= 6 ? $year + 1 : $year;
|
||||
|
||||
// Exclude incomplete current week: limit to last Sunday
|
||||
$isoDay = (int) $today->format('N');
|
||||
$limitDate = 7 === $isoDay ? $today : $today->modify('last sunday');
|
||||
|
||||
// Carry from previous exercise
|
||||
$carry = 0;
|
||||
$balance = $this->rttBalanceRepository->findOneByEmployeeAndYear($employee, $exerciseYear);
|
||||
@@ -166,8 +152,8 @@ class LeaveRecapPrintProvider implements ProviderInterface
|
||||
$carry = $previousTotal->totalMinutes;
|
||||
}
|
||||
|
||||
// Current exercise
|
||||
$current = $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $exerciseYear);
|
||||
// Current exercise (limited to completed weeks)
|
||||
$current = $this->rttRecoveryService->computeTotalRecoveryForExercise($employee, $exerciseYear, $limitDate);
|
||||
|
||||
// Paid RTT
|
||||
$paid = 0;
|
||||
@@ -179,41 +165,6 @@ class LeaveRecapPrintProvider implements ProviderInterface
|
||||
return $carry + $current->totalMinutes - $paid;
|
||||
}
|
||||
|
||||
private function computeForfaitAcquiredDays(Employee $employee, DateTimeImmutable $today): float
|
||||
{
|
||||
$year = (int) $today->format('Y');
|
||||
$from = new DateTimeImmutable(sprintf('%d-01-01', $year));
|
||||
$to = new DateTimeImmutable(sprintf('%d-12-31', $year));
|
||||
|
||||
$contractStartRaw = $employee->getCurrentContractStartDate();
|
||||
if (null !== $contractStartRaw && '' !== trim($contractStartRaw)) {
|
||||
$contractStart = DateTimeImmutable::createFromFormat('!Y-m-d', trim($contractStartRaw));
|
||||
if ($contractStart instanceof DateTimeImmutable && $contractStart > $from) {
|
||||
$from = $contractStart;
|
||||
}
|
||||
}
|
||||
|
||||
$contractEndRaw = $employee->getCurrentContractEndDate();
|
||||
if (null !== $contractEndRaw && '' !== trim($contractEndRaw)) {
|
||||
$contractEnd = DateTimeImmutable::createFromFormat('!Y-m-d', trim($contractEndRaw));
|
||||
if ($contractEnd instanceof DateTimeImmutable && $contractEnd < $to) {
|
||||
$to = $contractEnd;
|
||||
}
|
||||
}
|
||||
|
||||
$holidays = $this->publicHolidayService->getHolidaysDayByYears('metropole', (string) $year);
|
||||
$businessDays = 0;
|
||||
|
||||
for ($cursor = $from; $cursor <= $to; $cursor = $cursor->modify('+1 day')) {
|
||||
$weekDay = (int) $cursor->format('N');
|
||||
if ($weekDay <= 5 && !isset($holidays[$cursor->format('Y-m-d')])) {
|
||||
++$businessDays;
|
||||
}
|
||||
}
|
||||
|
||||
return (float) max(0, $businessDays - 218);
|
||||
}
|
||||
|
||||
private function formatMinutes(int $minutes): string
|
||||
{
|
||||
if (0 === $minutes) {
|
||||
|
||||
@@ -298,7 +298,7 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
$nightMinutesTotal += $wh->getNightHoursMinutes() ?? 0;
|
||||
$dayMin = $wh->getDayHoursMinutes() ?? 0;
|
||||
$nightMin = $wh->getNightHoursMinutes() ?? 0;
|
||||
if ($nightMin > $dayMin && $nightMin > 0) {
|
||||
if (($nightMin > $dayMin && $nightMin > 0) || $nightMin >= 240) {
|
||||
++$nightBasketCount;
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ class SalaryRecapPrintProvider implements ProviderInterface
|
||||
} else {
|
||||
$metrics = $this->computeNightMinutes($wh);
|
||||
$nightMinutesTotal += $metrics['nightMinutes'];
|
||||
if ($metrics['nightMinutes'] > $metrics['dayMinutes'] && $metrics['nightMinutes'] > 0) {
|
||||
if (($metrics['nightMinutes'] > $metrics['dayMinutes'] && $metrics['nightMinutes'] > 0) || $metrics['nightMinutes'] >= 240) {
|
||||
++$nightBasketCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
||||
$present = min(1.0, $morning + $afternoon + $creditedPresence);
|
||||
}
|
||||
|
||||
$hasNightBasket = $nightMinutes > $dayMinutes && $nightMinutes > 0;
|
||||
$hasNightBasket = ($nightMinutes > $dayMinutes && $nightMinutes > 0) || $nightMinutes >= 240;
|
||||
if ($hasNightBasket) {
|
||||
++$weeklyNightBasketCount;
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@
|
||||
<tr>
|
||||
<th style="text-align: left;">Nom</th>
|
||||
<th>Contrat</th>
|
||||
<th>CP Acquis<br>(N-1)</th>
|
||||
<th>CP N-1<br>restant</th>
|
||||
<th>Samedi<br>restant</th>
|
||||
<th>CP<br>N</th>
|
||||
<th>Samedi<br>acquis</th>
|
||||
<th>RTT</th>
|
||||
<th style="width: 40mm;">Observations</th>
|
||||
</tr>
|
||||
@@ -105,9 +105,9 @@
|
||||
<tr>
|
||||
<td class="name">{{ row.lastName }} {{ row.firstName }}</td>
|
||||
<td class="base">{{ row.contractName ?? '' }}</td>
|
||||
<td class="num">{{ row.acquiredDays }}</td>
|
||||
<td class="num">{{ row.cpN }}</td>
|
||||
<td class="num">{{ row.cpN1Remaining }}</td>
|
||||
<td class="num">{{ row.acquiredSaturdays }}</td>
|
||||
<td class="num">{{ row.cpN }}</td>
|
||||
<td class="num">{{ row.rtt }}</td>
|
||||
<td class="obs"></td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user