Files
MadJax 7e80427e09 Precision configurable des distances (arrondi superieur 0/1/2 decimales)
- Nouveau parametre global Precision_Distance (sous-menu Application de la
  Page_Parametrage, entre Compte et Vehicules), persiste via une entite
  singleton ParametresApplication, expose par GET/PUT /api/parametres-application
- Arrondi superieur (ceiling) applique a l'affichage et aux agregats : trajet
  simple, aller/retour (arrondi puis doublement), totaux jour/mois/annee,
  carte, export PDF et bareme kilometrique - jamais a la distance brute stockee
- Refonte du Montant_Previsionnel : le montant mensuel (80%) porte desormais
  sur le seul mois affiche (tranche determinee par le cumul annuel) ; nouvelle
  ligne Montant_Previsionnel_Annuel (100%, sans minoration, sur le cumul)
- Backend : PrecisionDistanceService (ArrondirDistance), integration dans
  ResumeService/TotauxService/RemboursementService/MontantRegleService,
  migration EF Core ; 7 tests de propriete (FsCheck) + tests existants, 62/62 verts
- Frontend : ParametresApplicationService (signal partage), composant de
  parametrage, fonction miroir arrondirDistance, adaptation de la liste des
  trajets, du panneau des totaux, de la carte et des montants regles
- Specs : nouvelle spec precision-distances-km (requirements/design/tasks) ;
  mise a jour de requirements.md, design.md et design-requirements.md de
  schotziad-km-tracker (Requirement 22, renvois croises, Montant_Previsionnel)
- Suppression du fichier chromium .deb suivi par erreur dans le depot
2026-09-25 12:02:52 +02:00

