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,143 @@
|
||||
using System.Globalization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SchotzIadKmTracker.Api.Data;
|
||||
using SchotzIadKmTracker.Api.Models.DTOs;
|
||||
using SchotzIadKmTracker.Api.Models.Entities;
|
||||
using SchotzIadKmTracker.Api.Repositories;
|
||||
using SchotzIadKmTracker.Api.Services.Exceptions;
|
||||
|
||||
namespace SchotzIadKmTracker.Api.Services;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class VehiculeService : IVehiculeService
|
||||
{
|
||||
private const string DateFormat = "dd/MM/yyyy";
|
||||
|
||||
private readonly IVehiculeRepository _repository;
|
||||
private readonly AppDbContext _context;
|
||||
|
||||
public VehiculeService(IVehiculeRepository repository, AppDbContext context)
|
||||
{
|
||||
_repository = repository;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<VehiculeDto>> GetHistoriqueAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var vehicules = await _repository.GetHistoriqueAsync(cancellationToken);
|
||||
return vehicules.Select(ToDto).ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<VehiculeDto> GetVehiculeCourantAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var aujourdHui = DateOnly.FromDateTime(DateTime.Today);
|
||||
var vehicule = await _repository.GetVehiculeCourantAsync(aujourdHui, cancellationToken)
|
||||
?? throw new VehiculeNotFoundException(
|
||||
"Aucun véhicule courant : aucun véhicule n'a de date d'effet inférieure ou égale à aujourd'hui.");
|
||||
|
||||
return ToDto(vehicule);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<VehiculeDto?> GetVehiculeADateAsync(DateOnly date, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var vehicule = await _repository.GetVehiculeCourantAsync(date, cancellationToken);
|
||||
return vehicule is null ? null : ToDto(vehicule);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<VehiculeCreeDto> CreateAsync(CreateVehiculeDto dto, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var dateEffet = ParseDateEffet(dto.DateEffet);
|
||||
|
||||
// Rejet d'une date d'effet dans le futur (Exigence 10.11).
|
||||
if (dateEffet > DateOnly.FromDateTime(DateTime.Today))
|
||||
{
|
||||
throw new DateEffetFutureException("La date d'effet ne peut pas être postérieure à aujourd'hui.");
|
||||
}
|
||||
|
||||
var vehicule = new Vehicule
|
||||
{
|
||||
Denomination = dto.Denomination,
|
||||
Plaque = dto.Plaque,
|
||||
PuissanceFiscale = dto.PuissanceFiscale,
|
||||
DateEffet = dateEffet,
|
||||
};
|
||||
|
||||
// Conservation de l'historique : on ajoute sans supprimer/modifier les véhicules existants (Property 22).
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
await _repository.AddAsync(vehicule, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
|
||||
// Détection des trajets à réaffecter : date >= DateEffet et puissance fiscale différente.
|
||||
var trajetsConcernes = await _repository.GetTrajetsAReaffecterAsync(
|
||||
dateEffet, vehicule.PuissanceFiscale, cancellationToken);
|
||||
|
||||
return new VehiculeCreeDto(
|
||||
ToDto(vehicule),
|
||||
ReaffectationProposee: trajetsConcernes.Count > 0,
|
||||
NombreTrajetsConcernes: trajetsConcernes.Count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ReaffecterAsync(ReaffectationDto dto, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var vehicule = await _repository.GetByIdAsync(dto.VehiculeId, cancellationToken)
|
||||
?? throw new VehiculeNotFoundException($"Le véhicule d'identifiant {dto.VehiculeId} est introuvable.");
|
||||
|
||||
// Réaffectation refusée : aucun changement (Exigence 10.17).
|
||||
if (!dto.Accepter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Réaffectation acceptée : mise à jour bornée à la date d'effet (Exigence 10.16 / Property 20).
|
||||
var trajets = await _repository.GetTrajetsAReaffecterAsync(
|
||||
vehicule.DateEffet, vehicule.PuissanceFiscale, cancellationToken);
|
||||
|
||||
if (trajets.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await using var transaction = await _context.Database.BeginTransactionAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
var maintenant = DateTime.UtcNow;
|
||||
foreach (var trajet in trajets)
|
||||
{
|
||||
trajet.VehiculeId = vehicule.Id;
|
||||
trajet.PuissanceFiscale = vehicule.PuissanceFiscale;
|
||||
trajet.UpdatedAt = maintenant;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static DateOnly ParseDateEffet(string value)
|
||||
{
|
||||
// La validation FluentValidation garantit normalement le format ; on reste défensif ici.
|
||||
return DateOnly.ParseExact(value, DateFormat, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static VehiculeDto ToDto(Vehicule v) =>
|
||||
new(v.Id, v.Denomination, v.Plaque, v.PuissanceFiscale, v.DateEffet);
|
||||
}
|
||||
Reference in New Issue
Block a user