feat : sélecteur d'année sur l'onglet Congés de la fiche employé

Permet de consulter les exercices passés (calendrier + compteurs) sur
l'onglet Congés. La plage proposée est bornée par max(début historique
contrat, RTT_START_DATE) pour ne pas remonter avant la mise en service
du logiciel. Édition des stocks N-1 et fractionnés verrouillée sur
exercices clos.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 09:51:19 +02:00
parent 3ec0d4b074
commit 7cadcfa362
10 changed files with 231 additions and 12 deletions

View File

@@ -69,6 +69,13 @@
- 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 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é. - **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é.
## Onglet Congés (fiche employé)
- Calendrier annuel des congés (`frontend/components/employees/LeaveTab.vue`) — période = Janvier→Décembre pour FORFAIT, Juin(N-1)→Mai(N) pour les autres contrats. Règle pilotée par le **contrat courant** (cf. `EmployeeLeaveSummaryProvider::resolveYear`), même quand on consulte une année passée.
- **Sélecteur d'année** en pied de calendrier (zone scrollable, à gauche). Plage : de l'exercice courant jusqu'à `max(floor_contrat, floor_data_start_date)``floor_contrat` = premier exercice avec contrat ouvert (`employee.contractHistory[].startDate`) ; `floor_data_start_date` = exercice contenant `RTT_START_DATE` (env, ex. `2026-02-23` → exercice 2026). Le double plancher empêche de remonter avant la mise en service du logiciel. Format : `2026` pour FORFAIT, `Juin 2025 → Mai 2026` sinon.
- Changement d'année → recharge complète de l'onglet via `useEmployeeLeave.setSelectedLeaveYear(year)` (reload de `getEmployeeLeaveSummary?year=YYYY` + `listAbsences` + `listPublicHolidays`). Backend : filtre `?year=YYYY` validé 2000-2100, et `EmployeeLeaveSummary` expose `dataStartDate` (env `RTT_START_DATE`, injecté via `services.yaml`).
- Sur un exercice passé (`selectedYear !== currentYear`), les boutons crayon **Jours fractionnés** et **Année N-1 payés** sont **désactivés** : pas d'édition rétroactive des stocks de report.
- Doc : `doc/leave-tab.md`.
## Récap. congés (écran) ## Récap. congés (écran)
- Accès via sidebar `Récap. congés`, conditionné au flag `User.hasLeaveRecapAccess` (défaut `false`) — activé au create/edit user. Le flag s'applique à tous les profils, y compris admin. - Accès via sidebar `Récap. congés`, conditionné au flag `User.hasLeaveRecapAccess` (défaut `false`) — activé au create/edit user. Le flag s'applique à tous les profils, y compris admin.
- Scope : `ROLE_ADMIN` → tous les employés, `ROLE_USER` (chef de site) → employés de ses sites, `ROLE_SELF` → sa ligne - Scope : `ROLE_ADMIN` → tous les employés, `ROLE_USER` (chef de site) → employés de ses sites, `ROLE_SELF` → sa ligne

View File

@@ -35,6 +35,10 @@ services:
arguments: arguments:
$rttStartDate: '%env(RTT_START_DATE)%' $rttStartDate: '%env(RTT_START_DATE)%'
App\State\EmployeeLeaveSummaryProvider:
arguments:
$dataStartDate: '%env(RTT_START_DATE)%'
App\Repository\Contract\AbsenceReadRepositoryInterface: '@App\Repository\AbsenceRepository' App\Repository\Contract\AbsenceReadRepositoryInterface: '@App\Repository\AbsenceRepository'
App\Repository\Contract\EmployeeContractPeriodReadRepositoryInterface: '@App\Repository\EmployeeContractPeriodRepository' App\Repository\Contract\EmployeeContractPeriodReadRepositoryInterface: '@App\Repository\EmployeeContractPeriodRepository'
App\Repository\Contract\EmployeeScopedRepositoryInterface: '@App\Repository\EmployeeRepository' App\Repository\Contract\EmployeeScopedRepositoryInterface: '@App\Repository\EmployeeRepository'

