Files
SchotzIAD/.kiro/specs/schotziad-km-tracker/tasks.md
T
MadJax 0e6a97ca61 Renommage : TrajetKmTracker -> SchotzIadKmTracker (backend, specs, frontend)
- Backend : projets, solution, namespaces et références
  TrajetKmTracker.Api/.Tests -> SchotzIadKmTracker.Api/.Tests
  (dossiers, .csproj, .sln, .http, namespaces C#). GUID UserSecretsId préservé.
- Specs : dossier .kiro/specs/trajet-km-tracker -> schotziad-km-tracker
  et mise à jour des références internes (chemins, tags de propriétés).
- Frontend : projet Angular trajet-km-tracker -> schotziad-km-tracker
  (package.json, angular.json, chemin dist, Dockerfile), titre applicatif
  'SchotzIAD KM Tracker'.
- Docker et scripts (backend/Dockerfile, frontend/Dockerfile, run-dev.sh) mis à jour.
- Suppression du fichier résiduel package.json.bak.

Backend (solution) et frontend (build production) compilent sans erreur.
2026-09-14 07:50:26 +02:00

433 lines
29 KiB
Markdown
Raw 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: SchotzIAD KM Tracker
## Overview
Ce plan décompose la conception en tâches de codage incrémentales. Le backend (ASP.NET Core / C# / EF Core / SQLite) est construit d'abord — entités, calculs métier, intégrations BAN/ORS, PDF — puis le frontend Angular consomme l'API. Chaque tâche s'appuie sur les précédentes et se termine par l'intégration, sans code orphelin. Les sous-tâches de test (unitaire, propriété, intégration) sont marquées optionnelles par `*` et référencent explicitement les propriétés de correction et exigences validées.
Langage backend : **C# (ASP.NET Core, .NET 8)**. Langage frontend : **TypeScript (Angular)**. Tests : **xUnit + FsCheck + FluentAssertions + Moq** (backend), **Jest + Angular Testing Library** (frontend).
## Tasks
- [x] 1. Initialiser la solution et le socle backend
- Créer la solution .NET et le projet `SchotzIadKmTracker.Api` (ASP.NET Core Web API, .NET 8)
- Créer le projet de tests `SchotzIadKmTracker.Tests` (xUnit) avec les dépendances FsCheck, FluentAssertions, Moq, Microsoft.AspNetCore.Mvc.Testing
- Ajouter les packages : EF Core + provider SQLite, FluentValidation.AspNetCore, QuestPDF
- Configurer `Program.cs` : services de base, Swagger, middleware `ProblemDetails`, CORS pour le frontend Angular
- _Requirements: 6.1_
- [x] 2. Modèle de données et contexte EF Core
- [x] 2.1 Définir les entités du domaine
- Créer `Trajet` (Date, AdresseDepart, AdresseArrivee, Distance, Commentaire, Position REAL, VehiculeId, PuissanceFiscale dénormalisée, CreatedAt/UpdatedAt)
- Créer `Vehicule` (Plaque, PuissanceFiscale, DateEffet), `Profil` (singleton Id=1, Nom/Prenom/Matricule/AdresseDomicile + coordonnées + DomicileGeocode)
- Créer `AdresseCache` (Adresse unique, Latitude/Longitude, GeometrieJson, DerniereUtilisation), `TrancheBareme` (Annee, PuissanceFiscale, KmMin, KmMax, Coefficient, Base), `MontantRegle` (Annee, Mois, VehiculeId, Montant)
- Créer `AdresseNommee` (Denomination unique 100 car., DenominationNormalisee, Adresse 255 car., Latitude?, Longitude?, GeocodageReussi, CreatedAt/UpdatedAt)
- _Requirements: 6.1, 6.2, 9.6, 10.1, 10.9, 13.1, 14.1, 15.4, 15.5_
- [x] 2.2 Configurer `AppDbContext` et les migrations
- Déclarer les `DbSet`, contraintes (CHECK Distance > 0, Montant >= 0, PuissanceFiscale > 0), index (Date, Date+Position, VehiculeId, DateEffet, Annee+PuissanceFiscale) et index uniques (Profil singleton, MontantsRegles par période/véhicule, AdresseCache par adresse)
- Ajouter la table `AdressesNommees` avec l'index unique `UX_AdresseNommee_Denomination` sur `DenominationNormalisee`
- Générer la migration initiale et l'appliquer à `trajets.db`
- _Requirements: 6.1, 6.2, 9.6, 14.3, 15.6_
- [ ]* 2.3 Écrire les tests d'intégration du schéma
- Vérifier la création de la base, les contraintes CHECK et les index uniques (rejet des doublons de période/véhicule)
- _Requirements: 6.1, 14.3_
- [x] 3. DTOs, options de configuration et validateurs
- [x] 3.1 Créer les DTOs et enregistrements
- `CreateTrajetDto`, `UpdateTrajetDto`, `TrajetDto`, `TrajetCarteDto`, `ReordonnerTrajetsDto`, `TrajetPositionDto`
- `ResumeMensuelDto`, `ResumeAnnuelDto`, `TotauxDto`, `AdresseCacheDto`, `BANAdresseDto`
- `ProfilDto`, `SaveProfilDto`, `VehiculeDto`, `CreateVehiculeDto`, `VehiculeCreeDto`, `ReaffectationDto`
- `TrancheBaremeDto`, `SaveTrancheBaremeDto`, `MontantRegleDto`, `MontantPrevisionnelDto`, `LigneSyntheseMoisDto`, `RegularisationAnnuelleDto`
- `AdresseNommeeDto`, `SaveAdresseNommeeDto`
- `EtatProfilDto` (EstConfigure)
- Créer `BANOptions` et `ORSOptions`, les lier depuis `appsettings.json`
- _Requirements: 1.1, 1.10, 2.1, 15.4, 15.5_
- [x] 3.2 Implémenter les validateurs FluentValidation
- `CreateTrajetDtoValidator` / `UpdateTrajetDtoValidator` : date obligatoire au format JJ/MM/AAAA et existante, adresses 1–255 car., commentaire ≤ 500 car.
- `SaveProfilDtoValidator` (matricule ≤ 8 car.), `CreateVehiculeDtoValidator` (DateEffet ≤ aujourd'hui), `MontantRegleDto` (montant ≥ 0)
- `SaveAdresseNommeeDtoValidator` : dénomination 1–100 car. (absente/vide/>100 → 15.6), adresse 1–255 car. (absente/vide/>255 → 15.8) ; l'unicité normalisée (doublon → 15.7) est vérifiée côté service
- _Requirements: 1.4, 1.5, 1.6, 1.7, 1.12, 6.3, 10.2, 10.11, 14.4, 15.6, 15.7, 15.8_
- [ ]* 3.3 Écrire les tests unitaires des validateurs
- **Property 4 : Rejet systématique des données d'entrée invalides (backend)**
- **Validates: Requirements 1.5, 1.6, 1.7, 6.3**
- [x] 4. Intégrations externes : BAN et ORS
- [x] 4.1 Implémenter `IBANClient` / `BANClient`
- Appel `GET /search/?q={query}&limit=5` avec timeout configurable ; mapper vers `BANAdresseDto` ; lever `BANIndisponibleException` en cas d'échec/timeout
- _Requirements: 1.13, 2.4, 9.1_
- [x] 4.2 Implémenter `IORSClient` / `ORSClient`
- Appel `POST /v2/directions/driving-car` avec coordonnées connues, extraction distance (km) + géométrie GeoJSON, timeout 10 s ; ne jamais appeler ORS avec des coordonnées nulles
- _Requirements: 2.1, 2.2, 8.7_
- [x] 4.3 Implémenter `IDistanceCalculator` / `DistanceCalculatorService`
- Coordonnées connues → ORS ; coordonnées manquantes → géocodage BAN puis ORS ; échec ORS/timeout → fallback Haversine ; échec BAN total → distance nulle + avertissement
- Implémenter `HaversineKm`, arrondi 2 décimales, borne 0,01–99 999,99 km
- _Requirements: 2.1, 2.2, 2.3, 2.4, 2.6_
- [ ]* 4.4 Écrire le test de propriété de priorité BAN sur ORS
- **Property 16 : Priorité BAN sur ORS pour la résolution des coordonnées**
- **Validates: Requirements 2.4**
- [ ]* 4.5 Écrire les tests unitaires BAN/ORS/Distance
- BAN succès/non trouvé/timeout, ORS succès/timeout → Haversine, coordonnées manquantes → BAN puis ORS
- _Requirements: 2.1, 2.2, 2.3, 2.6_
- [x] 5. Cache des adresses géocodées
- [x] 5.1 Implémenter `AdresseCacheService` et le repository associé
- Recherche locale (min 3 car.), upsert d'une adresse (coordonnées inchangées si existante, mise à jour de `DerniereUtilisation`), éviction LRU au-delà de 1 000 entrées
- _Requirements: 9.1, 9.2, 9.3, 9.5, 9.6_
- [ ]* 5.2 Écrire le test de propriété de cohérence du cache
- **Property 10 : Cohérence du cache adresses après géocodage**
- **Validates: Requirements 9.1, 9.2**
- [ ]* 5.3 Écrire le test de propriété LRU
- **Property 11 : Politique LRU du cache adresses**
- **Validates: Requirements 9.5**
- [x] 6. Gestion des véhicules et du profil
- [x] 6.1 Implémenter `VehiculeService` et le repository véhicules
- CRUD historique, détermination du Véhicule_Courant (DateEffet max ≤ aujourd'hui), rejet DateEffet future, conservation de l'historique
- Détection des trajets à réaffecter (date ≥ DateEffet et PuissanceFiscale différente) → `VehiculeCreeDto.ReaffectationProposee`
- Réaffectation acceptée (mise à jour VehiculeId + PuissanceFiscale des trajets ≥ DateEffet) / refusée (aucun changement), en transaction
- _Requirements: 10.9, 10.10, 10.11, 10.12, 10.13, 10.15, 10.16, 10.17, 10.18_
- [ ]* 6.2 Écrire le test de propriété d'association du véhicule courant
- **Property 19 : Association du véhicule courant par défaut**
- **Validates: Requirements 10.13, 10.14**
- [ ]* 6.3 Écrire le test de propriété de réaffectation bornée
- **Property 20 : Réaffectation bornée à la date d'effet**
- **Validates: Requirements 10.16, 10.17**
- [ ]* 6.4 Écrire le test de propriété de conservation de l'historique
- **Property 22 : Conservation de l'historique des véhicules**
- **Validates: Requirements 10.12, 10.18**
- [x] 6.5 Implémenter `ProfilService` et le controller `ProfilController`
- GET/PUT profil singleton, géocodage BAN de l'Adresse_Domicile (DomicileGeocode true/false), mise en cache, validation matricule ≤ 8 car.
- Exposer `GET /api/vehicules`, `GET /api/vehicules/courant`, `POST /api/vehicules`, `POST /api/vehicules/reaffectation`
- Exposer `GET /api/profil/etat` retournant `EtatProfilDto(EstConfigure)` (true ssi Profil Nom+Prénom non vides ET au moins un Véhicule)
- _Requirements: 10.1, 10.2, 10.3, 10.7, 10.8, 10.18, 16.1, 16.2_
- [ ]* 6.6 Écrire le test de propriété de cohérence du profil domicile
- **Property 12 : Cohérence du profil domicile**
- **Validates: Requirements 10.2, 10.7**
- [ ]* 6.7 Écrire le test de propriété de détermination de l'état de configuration du profil
- **Property 31 : Détermination de l'état de configuration du profil**
- **Validates: Requirements 16.1, 16.2**
- [x] 7. Checkpoint — S'assurer que tous les tests passent
- Ensure all tests pass, ask the user if questions arise.
- [x] 8. Service et API des trajets (CRUD + positions)
- [x] 8.1 Implémenter `TrajetRepository` et la gestion des positions
- Résolution du véhicule à la date du trajet et copie de la PuissanceFiscale (dénormalisation)
- Attribution de Position : fin de journée `max(Position)+1.0`, insertion `(P1+P2)/2`, renumérotation par pas de 1.0 si précision < 0.001 (transaction)
- _Requirements: 10.14, 11.7, 11.8_
- [x] 8.2 Implémenter `TrajetService` (création)
- Validation, association véhicule courant si VehiculeId absent, calcul de distance via `DistanceCalculatorService`, mise en cache des adresses résolues, persistance transactionnelle
- Gérer distance nulle + avertissement quand aucune coordonnée n'est résoluble
- _Requirements: 1.2, 1.3, 1.11, 2.4, 2.5, 6.3, 6.7, 6.8, 10.14_
- [ ]* 8.3 Écrire le test de propriété de précision des distances
- **Property 3 : Précision de stockage des distances**
- **Validates: Requirements 2.2, 2.5, 6.2**
- [ ]* 8.4 Écrire le test de propriété de persistance du commentaire
- **Property 13 : Persistance optionnelle du commentaire**
- **Validates: Requirements 1.11, 1.12**
- [x] 8.5 Implémenter `TrajetService` (modification, suppression, réordonnancement)
- Modification : recalcul distance uniquement si adresse modifiée, commentaire seul sans recalcul, rollback si recalcul échoue, 404 si inexistant
- Suppression transactionnelle ; `PATCH /api/trajets/reordonner` met à jour les Position sans altérer les autres champs
- _Requirements: 5.2, 5.3, 5.5, 5.6, 5.8, 5.9, 5.10, 6.7, 6.8, 11.9_
- [ ]* 8.6 Écrire le test de propriété de recalcul conditionnel
- **Property 6 : Recalcul conditionnel de la distance**
- **Validates: Requirements 5.2, 5.3**
- [ ]* 8.7 Écrire les tests de propriété sur les positions
- **Property 14 : Cohérence de l'ordre des trajets d'une journée**
- **Property 15 : Unicité et cohérence des positions au sein d'une journée**
- **Validates: Requirements 11.7, 11.8, 11.9**
- [x] 8.8 Implémenter `TrajetsController` et brancher le controller adresses
- `POST/GET/PUT/DELETE /api/trajets`, `PATCH /api/trajets/reordonner`, `GET /api/trajets/journee/{date}` (avec géométries), `GET /api/adresses/search`
- _Requirements: 1.3, 5.5, 5.6, 8.7, 9.3_
- [ ]* 8.9 Écrire le test de propriété d'invariant de suppression
- **Property 5 : Invariant de suppression**
- **Validates: Requirements 5.5, 6.4**
- [x] 9. Résumés, totaux et navigation temporelle
- [x] 9.1 Implémenter `ResumeService` (mensuel et annuel)
- Résumé mensuel filtré par véhicule, trié par date puis Position, total 2 décimales ; validation mois [1,12] et année [2000,2100]
- Résumé annuel : toujours 12 mois (0 si vide), total = somme des 12 mois
- _Requirements: 3.1, 3.2, 3.3, 3.4, 3.6, 3.8, 4.2, 4.3, 4.4, 4.7, 6.5, 6.6_
- [ ]* 9.2 Écrire les tests de propriété des totaux et résumés
- **Property 1 : Cohérence du total mensuel** — **Validates: Requirements 3.3, 6.5**
- **Property 2 : Cohérence du total annuel** — **Validates: Requirements 4.3, 6.6**
- **Property 7 : Mois toujours présents dans le résumé annuel** — **Validates: Requirements 4.2, 4.4**
- **Property 8 : Rejet des paramètres de résumé hors plage** — **Validates: Requirements 3.6**
- [x] 9.3 Implémenter `TotauxService` et la navigation temporelle
- Totaux jour/mois/Total_Annuel_Partiel filtrés par véhicule (cumul jusqu'au mois inclus, exclusion des mois postérieurs)
- Années disponibles (données + année courante), mois disponibles, jours disponibles
- _Requirements: 12.7, 12.8, 12.15, 12.16, 12.19_
- [ ]* 9.4 Écrire les tests de propriété des totaux filtrés et partiels
- **Property 17 : Total annuel partiel borné au mois sélectionné** — **Validates: Requirements 12.8**
- **Property 21 : Totaux filtrés par véhicule** — **Validates: Requirements 12.19**
- [x] 9.5 Implémenter `ResumesController` et les controllers de navigation
- `GET /api/resumes/mensuel/{annee}/{mois}`, `GET /api/resumes/annuel/{annee}`
- `GET /api/annees-disponibles`, `/api/mois-disponibles/{annee}`, `/api/jours-disponibles/{annee}/{mois}`, `/api/totaux/{date}`
- _Requirements: 3.8, 4.1, 12.15, 12.16_
- [x] 10. Barème kilométrique et régularisation
- [x] 10.1 Implémenter `BaremeService` et le controller barème
- CRUD des tranches, validation du non-chevauchement par (année, puissance fiscale)
- Sélection de tranche contenant le total annuel, formule `km × Coefficient + Base`, barème non configuré → montant zéro + avertissement
- `GET /api/bareme/{annee}`, `POST/PUT/DELETE /api/bareme`
- _Requirements: 13.1, 13.2, 13.3, 13.4_
- [ ]* 10.2 Écrire les tests de propriété de sélection de tranche
- **Property 24 : Sélection de tranche du barème** — **Validates: Requirements 13.3, 13.4**
- **Property 27 : Sélection de tranche par le total annuel** — **Validates: Requirements 13.3, 14.6**
- [x] 10.3 Implémenter `MontantRegleService` (upsert et montant prévisionnel)
- Upsert par (année, mois, véhicule), rejet montant négatif
- Montant_Previsionnel = 80% du barème sur le Total_Annuel_Partiel (indicatif, hors régularisation)
- _Requirements: 13.5, 13.6, 14.1, 14.2, 14.3, 14.4_
- [ ]* 10.4 Écrire les tests de propriété montants réglés / prévisionnel
- **Property 23 : Indication de versement à 80% (informative)** — **Validates: Requirements 13.5, 13.6**
- **Property 26 : Unicité du montant réglé par période et véhicule** — **Validates: Requirements 14.3**
- [x] 10.5 Implémenter `RemboursementService` (régularisation annuelle)
- Montant_Reel = barème plein 100% sur total annuel, Frais_Reel_Mensuel = km_mois × coef tranche annuelle, Regularisation = Montant_Reel − total réglé
- Construire `RegularisationAnnuelleDto` (12 lignes + totaux)
- `GET /api/montant-previsionnel/...`, `GET/PUT /api/montants-regles`, `GET /api/regularisation/{annee}`
- _Requirements: 14.5, 14.6, 14.7, 14.8_
- [ ]* 10.6 Écrire le test de propriété de régularisation
- **Property 25 : Cohérence de la régularisation annuelle (barème plein)**
- **Validates: Requirements 13.3, 14.5, 14.6, 14.7**
- [x] 11. Export PDF (QuestPDF)
- [x] 11.1 Implémenter `PdfService` — rapport mensuel
- En-tête : identité (nom, prénom, matricule), véhicule (plaque, puissance fiscale, coef/base tranche), période, adresse départ profil, totaux mois/année, TOTAL IK du mois, Montant_Regle
- Liste des trajets (date, motif/commentaire, départ → arrivée, km) ; timeout 15 s
- _Requirements: 7.1, 7.4, 7.6, 7.8, 7.10, 7.11_
- [x] 11.2 Implémenter `PdfService` — synthèse annuelle et rapports par véhicule
- Page synthèse (identité, année, domicile, barème de l'année, véhicule, tranche) + 12 lignes (km, réglé, frais réel) + totaux (km annuel, réglé, réel, régularisation)
- Génération d'un PDF distinct par véhicule si plusieurs véhicules sur la période
- _Requirements: 7.2, 7.4, 7.6, 7.7, 7.8, 7.9, 7.12_
- [x] 11.3 Implémenter `ExportsPdfController`
- `GET /api/exports/mensuel/{annee}/{mois}` et `GET /api/exports/annuel/{annee}` ; en-tête Content-Disposition `trajets_MM_AAAA.pdf` / `trajets_AAAA.pdf`, timeout → 503
- _Requirements: 7.3, 7.4, 7.5_
- [ ]* 11.4 Écrire le test de propriété de nommage PDF
- **Property 9 : Nommage des fichiers PDF exportés**
- **Validates: Requirements 7.4**
- [ ]* 11.5 Écrire les tests unitaires PDF
- En-tête identité/véhicule, PDF distinct par véhicule, montants mensuels, régularisation annuelle
- _Requirements: 7.6, 7.8, 7.9, 7.12_
- [x] 12. Checkpoint backend — S'assurer que tous les tests passent
- Ensure all tests pass, ask the user if questions arise.
- [ ]* 13. Tests d'intégration backend end-to-end
- [ ]* 13.1 Tests d'intégration trajets et distance (WebApplicationFactory + SQLite en mémoire)
- POST avec/sans coordonnées (BAN puis ORS, fallback Haversine, distance nulle + 201), insertion entre deux trajets, PATCH réordonner, PUT/DELETE
- _Requirements: 1.3, 2.4, 2.6, 5.5, 11.8_
- [ ]* 13.2 Tests d'intégration résumés, totaux, véhicules, barème et exports
- Résumés mensuel/annuel, totaux partiels, réaffectation accepter/refuser, barème chevauchant → 400, upsert montant réglé, régularisation, export PDF 2 véhicules → 2 fichiers
- _Requirements: 3.8, 4.1, 7.9, 10.16, 12.8, 13.2, 14.3, 14.7_
- [x] 14. Socle frontend Angular et services
- [x] 14.1 Initialiser l'application Angular et le socle core
- Créer l'app Angular, `app-routing.module.ts`, modèles TypeScript, `notification.service.ts`, `TimeoutInterceptor` (10 s), `DateJjMmAAAAValidator`
- _Requirements: 1.9, 1.5_
- [x] 14.2 Implémenter les services HTTP Angular
- `TrajetService`, `VehiculeService`, `BANService` (autocomplétion), `AdresseCacheService`, `ProfilService`, `BaremeService`, `RemboursementService`
- `ProfilService.getEtat()` : appelle `GET /api/profil/etat` et retourne `EtatProfilDto`
- _Requirements: 1.2, 9.3, 9.4, 16.2_
- [ ]* 14.3 Écrire les tests unitaires des services
- Mock HttpClient, comportement du TimeoutInterceptor
- _Requirements: 1.9_
- [x] 15. Saisie et édition des trajets (frontend)
- [x] 15.1 Implémenter `TrajetFormComponent`
- Reactive Form (date, adresses 1–255, commentaire 0–500), validations synchrones, mode création/édition/insertion, autocomplétion BAN dès 3 car. + stockage coordonnées, boutons « Utiliser mon domicile », avertissement saisie manuelle sans coordonnées
- _Requirements: 1.1, 1.4, 1.5, 1.8, 1.10, 1.13, 1.14, 1.15, 1.16, 1.17, 5.1, 10.4, 10.5, 10.6, 11.1, 11.2, 11.3, 11.4_
- [x] 15.2 Implémenter `TrajetListComponent`
- Liste triée par Position, actions modifier/supprimer (avec `ConfirmDialog`)/insérer après/insérer avant le suivant, drag & drop → `PATCH reordonner`, rafraîchissement < 1 000 ms, icône + tooltip commentaire
- _Requirements: 5.1, 5.4, 5.7, 11.6, 11.9_
- [ ]* 15.3 Écrire les tests des composants de saisie
- Validation, autocomplétion BAN, mode insertion pré-rempli, drag & drop, tri par Position
- _Requirements: 1.4, 1.5, 1.13, 11.1, 11.6_
- [x] 16. Consultation, navigation temporelle et carte (frontend)
- [x] 16.1 Implémenter `SelecteurTemporelComponent`
- Sélecteurs année/mois/jour avec surbrillance des périodes ayant des données, désactivation des dates futures
- _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5_
- [x] 16.2 Implémenter `PanneauTotauxComponent`
- Totaux jour/mois/Total_Annuel_Partiel à 2 décimales, Montant_Previsionnel 80% indicatif, recalcul au changement
- _Requirements: 12.7, 12.8, 13.5, 13.6, 13.7, 13.8_
- [x] 16.3 Implémenter `CarteTrajetsComponent`
- Leaflet + tuiles OpenStreetMap, tracés GeoJSON par trajet, fallback ligne droite, infobulles, zoom/déplacement, message si vide ou carte indisponible
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.8_
- [x] 16.4 Implémenter `ConsultationJourComponent` (layout principal)
- Deux colonnes (liste scrollable + panneau sticky totaux/carte), boutons jour ±1 (« suivant » désactivé si futur), sélecteur de véhicule filtrant liste/totaux/carte, rafraîchissement < 1 000 ms
- _Requirements: 12.6, 12.9, 12.10, 12.11, 12.12, 12.13, 12.14, 12.17, 12.18, 12.20_
- [ ]* 16.5 Écrire les tests des composants de consultation
- Surbrillance/dates futures, 3 totaux + prévisionnel, tracés carte + fallback, layout et filtrage par véhicule
- _Requirements: 8.2, 8.5, 12.5, 12.8, 12.18_
- [x] 17. Profil, véhicules, barème et montants réglés (frontend)
- [x] 17.1 Implémenter `ProfilComponent`, `VehiculesComponent` et `ReaffectationDialogComponent`
- Profil (nom/prénom/matricule 8 car./domicile) + avertissement géocodage ; historique véhicules trié, ajout avec DateEffet ≤ aujourd'hui, dialog de réaffectation (nombre de trajets, accepter/refuser)
- _Requirements: 10.1, 10.2, 10.8, 10.9, 10.10, 10.11, 10.15, 10.16, 10.17_
- [x] 17.2 Implémenter `BaremeComponent` et `MontantsReglesComponent`
- Saisie des 3 tranches par puissance fiscale/année avec détection de chevauchement ; saisie Montant_Regle par mois + prévisionnel indicatif, rejet négatif, montants 2 décimales
- _Requirements: 13.1, 13.7, 14.1, 14.4, 14.8_
- [x] 17.3 Implémenter `ResumeMensuelComponent` et `ResumeAnnuelComponent`
- Résumé mensuel (liste + total 2 décimales, bouton export), résumé annuel (12 lignes + total ≤ 999 999,99, bouton export), déclenchement téléchargement PDF + gestion d'erreur
- _Requirements: 3.1, 3.5, 3.7, 4.1, 4.5, 4.6, 7.3_
- [ ]* 17.4 Écrire les tests des composants profil/véhicules/barème/montants/résumés
- Matricule 8 car., date effet future bloquée, dialog réaffectation, chevauchement barème, montant négatif rejeté, affichage 2 décimales et max annuel
- _Requirements: 4.5, 10.2, 10.11, 13.1, 14.4_
- [x] 18. Checkpoint final — S'assurer que tous les tests passent
- Ensure all tests pass, ask the user if questions arise.
- [x] 19. Carnet d'adresses nommées (backend)
- [x] 19.1 Implémenter `AdresseNommeeService` et `AdressesNommeesController`
- CRUD des Adresses_Nommées ; normalisation (trim + minuscules + suppression des accents) pour l'unicité de la Dénomination et le matching du Cache_Adresses
- Réutilisation du Cache_Adresses si l'adresse normalisée y est présente (sans appel BAN) ; sinon géocodage/re-géocodage via BAN (timeout 5 s)
- Si BAN indisponible/timeout : persistance sans coordonnées + `GeocodageReussi = false` et réponse indiquant l'absence de coordonnées ; mise en cache des coordonnées obtenues
- Suppression sans impact sur les Trajets existants (conservation de leur adresse et coordonnées)
- Endpoints `GET/POST/PUT/DELETE /api/adresses-nommees`
- _Requirements: 15.1, 15.4, 15.5, 15.6, 15.7, 15.8, 15.9, 15.10, 15.11, 15.12, 15.14_
- [ ]* 19.2 Écrire le test de propriété d'unicité de la dénomination
- **Property 28 : Unicité normalisée de la dénomination**
- **Validates: Requirements 15.6, 15.7**
- [ ]* 19.3 Écrire le test de propriété de préservation des trajets à la suppression
- **Property 29 : Préservation des trajets à la suppression d'une adresse nommée**
- **Validates: Requirements 15.14**
- [ ]* 19.4 Écrire le test de propriété de réutilisation du cache et cohérence du géocodage
- **Property 30 : Réutilisation du cache et cohérence du géocodage des adresses nommées**
- **Validates: Requirements 15.9, 15.10, 15.11, 15.12**
- [x] 20. Carnet d'adresses nommées (frontend)
- [x] 20.1 Implémenter `AdresseNommeeService` (Angular) et `CarnetAdressesComponent`
- `AdresseNommeeService` : `getAdressesNommees`, `create`, `update`, `delete` (HTTP)
- `CarnetAdressesComponent` : liste triée par Dénomination, formulaire de création/modification avec autocomplétion BAN (min 3 car., debounce 300 ms, max 5 suggestions), confirmation de suppression, message de succès CRUD, avertissement si le géocodage a échoué
- _Requirements: 15.1, 15.2, 15.3, 15.4, 15.11, 15.13, 15.15_
- [x] 20.2 Étendre `TrajetFormComponent` avec le sélecteur d'Adresse_Nommee
- Pour les champs adresse de départ et adresse d'arrivée, ajouter un sélecteur d'Adresse_Nommee (par Dénomination) en mode création ET insertion
- À la sélection, remplir le champ + associer les coordonnées (remplace le contenu pré-rempli en mode insertion)
- _Requirements: 1.18, 1.19, 11.10, 11.11, 15.16_
- [ ]* 20.3 Écrire les tests des composants carnet et du sélecteur dans le formulaire
- Composant Carnet_Adresses (liste, formulaire, confirmation, message), sélecteur d'Adresse_Nommee dans `TrajetFormComponent`
- _Requirements: 15.2, 15.13, 1.19_
- [x] 21. Onboarding profil à la première utilisation (frontend)
- [x] 21.1 Implémenter `OnboardingComponent`
- Formulaire une étape : identité (nom, prénom, matricule), Adresse_Domicile optionnelle avec autocomplétion BAN, au moins un Véhicule (plaque, puissance fiscale, date d'effet ≤ aujourd'hui)
- Bouton « Commencer » désactivé tant que les champs requis manquent
- À l'enregistrement réussi du profil + véhicule → navigation vers la vue de consultation
- Gestion d'erreur de persistance : message d'erreur + conservation des données saisies
- _Requirements: 16.3, 16.5, 16.6, 16.7, 16.8, 16.9, 16.10, 16.12_
- [x] 21.2 Implémenter le guard `ProfilConfigureGuard` (CanActivate)
- Appelle `ProfilService.getEtat()` au chargement ; redirige vers `/onboarding` si `EstConfigure = false` et bloque l'accès aux routes trajets/consultation ; laisse passer si `true`
- Gère l'échec de récupération de l'état par un message de connexion + action « Réessayer »
- Déclarer le guard sur les routes protégées dans `app-routing`
- _Requirements: 16.3, 16.4, 16.11, 16.13_
- [ ]* 21.3 Écrire les tests du parcours d'onboarding et du guard
- Bouton « Commencer » désactivé, navigation après enregistrement, redirection du guard, cas `EstConfigure` true/false, erreur + retry
- _Requirements: 16.3, 16.8, 16.10, 16.11, 16.13_
- [x] 22. Conteneurisation et déploiement Docker Compose
- [x] 22.1 Écrire le `Dockerfile` backend multi-stage
- Étape build (SDK) → étape runtime (aspnet), exposition du port 8080
- Base SQLite sous le volume `/data/trajets.db` via `ConnectionStrings__Default`
- Application automatique des migrations EF Core au démarrage (`Database.MigrateAsync()` dans `Program.cs`)
- Endpoint de santé `GET /health` (`AddHealthChecks` / `MapHealthChecks`)
- _Requirements: 6.1_
- [x] 22.2 Écrire le `Dockerfile` frontend multi-stage et la configuration `nginx.conf`
- Étape build (node) → étape serve (nginx)
- `nginx.conf` : fallback SPA vers `index.html` + reverse-proxy `/api` vers `backend:8080`
- [x] 22.3 Écrire le `docker-compose.yml`
- Services backend + frontend, volume nommé `trajets-data:/data` pour la persistance SQLite
- Clé ORS via variable d'environnement `${ORS_API_KEY}` depuis un fichier `.env` non commité
- Mapping hôte 8080 → nginx, `depends_on: service_healthy`, `restart: unless-stopped`, healthcheck backend
- Ajouter `.env` au `.gitignore`
## Notes
- Les tâches marquées `*` sont optionnelles (tests unitaires, tests de propriété, tests d'intégration) et peuvent être ignorées pour un MVP plus rapide ; les tâches d'implémentation cœur ne le sont jamais.
- Chaque tâche référence des exigences précises pour la traçabilité ; les sous-tâches de test de propriété référencent explicitement la propriété de correction validée (numéro + exigences).
- Les 31 propriétés de correction sont couvertes par des tests FsCheck (backend) répartis au plus près de leur implémentation pour détecter les erreurs tôt.
- La clé ORS n'est jamais commitée : User Secrets en développement, variables d'environnement en production.
- Les checkpoints garantissent une validation incrémentale du backend puis du frontend.
## Task Dependency Graph
```json
{
"waves": [
{ "id": 0, "tasks": ["1"] },
{ "id": 1, "tasks": ["2.1"] },
{ "id": 2, "tasks": ["2.2", "3.1"] },
{ "id": 3, "tasks": ["2.3", "3.2", "4.1", "4.2"] },
{ "id": 4, "tasks": ["3.3", "4.3", "5.1", "6.1", "6.5"] },
{ "id": 5, "tasks": ["4.4", "4.5", "5.2", "5.3", "6.2", "6.3", "6.4", "6.6", "6.7", "8.1"] },
{ "id": 6, "tasks": ["8.2", "10.1", "19.1"] },
{ "id": 7, "tasks": ["8.3", "8.4", "8.5", "10.2", "10.3", "19.2", "19.3", "19.4"] },
{ "id": 8, "tasks": ["8.6", "8.7", "8.8", "10.4", "10.5"] },
{ "id": 9, "tasks": ["8.9", "9.1", "9.3", "10.6", "11.1", "11.2"] },
{ "id": 10, "tasks": ["9.2", "9.4", "9.5", "11.3"] },
{ "id": 11, "tasks": ["11.4", "11.5", "13.1", "13.2"] },
{ "id": 12, "tasks": ["14.1", "22.1"] },
{ "id": 13, "tasks": ["14.2"] },
{ "id": 14, "tasks": ["14.3", "15.1", "16.1", "16.3", "17.1", "20.1", "21.1"] },
{ "id": 15, "tasks": ["15.2", "16.2", "16.4", "17.2", "17.3", "20.2", "21.2", "22.2"] },
{ "id": 16, "tasks": ["15.3", "16.5", "17.4", "20.3", "21.3", "22.3"] }
]
}
```