fix(heures) : n'enregistrer que les lignes modifiées (anti-écrasement concurrent)

L'écran Heures / Heures Conducteurs envoyait au bulk-upsert une entrée pour
tous les employés visibles non verrouillés, à partir de l'état en mémoire.
Le backend traitant une entrée vide comme une suppression, un admin avec une
grille périmée pouvait supprimer une ligne saisie entre-temps par un autre
utilisateur (ex. ROLE_SELF non encore validé, donc non verrouillé).

handleSave capture désormais un instantané des lignes chargées (loadedRows,
dans hydrateRows) et ne transmet que les lignes dont l'état courant en diffère.
Une ligne intouchée n'est jamais envoyée → jamais supprimée.

Symétrique dans useHoursPage.ts et useDriverHoursPage.ts.
Doc : doc/hours-save-dirty-tracking.md + note CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 09:03:59 +02:00
parent 4d4bdba914
commit 5bec2c38cf
4 changed files with 146 additions and 53 deletions
+1
View File
@@ -64,6 +64,7 @@
- `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
- **Enregistrement = seules les lignes modifiées sont envoyées (anti-écrasement concurrent)** : l'écran Heures / Heures Conducteurs affiche toute la journée, et le bulk-upsert (`WorkHourBulkUpsertProcessor`) traite une **entrée vide comme une suppression**. Pour éviter qu'un admin avec une grille **périmée** ne supprime une ligne saisie entre-temps par un autre utilisateur (ex. `ROLE_SELF` non encore validé → non verrouillé), `handleSave` ne transmet **que les lignes dont l'état courant diffère de l'instantané chargé** (`loadedRows`, capturé dans `hydrateRows` ; comparaison `JSON.stringify(buildEntry(current)) !== buildEntry(original)`). Une ligne intouchée n'est jamais envoyée → jamais supprimée. Vidée volontairement → envoyée vide → supprimée (métier conservé). Symétrique dans `useHoursPage.ts` et `useDriverHoursPage.ts`. **Limite** : pas de verrou optimiste backend — l'édition explicite d'une ligne sur données périmées peut toujours écraser une saisie concurrente sur cette même ligne. Doc : `doc/hours-save-dirty-tracking.md`.
## Overtime Rules
- Contracts <= 35h: +25% from 35h to 43h, +50% beyond