# 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` ## 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. ## 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