Files
SIRH/CLAUDE.md
tristan 0897154460
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
feat : ajout d'un écran pour le récap congés et RTT
2026-04-14 15:08:45 +02:00

8.5 KiB
Raw Permalink Blame History

SIRH

Mandatory Rules

  • Any functional change MUST update doc/ in the same intervention
  • Any functional change MUST update the in-app documentation (frontend/data/documentation-content.ts) in the same intervention
  • At the end of every feature addition or functional modification, update this CLAUDE.md to reflect new patterns, rules, or conventions introduced

Commands

  • make start — start Docker stack
  • make test — run backend tests (PHPUnit)
  • make dev-nuxt — dev frontend
  • cd frontend && npm run build — build frontend
  • php bin/console cache:clear && php bin/console cache:warmup — clear cache after deploy

Stack

  • Backend: Symfony + API Platform + Doctrine ORM
  • Frontend: Nuxt 4 + Vue 3 + TypeScript + Tailwind CSS

Project Structure

  • src/ — Symfony domain, API resources, state providers/processors, services
  • frontend/ — Nuxt app (pages, components, composables, services)
  • migrations/ — Doctrine migrations (always include working down())
  • doc/ — functional rules and business documentation

Functional Rules

  • Reference: doc/functional-rules.md (mandatory reading before any business logic change)
  • Complementary: doc/leave-rollover.md, doc/rtt-rollover.md

Domain Model

  • Contracts: trackingMode (TIME=hours, PRESENCE=half-days), weeklyHours
  • Contract types: FORFAIT, THIRTY_FIVE_HOURS, THIRTY_NINE_HOURS, INTERIM, CUSTOM
  • Contract nature (per period): CDI, CDD, INTERIM
  • Employee contract history: employee_contract_periods, resolved by EmployeeContractResolver
  • Absences: stored per day (auto-split), AM/PM/full day, clear corresponding hour slots
  • Absences with countAsWorkedHours=true: credit minutes (TIME) or nothing (PRESENCE)
  • Driver periods (isDriver=true on EmployeeContractPeriod): separate screen /driver-hours, uses dayHoursMinutes/nightHoursMinutes + meal/overnight flags on WorkHour

Fériés

  • Source : API gouv via PublicHolidayService (cache 30j)
  • Exclusions : env EXCLUDED_PUBLIC_HOLIDAYS (CSV de libellés), défaut "Lundi de Pentecôte". Le filtre s'applique après le cache, côté service, donc frontend et calculs backend voient la même liste.
  • Écrans Heures / Heures Conducteurs (vue jour) : le nom du férié est affiché en badge #b3e5fc avec icône mdi:calendar-star dans la colonne Absence (distinct du pill absence). Bouton "Modifier" absence masqué sur férié (comme pour les formations).
  • Création/édition d'absence bloquée sur un férié
  • Saisie d'heures (ou de jours de présence) autorisée sur un férié — nécessaire pour éviter un déficit hebdomadaire (la référence hebdo n'est pas réduite par les fériés)

Validation Rules

  • isValid (RH): locks line for everyone (admin can only untoggle validation)
  • isSiteValid (site manager): locks for non-admin, admin can still edit
  • Any real modification resets both isSiteValid=false and isValid=false
  • No-op saves preserve existing validations

Overtime Rules

  • Contracts <= 35h: +25% from 35h to 43h, +50% beyond
  • Contracts >= 39h: +25% from 39h to 43h, +50% beyond
  • CUSTOM contracts (weeklyHours ≠ 35 and ≠ 39, not INTERIM/FORFAIT): reference = actual contractual hours, no 25%/50% bonuses (1h overtime = 1h recovery), deficit doesn't impact balance
  • INTERIM: no overtime bonuses, no recovery time
  • Driver contracts: RTT uses dayHoursMinutes + nightHoursMinutes + workshopHoursMinutes instead of morning/afternoon/evening time ranges
  • 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.

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.
  • Scope : ROLE_ADMIN → tous les employés, ROLE_USER (chef de site) → employés de ses sites, ROLE_SELF → sa ligne
  • Cutoff temporel : fin de la semaine S-2 (dimanche 23:59:59). Formule : dimanche(lundi_semaine_courante 14j). Pas de gate isValid.
  • Helper : App\Util\LeaveRecapCutoff::resolveCutoff()
  • Colonnes : Nom, Prénom, Contrat, CP N-1 restant, CP N, Samedis, RTT — identiques au PDF
  • Service partagé : LeaveRecapRowBuilder consommé par LeaveRecapPrintProvider (as-of today) et EmployeeLeaveRecapProvider (as-of cutoff)
  • EmployeeLeaveSummaryProvider::computeYearSummary() accepte un ?DateTimeImmutable $asOfDate qui cappe l'accrual et les absences sur l'année cible (null = comportement live inchangé)
  • Pas d'export PDF depuis cet écran
  • Doc détaillée : doc/leave-recap-screen.md

