Application complète de saisie et de suivi des trajets kilométriques avec calcul de distance routière (OpenRouteService), barème kilométrique, résumés et régularisation annuelle. Backend (ASP.NET Core .NET 8, EF Core, SQLite) : - Trajets (CRUD, réordonnancement, insertion), calcul de distance ORS avec fallback Haversine, géométrie d'itinéraire stockée par trajet, recalcul en masse. - Profil, véhicules (avec dénomination), carnet d'adresses nommées, cache d'adresses. - Barème kilométrique : tranches multiples par puissance fiscale, borne max exclusive avec 'illimité' (KmMax nullable), coefficient à 3 décimales, recopie du dernier barème connu. - Résumés mensuel/annuel, montants réglés, régularisation, export PDF. Frontend (Angular 22, zoneless + signals) : - Consultation par jour, navigation temporelle synchronisée, carte Leaflet, formulaire de trajet (adresses nommées, domicile, autocomplétion BAN). - Détection de changement zoneless (indépendante de zone.js). Hébergement : Docker Compose (nginx + backend), scripts run-dev.sh / run-docker.sh.
88 lines
4.5 KiB
C#
88 lines
4.5 KiB
C#
using TrajetKmTracker.Api.Models.Entities;
|
|
|
|
namespace TrajetKmTracker.Api.Repositories;
|
|
|
|
/// <summary>
|
|
/// Accès aux données des trajets et gestion des positions au sein d'une journée.
|
|
/// <para>
|
|
/// La <see cref="Trajet.Position"/> est une valeur décimale (REAL) permettant l'insertion
|
|
/// sans réordonnancement systématique : ajout en fin de journée → <c>max(Position) + 1.0</c>,
|
|
/// insertion entre deux positions P₁ et P₂ → <c>(P₁ + P₂) / 2</c>. Lorsque l'écart devient
|
|
/// trop faible (< <see cref="SeuilRenumerotation"/>), la journée est renumérotée par pas de
|
|
/// 1.0 en transaction en préservant l'ordre relatif (Property 15, Exigences 11.7/11.8).
|
|
/// </para>
|
|
/// <para>
|
|
/// Ce repository fournit uniquement les primitives d'accès et de calcul de position ;
|
|
/// la logique métier (validation, calcul de distance, mise en cache) relève de TrajetService.
|
|
/// </para>
|
|
/// </summary>
|
|
public interface ITrajetRepository
|
|
{
|
|
/// <summary>
|
|
/// Seuil de précision en deçà duquel une renumérotation de la journée est déclenchée.
|
|
/// Conforme à la conception : écart < 0.001.
|
|
/// </summary>
|
|
const decimal SeuilRenumerotation = 0.001m;
|
|
|
|
/// <summary>Retourne le trajet par identifiant, ou <c>null</c> s'il n'existe pas.</summary>
|
|
Task<Trajet?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>Retourne les trajets d'une journée triés par <see cref="Trajet.Position"/> croissante.</summary>
|
|
Task<List<Trajet>> GetByDateAsync(DateOnly date, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Résout le véhicule applicable à la date du trajet et copie sa <c>PuissanceFiscale</c>
|
|
/// sur le trajet (dénormalisation, Exigence 10.14). Ne modifie ni <c>VehiculeId</c> ni
|
|
/// <c>PuissanceFiscale</c> si aucun véhicule n'est applicable à cette date.
|
|
/// Coordonné avec <see cref="Services.IVehiculeService.GetVehiculeADateAsync"/>.
|
|
/// </summary>
|
|
Task AppliquerVehiculeADateAsync(Trajet trajet, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Calcule la position d'un trajet ajouté en fin de journée : <c>max(Position) + 1.0</c>
|
|
/// pour la date, ou <c>1.0</c> si la journée est vide.
|
|
/// </summary>
|
|
Task<decimal> CalculerPositionFinDeJourneeAsync(DateOnly date, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Calcule la position d'un trajet inséré entre les positions <paramref name="positionPrecedente"/>
|
|
/// (P₁) et <paramref name="positionSuivante"/> (P₂) : <c>(P₁ + P₂) / 2</c>.
|
|
/// </summary>
|
|
/// <exception cref="ArgumentException">Si P₁ n'est pas strictement inférieure à P₂.</exception>
|
|
decimal CalculerPositionEntre(decimal positionPrecedente, decimal positionSuivante);
|
|
|
|
/// <summary>
|
|
/// Indique si l'écart entre deux positions adjacentes est devenu trop faible
|
|
/// (< <see cref="SeuilRenumerotation"/>) pour insérer une nouvelle valeur intermédiaire.
|
|
/// </summary>
|
|
bool NecessiteRenumerotation(decimal positionPrecedente, decimal positionSuivante);
|
|
|
|
/// <summary>Ajoute un trajet (sans persister). L'appelant gère la transaction et l'appel à <see cref="SauvegarderAsync"/>.</summary>
|
|
Task AddAsync(Trajet trajet, CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>Marque un trajet existant comme modifié (sans persister).</summary>
|
|
void Update(Trajet trajet);
|
|
|
|
/// <summary>Supprime un trajet (sans persister).</summary>
|
|
void Delete(Trajet trajet);
|
|
|
|
/// <summary>Persiste les changements en attente.</summary>
|
|
Task<int> SauvegarderAsync(CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Applique un réordonnancement manuel : met à jour uniquement la <see cref="Trajet.Position"/>
|
|
/// des trajets fournis (par identifiant) sans altérer leurs autres champs, en transaction.
|
|
/// Les identifiants inconnus sont ignorés.
|
|
/// </summary>
|
|
Task ReordonnerAsync(
|
|
IReadOnlyList<(int Id, decimal Position)> positions,
|
|
CancellationToken cancellationToken = default);
|
|
|
|
/// <summary>
|
|
/// Renumérote les trajets d'une journée par pas de 1.0 (1.0, 2.0, 3.0, …) en préservant
|
|
/// l'ordre relatif courant (tri par Position puis Id), en transaction. Retourne la liste
|
|
/// renumérotée triée par Position croissante (Property 15).
|
|
/// </summary>
|
|
Task<List<Trajet>> RenumeroterJourneeAsync(DateOnly date, CancellationToken cancellationToken = default);
|
|
}
|