Renommage : TrajetKmTracker -> SchotzIadKmTracker (backend, specs, frontend)
- Backend : projets, solution, namespaces et références TrajetKmTracker.Api/.Tests -> SchotzIadKmTracker.Api/.Tests (dossiers, .csproj, .sln, .http, namespaces C#). GUID UserSecretsId préservé. - Specs : dossier .kiro/specs/trajet-km-tracker -> schotziad-km-tracker et mise à jour des références internes (chemins, tags de propriétés). - Frontend : projet Angular trajet-km-tracker -> schotziad-km-tracker (package.json, angular.json, chemin dist, Dockerfile), titre applicatif 'SchotzIAD KM Tracker'. - Docker et scripts (backend/Dockerfile, frontend/Dockerfile, run-dev.sh) mis à jour. - Suppression du fichier résiduel package.json.bak. Backend (solution) et frontend (build production) compilent sans erreur.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SchotzIadKmTracker.Api.Data;
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Implémentation EF Core de <see cref="IAdresseCacheRepository"/> au-dessus d'<see cref="AppDbContext"/>.
|
||||
/// </summary>
|
||||
public class AdresseCacheRepository : IAdresseCacheRepository
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public AdresseCacheRepository(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AdresseCache>> RechercherAsync(
|
||||
string fragmentNormalise,
|
||||
int limite,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// La colonne Adresse stocke déjà la forme normalisée : la comparaison se fait donc
|
||||
// directement sur celle-ci. EF.Functions.Like assure une comparaison indépendante
|
||||
// de la casse côté SQLite.
|
||||
var motif = $"%{fragmentNormalise}%";
|
||||
|
||||
return await _db.AdresseCache
|
||||
.Where(a => EF.Functions.Like(a.Adresse, motif))
|
||||
.OrderByDescending(a => a.DerniereUtilisation)
|
||||
.Take(limite)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<AdresseCache?> ObtenirParAdresseNormaliseeAsync(
|
||||
string adresseNormalisee,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _db.AdresseCache
|
||||
.FirstOrDefaultAsync(a => a.Adresse == adresseNormalisee, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task AjouterAsync(AdresseCache entree, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _db.AdresseCache.AddAsync(entree, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<int> CompterAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _db.AdresseCache.CountAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AdresseCache>> ObtenirPlusAnciennesAsync(
|
||||
int nombre,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (nombre <= 0)
|
||||
{
|
||||
return Array.Empty<AdresseCache>();
|
||||
}
|
||||
|
||||
return await _db.AdresseCache
|
||||
.OrderBy(a => a.DerniereUtilisation)
|
||||
.Take(nombre)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public void Supprimer(IEnumerable<AdresseCache> entrees)
|
||||
{
|
||||
_db.AdresseCache.RemoveRange(entrees);
|
||||
}
|
||||
|
||||
public Task<int> SauvegarderAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Accès aux données du Cache_Adresses (table <c>AdresseCache</c>).
|
||||
/// Toutes les correspondances d'adresse s'effectuent sur le texte normalisé
|
||||
/// (voir <see cref="AdresseCacheService"/>).
|
||||
/// </summary>
|
||||
public interface IAdresseCacheRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Retourne les entrées dont l'adresse normalisée contient le fragment normalisé fourni,
|
||||
/// triées de la plus récemment utilisée à la plus ancienne, limitées à <paramref name="limite"/>.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<AdresseCache>> RechercherAsync(
|
||||
string fragmentNormalise,
|
||||
int limite,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Retourne l'entrée correspondant exactement à l'adresse normalisée, ou <c>null</c>.</summary>
|
||||
Task<AdresseCache?> ObtenirParAdresseNormaliseeAsync(
|
||||
string adresseNormalisee,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Ajoute une nouvelle entrée au cache.</summary>
|
||||
Task AjouterAsync(AdresseCache entree, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Compte le nombre total d'entrées du cache.</summary>
|
||||
Task<int> CompterAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retourne les entrées les plus anciennes (par <c>DerniereUtilisation</c> croissante),
|
||||
/// limitées à <paramref name="nombre"/>. Utilisé pour l'éviction LRU.
|
||||
/// </summary>
|
||||
Task<IReadOnlyList<AdresseCache>> ObtenirPlusAnciennesAsync(
|
||||
int nombre,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Supprime les entrées fournies du cache.</summary>
|
||||
void Supprimer(IEnumerable<AdresseCache> entrees);
|
||||
|
||||
/// <summary>Persiste les modifications en attente.</summary>
|
||||
Task<int> SauvegarderAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
|
||||
namespace SchotzIadKmTracker.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);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Accès aux données des véhicules et aux trajets impactés par une réaffectation.
|
||||
/// L'historique des véhicules est conservé (aucune suppression) — Exigence 10.12 / Property 22.
|
||||
/// </summary>
|
||||
public interface IVehiculeRepository
|
||||
{
|
||||
/// <summary>Retourne l'historique complet des véhicules, trié par Date_Effet décroissante puis Id décroissant.</summary>
|
||||
Task<List<Vehicule>> GetHistoriqueAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Retourne le véhicule par identifiant, ou <c>null</c> s'il n'existe pas.</summary>
|
||||
Task<Vehicule?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Résout le véhicule courant à une date donnée : Date_Effet la plus récente parmi
|
||||
/// celles <= <paramref name="date"/> (ORDER BY DateEffet DESC, Id DESC LIMIT 1).
|
||||
/// Retourne <c>null</c> si aucun véhicule n'a de Date_Effet <= <paramref name="date"/>.
|
||||
/// </summary>
|
||||
Task<Vehicule?> GetVehiculeCourantAsync(DateOnly date, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>Ajoute un nouveau véhicule (sans persister). L'appelant gère la transaction.</summary>
|
||||
Task AddAsync(Vehicule vehicule, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Retourne les trajets dont la Date est >= <paramref name="dateEffet"/> et dont la
|
||||
/// PuissanceFiscale diffère de <paramref name="puissanceFiscale"/> (candidats à la réaffectation).
|
||||
/// </summary>
|
||||
Task<List<Trajet>> GetTrajetsAReaffecterAsync(
|
||||
DateOnly dateEffet,
|
||||
int puissanceFiscale,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SchotzIadKmTracker.Api.Data;
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
using SchotzIadKmTracker.Api.Services;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Repositories;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TrajetRepository : ITrajetRepository
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private readonly IVehiculeService _vehiculeService;
|
||||
|
||||
public TrajetRepository(AppDbContext context, IVehiculeService vehiculeService)
|
||||
{
|
||||
_context = context;
|
||||
_vehiculeService = vehiculeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Trajet?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.Trajets
|
||||
.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Trajet>> GetByDateAsync(DateOnly date, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// SQLite ne sait pas trier (ORDER BY) sur une colonne decimal : on ramène les
|
||||
// trajets du jour puis on trie côté client (volume par journée très réduit).
|
||||
var trajets = await _context.Trajets
|
||||
.Where(t => t.Date == date)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return trajets
|
||||
.OrderBy(t => t.Position)
|
||||
.ThenBy(t => t.Id)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AppliquerVehiculeADateAsync(Trajet trajet, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Résolution du véhicule applicable à la date du trajet (Date_Effet la plus récente <= Date).
|
||||
var vehicule = await _vehiculeService.GetVehiculeADateAsync(trajet.Date, cancellationToken);
|
||||
if (vehicule is null)
|
||||
{
|
||||
// Aucun véhicule applicable à cette date : on laisse la logique métier (TrajetService) décider.
|
||||
return;
|
||||
}
|
||||
|
||||
// Dénormalisation : copie de la puissance fiscale du véhicule au moment de l'association (Exigence 10.14).
|
||||
trajet.VehiculeId = vehicule.Id;
|
||||
trajet.PuissanceFiscale = vehicule.PuissanceFiscale;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<decimal> CalculerPositionFinDeJourneeAsync(DateOnly date, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// SQLite ne sait pas appliquer MAX sur une colonne decimal : on ramène les positions
|
||||
// du jour et on calcule le maximum côté client (volume par journée très réduit).
|
||||
var positions = await _context.Trajets
|
||||
.Where(t => t.Date == date)
|
||||
.Select(t => t.Position)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (positions.Count == 0)
|
||||
{
|
||||
return 1.0m;
|
||||
}
|
||||
|
||||
return positions.Max() + 1.0m;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public decimal CalculerPositionEntre(decimal positionPrecedente, decimal positionSuivante)
|
||||
{
|
||||
if (positionPrecedente >= positionSuivante)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"La position précédente ({positionPrecedente}) doit être strictement inférieure à la position suivante ({positionSuivante}).",
|
||||
nameof(positionPrecedente));
|
||||
}
|
||||
|
||||
return (positionPrecedente + positionSuivante) / 2.0m;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool NecessiteRenumerotation(decimal positionPrecedente, decimal positionSuivante)
|
||||
{
|
||||
return positionSuivante - positionPrecedente < ITrajetRepository.SeuilRenumerotation;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddAsync(Trajet trajet, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Trajets.Add(trajet);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Update(Trajet trajet)
|
||||
{
|
||||
_context.Trajets.Update(trajet);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Delete(Trajet trajet)
|
||||
{
|
||||
_context.Trajets.Remove(trajet);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> SauvegarderAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ReordonnerAsync(
|
||||
IReadOnlyList<(int Id, decimal Position)> positions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (positions.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
var ids = positions.Select(p => p.Id).ToList();
|
||||
var trajets = await _context.Trajets
|
||||
.Where(t => ids.Contains(t.Id))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var positionParId = positions.ToDictionary(p => p.Id, p => p.Position);
|
||||
|
||||
foreach (var trajet in trajets)
|
||||
{
|
||||
if (positionParId.TryGetValue(trajet.Id, out var nouvellePosition))
|
||||
{
|
||||
// Seule la Position est modifiée ; aucun autre champ n'est altéré (Exigence 11.9).
|
||||
trajet.Position = nouvellePosition;
|
||||
trajet.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Trajet>> RenumeroterJourneeAsync(DateOnly date, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
// Préservation de l'ordre relatif courant (Position croissante, Id en départage) — Property 15.
|
||||
var trajets = (await _context.Trajets
|
||||
.Where(t => t.Date == date)
|
||||
.ToListAsync(cancellationToken))
|
||||
.OrderBy(t => t.Position)
|
||||
.ThenBy(t => t.Id)
|
||||
.ToList();
|
||||
|
||||
decimal position = 1.0m;
|
||||
foreach (var trajet in trajets)
|
||||
{
|
||||
trajet.Position = position;
|
||||
trajet.UpdatedAt = DateTime.UtcNow;
|
||||
position += 1.0m;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
|
||||
return trajets;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SchotzIadKmTracker.Api.Data;
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Repositories;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class VehiculeRepository : IVehiculeRepository
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public VehiculeRepository(AppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Vehicule>> GetHistoriqueAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.Vehicules
|
||||
.OrderByDescending(v => v.DateEffet)
|
||||
.ThenByDescending(v => v.Id)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Vehicule?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.Vehicules
|
||||
.FirstOrDefaultAsync(v => v.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Vehicule?> GetVehiculeCourantAsync(DateOnly date, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Design SQL : SELECT * FROM Vehicules WHERE DateEffet <= date ORDER BY DateEffet DESC, Id DESC LIMIT 1
|
||||
return await _context.Vehicules
|
||||
.Where(v => v.DateEffet <= date)
|
||||
.OrderByDescending(v => v.DateEffet)
|
||||
.ThenByDescending(v => v.Id)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task AddAsync(Vehicule vehicule, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_context.Vehicules.Add(vehicule);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<List<Trajet>> GetTrajetsAReaffecterAsync(
|
||||
DateOnly dateEffet,
|
||||
int puissanceFiscale,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.Trajets
|
||||
.Where(t => t.Date >= dateEffet && t.PuissanceFiscale != puissanceFiscale)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user