mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 07:13:12 +00:00
495 lines
19 KiB
C#
495 lines
19 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 => u.DispatchId)
|
|
.IsUnique()
|
|
.HasFilter("[Status] IN ('Pending', 'ChangesRequested')");
|
|
|
|
builder.Entity<DispatchUpliftRequest>()
|
|
.HasIndex(u => new { u.DispatchId, u.RequestKey })
|
|
.IsUnique()
|
|
.HasFilter("[RequestKey] IS NOT NULL");
|
|
|
|
builder.Entity<DispatchUpliftRequest>()
|
|
.Property(u => u.RowVersion)
|
|
.IsRowVersion();
|
|
|
|
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>()
|
|
.HasOne(w => w.ServiceDefinition)
|
|
.WithMany()
|
|
.HasForeignKey(w => w.ServiceId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
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<Locations>()
|
|
.HasOne(l => l.Account)
|
|
.WithMany()
|
|
.HasForeignKey(l => l.AccountId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
builder.Entity<Locations>()
|
|
.HasIndex(l => l.AccountId);
|
|
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<WorkOrder>()
|
|
.HasOne(w => w.Account)
|
|
.WithMany()
|
|
.HasForeignKey(w => w.AccountId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
builder.Entity<WorkOrder>()
|
|
.HasIndex(w => w.AccountId);
|
|
|
|
builder.Entity<ApplicationUser>()
|
|
.HasOne(u => u.Account)
|
|
.WithMany()
|
|
.HasForeignKey(u => u.AccountId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
builder.Entity<ApplicationUser>()
|
|
.HasIndex(u => u.AccountId);
|
|
|
|
builder.Entity<ApplicationUser>()
|
|
.HasIndex(u => u.IsAccountOwner)
|
|
.IsUnique()
|
|
.HasFilter("IsAccountOwner = 1")
|
|
.HasDatabaseName("IX_AspNetUsers_IsAccountOwner");
|
|
|
|
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");
|
|
|
|
// SH-249 bounded lengths: unique index keys must not be nvarchar(max).
|
|
builder.Entity<Trade>()
|
|
.Property(t => t.Name)
|
|
.HasMaxLength(128);
|
|
|
|
builder.Entity<Trade>()
|
|
.Property(t => t.NormalizedName)
|
|
.HasMaxLength(128);
|
|
|
|
builder.Entity<Trade>()
|
|
.HasIndex(t => t.NormalizedName)
|
|
.IsUnique();
|
|
|
|
// Bounded lengths keep the unique NormalizedName key SQL
|
|
// Server-indexable (nvarchar(max) is not).
|
|
builder.Entity<Service>()
|
|
.Property(s => s.Name)
|
|
.HasMaxLength(200);
|
|
|
|
builder.Entity<Service>()
|
|
.Property(s => s.NormalizedName)
|
|
.HasMaxLength(200);
|
|
|
|
builder.Entity<Service>()
|
|
.HasIndex(s => s.NormalizedName)
|
|
.IsUnique();
|
|
|
|
builder.Entity<Service>()
|
|
.Property(s => s.Trade)
|
|
.HasMaxLength(64);
|
|
|
|
builder.Entity<Service>()
|
|
.HasIndex(s => s.Trade);
|
|
|
|
builder.Entity<Service>()
|
|
.Property(s => s.IconKey)
|
|
.HasMaxLength(64);
|
|
|
|
builder.Entity<Service>()
|
|
.HasIndex(s => s.IsActive);
|
|
|
|
builder.Entity<Service>()
|
|
.HasOne(s => s.CompletionDocTemplate)
|
|
.WithMany()
|
|
.HasForeignKey(s => s.CompletionDocTemplateId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
builder.Entity<Service>()
|
|
.HasIndex(s => s.CompletionDocTemplateId);
|
|
|
|
builder.Entity<ServiceWorkOrderType>()
|
|
.HasIndex(t => new { t.ServiceId, t.WorkOrderType })
|
|
.IsUnique();
|
|
|
|
builder.Entity<ServiceWorkOrderType>()
|
|
.HasOne(t => t.Service)
|
|
.WithMany(s => s.SupportedWorkOrderTypes)
|
|
.HasForeignKey(t => t.ServiceId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
// SH-278 Area catalogue: bounded names so the unique key is indexable.
|
|
builder.Entity<Area>()
|
|
.Property(a => a.Name)
|
|
.HasMaxLength(128);
|
|
|
|
builder.Entity<Area>()
|
|
.Property(a => a.NormalizedName)
|
|
.HasMaxLength(128);
|
|
|
|
builder.Entity<Area>()
|
|
.HasIndex(a => a.NormalizedName)
|
|
.IsUnique();
|
|
|
|
builder.Entity<VendorCompany>()
|
|
.HasOne(c => c.Area)
|
|
.WithMany()
|
|
.HasForeignKey(c => c.AreaId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
// Per-person team permission overrides. Composite primary key
|
|
// (UserId + PermissionKey) plus an explicitly named unique composite
|
|
// index; missing rows behave as Unset.
|
|
builder.Entity<UserPermissionOverride>(entity =>
|
|
{
|
|
entity.HasKey(o => new { o.UserId, o.PermissionKey });
|
|
|
|
entity.Property(o => o.UserId)
|
|
.HasMaxLength(450);
|
|
|
|
entity.Property(o => o.PermissionKey)
|
|
.HasMaxLength(64);
|
|
|
|
entity.Property(o => o.State)
|
|
.IsRequired();
|
|
|
|
entity.HasOne(o => o.User)
|
|
.WithMany()
|
|
.HasForeignKey(o => o.UserId)
|
|
.OnDelete(DeleteBehavior.Restrict);
|
|
|
|
entity.HasIndex(o => new { o.UserId, o.PermissionKey })
|
|
.IsUnique()
|
|
.HasDatabaseName("IX_UserPermissionOverrides_UserId_PermissionKey");
|
|
});
|
|
|
|
builder.Entity<UserServiceArea>(entity =>
|
|
{
|
|
entity.HasKey(area => new { area.UserId, area.Area });
|
|
|
|
entity.Property(area => area.UserId)
|
|
.HasMaxLength(450);
|
|
|
|
entity.Property(area => area.Area)
|
|
.HasMaxLength(32);
|
|
|
|
entity.HasOne(area => area.User)
|
|
.WithMany(user => user.ServiceAreas)
|
|
.HasForeignKey(area => area.UserId)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
entity.HasIndex(area => new { area.UserId, area.Area })
|
|
.IsUnique()
|
|
.HasDatabaseName("IX_UserServiceAreas_UserId_Area");
|
|
});
|
|
}
|
|
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<Trade> Trades { get; set; }
|
|
public DbSet<Area> Areas { 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; }
|
|
public DbSet<Service> Services { get; set; }
|
|
public DbSet<ServiceWorkOrderType> ServiceWorkOrderTypes { 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 DbSet<UserPermissionOverride> UserPermissionOverrides { get; set; }
|
|
public DbSet<UserServiceArea> UserServiceAreas { 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);
|
|
}
|
|
|
|
foreach (var entry in ChangeTracker.Entries<DispatchUpliftRequest>())
|
|
{
|
|
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 bool IsAccountOwner { get; set; }
|
|
public bool? PendingRegistration { get; set; }
|
|
public DateTime? PendingRegistrationCreatedDate { get; set; }
|
|
/// <summary>Optional CRM account membership for server-derived media scope (SH-221).</summary>
|
|
public int? AccountId { get; set; }
|
|
[ForeignKey(nameof(AccountId))]
|
|
public virtual Accounts? Account { get; set; }
|
|
public ICollection<Template>? Templates { get; set; }
|
|
public ICollection<WorkOrder>? WorkOrders { get; set; }
|
|
public ICollection<UserServiceArea> ServiceAreas { get; set; } = new List<UserServiceArea>();
|
|
}
|
|
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; }
|
|
|
|
}
|
|
|
|
}
|