using Microsoft.EntityFrameworkCore; using SchotzIadKmTracker.Api.Data; using SchotzIadKmTracker.Api.Models.DTOs; namespace SchotzIadKmTracker.Api.Services; /// public class TotauxService : ITotauxService { private readonly AppDbContext _context; private readonly IVehiculeService _vehiculeService; public TotauxService(AppDbContext context, IVehiculeService vehiculeService) { _context = context; _vehiculeService = vehiculeService; } /// public async Task 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) // Projection : on ramène la distance simple et l'indicateur aller/retour, puis on // calcule la distance EFFECTIVE (doublée si A/R) côté client lors des sommes. .Select(t => new { t.Date, t.Distance, t.AllerRetour }) .ToListAsync(cancellationToken); // Distance comptée (effective) : distance simple × 2 si aller/retour, null → 0. static decimal Effective(decimal? distance, bool allerRetour) => distance.GetValueOrDefault() * (allerRetour ? 2m : 1m); // TotalJour : somme des distances effectives de la journée sélectionnée (Exigence 12.7). var totalJour = trajetsAnnee .Where(t => t.Date == date) .Sum(t => Effective(t.Distance, t.AllerRetour)); // TotalMois : somme des distances effectives du mois de la date sélectionnée (Exigence 12.7). var totalMois = trajetsAnnee .Where(t => t.Date.Month == date.Month) .Sum(t => Effective(t.Distance, t.AllerRetour)); // 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 => Effective(t.Distance, t.AllerRetour)); 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)); } /// public async Task> 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; } /// public async Task> 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; } /// public async Task> 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; } }