feat(leave) : align forfait CP N + display remaining workdays + N-1 absences exempt from presence

- LeaveRecapRowBuilder: forfait CP N now reflects remainingDays (acquis − pris depuis N) instead of the constant acquiredDays. Affects both PDF export and screen recap.
- Employee detail header: forfait label becomes "Forfait - 218 jours (X restants)" where X = 218 − presence days from Jan 1 to today (today included).
- New EmployeeLeaveSummary.presenceDaysToToday field, computed via the same logic as presenceDaysByMonth but capped at today.
- Forfait business rule: leaves attributed to N-1 stock no longer decrement presence days. Implemented by chronologically consuming an N-1 budget (= previousYearTakenDays) inside computePresenceDaysByMonth before counting any absence. Non-forfait unaffected (budget is 0).
- Doc updates: CLAUDE.md (forfait/N-1 rule), functional-rules.md (CP N forfait semantics).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 10:20:23 +02:00
parent bd93c52197
commit 5302586644
8 changed files with 73 additions and 9 deletions

View File

@@ -10,10 +10,11 @@ export const useEmployeeDetailPage = () => {
const showLeaveTab = computed(() => employee.value?.currentContractNature !== 'INTERIM')
const showRttTab = computed(() => employee.value?.contract?.type !== CONTRACT_TYPES.FORFAIT)
const isForfait = computed(() => employee.value?.contract?.type === CONTRACT_TYPES.FORFAIT)
const employeeContractWorkLabel = computed(() => {
const contract = employee.value?.contract
if (!contract) return '-'
if (contract.type === CONTRACT_TYPES.FORFAIT) return 'Forfait'
if (contract.type === CONTRACT_TYPES.FORFAIT) return 'Forfait - 218 jours'
if (contract.weeklyHours !== null && contract.weeklyHours !== undefined) return `${contract.weeklyHours} heures`
return contract.name || '-'
})
@@ -55,6 +56,9 @@ export const useEmployeeDetailPage = () => {
await bonus.loadBonusData()
} else if (activeTab.value === 'observation') {
await observation.loadObservationData()
} else if (isForfait.value && showLeaveTab.value) {
// Eager load: needed for the "X jours restants" header label on forfait employees.
await leave.loadLeaveData()
}
} finally {
isLoading.value = false
@@ -63,6 +67,13 @@ export const useEmployeeDetailPage = () => {
const contract = useEmployeeContract(employee, loadEmployee)
const leave = useEmployeeLeave(employee, loadEmployee)
const forfaitRemainingDaysLabel = computed(() => {
if (!isForfait.value) return ''
const presence = leave.leaveSummary.value?.presenceDaysToToday
if (presence === undefined || presence === null) return ''
const remaining = 218 - presence
return ` (${remaining} restants)`
})
const rtt = useEmployeeRtt(employee, loadEmployee)
const mileage = useEmployeeMileage(employee, loadEmployee)
const formation = useEmployeeFormation(employee, loadEmployee)
@@ -97,6 +108,7 @@ export const useEmployeeDetailPage = () => {
showLeaveTab,
showRttTab,
employeeContractWorkLabel,
forfaitRemainingDaysLabel,
...contract,
...leave,
...rtt,