- 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.
115 lines
4.6 KiB
C#
115 lines
4.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using SchotzIadKmTracker.Api.Models.Entities;
|
|
|
|
namespace SchotzIadKmTracker.Api.Data;
|
|
|
|
/// <summary>
|
|
/// Contexte EF Core de l'application. Déclare les <see cref="DbSet{TEntity}"/>, les contraintes CHECK,
|
|
/// les index et les index uniques conformément à la section « Schéma SQLite » de la conception.
|
|
/// </summary>
|
|
public class AppDbContext : DbContext
|
|
{
|
|
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
|
{
|
|
}
|
|
|
|
public DbSet<Trajet> Trajets => Set<Trajet>();
|
|
public DbSet<Vehicule> Vehicules => Set<Vehicule>();
|
|
public DbSet<Profil> Profil => Set<Profil>();
|
|
public DbSet<AdresseCache> AdresseCache => Set<AdresseCache>();
|
|
public DbSet<AdresseNommee> AdressesNommees => Set<AdresseNommee>();
|
|
public DbSet<TrancheBareme> TranchesBareme => Set<TrancheBareme>();
|
|
public DbSet<MontantRegle> MontantsRegles => Set<MontantRegle>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
// --- Trajets ---
|
|
modelBuilder.Entity<Trajet>(entity =>
|
|
{
|
|
entity.ToTable("Trajets", t =>
|
|
{
|
|
// Distance nulle autorisée (Exigence 2.4 : distance non calculable) ; sinon strictement positive.
|
|
t.HasCheckConstraint("CK_Trajets_Distance", "\"Distance\" IS NULL OR \"Distance\" > 0");
|
|
t.HasCheckConstraint("CK_Trajets_PuissanceFiscale", "\"PuissanceFiscale\" > 0");
|
|
});
|
|
|
|
entity.HasIndex(t => t.Date, "IX_Trajets_Date");
|
|
entity.HasIndex(t => new { t.Date, t.Position }, "IX_Trajets_Date_Position");
|
|
entity.HasIndex(t => t.VehiculeId, "IX_Trajets_Vehicule");
|
|
|
|
entity.HasOne(t => t.Vehicule)
|
|
.WithMany(v => v.Trajets)
|
|
.HasForeignKey(t => t.VehiculeId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
});
|
|
|
|
// --- Vehicules ---
|
|
modelBuilder.Entity<Vehicule>(entity =>
|
|
{
|
|
entity.ToTable("Vehicules", t =>
|
|
t.HasCheckConstraint("CK_Vehicules_PuissanceFiscale", "\"PuissanceFiscale\" > 0"));
|
|
|
|
entity.HasIndex(v => v.DateEffet, "IX_Vehicules_DateEffet");
|
|
});
|
|
|
|
// --- Profil (singleton Id = 1) ---
|
|
modelBuilder.Entity<Profil>(entity =>
|
|
{
|
|
entity.ToTable("Profil");
|
|
entity.HasKey(p => p.Id);
|
|
entity.Property(p => p.Id).ValueGeneratedNever();
|
|
entity.HasIndex(p => p.Id, "UX_Profil_Singleton").IsUnique();
|
|
});
|
|
|
|
// --- AdresseCache (Adresse unique) ---
|
|
modelBuilder.Entity<AdresseCache>(entity =>
|
|
{
|
|
entity.ToTable("AdresseCache");
|
|
entity.HasIndex(a => a.Adresse).IsUnique();
|
|
});
|
|
|
|
// --- AdressesNommees (unicité sur DenominationNormalisee) ---
|
|
modelBuilder.Entity<AdresseNommee>(entity =>
|
|
{
|
|
entity.ToTable("AdressesNommees");
|
|
entity.HasIndex(a => a.DenominationNormalisee, "UX_AdresseNommee_Denomination").IsUnique();
|
|
});
|
|
|
|
// --- TranchesBareme ---
|
|
modelBuilder.Entity<TrancheBareme>(entity =>
|
|
{
|
|
// Convention [KmMin, KmMax[ : borne max EXCLUSIVE, ou NULL = illimité.
|
|
// La contrainte tolère KmMax NULL et impose sinon KmMin < KmMax.
|
|
// Convention [KmMin, KmMax[ : borne max EXCLUSIVE, ou NULL = illimité.
|
|
// Les décimaux sont stockés en TEXT par SQLite : on CAST en REAL pour comparer
|
|
// numériquement (sinon "5000" > "20000" en comparaison lexicographique de chaînes).
|
|
entity.ToTable("TranchesBareme", t =>
|
|
t.HasCheckConstraint(
|
|
"CK_TranchesBareme_Km",
|
|
"\"KmMax\" IS NULL OR CAST(\"KmMin\" AS REAL) < CAST(\"KmMax\" AS REAL)"));
|
|
|
|
entity.HasIndex(t => new { t.Annee, t.PuissanceFiscale }, "IX_TranchesBareme_Annee_PF");
|
|
});
|
|
|
|
// --- MontantsRegles ---
|
|
modelBuilder.Entity<MontantRegle>(entity =>
|
|
{
|
|
entity.ToTable("MontantsRegles", t =>
|
|
{
|
|
t.HasCheckConstraint("CK_MontantsRegles_Montant", "\"Montant\" >= 0");
|
|
t.HasCheckConstraint("CK_MontantsRegles_Mois", "\"Mois\" BETWEEN 1 AND 12");
|
|
});
|
|
|
|
entity.HasIndex(m => new { m.Annee, m.Mois, m.VehiculeId }, "UX_MontantsRegles_Periode_Vehicule")
|
|
.IsUnique();
|
|
|
|
entity.HasOne(m => m.Vehicule)
|
|
.WithMany()
|
|
.HasForeignKey(m => m.VehiculeId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
});
|
|
}
|
|
}
|