using CutList.Web.Data.Entities; using Microsoft.EntityFrameworkCore; namespace CutList.Web.Data; public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } public DbSet Materials => Set(); public DbSet MaterialDimensions => Set(); public DbSet Suppliers => Set(); public DbSet StockItems => Set(); public DbSet SupplierOfferings => Set(); public DbSet StockTransactions => Set(); public DbSet CuttingTools => Set(); public DbSet Jobs => Set(); public DbSet JobParts => Set(); public DbSet JobStocks => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Material modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Shape) .HasMaxLength(50) .IsRequired() .HasConversion( v => v.ToString(), // Enum to string (uses enum name) v => Enum.Parse(v)); // String to enum entity.Property(e => e.Size).HasMaxLength(100).IsRequired(); entity.Property(e => e.Type) .HasMaxLength(20) .HasConversion( v => v.ToString(), v => Enum.Parse(v)); entity.Property(e => e.Grade).HasMaxLength(50); entity.Property(e => e.Description).HasMaxLength(255); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); }); // MaterialDimensions - TPH inheritance modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); // 1:1 relationship with Material entity.HasOne(e => e.Material) .WithOne(m => m.Dimensions) .HasForeignKey(e => e.MaterialId) .OnDelete(DeleteBehavior.Cascade); // TPH discriminator entity.HasDiscriminator("DimensionType") .HasValue("RoundBar") .HasValue("RoundTube") .HasValue("FlatBar") .HasValue("SquareBar") .HasValue("SquareTube") .HasValue("RectangularTube") .HasValue("Angle") .HasValue("Channel") .HasValue("IBeam") .HasValue("Pipe"); }); // Configure each dimension type's properties modelBuilder.Entity(entity => { entity.Property(e => e.Diameter).HasPrecision(10, 4); entity.HasIndex(e => e.Diameter); }); modelBuilder.Entity(entity => { entity.Property(e => e.OuterDiameter).HasPrecision(10, 4); entity.Property(e => e.Wall).HasColumnName("Wall").HasPrecision(10, 4); entity.HasIndex(e => e.OuterDiameter); }); modelBuilder.Entity(entity => { entity.Property(e => e.Width).HasColumnName("Width").HasPrecision(10, 4); entity.Property(e => e.Thickness).HasColumnName("Thickness").HasPrecision(10, 4); entity.HasIndex(e => e.Width); }); modelBuilder.Entity(entity => { entity.Property(e => e.Size).HasColumnName("Size").HasPrecision(10, 4); entity.HasIndex(e => e.Size); }); modelBuilder.Entity(entity => { entity.Property(e => e.Size).HasColumnName("Size").HasPrecision(10, 4); entity.Property(e => e.Wall).HasColumnName("Wall").HasPrecision(10, 4); entity.HasIndex(e => e.Size); }); modelBuilder.Entity(entity => { entity.Property(e => e.Width).HasColumnName("Width").HasPrecision(10, 4); entity.Property(e => e.Height).HasColumnName("Height").HasPrecision(10, 4); entity.Property(e => e.Wall).HasColumnName("Wall").HasPrecision(10, 4); entity.HasIndex(e => e.Width); }); modelBuilder.Entity(entity => { entity.Property(e => e.Leg1).HasPrecision(10, 4); entity.Property(e => e.Leg2).HasPrecision(10, 4); entity.Property(e => e.Thickness).HasColumnName("Thickness").HasPrecision(10, 4); entity.HasIndex(e => e.Leg1); }); modelBuilder.Entity(entity => { entity.Property(e => e.Height).HasColumnName("Height").HasPrecision(10, 4); entity.Property(e => e.Flange).HasPrecision(10, 4); entity.Property(e => e.Web).HasPrecision(10, 4); entity.HasIndex(e => e.Height); }); modelBuilder.Entity(entity => { entity.Property(e => e.Height).HasColumnName("Height").HasPrecision(10, 4); entity.Property(e => e.WeightPerFoot).HasPrecision(10, 4); entity.HasIndex(e => e.Height); }); modelBuilder.Entity(entity => { entity.Property(e => e.NominalSize).HasPrecision(10, 4); entity.Property(e => e.Wall).HasColumnName("Wall").HasPrecision(10, 4); entity.Property(e => e.Schedule).HasMaxLength(20); entity.HasIndex(e => e.NominalSize); }); // Supplier modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).HasMaxLength(100).IsRequired(); entity.Property(e => e.ContactInfo).HasMaxLength(500); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); }); // StockItem modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.LengthInches).HasPrecision(10, 4); entity.Property(e => e.Name).HasMaxLength(100); entity.Property(e => e.Notes).HasMaxLength(255); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); entity.HasOne(e => e.Material) .WithMany(m => m.StockItems) .HasForeignKey(e => e.MaterialId) .OnDelete(DeleteBehavior.Cascade); entity.HasIndex(e => new { e.MaterialId, e.LengthInches }).IsUnique(); }); // StockTransaction modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Notes).HasMaxLength(500); entity.Property(e => e.UnitPrice).HasPrecision(10, 2); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); entity.HasOne(e => e.StockItem) .WithMany(s => s.Transactions) .HasForeignKey(e => e.StockItemId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(e => e.Job) .WithMany() .HasForeignKey(e => e.JobId) .OnDelete(DeleteBehavior.SetNull); entity.HasOne(e => e.Supplier) .WithMany() .HasForeignKey(e => e.SupplierId) .OnDelete(DeleteBehavior.SetNull); }); // SupplierOffering modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.PartNumber).HasMaxLength(100); entity.Property(e => e.SupplierDescription).HasMaxLength(255); entity.Property(e => e.Price).HasPrecision(10, 2); entity.Property(e => e.Notes).HasMaxLength(255); entity.HasOne(e => e.StockItem) .WithMany(s => s.SupplierOfferings) .HasForeignKey(e => e.StockItemId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(e => e.Supplier) .WithMany(s => s.Offerings) .HasForeignKey(e => e.SupplierId) .OnDelete(DeleteBehavior.Cascade); entity.HasIndex(e => new { e.SupplierId, e.StockItemId }).IsUnique(); }); // CuttingTool modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).HasMaxLength(50).IsRequired(); entity.Property(e => e.KerfInches).HasPrecision(6, 4); }); // Job modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.JobNumber).HasMaxLength(20).IsRequired(); entity.Property(e => e.Name).HasMaxLength(100); entity.Property(e => e.Customer).HasMaxLength(100); entity.Property(e => e.CreatedAt).HasDefaultValueSql("GETUTCDATE()"); entity.HasIndex(e => e.JobNumber).IsUnique(); entity.HasOne(e => e.CuttingTool) .WithMany(t => t.Jobs) .HasForeignKey(e => e.CuttingToolId) .OnDelete(DeleteBehavior.SetNull); }); // JobPart modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.Name).HasMaxLength(100); entity.Property(e => e.LengthInches).HasPrecision(10, 4); entity.HasOne(e => e.Job) .WithMany(p => p.Parts) .HasForeignKey(e => e.JobId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(e => e.Material) .WithMany(m => m.JobParts) .HasForeignKey(e => e.MaterialId) .OnDelete(DeleteBehavior.Restrict); }); // JobStock modelBuilder.Entity(entity => { entity.HasKey(e => e.Id); entity.Property(e => e.LengthInches).HasPrecision(10, 4); entity.HasOne(e => e.Job) .WithMany(j => j.Stock) .HasForeignKey(e => e.JobId) .OnDelete(DeleteBehavior.Cascade); entity.HasOne(e => e.Material) .WithMany() .HasForeignKey(e => e.MaterialId) .OnDelete(DeleteBehavior.Restrict); entity.HasOne(e => e.StockItem) .WithMany() .HasForeignKey(e => e.StockItemId) .OnDelete(DeleteBehavior.SetNull); }); // Seed default cutting tools modelBuilder.Entity().HasData( new CuttingTool { Id = 1, Name = "Bandsaw", KerfInches = 0.0625m, IsDefault = true, IsActive = true }, new CuttingTool { Id = 2, Name = "Chop Saw", KerfInches = 0.125m, IsDefault = false, IsActive = true }, new CuttingTool { Id = 3, Name = "Cold Cut Saw", KerfInches = 0.0625m, IsDefault = false, IsActive = true }, new CuttingTool { Id = 4, Name = "Hacksaw", KerfInches = 0.0625m, IsDefault = false, IsActive = true } ); } }