Gestion du changement de type de contrat + correction du calcule des RTT sur un contrat qui commence en milieu de semaine (#19)
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled

| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [x] CHANGELOG modifié

Reviewed-on: #19
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #19.
This commit is contained in:
2026-05-22 06:42:33 +00:00
committed by Autin
parent b541f9ded8
commit abdaf809f8
40 changed files with 5021 additions and 153 deletions

View File

@@ -1,6 +1,7 @@
import type { Employee } from '~/services/dto/employee'
import { CONTRACT_TYPES } from '~/services/dto/contract'
import { getEmployee } from '~/services/employees'
import { useEmployeeContractPhase } from '~/composables/useEmployeeContractPhase'
export const useEmployeeDetailPage = () => {
const route = useRoute()
@@ -8,13 +9,21 @@ export const useEmployeeDetailPage = () => {
const isLoading = ref(false)
const activeTab = ref<'contract' | 'leave' | 'rtt' | 'mileage' | 'formation' | 'bonus' | 'observation'>('contract')
const phase = useEmployeeContractPhase(employee)
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 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 || '-'
})
@@ -29,6 +38,7 @@ export const useEmployeeDetailPage = () => {
isLoading.value = true
try {
employee.value = await getEmployee(employeeId)
phase.resetToCurrent()
if (!showLeaveTab.value && activeTab.value === 'leave') {
activeTab.value = 'contract'
@@ -56,8 +66,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.
} else if (showLeaveTab.value) {
// Eager load: the header shows présence (et jours à travailler/restant pour le forfait),
// qui proviennent du récap congés — nécessaire même quand on ouvre un autre onglet.
await leave.loadLeaveData()
}
} finally {
@@ -66,20 +77,46 @@ export const useEmployeeDetailPage = () => {
}
const contract = useEmployeeContract(employee, loadEmployee)
const leave = useEmployeeLeave(employee, loadEmployee)
const leave = useEmployeeLeave(employee, loadEmployee, phase.selectedPhase)
const formatDays = (n: number) => (Number.isInteger(n) ? String(n) : (Math.round(n * 100) / 100).toFixed(2).replace('.', ','))
// Forfait : « (présence · restant à travailler) ». restant = jours à travailler (prorata) présence.
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 remaining = forfaitWorkTargetDays.value - presence
return ` (${formatDays(presence)} présence · ${formatDays(remaining)} restants)`
})
const rtt = useEmployeeRtt(employee, loadEmployee)
// Non-forfait : « (présence) » seul (pas de cible de jours à travailler).
const nonForfaitPresenceLabel = computed(() => {
if (isForfait.value) return ''
const presence = leave.leaveSummary.value?.presenceDaysToToday
if (presence === undefined || presence === null) return ''
return ` (${formatDays(presence)} présence)`
})
const rtt = useEmployeeRtt(employee, loadEmployee, phase.selectedPhase)
const mileage = useEmployeeMileage(employee, loadEmployee)
const formation = useEmployeeFormation(employee, loadEmployee)
const bonus = useEmployeeBonus(employee, loadEmployee)
const observation = useEmployeeObservation(employee, loadEmployee)
watch(() => phase.selectedPhase.value?.id, (newId, oldId) => {
if (newId === oldId || oldId === undefined) return
// Bascule onglet si on entre dans une phase qui ne supporte plus le tab actuel
if (!showRttTab.value && activeTab.value === 'rtt') {
activeTab.value = 'leave'
}
// Recharger l'onglet courant ; sinon recharger quand même le récap congés
// pour que le libellé de présence / jours à travailler du header reste à jour.
if (activeTab.value === 'leave' && showLeaveTab.value) {
leave.loadLeaveData()
} else if (activeTab.value === 'rtt' && showRttTab.value) {
rtt.loadRttData()
} else if (showLeaveTab.value) {
leave.loadLeaveData()
}
})
watch(activeTab, (tab) => {
if (tab === 'leave' && !leave.leaveDataLoaded.value && showLeaveTab.value) {
leave.loadLeaveData()
@@ -109,6 +146,8 @@ export const useEmployeeDetailPage = () => {
showRttTab,
employeeContractWorkLabel,
forfaitRemainingDaysLabel,
nonForfaitPresenceLabel,
...phase,
...contract,
...leave,
...rtt,