line-gestao-api/Data/AppDbContext.cs

304 lines
13 KiB
C#

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<ApplicationUser, IdentityRole<Guid>, Guid>
{
private readonly ITenantProvider _tenantProvider;
public AppDbContext(DbContextOptions<AppDbContext> options, ITenantProvider tenantProvider) : base(options)
{
_tenantProvider = tenantProvider;
}
public DbSet<Tenant> Tenants => Set<Tenant>();
// ✅ tabela para espelhar a planilha (GERAL)
public DbSet<MobileLine> MobileLines => Set<MobileLine>();
// ✅ tabela para espelhar a aba MUREG
public DbSet<MuregLine> MuregLines => Set<MuregLine>();
// ✅ tabela para espelhar o FATURAMENTO (PF/PJ)
public DbSet<BillingClient> BillingClients => Set<BillingClient>();
// ✅ tabela DADOS DOS USUÁRIOS
public DbSet<UserData> UserDatas => Set<UserData>();
// ✅ tabela VIGÊNCIA
public DbSet<VigenciaLine> VigenciaLines => Set<VigenciaLine>();
// ✅ tabela TROCA DE NÚMERO
public DbSet<TrocaNumeroLine> TrocaNumeroLines => Set<TrocaNumeroLine>();
// ✅ tabela CHIPS VIRGENS
public DbSet<ChipVirgemLine> ChipVirgemLines => Set<ChipVirgemLine>();
// ✅ tabela CONTROLE DE RECEBIDOS
public DbSet<ControleRecebidoLine> ControleRecebidoLines => Set<ControleRecebidoLine>();
// ✅ tabela NOTIFICAÇÕES
public DbSet<Notification> Notifications => Set<Notification>();
// ✅ tabela RESUMO
public DbSet<ResumoMacrophonyPlan> ResumoMacrophonyPlans => Set<ResumoMacrophonyPlan>();
public DbSet<ResumoMacrophonyTotal> ResumoMacrophonyTotals => Set<ResumoMacrophonyTotal>();
public DbSet<ResumoVivoLineResumo> ResumoVivoLineResumos => Set<ResumoVivoLineResumo>();
public DbSet<ResumoVivoLineTotal> ResumoVivoLineTotals => Set<ResumoVivoLineTotal>();
public DbSet<ResumoClienteEspecial> ResumoClienteEspeciais => Set<ResumoClienteEspecial>();
public DbSet<ResumoPlanoContratoResumo> ResumoPlanoContratoResumos => Set<ResumoPlanoContratoResumo>();
public DbSet<ResumoPlanoContratoTotal> ResumoPlanoContratoTotals => Set<ResumoPlanoContratoTotal>();
public DbSet<ResumoLineTotais> ResumoLineTotais => Set<ResumoLineTotais>();
public DbSet<ResumoReservaLine> ResumoReservaLines => Set<ResumoReservaLine>();
public DbSet<ResumoReservaTotal> ResumoReservaTotals => Set<ResumoReservaTotal>();
// ✅ tabela PARCELAMENTOS
public DbSet<ParcelamentoLine> ParcelamentoLines => Set<ParcelamentoLine>();
public DbSet<ParcelamentoMonthValue> ParcelamentoMonthValues => Set<ParcelamentoMonthValue>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// =========================
// ✅ USER (Identity)
// =========================
modelBuilder.Entity<ApplicationUser>(e =>
{
e.Property(x => x.Name).HasMaxLength(120);
e.HasIndex(x => new { x.TenantId, x.NormalizedEmail })
.IsUnique();
});
// =========================
// ✅ GERAL (MobileLine)
// =========================
modelBuilder.Entity<MobileLine>(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<MuregLine>(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<BillingClient>(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<UserData>(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<VigenciaLine>(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<TrocaNumeroLine>(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<ChipVirgemLine>(e =>
{
e.HasIndex(x => x.Item);
e.HasIndex(x => x.NumeroDoChip);
e.HasIndex(x => x.TenantId);
});
// =========================
// ✅ CONTROLE DE RECEBIDOS
// =========================
modelBuilder.Entity<ControleRecebidoLine>(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<Notification>(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<ParcelamentoLine>(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<ParcelamentoMonthValue>(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<MobileLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<MuregLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<BillingClient>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<UserData>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<VigenciaLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<TrocaNumeroLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ChipVirgemLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ControleRecebidoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<Notification>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoMacrophonyPlan>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoMacrophonyTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoVivoLineResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoVivoLineTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoClienteEspecial>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoPlanoContratoResumo>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoPlanoContratoTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoLineTotais>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoReservaLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ResumoReservaTotal>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ParcelamentoLine>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ParcelamentoMonthValue>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
modelBuilder.Entity<ApplicationUser>().HasQueryFilter(x => _tenantProvider.TenantId != null && x.TenantId == _tenantProvider.TenantId);
}
public override int SaveChanges()
{
ApplyTenantIds();
return base.SaveChanges();
}
public override Task<int> 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<ITenantEntity>().Where(e => e.State == EntityState.Added))
{
if (entry.Entity.TenantId == Guid.Empty)
{
entry.Entity.TenantId = tenantId;
}
}
}
}