Frais (MileageAllowance)

  • Onglet "Frais" (anciennement "Frais Kms") sur la fiche employé
  • Validation: mois obligatoire + au moins kilometers > 0 ou amount > 0
  • Les deux champs km et montant sont optionnels individuellement mais au moins un requis

Formations

  • Onglet "Formation" sur la fiche employé (admin uniquement)
  • Champs : date début, date fin, justificatif PDF optionnel, commentaire
  • Validation: dates obligatoires, endDate >= startDate, fichier PDF uniquement
  • Justificatif stocké dans var/uploads/formations/{année}/{mois}/{uuid}.pdf (année/mois = startDate)
  • Suppression et remplacement du justificatif nettoient l'ancien fichier disque
  • Tri tableau par startDate DESC
  • Affichage écran Heures (jour) : pill "Formation" (indigo) dans la colonne Absence. Quand une formation existe, le bouton "Modifier" de la colonne Absence est masqué (lockdown complet du jour pour la gestion d'absence)
  • Affichage Calendrier : cellule "F" (indigo) si formation seule, ou icône école en coin si formation + absence. Cellules avec formation non cliquables. Légende dédiée. PDF export : code "F" indigo ou astérisque à côté du code d'absence
  • Le CRUD formation est exclusivement géré depuis la fiche employé > onglet Formation

Frontend Patterns

Table styling (standard across all pages)

  • Header: grid border border-black bg-tertiary-500 px-6 py-3 text-[20px] font-semibold text-black rounded-t-md sticky top-0 z-10
  • Body wrapper: border-x border-b border-primary-500 rounded-b-md
  • Rows: grid items-center gap-4 border-b border-primary-500 px-6 py-3 text-md font-bold text-primary-500 last:border-b-0 cursor-pointer hover:bg-tertiary-500
  • Page wrapper for scroll: h-full flex flex-col overflow-hidden, table container: min-h-0 overflow-auto rounded-md bg-white

Drawer buttons (AppDrawer)

  • Edit mode: grid grid-cols-2 gap-3 → Supprimer (red, left) + Modifier (primary, right)
  • Create mode: centered + Ajouter button, w-[200px]
  • Exception: Users drawer has NO delete button
  • All "Ajouter" buttons across the app use "+" prefix

API Platform (backend)

  • Custom operations use Processor (write) / Provider (read)
  • File uploads: deserialize: false on Post, access file via RequestStack
  • Upload dir: %kernel.project_dir%/var/uploads

Audit Logging

  • All processors that modify entities impacting calculations (heures, absences, contrats, RTT) MUST inject AuditLogger and log create/update/delete actions
  • AuditLogger::log() persists without flushing — the processor's flush() handles both the data change and the audit entry atomically
  • Audit logs are accessible only via ROLE_SUPER_ADMIN (hidden role, added manually in DB)
  • Documentation: doc/audit-logging.md

Backend Conventions

  • Prefer explicit DTOs over associative arrays
  • Business rules in backend (providers/processors/services), frontend is display/interaction only
  • Keep backend PHP DTOs aligned with frontend TS DTOs (frontend/services/dto/*)
  • Update unit tests when constructor/service signatures change

In-App Documentation

  • Content: frontend/data/documentation-content.ts — structured TypeScript data with all user-facing documentation
  • Types: frontend/types/documentation.ts — DocSection, DocArticle, DocBlock
  • Composable: frontend/composables/useDocumentation.ts — role-based filtering (employee < site_manager < admin)
  • Components: frontend/components/documentation/ — DocumentationPage, DocumentationSection, DocumentationArticle
  • Page: frontend/pages/documentation.vue
  • 3 access levels: employee (ROLE_SELF), site_manager (ROLE_USER), admin (ROLE_ADMIN) — cumulative (admin sees everything)
  • Each section/article has a requiredLevel that controls visibility
  • When adding or modifying a feature, update the corresponding section in documentation-content.ts

Language

  • UI is in French
  • User communicates in French
  • Code (variables, comments) in English