197 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: Précision des distances (km)
## Overview
Cette implémentation ajoute un paramètre global `Precision_Distance` (0/1/2 décimales) avec arrondi supérieur (ceiling) systématique côté backend et frontend, refond le calcul du Montant_Previsionnel (mensuel recentré sur le mois affiché + nouveau montant annuel 100%), et propage la cohérence de l'arrondi à la liste des trajets, aux totaux, à la carte, à l'export PDF et au barème kilométrique.
L'ordre des tâches suit les dépendances réelles : entité + migration → service de précision pur → intégration dans les services d'agrégation existants → refonte du calcul prévisionnel → cohérence carte/PDF → tests backend → couche frontend (service/util → composant/route → consommateurs) → vérification finale.
## Tasks
- [x] 1. Backend — Entité `ParametresApplication` et migration EF Core
- Créer `Models/Entities/ParametresApplication.cs` (singleton `Id = 1`, `PrecisionDistanceKm` défaut 2, `UpdatedAt`)
- Ajouter `DbSet<ParametresApplication> ParametresApplication` et la configuration `OnModelCreating` (contrainte CHECK `IN (0,1,2)`, clé `ValueGeneratedNever()`, index unique singleton) dans `Data/AppDbContext.cs`
- Générer la migration EF Core (`dotnet ef migrations add AjoutParametresApplication`) créant la table avec la contrainte CHECK
- _Requirements: 1.1, 1.2_
- [x] 2. Backend — Service de précision et exception métier
- [x] 2.1 Créer l'exception `Services/Exceptions/PrecisionDistanceInvalideException.cs` (même modèle que `MontantRegleNegatifException`)
- _Requirements: 1.6_
- [x] 2.2 Créer `Services/IPrecisionDistanceService.cs` (`ObtenirAsync`, `DefinirAsync`, `ArrondirDistance`)
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.6, 1.8, 3.1, 3.2, 3.3, 3.4, 3.5, 4.2_
- [x] 2.3 Implémenter `Services/PrecisionDistanceService.cs`
- `GetOrCreateAsync` privé créant la ligne singleton par défaut (2) si absente
- `ObtenirAsync`/`DefinirAsync` avec validation stricte `{0,1,2}` levant `PrecisionDistanceInvalideException`
- `ArrondirDistance` : `Ceiling(distance × 10^precision) / 10^precision`
- _Requirements: 1.1, 1.2, 1.5, 1.6, 1.8, 3.1, 3.2, 3.3, 3.4, 3.5_
- [x]* 2.4 Écrire les tests de propriété pour `PrecisionDistanceService`
- **Property 1: Arrondi supérieur exact et jamais inférieur à la distance brute** — _Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5_
- **Property 4: Validation stricte de la précision** — _Validates: Requirements 1.6_
- **Property 5: Invariance de la distance brute stockée face à un changement de précision** — _Validates: Requirements 1.5, 1.8_
- [ ]* 2.5 Écrire les tests unitaires `PrecisionDistanceService`
- `ObtenirAsync` retourne 2 par défaut quand la table est vide (Exigence 1.2)
- `DefinirAsync(3)` lève `PrecisionDistanceInvalideException` (Exigence 1.6, cas concret)
- _Requirements: 1.2, 1.6_
- [x] 3. Backend — DTOs et contrôleur `ParametresApplicationController`
- [x] 3.1 Créer `Models/DTOs/ParametresApplicationDtos.cs` (`ParametresApplicationDto`, `SavePrecisionDistanceDto`)
- _Requirements: 1.3, 1.4_
- [x] 3.2 Créer `Controllers/ParametresApplicationController.cs` (GET/PUT `api/parametres-application`, mapping `PrecisionDistanceInvalideException` → 400)
- _Requirements: 1.3, 1.4, 1.6, 1.7_
- [x] 3.3 Enregistrer `IPrecisionDistanceService`/`PrecisionDistanceService` dans `Program.cs` (`AddScoped`, section services métier)
- _Requirements: 1.1, 1.3, 1.4_
- [ ]* 3.4 Écrire les tests d'intégration du contrôleur
- `PUT /api/parametres-application` avec `precisionDistanceKm = 3` retourne 400 (Exigence 1.6)
- `GET`/`PUT /api/parametres-application` sans jeton retourne 401 (Exigence 1.7)
- _Requirements: 1.3, 1.4, 1.6, 1.7_
- [x] 4. Checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
- [x] 5. Backend — Intégration de l'arrondi dans les services d'agrégation existants
- [x] 5.1 Modifier `Services/ResumeService.cs` : injecter `IPrecisionDistanceService`, appliquer `ArrondirDistance(Distance, precision) × (AllerRetour ? 2 : 1)` par trajet dans `ConstruireResumeMensuel`, exposer `TrajetDto.Distance` déjà arrondi, sommer les valeurs déjà arrondies pour `TotalKm` sans second arrondi
- _Requirements: 4.1, 4.2, 4.3, 5.2, 5.3_
- [x] 5.2 Modifier `Services/TotauxService.cs` : injecter `IPrecisionDistanceService`, remplacer la fonction locale `Effective(distance, allerRetour)` par l'arrondi avant doublement, supprimer le `Math.Round(..., 2, ...)` final existant sur `TotalJour`/`TotalMois`/`TotalAnnuelPartiel`
- _Requirements: 4.1, 4.2, 4.3, 5.1, 5.4_
- [x] 5.3 Modifier `Services/RemboursementService.cs` : injecter `IPrecisionDistanceService`, construire `kmMoisMap` à partir des distances arrondies par trajet (au lieu de la distance brute), garantissant `totalKmAnnuel` et chaque `KmMois` comme sommes de distances déjà arrondies
- _Requirements: 4.1, 4.2, 4.3, 6.3, 6.4, 6.6_
- [x]* 5.4 Écrire le test de propriété pour la sommation
- **Property 2: Arrondi avant doublement pour Aller_Retour** — _Validates: Requirements 4.1, 4.2, 4.3_
- **Property 3: Sommation de distances déjà arrondies sans arrondi supplémentaire** — _Validates: Requirements 5.1, 5.2, 5.3, 5.4_
- [x]* 5.5 Écrire le test unitaire de l'exemple chiffré Aller_Retour
- Cas 12.30 km, précision 0 → 26 km (et non 25 km), en plus de la Property 2
- _Requirements: 4.2_
- [x] 6. Backend — Refonte de `MontantRegleService.GetMontantPrevisionnelAsync`
- [x] 6.1 Créer/modifier le DTO `MontantPrevisionnelDto` (`TotalMoisKm`, `TotalAnnuelPartielKm`, `MontantPrevisionnelMensuel`, `MontantPrevisionnelAnnuel`) dans `Models/DTOs/`
- _Requirements: 6.1, 6.2, 7.1, 8.1, 8.2, 8.4_
- [x] 6.2 Modifier `Services/IMontantRegleService.cs` et `Services/MontantRegleService.cs` : injecter `IPrecisionDistanceService`, calculer `totalMoisAffiche`/`totalAnnuelPartiel` à partir des distances arrondies, déterminer la tranche via le cumul annuel (`baremeAnnuel`), appliquer son coefficient/base au total du mois pour `MontantPrevisionnelMensuel` (80%), calculer `MontantPrevisionnelAnnuel` (100% sur le cumul)
- _Requirements: 6.1, 6.2, 6.6, 7.1, 7.2, 7.4, 8.1, 8.2, 8.5_
- [x]* 6.3 Écrire les tests de propriété pour le calcul prévisionnel
- **Property 6: Montant_Previsionnel_Mensuel dépend uniquement du mois affiché** — _Validates: Requirements 7.1, 7.2_
- **Property 7: Montant_Previsionnel_Annuel égal au barème plein sur le cumul** — _Validates: Requirements 8.2, 8.5_
- [ ]* 6.4 Écrire les tests unitaires de `GetMontantPrevisionnelAsync`
- Véhicule introuvable → `VehiculeNotFoundException` (comportement existant inchangé)
- Cas limite : trajets avec distance `null` ignorés dans la somme
- _Requirements: 6.1, 6.2, 7.1, 8.1, 8.2_
- [x] 7. Backend — Cohérence carte et export PDF
- [x] 7.1 Vérifier/corriger l'endpoint trajets/journée (`ITrajetService`/`TrajetsController`, méthode consommée par `TrajetService.getTrajetsJournee`) pour renvoyer la Distance_Effective_Arrondie via `IPrecisionDistanceService`
- _Requirements: 9.1, 9.4_
- [x] 7.2 Vérifier `Services/PdfService.cs` (`AssemblerModeleMensuelAsync`, composition des tables mensuelle/annuelle) : confirmer qu'aucun recalcul de distance brute ne subsiste et que le modèle assemblé provient intégralement de `IResumeService`/`IRemboursementService` déjà corrigés
- _Requirements: 9.2, 9.3_
- [ ]* 7.3 Écrire les tests de propriété de cohérence carte/PDF
- **Property 8: Cohérence de l'infobulle carte avec la Distance_Effective_Arrondie** — _Validates: Requirements 9.1_
- **Property 9: Modèle de données PDF cohérent avec les résumés/régularisations** — _Validates: Requirements 9.2, 9.3_
- [ ]* 7.4 Écrire le test d'intégration de cohérence croisée
- Scénario bout-en-bout (SQLite en mémoire) : créer des trajets sur plusieurs mois, changer Precision_Distance, vérifier l'égalité des valeurs entre `ResumeService`, `TotauxService` et `RemboursementService` (Exigence 9.4)
- _Requirements: 9.4_
- [ ]* 7.5 Écrire les tests d'intégration de génération PDF
- Génération effective d'un PDF mensuel et d'un PDF annuel (1 exemple chacun), vérifier l'absence d'exception après les changements de modèle
- _Requirements: 9.2, 9.3_
- [x] 8. Checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
- [x] 9. Frontend — Modèle, service et utilitaire d'arrondi
- [x] 9.1 Créer `core/models/parametres-application.model.ts` (`ParametresApplication { precisionDistanceKm: number }`)
- _Requirements: 1.3, 1.4_
- [x] 9.2 Créer `core/services/parametres-application.service.ts` (`ParametresApplicationService` : signal partagé `precisionDistance`, `charger()`, `definirPrecisionDistance()`)
- _Requirements: 1.3, 1.4, 2.3, 2.4, 2.5_
- [x] 9.3 Créer `shared/utils/arrondir-distance.ts` (`arrondirDistance`, `distanceEffectiveArrondie`), miroir exact de la formule backend
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.3, 5.5_
- [ ]* 9.4 Écrire les tests unitaires de `arrondirDistance`/`distanceEffectiveArrondie`
- Cas 12.30 km, précision 0, aller-retour → 26 km (miroir du test backend)
- Cas limites par précision (0/1/2) et valeurs proches d'un seuil d'arrondi
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.3_
- [x]* 9.5 Mettre à jour `core/models/bareme.model.ts` et `core/services/bareme.service.ts` avec les nouveaux champs `MontantPrevisionnel` (`totalMoisKm`, `totalAnnuelPartielKm`, `montantPrevisionnelMensuel`, `montantPrevisionnelAnnuel`)
- _Requirements: 6.1, 7.1, 8.1, 8.2, 8.4_
- [x] 10. Frontend — Composant `ParametresApplicationComponent` et route/menu
- [x] 10.1 Créer `features/parametres-application/parametres-application.component.ts` (+ `.html`, `.scss`) : sélection 0/1/2, présélection de la valeur courante, confirmation de succès, restauration + notification d'erreur en cas d'échec
- _Requirements: 2.2, 2.3, 2.4, 2.5_
- [x] 10.2 Ajouter la route `/parametrage/application` dans `app.routes.ts`, entre `compte` et `vehicules`, gardée uniquement par `authGuard` (pas `profilConfigureGuard`)
- _Requirements: 2.1, 2.6_
- [x] 10.3 Ajouter le lien « Application » entre « Compte » et « Véhicules » dans `features/parametrage/parametrage.component.html`
- _Requirements: 2.1_
- [ ]* 10.4 Écrire les tests unitaires du composant `ParametresApplicationComponent`
- Présélection de la valeur courante au chargement (Exigence 2.3)
- `choisir()` restaure la sélection précédente et notifie une erreur si l'appel HTTP échoue (Exigence 2.5)
- _Requirements: 2.3, 2.4, 2.5_
- [ ]* 10.5 Écrire le test de structure du menu de paramétrage
- Vérifier le positionnement du lien « Application » entre « Compte » et « Véhicules »
- _Requirements: 2.1_
- [x] 11. Frontend — Chargement initial dans `AppComponent`
- [x] 11.1 Modifier `app.component.ts` pour appeler `ParametresApplicationService.charger()` au même moment que le chargement du profil (effect déclenché à l'authentification)
- _Requirements: 1.3, 5.5_
- [x] 12. Checkpoint — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
- [x] 13. Frontend — Adaptation des composants consommateurs
- [x] 13.1 Modifier `features/trajet-list/trajet-list.component.ts` (méthode `distanceAffichee`) : formater selon `ParametresApplicationService.precisionDistance()` au lieu d'un arrondi fixe à 2 décimales
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_
- [x] 13.2 Modifier `features/panneau-totaux/panneau-totaux.component.ts` + `.html` : format dynamique des distances basé sur `precisionDistance()`, ajout de la ligne « Montant prévisionnel annuel » sous l'indication de versement mensuelle, consommation de `montantPrevisionnelMensuel`/`montantPrevisionnelAnnuel`
- _Requirements: 5.4, 5.5, 8.1, 8.3, 8.4_
- [x] 13.3 Modifier `features/carte-trajets/carte-trajets.component.ts` (méthode `contenuInfobulle`) : `toFixed(precision)` dynamique basé sur `ParametresApplicationService.precisionDistance()`
- _Requirements: 9.1_
- [x] 13.4 Modifier `features/montants-regles/montants-regles.component.ts` : lire `montantPrevisionnelMensuel` au lieu de `montantPrevisionnel`
- _Requirements: 7.1, 7.3_
- [ ]* 13.5 Écrire les tests unitaires des composants adaptés
- `TrajetListComponent.distanceAffichee` formate selon la précision courante
- `PanneauTotauxComponent` affiche la nouvelle ligne « Montant prévisionnel annuel »
- `CarteTrajetsComponent.contenuInfobulle` utilise la précision courante
- `MontantsReglesComponent` affiche `montantPrevisionnelMensuel` sans erreur de liaison après le renommage du DTO
- _Requirements: 3.1, 5.4, 7.1, 7.3, 8.1, 8.3, 8.4, 9.1_
- [x] 14. Checkpoint final — Ensure all tests pass
- Ensure all tests pass, ask the user if questions arise.
- [x] 15. Vérification finale
- [x] 15.1 Exécuter le build backend (`dotnet build`) et le build frontend (`ng build` / `npm run build`), corriger toute erreur de compilation résultant des changements de DTO/modèle
- _Requirements: 1.1–9.4 (non-régression globale)_
- [x] 15.2 Exécuter l'ensemble des tests backend (`dotnet test`) et frontend (`ng test` / `vitest --run`), vérifier que toutes les propriétés et tous les tests unitaires/intégration passent
- _Requirements: 1.1–9.4 (non-régression globale)_
## Notes
- Les tâches marquées `*` sont optionnelles (tests) et peuvent être ignorées pour un MVP plus rapide, mais restent recommandées vu la présence de 9 Correctness Properties dans le design.
- Chaque tâche référence les exigences granulaires concernées pour la traçabilité.
- Les checkpoints garantissent une validation incrémentale avant de passer à la couche suivante (backend cœur → calcul prévisionnel → cohérence carte/PDF → frontend).
- Les tests de propriété utilisent FsCheck (déjà présent dans `SchotzIadKmTracker.Tests.csproj`), avec au moins 100 itérations par propriété et le tag de traçabilité `// Feature: precision-distances-km, Property {n}: {texte}`.
- La tâche 6.2 constitue une rupture de contrat mineure sur `MontantPrevisionnelDto` : les tâches frontend 9.5 et 13.4 doivent être réalisées après la tâche 6.1 pour rester synchronisées avec les nouveaux noms de champs.
## Task Dependency Graph
```json
{
"waves": [
{ "id": 0, "tasks": ["1"] },
{ "id": 1, "tasks": ["2.1"] },
{ "id": 2, "tasks": ["2.2"] },
{ "id": 3, "tasks": ["2.3"] },
{ "id": 4, "tasks": ["2.4", "2.5", "3.1"] },
{ "id": 5, "tasks": ["3.2"] },
{ "id": 6, "tasks": ["3.3"] },
{ "id": 7, "tasks": ["3.4"] },
{ "id": 8, "tasks": ["5.1", "5.2", "5.3"] },
{ "id": 9, "tasks": ["5.4", "5.5", "6.1"] },
{ "id": 10, "tasks": ["6.2"] },
{ "id": 11, "tasks": ["6.3", "6.4", "7.1"] },
{ "id": 12, "tasks": ["7.2"] },
{ "id": 13, "tasks": ["7.3", "7.4", "7.5"] },
{ "id": 14, "tasks": ["9.1"] },
{ "id": 15, "tasks": ["9.2"] },
{ "id": 16, "tasks": ["9.3", "9.5"] },
{ "id": 17, "tasks": ["9.4", "10.1"] },
{ "id": 18, "tasks": ["10.2", "10.3"] },
{ "id": 19, "tasks": ["10.4", "10.5", "11.1"] },
{ "id": 20, "tasks": ["13.1", "13.2", "13.3", "13.4"] },
{ "id": 21, "tasks": ["13.5"] },
{ "id": 22, "tasks": ["15.1"] },
{ "id": 23, "tasks": ["15.2"] }
]
}
```