shoc-backend/Data.SeaHavenIndustries/Auth/ApplicationDbContext.cs
Alexandre Brandizzi 669e9b2932
Some checks are pending
Validate and deploy dev / Validate deployable source bundle (push) Waiting to run
Validate and deploy dev / Deploy shoc-backend to Elastic Beanstalk dev (push) Blocked by required conditions
feat(vendors): add company roster management (SH-198) (#48)
* feat(vendors): add company roster management

* fix(security): remove request-controlled write guards

* fix(vendors): synchronize roster company fields

* fix(vendors): source facets from companies
2026-08-03 17:53:24 -03:00

304 lines
12 KiB
C#

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
namespace Data.SeaHavenIndustries
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
foreach (var foreignKey in builder.Model.GetEntityTypes().SelectMany
(e => e.GetForeignKeys()))
{
foreignKey.DeleteBehavior = DeleteBehavior.Restrict;
}
builder.Entity<VendorAccessToken>()
.HasIndex(t => t.Token)
.IsUnique();
builder.Entity<DispatchUpliftRequest>()
.HasIndex(u => new { u.DispatchId, u.Status })
.IsUnique()
.HasFilter("[Status] = 'Pending'");
builder.Entity<WorkOrderFieldLock>()
.HasIndex(l => new { l.WorkOrderId, l.FieldName })
.IsUnique();
builder.Entity<WorkOrderWeekRolledLedger>()
.HasIndex(l => new { l.WorkOrderId, l.SourceWeekStart })
.IsUnique();
builder.Entity<CompletionDocTemplate>()
.HasIndex(t => new { t.ServiceKey, t.IsActive });
builder.Entity<WorkOrder>()
.Property(w => w.RowVersion)
.IsRowVersion();
if (Database.IsSqlServer())
builder.HasSequence<long>("WorkOrderInternalNumberSequence");
// Bounded lengths required for SQL Server index keys (nvarchar(max) is not indexable).
// SiteCode: board create contract MaximumLength(32). InternalWONumber: normalized to 11 digits.
builder.Entity<WorkOrder>()
.Property(w => w.SiteCode)
.HasMaxLength(32);
builder.Entity<WorkOrder>()
.Property(w => w.InternalWONumber)
.HasMaxLength(11);
builder.Entity<WorkOrder>()
.HasIndex(w => w.SiteCode)
.HasFilter("[istemplate] = 0");
builder.Entity<WorkOrder>()
.HasIndex(w => w.InternalWONumber)
.HasFilter("[istemplate] = 0");
builder.Entity<WorkOrder>()
.HasIndex(w => new { w.LifecycleStatus, w.ScheduledDate })
.HasFilter("[istemplate] = 0");
// These indexes already exist in the migration history. Keep them in
// the runtime model so future additive migrations do not drop them.
builder.Entity<WorkOrder>()
.HasIndex(w => new { w.AssignTo, w.ScheduledDate });
builder.Entity<WorkOrder>()
.HasIndex(w => w.ScheduledDate);
builder.Entity<WorkOrder>()
.HasIndex(w => w.WorkOrderType);
builder.Entity<WorkOrder>()
.Property(w => w.ExternalWorkOrderId)
.HasMaxLength(450);
builder.Entity<WorkOrder>()
.HasIndex(w => w.ExternalWorkOrderId)
.IsUnique()
.HasFilter("[ExternalWorkOrderId] IS NOT NULL AND [ExternalWorkOrderId] <> ''");
builder.Entity<WorkOrderWebhookDelivery>()
.HasIndex(d => d.DeliveryId)
.IsUnique();
builder.Entity<Comments>()
.Property(c => c.ExternalSource)
.HasMaxLength(128);
builder.Entity<Comments>()
.Property(c => c.ExternalVersionHash)
.HasMaxLength(64);
builder.Entity<Comments>()
.Property(c => c.ExternalSourceEmailS3Key)
.HasMaxLength(2048);
builder.Entity<Locations>()
.Property(l => l.ExternalSource)
.HasMaxLength(128);
builder.Entity<Locations>()
.Property(l => l.ExternalLocationId)
.HasMaxLength(450);
builder.Entity<WorkOrderExternalReceipt>()
.HasIndex(r => new { r.Source, r.Kind, r.ExternalId })
.IsUnique()
.HasDatabaseName("IX_WorkOrderExternalReceipts_Source_Kind_ExternalId");
builder.Entity<WorkOrderReconciliationJob>()
.HasIndex(j => j.State);
builder.Entity<WorkOrderReconciliationJob>()
.Property(j => j.Id)
.ValueGeneratedNever();
builder.Entity<Dispatch>()
.Property(d => d.RowVersion)
.IsRowVersion();
builder.Entity<WorkOrder>()
.HasOne(w => w.PrimaryDispatch)
.WithMany()
.HasForeignKey(w => w.PrimaryDispatchId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<VendorCompany>()
.HasIndex(c => c.NormalizedName)
.IsUnique();
builder.Entity<VendorCompany>()
.Property(c => c.RowVersion)
.IsRowVersion();
builder.Entity<Vendor>()
.HasOne(v => v.Company)
.WithMany(c => c.Vendors)
.HasForeignKey(v => v.CompanyId)
.OnDelete(DeleteBehavior.Restrict);
builder.Entity<SitePreferredVendor>()
.HasIndex(p => new { p.LocationId, p.VendorId, p.Trade })
.IsUnique()
.HasFilter("[Trade] IS NOT NULL");
builder.Entity<SitePreferredVendor>()
.HasIndex(p => new { p.LocationId, p.VendorId })
.IsUnique()
.HasFilter("[Trade] IS NULL");
}
public DbSet<Category> Categories { get; set; }
public DbSet<Locations> Locations { get; set; }
public DbSet<Contacts> Contacts { get; set; }
public DbSet<WorkOrder> workOrders { get; set; }
public DbSet<Template> Templates { get; set; }
public DbSet<Comments> Comments { get; set; }
public DbSet<Quotes> Quotes { get; set; }
public DbSet<QuotesLineItems> QuotesLines { get; set; }
public DbSet<ForgetPasswordCode> ForgetPasswordCodes { get; set; }
public DbSet<WorkOrderContacts> WorkOrderContacts { get; set; }
public DbSet<WorkOrderCategories> workOrderCategories { get; set; }
public DbSet<WorkOrderAttachments> workOrderAttachments { get; set; }
public DbSet<CompletionDocTemplate> CompletionDocTemplates { get; set; }
public DbSet<WorkOrderAuditLog> WorkOrderAuditLogs { get; set; }
public DbSet<WorkOrderFieldLock> WorkOrderFieldLocks { get; set; }
public DbSet<WorkOrderWeekRolledLedger> WorkOrderWeekRolledLedgers { get; set; }
public DbSet<WorkOrderWebhookDelivery> WorkOrderWebhookDeliveries { get; set; }
public DbSet<WorkOrderReconciliationJob> WorkOrderReconciliationJobs { get; set; }
public DbSet<WorkOrderExternalReceipt> WorkOrderExternalReceipts { get; set; }
public DbSet<DropdownOption> DropdownOptions { get; set; }
public DbSet<Vendor> Vendors { get; set; }
public DbSet<VendorCompany> VendorCompanies { get; set; }
public DbSet<VendorAuditLog> VendorAuditLogs { get; set; }
public DbSet<VendorAccessToken> VendorAccessTokens { get; set; }
public DbSet<VendorCompletionDocument> VendorCompletionDocuments { get; set; }
public DbSet<SitePreferredVendor> SitePreferredVendors { get; set; }
public DbSet<Dispatch> Dispatches { get; set; }
public DbSet<DispatchWorkOrder> DispatchWorkOrders { get; set; }
public DbSet<DispatchChecklistItem> DispatchChecklistItems { get; set; }
public DbSet<DispatchSignoff> DispatchSignoffs { get; set; }
public DbSet<DispatchUpliftRequest> DispatchUpliftRequests { get; set; }
public DbSet<TaskListTemplate> TaskListTemplates { get; set; }
public DbSet<TaskListTemplateItem> TaskListTemplateItems { get; set; }
// New DbSets for added entities
public DbSet<Accounts> Accounts { get; set; }
public DbSet<Addresses> Addresses { get; set; }
public DbSet<Assets> Assets { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<FollowUps> FollowUps { get; set; }
public DbSet<PMSchedules> PMSchedules { get; set; }
public DbSet<ContactDetails> ContactDetails { get; set; }
public DbSet<Events> Events { get; set; }
// Employee related DbSets
public DbSet<EmployeeAddress> EmployeeAddresses { get; set; }
public DbSet<EmployeePhone> EmployeePhones { get; set; }
public DbSet<EmployeeEmail> EmployeeEmails { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<JobTitle> JobTitles { get; set; }
public DbSet<Region> Regions { get; set; }
public override int SaveChanges()
{
BumpRowVersionsForInMemoryProvider();
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
BumpRowVersionsForInMemoryProvider();
return base.SaveChangesAsync(cancellationToken);
}
private void BumpRowVersionsForInMemoryProvider()
{
if (!Database.ProviderName?.Contains("InMemory", StringComparison.OrdinalIgnoreCase) ?? true)
return;
foreach (var entry in ChangeTracker.Entries<WorkOrder>())
{
if (entry.State == EntityState.Modified)
entry.Entity.RowVersion = IncrementRowVersion(entry.Entity.RowVersion);
}
foreach (var entry in ChangeTracker.Entries<Dispatch>())
{
if (entry.State == EntityState.Modified)
entry.Entity.RowVersion = IncrementRowVersion(entry.Entity.RowVersion);
}
foreach (var entry in ChangeTracker.Entries<WorkOrderReconciliationJob>())
{
if (entry.State == EntityState.Modified)
entry.Entity.RowVersion = IncrementRowVersion(entry.Entity.RowVersion);
}
foreach (var entry in ChangeTracker.Entries<VendorCompany>())
{
if (entry.State == EntityState.Modified)
entry.Entity.RowVersion = IncrementRowVersion(entry.Entity.RowVersion);
}
}
private static byte[] IncrementRowVersion(byte[]? current)
{
var bytes = current != null && current.Length > 0
? (byte[])current.Clone()
: new byte[] { 0, 0, 0, 0, 0, 0, 0, 1 };
for (var i = 0; i < bytes.Length; i++)
{
if (++bytes[i] != 0)
break;
}
return bytes;
}
}
public class ApplicationUser : IdentityUser
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? UniqueName { get; set; }
public string? Locations { get; set; }
public string? Contact { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public bool? IsDeleted { get; set; }
public string? UserImage { get; set; }
public string? Initials { get; set; }
public string? Color { get; set; }
public int? Type { get; set; } // 1 for users 0 for admin
public ICollection<Template>? Templates { get; set; }
public ICollection<WorkOrder>? WorkOrders { get; set; }
}
public class FullAuditEntity
{
[Key]
public int Id { get; set; }
public bool? IsDeleted { get; set; }
public string? createdby { get; set; }
public string? DeleterUserId { get; set; }
public DateTime? DeletionTime { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? LastModificationTime { get; set; }
public int? LastModifierUserId { get; set; }
}
}