feat(leave) : prorate forfait work-target days in employee header

The header hardcoded '218 jours' and '218 - presence restants', wrong for a
forfait entered mid-year. Expose forfaitWorkTargetDays = businessDays(period) -
acquiredDays (218 full year, prorated otherwise) and show
'Forfait - {target} jours ({presence} présence · {target-presence} restants)'.
Grégory: 155 jours (11 présence · 144 restants).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 17:51:48 +02:00
parent 5f2ab39cd7
commit e310f65d81
4 changed files with 28 additions and 3 deletions

View File

@@ -14,10 +14,16 @@ export const useEmployeeDetailPage = () => {
const showLeaveTab = computed(() => employee.value?.currentContractNature !== 'INTERIM')
const showRttTab = computed(() => phase.selectedPhase.value?.contractType !== CONTRACT_TYPES.FORFAIT)
const isForfait = computed(() => phase.selectedPhase.value?.contractType === CONTRACT_TYPES.FORFAIT)
// Jours à travailler du forfait : prorata exposé par le backend (218 sur année pleine,
// moins sur une entrée en cours d'année). Fallback 218 tant que le récap n'est pas chargé.
const forfaitWorkTargetDays = computed(() => {
const target = leave.leaveSummary.value?.forfaitWorkTargetDays
return (target === null || target === undefined) ? 218 : Math.round(target)
})
const employeeContractWorkLabel = computed(() => {
const contract = employee.value?.contract
if (!contract) return '-'
if (contract.type === CONTRACT_TYPES.FORFAIT) return 'Forfait - 218 jours'
if (contract.type === CONTRACT_TYPES.FORFAIT) return `Forfait - ${forfaitWorkTargetDays.value} jours`
if (contract.weeklyHours !== null && contract.weeklyHours !== undefined) return `${contract.weeklyHours} heures`
return contract.name || '-'
})
@@ -75,8 +81,10 @@ export const useEmployeeDetailPage = () => {
if (!isForfait.value) return ''
const presence = leave.leaveSummary.value?.presenceDaysToToday
if (presence === undefined || presence === null) return ''
const remaining = 218 - presence
return ` (${remaining} restants)`
const fmt = (n: number) => (Number.isInteger(n) ? String(n) : (Math.round(n * 100) / 100).toFixed(2).replace('.', ','))
// restant à travailler = jours à travailler (prorata) jours de présence déjà effectués
const remaining = forfaitWorkTargetDays.value - presence
return ` (${fmt(presence)} présence · ${fmt(remaining)} restants)`
})
const rtt = useEmployeeRtt(employee, loadEmployee, phase.selectedPhase)
const mileage = useEmployeeMileage(employee, loadEmployee)

View File

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

View File

@@ -42,6 +42,14 @@ final class EmployeeLeaveSummary
/** 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;
/**
* FORFAIT uniquement : jours à travailler sur l'exercice = jours ouvrés de la période congés acquis.
* Vaut 218 sur une année pleine (252 34) et le prorata sur une entrée en cours d'année
* (ex. Grégory : 168 13 ≈ 155). Null pour les non-forfait. Le « restant à travailler »
* affiché = forfaitWorkTargetDays presenceDaysToToday.
*/
public ?float $forfaitWorkTargetDays = null;
/** 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

@@ -133,6 +133,14 @@ final readonly class EmployeeLeaveSummaryProvider implements ProviderInterface
$summary->previousYearPaidDays = $paidLeaveDays;
[$periodFrom, $periodTo] = $this->resolvePeriodBounds($employee, $year, $phase);
// Forfait : jours à travailler sur l'exercice = jours ouvrés de la période congés acquis.
// Année pleine → 218 (252 34) ; entrée en cours d'année → prorata (ex. 168 13 ≈ 155).
if (LeaveRuleCode::FORFAIT_218->value === $summary->ruleCode) {
$businessDaysInPeriod = $this->countBusinessDays($periodFrom, $periodTo, $this->buildRawPublicHolidayMap($periodFrom, $periodTo));
$summary->forfaitWorkTargetDays = $businessDaysInPeriod - $summary->acquiredDays;
}
// Forfait-only: leaves taken from N-1 stock do NOT decrement presence days.
// For non-forfait, previousYearTakenDays is always 0, so the budget has no effect.
$n1AbsencesBudget = $yearSummary['previousYearTakenDays'];