using line_gestao_api.Models; using line_gestao_api.Services; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace line_gestao_api.Data; public class AppDbContext : IdentityDbContext, Guid> { private readonly ITenantProvider _tenantProvider; public AppDbContext(DbContextOptions options, ITenantProvider tenantProvider) : base(options) { _tenantProvider = tenantProvider; } public DbSet Tenants => Set(); // ✅ tabela para espelhar a planilha (GERAL) public DbSet MobileLines => Set(); // ✅ tabela para espelhar a aba MUREG public DbSet MuregLines => Set(); // ✅ tabela para espelhar o FATURAMENTO (PF/PJ) public DbSet BillingClients => Set(); // ✅ tabela DADOS DOS USUÁRIOS public DbSet UserDatas => Set(); // ✅ tabela VIGÊNCIA public DbSet VigenciaLines => Set(); // ✅ tabela TROCA DE NÚMERO public DbSet TrocaNumeroLines => Set(); // ✅ tabela CHIPS VIRGENS public DbSet ChipVirgemLines => Set(); // ✅ tabela CONTROLE DE RECEBIDOS public DbSet ControleRecebidoLines => Set(); // ✅ tabela NOTIFICAÇÕES public DbSet Notifications => Set(); // ✅ tabela RESUMO public DbSet ResumoMacrophonyPlans => Set(); public DbSet ResumoMacrophonyTotals => Set(); public DbSet ResumoVivoLineResumos => Set(); public DbSet ResumoVivoLineTotals => Set(); public DbSet ResumoClienteEspeciais => Set(); public DbSet ResumoPlanoContratoResumos => Set(); public DbSet ResumoPlanoContratoTotals => Set(); public DbSet ResumoLineTotais => Set(); public DbSet ResumoReservaLines => Set(); public DbSet ResumoReservaTotals => Set(); // ✅ tabela PARCELAMENTOS public DbSet ParcelamentoLines => Set(); public DbSet ParcelamentoMonthValues => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // ========================= // ✅ USER (Identity) // ========================= modelBuilder.Entity(e => { e.Property(x => x.Name).HasMaxLength(120); e.HasIndex(x => new { x.TenantId, x.NormalizedEmail }) .IsUnique(); }); // ========================= // ✅ GERAL (MobileLine) // ========================= modelBuilder.Entity(e => { // Mantém UNIQUE por Linha por tenant (se Linha puder ser null no banco, Postgres aceita múltiplos nulls) e.HasIndex(x => new { x.TenantId, x.Linha }).IsUnique(); // performance e.HasIndex(x => x.Chip); e.HasIndex(x => x.Cliente); e.HasIndex(x => x.Usuario); e.HasIndex(x => x.Skil); e.HasIndex(x => x.Status); }); // ========================= // ✅ MUREG (FK para MobileLines) // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Item); e.HasIndex(x => x.ICCID); e.HasIndex(x => x.LinhaAntiga); e.HasIndex(x => x.LinhaNova); e.HasIndex(x => x.TenantId); // FK + index e.HasIndex(x => x.MobileLineId); e.HasOne(x => x.MobileLine) .WithMany(m => m.Muregs) .HasForeignKey(x => x.MobileLineId) .OnDelete(DeleteBehavior.Restrict); }); // ========================= // ✅ FATURAMENTO (BillingClient) // ========================= modelBuilder.Entity(e => { // ⚠️ só mantenha se seu banco realmente usa esse nome e.ToTable("billing_clients"); e.HasKey(x => x.Id); e.Property(x => x.Tipo).HasMaxLength(2); e.Property(x => x.Cliente).HasMaxLength(255); e.HasIndex(x => x.Tipo); e.HasIndex(x => x.Cliente); e.HasIndex(x => new { x.Tipo, x.Cliente }); e.HasIndex(x => x.Item); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ DADOS DOS USUÁRIOS (UserData) // ✅ (SEM "Nome" pq não existe no model) // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Item); e.HasIndex(x => x.Cliente); e.HasIndex(x => x.Linha); e.HasIndex(x => x.Cpf); e.HasIndex(x => x.Email); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ VIGÊNCIA // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Item); e.HasIndex(x => x.Cliente); e.HasIndex(x => x.Linha); e.HasIndex(x => x.DtTerminoFidelizacao); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ TROCA NÚMERO // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Item); e.HasIndex(x => x.LinhaAntiga); e.HasIndex(x => x.LinhaNova); e.HasIndex(x => x.ICCID); e.HasIndex(x => x.DataTroca); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ CHIPS VIRGENS // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Item); e.HasIndex(x => x.NumeroDoChip); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ CONTROLE DE RECEBIDOS // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.Ano); e.HasIndex(x => x.Item); e.HasIndex(x => x.NotaFiscal); e.HasIndex(x => x.Chip); e.HasIndex(x => x.Serial); e.HasIndex(x => x.NumeroDaLinha); e.HasIndex(x => x.DataDaNf); e.HasIndex(x => x.DataDoRecebimento); e.HasIndex(x => x.TenantId); }); // ========================= // ✅ NOTIFICAÇÕES // ========================= modelBuilder.Entity(e => { e.HasIndex(x => x.DedupKey).IsUnique(); e.HasIndex(x => x.UserId); e.HasIndex(x => x.Cliente); e.HasIndex(x => x.Lida); e.HasIndex(x => x.Data); e.HasIndex(x => x.VigenciaLineId); e.HasIndex(x => x.TenantId); e.HasOne(x => x.User) .WithMany() .HasForeignKey(x => x.UserId) .OnDelete(DeleteBehavior.Restrict); e.HasOne(x => x.VigenciaLine) .WithMany() .HasForeignKey(x => x.VigenciaLineId) .OnDelete(DeleteBehavior.Restrict); }); // ========================= // ✅ PARCELAMENTOS // ========================= modelBuilder.Entity(e => { e.Property(x => x.Linha).HasMaxLength(32); e.Property(x => x.Cliente).HasMaxLength(120); e.Property(x => x.QtParcelas).HasMaxLength(32); e.Property(x => x.ValorCheio).HasPrecision(18, 2); e.Property(x => x.Desconto).HasPrecision(18, 2); e.Property(x => x.ValorComDesconto).HasPrecision(18, 2); e.HasIndex(x => new { x.AnoRef, x.Item }).IsUnique(); e.HasIndex(x => x.Linha); e.HasIndex(x => x.TenantId); }); modelBuilder.Entity(e => { e.Property(x => x.Valor).HasPrecision(18, 2); e.HasIndex(x => new { x.ParcelamentoLineId, x.Competencia }).IsUnique(); e.HasIndex(x => x.TenantId); e.HasOne(x => x.ParcelamentoLine) .WithMany(x => x.MonthValues) .HasForeignKey(x => x.ParcelamentoLineId) .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); modelBuilder.Entity().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId); } public override int SaveChanges() { ApplyTenantIds(); return base.SaveChanges(); } public override Task SaveChangesAsync(CancellationToken cancellationToken = default) { ApplyTenantIds(); return base.SaveChangesAsync(cancellationToken); } private void ApplyTenantIds() { if (_tenantProvider.TenantId == null) { return; } var tenantId = _tenantProvider.TenantId.Value; foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added)) { if (entry.Entity.TenantId == Guid.Empty) { entry.Entity.TenantId = tenantId; } } } }