feat: réutilisation des coordonnées déjà géocodées (domicile, insertion de trajets)

Expose les coordonnées déjà géocodées dans ProfilDto
(DomicileLatitude/Longitude) et TrajetDto (DepartLatitude/Longitude,
ArriveeLatitude/Longitude) afin que le Frontend les réutilise sans
déclencher un nouveau géocodage BAN :

- Bouton « Utiliser mon domicile » (trajet-form) : utilise désormais
  les coordonnées du Profil au lieu de forcer un re-géocodage.
- Actions « insérer avant/après » (trajet-list) : transmettent les
  coordonnées déjà connues du point de fin repris au formulaire
  d'insertion.

Le résumé mensuel (ResumeService) résout les coordonnées via le
Cache_Adresses pour chaque trajet (nouvelle méthode
ConstruireResumeMensuelAvecCoordonneesAsync) ; le résumé annuel reste
inchangé pour éviter des lookups de cache inutiles sur une vue qui
n'affiche pas le détail des trajets.

Met à jour design.md et design-requirements.md (Requirements 10 et 11)
pour refléter le nouveau contrat d'API.
This commit is contained in:
2026-10-13 20:52:04 +02:00
parent 7695deae10
commit d015ca1c98
13 changed files with 170 additions and 35 deletions
@@ -7,6 +7,10 @@ export interface Profil {
matricule: string | null;
adresseDomicile: string | null;
domicileGeocode: boolean;
// Coordonnées du domicile déjà géocodées (null si non résolues). Permettent de
// transmettre les coordonnées lors de « Utiliser mon domicile » sans re-géocoder.
domicileLatitude: number | null;
domicileLongitude: number | null;
}
/** Création / mise à jour du profil — miroir de SaveProfilDto. */
@@ -44,6 +44,12 @@ export interface Trajet {
vehiculeId: number;
puissanceFiscale: number;
allerRetour: boolean; // information aller/retour conservée par trajet
// Coordonnées déjà géocodées (via le Cache_Adresses), si connues ; null sinon. Permettent de
// réutiliser l'adresse d'un trajet existant (ex. « Insérer avant/après ») sans re-géocoder.
departLatitude?: number | null;
departLongitude?: number | null;
arriveeLatitude?: number | null;
arriveeLongitude?: number | null;
}
/**
@@ -73,6 +73,15 @@ export interface TrajetInsertionPrefill {
* le nouveau trajet soit rattaché à la BONNE journée (Exigences 11.1–11.4).
*/
date?: string | null;
/**
* Coordonnées du point de fin repris (adresse de départ/arrivée du Trajet_Précédent
* ou Trajet_Suivant), si déjà géocodées. Transmises pour éviter un re-géocodage BAN
* côté backend lors de la création du trajet inséré.
*/
departLatitude?: number | null;
departLongitude?: number | null;
arriveeLatitude?: number | null;
arriveeLongitude?: number | null;
}
/** Identifie lequel des deux champs adresse est concerné. */
@@ -427,9 +436,23 @@ export class TrajetFormComponent implements OnInit, OnDestroy {
// Backend) : source fiable, pas d'avertissement de coordonnées manquantes.
if (this.prefill.adresseDepart?.trim()) {
this.sourceFiableDepart.set(true);
// Coordonnées déjà géocodées transmises par le conteneur (trajet-list) si connues :
// on les mémorise pour éviter un re-géocodage BAN côté backend.
if (this.prefill.departLatitude != null && this.prefill.departLongitude != null) {
this.coordDepart.set({
latitude: this.prefill.departLatitude,
longitude: this.prefill.departLongitude
});
}
}
if (this.prefill.adresseArrivee?.trim()) {
this.sourceFiableArrivee.set(true);
if (this.prefill.arriveeLatitude != null && this.prefill.arriveeLongitude != null) {
this.coordArrivee.set({
latitude: this.prefill.arriveeLatitude,
longitude: this.prefill.arriveeLongitude
});
}
}
if (this.prefill.vehiculeId != null) {
this.vehiculeId = this.prefill.vehiculeId;
@@ -509,20 +532,20 @@ export class TrajetFormComponent implements OnInit, OnDestroy {
if (!adresse) {
return;
}
// On remplit le libellé du domicile ; les coordonnées seront résolues côté
// backend via le Cache_Adresses ou la BAN (Exigence 2.4). On ne dispose pas
// des coordonnées du domicile dans le profil exposé au frontend.
const controle = champ === 'depart' ? this.departCtrl : this.arriveeCtrl;
controle.setValue(adresse);
controle.markAsTouched();
if (champ === 'depart') {
this.coordDepart.set(null);
this.sourceFiableDepart.set(true);
this.suggestionsDepart.set([]);
} else {
this.coordArrivee.set(null);
this.sourceFiableArrivee.set(true);
this.suggestionsArrivee.set([]);
// On remplit le libellé du domicile ET on transmet ses coordonnées déjà
// géocodées (exposées dans le Profil), afin d'éviter un re-géocodage BAN côté
// backend à chaque trajet. Si le domicile n'a pas été géocodé (coordonnées
// absentes), on retombe sur null : le Backend résoudra alors l'adresse (Exigence 2.4).
const profil = this.profil();
const coord =
profil?.domicileLatitude != null && profil?.domicileLongitude != null
? { latitude: profil.domicileLatitude, longitude: profil.domicileLongitude }
: null;
// `appliquerAdresse` remplit le champ, mémorise les coordonnées, marque la source
// comme fiable et vide les suggestions du champ concerné.
this.appliquerAdresse(champ, adresse, coord);
if (champ === 'arrivee') {
// Confort de saisie : un trajet dont l'arrivée est le domicile est typiquement
// un retour. On pré-remplit le commentaire avec « Retour domicile » s'il est
// encore vide (on ne remplace jamais un commentaire déjà saisi).
@@ -343,9 +343,15 @@ export class TrajetListComponent {
*/
insererApres(trajet: Trajet): void {
const pointDeFin = trajet.allerRetour ? trajet.adresseDepart : trajet.adresseArrivee;
// Coordonnées du point de fin (déjà géocodées si connues) : évite un re-géocodage
// BAN côté backend pour une adresse déjà résolue.
const latitudeFin = trajet.allerRetour ? trajet.departLatitude : trajet.arriveeLatitude;
const longitudeFin = trajet.allerRetour ? trajet.departLongitude : trajet.arriveeLongitude;
this.insertAfter.emit({
prefill: {
adresseDepart: pointDeFin,
departLatitude: latitudeFin,
departLongitude: longitudeFin,
vehiculeId: trajet.vehiculeId,
// Rattache l'insertion à la JOURNÉE du trajet concerné (et non à la date de
// la carte), pour éviter les erreurs de saisie de date.
@@ -369,11 +375,19 @@ export class TrajetListComponent {
// Point de fin du trajet courant : son arrivée, ou son départ s'il est aller/retour
// (le véhicule est alors revenu à son point d'origine).
const finCourant = courant.allerRetour ? courant.adresseDepart : courant.adresseArrivee;
// Coordonnées du point de fin du trajet courant et du départ du trajet suivant
// (déjà géocodées si connues) : évite un re-géocodage BAN côté backend.
const latitudeFinCourant = courant.allerRetour ? courant.departLatitude : courant.arriveeLatitude;
const longitudeFinCourant = courant.allerRetour ? courant.departLongitude : courant.arriveeLongitude;
this.insertBetween.emit({
prefill: {
adresseDepart: finCourant,
departLatitude: latitudeFinCourant,
departLongitude: longitudeFinCourant,
// Le trajet suivant part toujours de son adresse de départ (indépendant de l'A/R).
adresseArrivee: suivant.adresseDepart,
arriveeLatitude: suivant.departLatitude,
arriveeLongitude: suivant.departLongitude,
vehiculeId: courant.vehiculeId,
// Journée du trajet courant (insertion au sein de la même journée).
date: (courant.date ?? '').slice(0, 10)