feat(overtime-contingent) : contingent d'heures supplémentaires payées (#29)
Auto Tag Develop / tag (push) Successful in 7s

## Résumé
Suivi par **année civile** (Janv–Déc) des heures supplémentaires payées des employés non-forfait (chauffeurs inclus) face au plafond légal (**350 h** chauffeurs / **220 h** autres).

- **Fiche employé** : encart header `Total H.payés {année} : X h / plafond h` (année civile courante, rouge si dépassement), via `GET /employees/{id}/overtime-contingent`.
- **Export PDF** `GET /overtime-contingent/print?year=&siteIds=` (ROLE_USER, périmètre `findScoped`) : groupé par site, colonnes Janv–Déc + colonne `Total payé / payable`. Drawer liste employés (année + sites).
- Heures payées = `base25 + base50` (hors majoration). Mapping exercice→civil : `mois ≥ 6 ? exercice−1 : exercice`.
- Cœur partagé pur `OvertimePaidContingentCalculator`.
- Ajout « Année civile » dans le titre des deux exports PDF (contingent H.supp. et heures de nuit).

## Tests
- 214 tests PHPUnit verts (calculateur : mapping civil, base-only, plafond ; builder : ventilation mensuelle, ligne à zéro).

## Hors périmètre (consigné)
- Bug latent `SalaryRecapPrintProvider` : rattachement des paiements RTT des mois Juin–Déc par année civile sur un stockage par exercice. À traiter séparément.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #29
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #29.
This commit is contained in:
2026-06-11 15:47:19 +00:00
committed by Autin
parent ceba1121f0
commit 327c10fda4
21 changed files with 2148 additions and 5 deletions
@@ -2,12 +2,14 @@ import type { Employee } from '~/services/dto/employee'
import { CONTRACT_TYPES } from '~/services/dto/contract'
import { getEmployee } from '~/services/employees'
import { useEmployeeContractPhase } from '~/composables/useEmployeeContractPhase'
import { getEmployeeOvertimeContingent, type OvertimeContingent } from '~/services/employee-overtime-contingent'
export const useEmployeeDetailPage = () => {
const route = useRoute()
const employee = ref<Employee | null>(null)
const isLoading = ref(false)
const activeTab = ref<'contract' | 'leave' | 'rtt' | 'mileage' | 'formation' | 'bonus' | 'observation'>('contract')
const overtimeContingent = ref<OvertimeContingent | null>(null)
const phase = useEmployeeContractPhase(employee)
@@ -28,6 +30,18 @@ export const useEmployeeDetailPage = () => {
return contract.name || '-'
})
const loadOvertimeContingent = async () => {
if (!employee.value || !showRttTab.value) {
overtimeContingent.value = null
return
}
try {
overtimeContingent.value = await getEmployeeOvertimeContingent(employee.value.id)
} catch {
overtimeContingent.value = null
}
}
const loadEmployee = async () => {
const idParam = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id
const employeeId = Number(idParam)
@@ -71,6 +85,7 @@ export const useEmployeeDetailPage = () => {
// qui proviennent du récap congés — nécessaire même quand on ouvre un autre onglet.
await leave.loadLeaveData()
}
await loadOvertimeContingent()
} finally {
isLoading.value = false
}
@@ -94,6 +109,18 @@ export const useEmployeeDetailPage = () => {
if (presence === undefined || presence === null) return ''
return ` (${formatDays(presence)} présence)`
})
const overtimeContingentLabel = computed(() => {
if (!showRttTab.value) return ''
const c = overtimeContingent.value
if (!c) return ''
const h = c.paidMinutes / 60
const hStr = Number.isInteger(h) ? String(h) : (Math.round(h * 10) / 10).toFixed(1).replace('.', ',')
return `Total H.payés ${c.year} : ${hStr} h / ${c.capHours} h`
})
const overtimeContingentExceeded = computed(() => {
const c = overtimeContingent.value
return c ? c.paidMinutes > c.capHours * 60 : false
})
const rtt = useEmployeeRtt(employee, loadEmployee, phase.selectedPhase)
const mileage = useEmployeeMileage(employee, loadEmployee)
const formation = useEmployeeFormation(employee, loadEmployee)
@@ -147,6 +174,8 @@ export const useEmployeeDetailPage = () => {
employeeContractWorkLabel,
forfaitRemainingDaysLabel,
nonForfaitPresenceLabel,
overtimeContingentLabel,
overtimeContingentExceeded,
...phase,
...contract,
...leave,