Files
SIRH/docs/superpowers/specs/2026-06-01-day-view-per-date-tracking-mode-design.md
T
tristan a41bd632cf
Auto Tag Develop / tag (push) Successful in 11s
Retour RH: vue jour par date, RTT mi-semaine, récap salaire & exports, panier de nuit (#21)
## Correctifs RH (branche fix/retour-rh)

### Vue Jour (Heures)
- Mode saisie/présence, libellé de contrat et sauvegarde résolus **à la date affichée** (et non au contrat courant). Corrige les salariés passés 39h/35h → Forfait.

### RTT — heures supplémentaires
- Proratisation du **plafond 25%/50%** pour les embauches en milieu de semaine (la bande +25% se décale au lieu de rester bloquée à 43h). Témoin Dylan : 4h à 25% + 3h à 50%.

### Récap salaire (PDF mensuel)
- Forfait : congés imputés **N-1** non affichés et comptés en présence.
- Colonne « Heures payés » **scindée 25% / 50%** (en-tête fusionné).
- **Exclusion des salariés sans contrat** sur le mois (ex. Marine, contrat terminé).

### Exports heures annuelles (par salarié + tous)
- **Tous les jours sous contrat** affichés, même vides/non saisis (corrige les lignes manquantes).
- Samedis/dimanches en **gris plus foncé**.

### Panier de nuit
- **Ne s'applique pas aux conducteurs** (vue semaine + récap salaire).

## Tests
- 11 tests ajoutés. Suite verte hors un test legacy pré-existant dépendant de la date (`EmployeeRttSummaryProviderTest::testNoQueryParamsKeepsLegacyYearDefaulting`, non modifié par cette branche).

## À noter (hors scope)
- L'export heures annuelles *tous salariés* peut dépasser `memory_limit=256M` (Dompdf) — limitation **pré-existante**, non corrigée ici.

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

Reviewed-on: #21
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-02 06:26:40 +00:00

4.0 KiB

Vue Jour (Heures) — résolution du contrat à la date affichée

Date : 2026-06-01

Problème

Sur l'écran Heures — vue Jour (HoursDayView), l'affichage saisie d'heures (TIME) vs cases de présence (PRESENCE), ainsi que le libellé de contrat entre parenthèses, sont résolus à partir de employee.contract — c'est-à-dire le contrat courant de l'employé (résolu à aujourd'hui), pas le contrat valable à la date affichée.

Conséquence pour un salarié passé d'un contrat 39h/35h (TIME) à un Forfait (PRESENCE) :

  • toutes les dates passées s'affichent en cases de présence alors qu'elles relevaient d'un contrat en heures ;
  • pire, handleSave (useHoursPage.ts:1073) se base sur le même test : éditer une date passée écrit des flags de présence au lieu des heures et écrase la saisie.

La vue Semaine est déjà correcte : elle résout le trackingMode par date côté backend via WeeklySummaryRow.trackingMode. Le périmètre de ce correctif est donc la vue Jour uniquement.

Principe

Le provider backend WorkHourDayContextProvider::provide() résout déjà le contrat à la date affichée (EmployeeContractResolver::resolveForEmployeeAndDate) et expose déjà contractNature par date sur chaque ligne. Il suffit :

  1. d'exposer sur la ligne du jour les champs de contrat manquants ;
  2. de faire lire ces champs au frontend (au lieu de employee.contract).

L'ensemble de la ligne (toggle saisie/présence + libellé 39h/Forfait + logique 4h) devient ainsi cohérent avec le contrat valable à la date affichée.

Changements

Backend

  1. src/Dto/WorkHours/DayContextRow.php — ajouter 4 champs au constructeur et à toArray() (+ mettre à jour le PHPDoc du retour de toArray()) :

    • trackingMode: ?string
    • weeklyHours: ?int
    • contractType: ?string
    • contractName: ?string
  2. src/State/WorkHourDayContextProvider.php — peupler ces champs depuis le $contract déjà résolu (lignes 60-71) :

    • trackingMode = $contract?->getTrackingMode()
    • weeklyHours = $contract?->getWeeklyHours()
    • contractType = $contract?->getType()->value
    • contractName = $contract?->getName()
    • tous null si pas de contrat à la date (cohérent avec hasContractAtDate).

Frontend

  1. frontend/services/dto/work-hour.ts — refléter les 4 champs sur WorkHourDayContextRow.

  2. frontend/composables/useHoursPage.tsisPresenceTracking, isTimeTracking, contractLabel, is4hContract lisent le dayContextByEmployeeId.get(employeeId) (résolu par date), avec fallback sur employee.contract si aucune ligne du jour n'existe. Cela corrige automatiquement handleSave (ligne 1073), qui s'appuie sur isPresenceTracking.

    Les signatures actuelles prennent un Employee ; on conserve la signature et on utilise employee.id en interne pour récupérer la ligne du jour.

Hors périmètre

  • Vue Semaine (déjà par date).
  • Heures Conducteurs (toujours en mode TIME, pas de toggle).
  • Processor de sauvegarde backend : inchangé — le frontend enverra déjà la bonne forme (heures vs présence) par date.

Tests / vérification

  • Test backend (WorkHourDayContextProvider) : pour un employé avec historique 39h → Forfait, la ligne renvoyée porte trackingMode=TIME/weeklyHours=39/ contractType non-forfait sur une date avant la bascule, et trackingMode=PRESENCE/contractType=FORFAIT sur une date après.
  • Vérification manuelle : naviguer une date avant et après la bascule sur la fiche du salarié → champs d'heures puis cases de présence, libellé cohérent.

Documentation (règle obligatoire)

  • doc/ : section vue Jour — résolution du contrat (mode + libellé) à la date affichée.
  • frontend/data/documentation-content.ts : note utilisateur correspondante.
  • CLAUDE.md : préciser que la vue Jour résout trackingMode/libellé à la date filtrée (au même titre que contractNature déjà documenté).