# 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"] } ] } ```