60
doc/leave-tab.md Normal file
View File

@@ -0,0 +1,60 @@
# Onglet "Congés" — fiche employé
## Vue d'ensemble
L'onglet **Congés** de la fiche employé (`frontend/components/employees/LeaveTab.vue`) affiche :
- un bandeau de compteurs (acquis, pris, reste, en cours d'acquisition, N-1 ou samedis selon le contrat) ;
- un calendrier annuel coloré des congés posés (12 mois en grille 4×3) ;
- pour chaque mois, le nombre de jours de présence (`presenceDaysByMonth`) ;
- un sélecteur d'année en pied de calendrier.
## Période affichée
La période dépend du **type de contrat actuel** de l'employé :
| Type de contrat | Période affichée |
|-------------------|--------------------------------|
| FORFAIT | Janvier → Décembre (année civile) |
| Autres | Juin (Y-1) → Mai (Y) (exercice CP) |
Cette règle suit `EmployeeLeaveSummaryProvider::resolveYear()` côté backend : la sélection FORFAIT vs non-FORFAIT se fait toujours sur le contrat **courant**, pas sur celui qui était en vigueur à l'année consultée.
## Sélecteur d'année
Position : **en bas du calendrier**, à gauche, à l'intérieur de la zone scrollable. Il scrolle donc avec les mois et apparaît sous la grille.
Plage proposée :
- du plus récent (= année courante) au plus ancien ;
- **double plancher** : l'année minimum est `max(floor_historique_contrat, floor_data_start_date)`
- **floor_historique_contrat** : dérivé de `employee.contractHistory[].startDate` — premier exercice où l'employé avait un contrat ouvert
- **floor_data_start_date** : dérivé de l'env `RTT_START_DATE` (date de mise en service du logiciel, ex. `2026-02-23` → exercice 2026 / année forfait 2026). Aucune donnée historique n'existe avant cette date, donc on ne propose pas d'années antérieures même si le contrat de l'employé est plus ancien.
- la valeur est exposée par l'API `GET /employees/{id}/leave-summary` via le champ `dataStartDate` (peuplé depuis l'env serveur).
- en cas d'historique manquant **et** d'env absente, la plage se réduit à l'année courante.
Format des libellés :
- FORFAIT : `2026`, `2025`, `2024`
- Autres : `Juin 2025 → Mai 2026`, `Juin 2024 → Mai 2025`
Comportement :
- changer d'année recharge l'intégralité de l'onglet (`getEmployeeLeaveSummary?year=YYYY` + `listAbsences` + `listPublicHolidays`) ;
- les compteurs du bandeau reflètent l'année sélectionnée.
## Verrouillage des éditions sur années passées
Quand `selectedYear !== currentYear` (consultation d'une année antérieure) :
- le bouton crayon **Jours fractionnés** (non-FORFAIT) est désactivé ;
- le bouton crayon **Année N-1 payés** (FORFAIT) est désactivé.
Justification : modifier rétroactivement les stocks de report ou les jours fractionnés d'un exercice clos décalerait silencieusement les soldes de toutes les années postérieures. La consultation reste possible, l'édition non.
## Implémentation
- Composable : `frontend/composables/useEmployeeLeave.ts`
- État : `selectedLeaveYear`, computed `currentLeaveYear`, `availableLeaveYears`
- API : `setSelectedLeaveYear(year)`, `loadLeaveData()`, `resetLoaded()`
- `resetLoaded()` (appelé au changement d'employé) remet `selectedLeaveYear = null` pour que la valeur par défaut soit recalculée à partir du nouveau contrat.
- Composant : `frontend/components/employees/LeaveTab.vue`
- Props : `selectedYear`, `availableYears`, `currentYear`
- Event : `update-selected-year`
- Page : `frontend/pages/employees/[id].vue` (câble le composable au composant)
- Backend : `EmployeeLeaveSummaryProvider` reçoit `RTT_START_DATE` via `services.yaml` (argument `$dataStartDate`) et l'expose dans la réponse `EmployeeLeaveSummary.dataStartDate`. Le filtrage `?year=YYYY` était déjà accepté (validation 20002100).

View File

@@ -39,6 +39,8 @@
</div> </div>
<button <button
class="flex items-center" class="flex items-center"
:class="isHistoricalYear ? 'opacity-40 cursor-not-allowed' : ''"
:disabled="isHistoricalYear"
@click="openPaidLeaveDrawer" @click="openPaidLeaveDrawer"
> >
<Icon name="mdi:edit-box" size="24"/> <Icon name="mdi:edit-box" size="24"/>
@@ -51,6 +53,8 @@
</div> </div>
<button <button
class="flex items-center" class="flex items-center"
:class="isHistoricalYear ? 'opacity-40 cursor-not-allowed' : ''"
:disabled="isHistoricalYear"
@click="openFractionedDrawer" @click="openFractionedDrawer"
> >
<Icon name="mdi:edit-box" size="24"/> <Icon name="mdi:edit-box" size="24"/>
@@ -90,6 +94,22 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mt-6 flex items-center gap-3">
<label for="leave-year-select" class="text-md font-semibold text-primary-500 uppercase">
{{ isForfaitRule ? 'Année :' : 'Exercice :' }}
</label>
<select
id="leave-year-select"
:value="selectedYear ?? ''"
:disabled="!availableYears.length"
class="border border-primary-500 rounded-md px-3 py-1 text-md font-semibold text-primary-500 bg-white focus:outline-none focus:ring-2 focus:ring-secondary-500/20 disabled:opacity-50"
@change="handleYearChange"
>
<option v-for="option in availableYears" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</div>
</div> </div>
<AppDrawer v-model="isFractionedDrawerOpen" title="Jours fractionnés"> <AppDrawer v-model="isFractionedDrawerOpen" title="Jours fractionnés">
<form class="space-y-4" @submit.prevent="handleSubmitFractioned"> <form class="space-y-4" @submit.prevent="handleSubmitFractioned">
@@ -173,17 +193,39 @@ type DayLeaveState = {
colors: string[] colors: string[]
} }
type LeaveYearOption = {
value: number
label: string
}
const props = defineProps<{ const props = defineProps<{
absences: Absence[] absences: Absence[]
summary: EmployeeLeaveSummary | null summary: EmployeeLeaveSummary | null
publicHolidays: Record<string, string> publicHolidays: Record<string, string>
selectedYear: number | null
availableYears: LeaveYearOption[]
currentYear: number | null
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
(event: 'update-fractioned-days', days: number): void (event: 'update-fractioned-days', days: number): void
(event: 'update-paid-leave-days', days: number): void (event: 'update-paid-leave-days', days: number): void
(event: 'update-selected-year', year: number): void
}>() }>()
const isHistoricalYear = computed(() =>
props.selectedYear !== null
&& props.currentYear !== null
&& props.selectedYear !== props.currentYear
)
const handleYearChange = (event: Event) => {
const target = event.target as HTMLSelectElement
const value = Number(target.value)
if (Number.isNaN(value)) return
emit('update-selected-year', value)
}
const isFractionedDrawerOpen = ref(false) const isFractionedDrawerOpen = ref(false)
const fractionedForm = reactive({days: 0}) const fractionedForm = reactive({days: 0})
@@ -239,6 +281,7 @@ const currentYearTakenDays = computed(() => {
}) })
const displayedYear = computed(() => { const displayedYear = computed(() => {
if (props.selectedYear) return props.selectedYear
if (props.summary?.year) return props.summary.year if (props.summary?.year) return props.summary.year
const today = new Date() const today = new Date()
const year = today.getFullYear() const year = today.getFullYear()

View File

@@ -7,27 +7,91 @@ import { listAbsences } from '~/services/absences'
import { getEmployeeLeaveSummary, updateFractionedDays, updatePaidLeaveDays } from '~/services/employee-leave-summary' import { getEmployeeLeaveSummary, updateFractionedDays, updatePaidLeaveDays } from '~/services/employee-leave-summary'
import { listPublicHolidays } from '~/services/public-holidays' import { listPublicHolidays } from '~/services/public-holidays'
export type LeaveYearOption = {
value: number
label: string
}
export const useEmployeeLeave = (employee: Ref<Employee | null>, reloadEmployee: () => Promise<void>) => { export const useEmployeeLeave = (employee: Ref<Employee | null>, reloadEmployee: () => Promise<void>) => {
const employeeAbsences = ref<Absence[]>([]) const employeeAbsences = ref<Absence[]>([])
const leaveSummary = ref<EmployeeLeaveSummary | null>(null) const leaveSummary = ref<EmployeeLeaveSummary | null>(null)
const publicHolidays = ref<Record<string, string>>({}) const publicHolidays = ref<Record<string, string>>({})
const isLeaveLoading = ref(false) const isLeaveLoading = ref(false)
const leaveDataLoaded = ref(false) const leaveDataLoaded = ref(false)
const selectedLeaveYear = ref<number | null>(null)
const getLeaveYear = () => { const isForfaitContract = (emp: Employee | null) =>
const now = new Date() emp?.contract?.type === CONTRACT_TYPES.FORFAIT
const isForfait = employee.value?.contract?.type === CONTRACT_TYPES.FORFAIT
return isForfait const computeLeaveYearForDate = (emp: Employee | null, date: Date): number => {
? now.getFullYear() if (isForfaitContract(emp)) return date.getFullYear()
: (now.getMonth() >= 5 ? now.getFullYear() + 1 : now.getFullYear()) return date.getMonth() >= 5 ? date.getFullYear() + 1 : date.getFullYear()
}
const currentLeaveYear = computed<number | null>(() => {
if (!employee.value) return null
return computeLeaveYearForDate(employee.value, new Date())
})
const formatLeaveYearLabel = (year: number, isForfait: boolean): string => {
if (isForfait) return String(year)
return `Juin ${year - 1} → Mai ${year}`
}
const availableLeaveYears = computed<LeaveYearOption[]>(() => {
if (!employee.value || currentLeaveYear.value === null) return []
const isForfait = isForfaitContract(employee.value)
const current = currentLeaveYear.value
const startDates: string[] = []
for (const period of employee.value.contractHistory ?? []) {
if (period.startDate) startDates.push(period.startDate)
}
if (employee.value.entryDate) startDates.push(employee.value.entryDate)
let contractFloor = current
for (const raw of startDates) {
const date = new Date(`${raw.substring(0, 10)}T00:00:00`)
if (Number.isNaN(date.getTime())) continue
const leaveYear = computeLeaveYearForDate(employee.value, date)
if (leaveYear < contractFloor) contractFloor = leaveYear
}
// Hard floor : data-start-date (env RTT_START_DATE) — le logiciel n'a pas
// d'historique avant cette date, inutile de proposer des années antérieures.
let dataFloor: number | null = null
const dataStart = leaveSummary.value?.dataStartDate
if (dataStart) {
const dataStartDate = new Date(`${dataStart.substring(0, 10)}T00:00:00`)
if (!Number.isNaN(dataStartDate.getTime())) {
dataFloor = computeLeaveYearForDate(employee.value, dataStartDate)
}
}
const minYear = dataFloor !== null ? Math.max(contractFloor, dataFloor) : contractFloor
const years: LeaveYearOption[] = []
for (let y = current; y >= minYear; y -= 1) {
years.push({ value: y, label: formatLeaveYearLabel(y, isForfait) })
}
return years
})
const initSelectedLeaveYear = () => {
if (selectedLeaveYear.value !== null) return
if (currentLeaveYear.value !== null) {
selectedLeaveYear.value = currentLeaveYear.value
}
} }
const loadLeaveData = async () => { const loadLeaveData = async () => {
if (!employee.value || isLeaveLoading.value) return if (!employee.value || isLeaveLoading.value) return
initSelectedLeaveYear()
if (selectedLeaveYear.value === null) return
isLeaveLoading.value = true isLeaveLoading.value = true
try { try {
const isForfait = employee.value.contract?.type === CONTRACT_TYPES.FORFAIT const isForfait = isForfaitContract(employee.value)
const leaveYear = getLeaveYear() const leaveYear = selectedLeaveYear.value
const from = isForfait ? `${leaveYear}-01-01` : `${leaveYear - 1}-06-01` const from = isForfait ? `${leaveYear}-01-01` : `${leaveYear - 1}-06-01`
const to = isForfait ? `${leaveYear}-12-31` : `${leaveYear}-05-31` const to = isForfait ? `${leaveYear}-12-31` : `${leaveYear}-05-31`
const holidayYears = isForfait ? [leaveYear] : [leaveYear - 1, leaveYear] const holidayYears = isForfait ? [leaveYear] : [leaveYear - 1, leaveYear]
@@ -46,8 +110,16 @@ export const useEmployeeLeave = (employee: Ref<Employee | null>, reloadEmployee:
} }
} }
const setSelectedLeaveYear = async (year: number) => {
if (selectedLeaveYear.value === year) return
selectedLeaveYear.value = year
leaveDataLoaded.value = false
await loadLeaveData()
}
const resetLoaded = () => { const resetLoaded = () => {
leaveDataLoaded.value = false leaveDataLoaded.value = false
selectedLeaveYear.value = null
} }
const submitFractionedDays = async (days: number) => { const submitFractionedDays = async (days: number) => {
@@ -70,6 +142,10 @@ export const useEmployeeLeave = (employee: Ref<Employee | null>, reloadEmployee:
publicHolidays, publicHolidays,
isLeaveLoading, isLeaveLoading,
leaveDataLoaded, leaveDataLoaded,
selectedLeaveYear,
currentLeaveYear,
availableLeaveYears,
setSelectedLeaveYear,
loadLeaveData, loadLeaveData,
resetLoaded, resetLoaded,
submitFractionedDays, submitFractionedDays,

View File

@@ -457,6 +457,17 @@ export const documentationSections: DocSection[] = [
{ type: 'paragraph', content: 'Compteurs visibles sur l\'onglet Congé de la fiche employé : acquis, en cours d\'acquisition, pris, restant.' }, { type: 'paragraph', content: 'Compteurs visibles sur l\'onglet Congé de la fiche employé : acquis, en cours d\'acquisition, pris, restant.' },
], ],
}, },
{
id: 'onglet-conges-fiche-employe',
title: 'Onglet Congés (fiche employé)',
requiredLevel: 'admin',
blocks: [
{ type: 'paragraph', content: 'L\'onglet "Congés" sur la fiche employé affiche un calendrier annuel des congés posés (12 mois en grille 4×3) ainsi que les compteurs (acquis, pris, reste, en cours d\'acquisition, N-1 ou samedis selon le contrat).' },
{ type: 'paragraph', content: 'La période affichée dépend du type de contrat actuel : Janvier → Décembre pour FORFAIT, Juin (N-1) → Mai (N) pour les autres contrats.' },
{ type: 'paragraph', content: 'Un sélecteur d\'année est disponible en bas du calendrier (zone scrollable, à gauche). Il permet de consulter les exercices passés. La plage proposée part de l\'exercice courant et remonte jusqu\'au plus récent entre (a) le premier exercice où l\'employé avait un contrat ouvert et (b) l\'exercice de mise en service du logiciel — il est inutile de remonter plus loin, aucune donnée n\'a été saisie avant.' },
{ type: 'note', content: 'Sur un exercice passé, les boutons d\'édition "Jours fractionnés" et "Année N-1 payés" sont désactivés. La consultation reste possible, mais on n\'autorise pas la modification rétroactive d\'un exercice clos.' },
],
},
{ {
id: 'ecran-recap-conges', id: 'ecran-recap-conges',
title: 'Écran Récap. congés', title: 'Écran Récap. congés',

View File

@@ -160,8 +160,12 @@
:absences="employeeAbsences" :absences="employeeAbsences"
:summary="leaveSummary" :summary="leaveSummary"
:public-holidays="publicHolidays" :public-holidays="publicHolidays"
:selected-year="selectedLeaveYear"
:available-years="availableLeaveYears"
:current-year="currentLeaveYear"
@update-fractioned-days="submitFractionedDays" @update-fractioned-days="submitFractionedDays"
@update-paid-leave-days="submitPaidLeaveDays" @update-paid-leave-days="submitPaidLeaveDays"
@update-selected-year="setSelectedLeaveYear"
/> />
</div> </div>
<div v-else-if="showRttTab && activeTab === 'rtt'" class="h-full"> <div v-else-if="showRttTab && activeTab === 'rtt'" class="h-full">
@@ -253,6 +257,10 @@ const {
leaveSummary, leaveSummary,
rttSummary, rttSummary,
publicHolidays, publicHolidays,
selectedLeaveYear,
currentLeaveYear,
availableLeaveYears,
setSelectedLeaveYear,
showLeaveTab, showLeaveTab,
showRttTab, showRttTab,
contractHistory, contractHistory,

View File

@@ -16,5 +16,6 @@ export type EmployeeLeaveSummary = {
previousYearPaidDays: number previousYearPaidDays: number
presenceDaysByMonth: Record<string, number> presenceDaysByMonth: Record<string, number>
presenceDaysToToday: number presenceDaysToToday: number
dataStartDate: string | null
} }

View File

@@ -41,4 +41,7 @@ final class EmployeeLeaveSummary
/** Cumul des jours de présence depuis le début de l'année de congé jusqu'à aujourd'hui (forfait). */ /** Cumul des jours de présence depuis le début de l'année de congé jusqu'à aujourd'hui (forfait). */
public float $presenceDaysToToday = 0.0; public float $presenceDaysToToday = 0.0;
/** Date de mise en service du logiciel (env RTT_START_DATE) — borne minimale pour les sélecteurs d'historique. */
public ?string $dataStartDate = null;
} }

View File

@@ -45,6 +45,8 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
private const float CDI_NON_FORFAIT_4H_SATURDAY_ACCRUAL_PER_MONTH = 0.0; private const float CDI_NON_FORFAIT_4H_SATURDAY_ACCRUAL_PER_MONTH = 0.0;
private const float LONG_MALADIE_MONTHLY_ACCRUAL = 2.0; private const float LONG_MALADIE_MONTHLY_ACCRUAL = 2.0;
private ?string $dataStartDate;
public function __construct( public function __construct(
private Security $security, private Security $security,
private RequestStack $requestStack, private RequestStack $requestStack,
@@ -58,7 +60,10 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
private PublicHolidayServiceInterface $publicHolidayService, private PublicHolidayServiceInterface $publicHolidayService,
private SuspensionDaysCalculator $suspensionDaysCalculator, private SuspensionDaysCalculator $suspensionDaysCalculator,
private WorkHourRepository $workHourRepository, private WorkHourRepository $workHourRepository,
) {} string $dataStartDate = '',
) {
$this->dataStartDate = '' !== $dataStartDate ? $dataStartDate : null;
}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): EmployeeLeaveSummary public function provide(Operation $operation, array $uriVariables = [], array $context = []): EmployeeLeaveSummary
{ {
@@ -83,9 +88,10 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
$year = $this->resolveYear($employee); $year = $this->resolveYear($employee);
$summary = new EmployeeLeaveSummary(); $summary = new EmployeeLeaveSummary();
$summary->year = $year; $summary->year = $year;
$summary->ruleCode = LeaveRuleCode::UNSUPPORTED->value; $summary->ruleCode = LeaveRuleCode::UNSUPPORTED->value;
$summary->dataStartDate = $this->dataStartDate;
$yearSummary = $this->computeYearSummary($employee, $year); $yearSummary = $this->computeYearSummary($employee, $year);
if (null === $yearSummary) { if (null === $yearSummary) {