SchotzIAD KM Tracker : suivi des trajets kilométriques (Angular + .NET 8)
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.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TrajetKmTracker.Api.Data;
|
||||
using TrajetKmTracker.Api.Models.DTOs;
|
||||
|
||||
namespace TrajetKmTracker.Api.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TotauxService : ITotauxService
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private readonly IVehiculeService _vehiculeService;
|
||||
|
||||
public TotauxService(AppDbContext context, IVehiculeService vehiculeService)
|
||||
{
|
||||
_context = context;
|
||||
_vehiculeService = vehiculeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<TotauxDto> GetTotauxAsync(
|
||||
DateOnly date, int? vehiculeId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Véhicule cible : celui fourni, sinon le Véhicule_Courant (Exigence 12.19).
|
||||
var vehiculeCible = vehiculeId
|
||||
?? (await _vehiculeService.GetVehiculeCourantAsync(cancellationToken)).Id;
|
||||
|
||||
// Trajets du véhicule pour l'année sélectionnée (Property 21 : filtrage par véhicule).
|
||||
// Distance nullable → 0 lors des sommes (traitée côté client après projection).
|
||||
var trajetsAnnee = await _context.Trajets
|
||||
.AsNoTracking()
|
||||
.Where(t => t.VehiculeId == vehiculeCible && t.Date.Year == date.Year)
|
||||
.Select(t => new { t.Date, Distance = (decimal?)t.Distance ?? 0m })
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// TotalJour : somme des distances de la journée sélectionnée (Exigence 12.7).
|
||||
var totalJour = trajetsAnnee
|
||||
.Where(t => t.Date == date)
|
||||
.Sum(t => t.Distance);
|
||||
|
||||
// TotalMois : somme des distances du mois de la date sélectionnée (Exigence 12.7).
|
||||
var totalMois = trajetsAnnee
|
||||
.Where(t => t.Date.Month == date.Month)
|
||||
.Sum(t => t.Distance);
|
||||
|
||||
// Total_Annuel_Partiel : cumul de l'année jusqu'au mois sélectionné INCLUS,
|
||||
// exclusion des mois strictement postérieurs (Exigence 12.8, Property 17).
|
||||
var totalAnnuelPartiel = trajetsAnnee
|
||||
.Where(t => t.Date.Month <= date.Month)
|
||||
.Sum(t => t.Distance);
|
||||
|
||||
return new TotauxDto(
|
||||
Date: date,
|
||||
VehiculeId: vehiculeCible,
|
||||
TotalJour: Math.Round(totalJour, 2, MidpointRounding.AwayFromZero),
|
||||
TotalMois: Math.Round(totalMois, 2, MidpointRounding.AwayFromZero),
|
||||
TotalAnnuelPartiel: Math.Round(totalAnnuelPartiel, 2, MidpointRounding.AwayFromZero));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<int>> GetAnneesDisponiblesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Années comportant au moins un Trajet (Exigence 12.15).
|
||||
var annees = await _context.Trajets
|
||||
.AsNoTracking()
|
||||
.Select(t => t.Date.Year)
|
||||
.Distinct()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
// Ajout de l'année courante si absente.
|
||||
var anneeCourante = DateOnly.FromDateTime(DateTime.Today).Year;
|
||||
if (!annees.Contains(anneeCourante))
|
||||
{
|
||||
annees.Add(anneeCourante);
|
||||
}
|
||||
|
||||
annees.Sort();
|
||||
return annees;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<int>> GetMoisDisponiblesAsync(int annee, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Mois de l'année comportant au moins un Trajet (Exigence 12.16).
|
||||
var mois = await _context.Trajets
|
||||
.AsNoTracking()
|
||||
.Where(t => t.Date.Year == annee)
|
||||
.Select(t => t.Date.Month)
|
||||
.Distinct()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
mois.Sort();
|
||||
return mois;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<int>> GetJoursDisponiblesAsync(
|
||||
int annee, int mois, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Jours du mois comportant au moins un Trajet (Exigence 12.16).
|
||||
var jours = await _context.Trajets
|
||||
.AsNoTracking()
|
||||
.Where(t => t.Date.Year == annee && t.Date.Month == mois)
|
||||
.Select(t => t.Date.Day)
|
||||
.Distinct()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
jours.Sort();
|
||||
return jours;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user