Initial commit
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
-- Check customer 2266 details
|
||||
SELECT
|
||||
Id,
|
||||
CompanyName,
|
||||
ContactFirstName,
|
||||
ContactLastName,
|
||||
Email,
|
||||
Phone,
|
||||
IsCommercial,
|
||||
IsDeleted,
|
||||
CreatedAt
|
||||
FROM Customers
|
||||
WHERE Id = 2266;
|
||||
@@ -0,0 +1,59 @@
|
||||
-- Quick Database State Check for Lookup Tables
|
||||
-- =====================================================
|
||||
|
||||
PRINT '=== LOOKUP TABLES STATUS ===';
|
||||
PRINT '';
|
||||
|
||||
-- Check if lookup tables exist
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'JobStatusLookups')
|
||||
PRINT '✓ JobStatusLookups table exists'
|
||||
ELSE
|
||||
PRINT '✗ JobStatusLookups table MISSING';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'JobPriorityLookups')
|
||||
PRINT '✓ JobPriorityLookups table exists'
|
||||
ELSE
|
||||
PRINT '✗ JobPriorityLookups table MISSING';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'QuoteStatusLookups')
|
||||
PRINT '✓ QuoteStatusLookups table exists'
|
||||
ELSE
|
||||
PRINT '✗ QuoteStatusLookups table MISSING';
|
||||
|
||||
PRINT '';
|
||||
PRINT '=== DATA COUNTS ===';
|
||||
PRINT '';
|
||||
|
||||
-- Count lookup records per company
|
||||
SELECT
|
||||
c.Name AS CompanyName,
|
||||
(SELECT COUNT(*) FROM JobStatusLookups WHERE CompanyId = c.Id) AS JobStatuses,
|
||||
(SELECT COUNT(*) FROM JobPriorityLookups WHERE CompanyId = c.Id) AS JobPriorities,
|
||||
(SELECT COUNT(*) FROM QuoteStatusLookups WHERE CompanyId = c.Id) AS QuoteStatuses,
|
||||
(SELECT COUNT(*) FROM Jobs WHERE CompanyId = c.Id) AS Jobs,
|
||||
(SELECT COUNT(*) FROM Quotes WHERE CompanyId = c.Id) AS Quotes
|
||||
FROM Companies c
|
||||
WHERE c.IsDeleted = 0;
|
||||
|
||||
PRINT '';
|
||||
PRINT '=== JOBS WITH/WITHOUT LOOKUP IDS ===';
|
||||
PRINT '';
|
||||
|
||||
-- Check if Jobs have lookup IDs
|
||||
SELECT
|
||||
'Jobs with NULL JobStatusId' AS Issue,
|
||||
COUNT(*) AS Count
|
||||
FROM Jobs
|
||||
WHERE JobStatusId IS NULL
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Jobs with NULL JobPriorityId' AS Issue,
|
||||
COUNT(*) AS Count
|
||||
FROM Jobs
|
||||
WHERE JobPriorityId IS NULL
|
||||
UNION ALL
|
||||
SELECT
|
||||
'Quotes with NULL QuoteStatusId' AS Issue,
|
||||
COUNT(*) AS Count
|
||||
FROM Quotes
|
||||
WHERE QuoteStatusId IS NULL;
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Check quote QT-2602-0050 and its customer
|
||||
DECLARE @QuoteNumber NVARCHAR(50) = 'QT-2602-0050';
|
||||
|
||||
-- Find the quote and customer info
|
||||
SELECT
|
||||
q.Id AS QuoteId,
|
||||
q.QuoteNumber,
|
||||
q.CustomerId,
|
||||
CASE WHEN q.CustomerId IS NULL THEN 1 ELSE 0 END AS IsProspect,
|
||||
q.ProspectCompanyName,
|
||||
q.ProspectContactName,
|
||||
q.IsDeleted AS QuoteIsDeleted,
|
||||
c.Id AS CustomerIdFromTable,
|
||||
c.CompanyName AS CustomerCompanyName,
|
||||
c.IsDeleted AS CustomerIsDeleted
|
||||
FROM Quotes q
|
||||
LEFT JOIN Customers c ON q.CustomerId = c.Id
|
||||
WHERE q.QuoteNumber = @QuoteNumber;
|
||||
|
||||
-- Show all prospect and customer fields
|
||||
SELECT
|
||||
QuoteNumber,
|
||||
CustomerId,
|
||||
CASE WHEN CustomerId IS NULL THEN 'YES' ELSE 'NO' END AS IsProspect,
|
||||
ProspectCompanyName,
|
||||
ProspectContactName,
|
||||
ProspectEmail,
|
||||
ProspectPhone
|
||||
FROM Quotes
|
||||
WHERE QuoteNumber = @QuoteNumber;
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
-- ============================================================
|
||||
-- Delete All Users from a Company
|
||||
-- Usage: Set @CompanyId to the target company's ID
|
||||
-- WARNING: This is a hard delete. Data cannot be recovered.
|
||||
-- ============================================================
|
||||
|
||||
DECLARE @CompanyId INT = 1 -- <-- Change this to the target company ID
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
BEGIN TRY
|
||||
|
||||
-- Get the user IDs to be deleted
|
||||
DECLARE @UserIds TABLE (Id NVARCHAR(450));
|
||||
INSERT INTO @UserIds
|
||||
SELECT Id FROM AspNetUsers WHERE CompanyId = @CompanyId;
|
||||
|
||||
PRINT CONCAT('Users to be deleted: ', (SELECT COUNT(*) FROM @UserIds));
|
||||
|
||||
-- 1. Delete Identity-related child records first
|
||||
DELETE FROM AspNetUserTokens WHERE UserId IN (SELECT Id FROM @UserIds);
|
||||
DELETE FROM AspNetUserLogins WHERE UserId IN (SELECT Id FROM @UserIds);
|
||||
DELETE FROM AspNetUserClaims WHERE UserId IN (SELECT Id FROM @UserIds);
|
||||
DELETE FROM AspNetUserRoles WHERE UserId IN (SELECT Id FROM @UserIds);
|
||||
|
||||
-- 2. Nullify FK references in business tables (quotes, maintenance records)
|
||||
UPDATE Quotes
|
||||
SET PreparedById = NULL
|
||||
WHERE PreparedById IN (SELECT Id FROM @UserIds);
|
||||
|
||||
UPDATE MaintenanceRecords
|
||||
SET PerformedById = NULL
|
||||
WHERE PerformedById IN (SELECT Id FROM @UserIds);
|
||||
|
||||
-- 3. Delete the users
|
||||
DELETE FROM AspNetUsers WHERE Id IN (SELECT Id FROM @UserIds);
|
||||
|
||||
PRINT 'Users deleted successfully.';
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
ROLLBACK TRANSACTION;
|
||||
PRINT CONCAT('Error: ', ERROR_MESSAGE());
|
||||
THROW;
|
||||
END CATCH;
|
||||
@@ -0,0 +1,151 @@
|
||||
# Fix-Passwords.ps1
|
||||
# Resets password hashes for both SuperAdmin users in the Azure SQL database.
|
||||
# Runs directly via sqlcmd to avoid copy-paste corruption of Base64 characters.
|
||||
#
|
||||
# Usage (interactive - will prompt for connection details):
|
||||
# .\Fix-Passwords.ps1
|
||||
#
|
||||
# Usage (non-interactive):
|
||||
# .\Fix-Passwords.ps1 -Server "yourserver.database.windows.net" -Database "PowderCoatingDb" -User "sqladmin" -Password "yourpassword"
|
||||
|
||||
param(
|
||||
[string]$Server,
|
||||
[string]$Database = "PowderCoatingapp",
|
||||
[string]$User,
|
||||
[string]$Password
|
||||
)
|
||||
|
||||
function New-IdentityPasswordHash {
|
||||
param([string]$Password)
|
||||
|
||||
$salt = [byte[]]::new(16)
|
||||
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
||||
$rng.GetBytes($salt)
|
||||
$rng.Dispose()
|
||||
|
||||
$pbkdf2 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new(
|
||||
$Password, $salt, 100000,
|
||||
[System.Security.Cryptography.HashAlgorithmName]::SHA256)
|
||||
$subkey = $pbkdf2.GetBytes(32)
|
||||
$pbkdf2.Dispose()
|
||||
|
||||
# ASP.NET Core Identity v3 format
|
||||
$buf = [byte[]]::new(61)
|
||||
$buf[0] = 0x01
|
||||
$buf[1] = 0x00; $buf[2] = 0x00; $buf[3] = 0x00; $buf[4] = 0x01 # PRF = HMACSHA256
|
||||
$buf[5] = 0x00; $buf[6] = 0x01; $buf[7] = 0x86; $buf[8] = 0xA0 # 100000 iterations
|
||||
$buf[9] = 0x00; $buf[10] = 0x00; $buf[11] = 0x00; $buf[12] = 0x10 # salt length = 16
|
||||
[Array]::Copy($salt, 0, $buf, 13, 16)
|
||||
[Array]::Copy($subkey, 0, $buf, 29, 32)
|
||||
|
||||
return [Convert]::ToBase64String($buf)
|
||||
}
|
||||
|
||||
# ---- Prompt for connection details if not provided ----------------------------
|
||||
|
||||
if (-not $Server) {
|
||||
$Server = Read-Host "Azure SQL Server (e.g. yourserver.database.windows.net)"
|
||||
}
|
||||
if (-not $User) {
|
||||
$User = Read-Host "SQL Username"
|
||||
}
|
||||
if (-not $Password) {
|
||||
$securePass = Read-Host "SQL Password" -AsSecureString
|
||||
$Password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
||||
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePass))
|
||||
}
|
||||
|
||||
# ---- Check sqlcmd is available -----------------------------------------------
|
||||
|
||||
$sqlcmd = Get-Command sqlcmd -ErrorAction SilentlyContinue
|
||||
if (-not $sqlcmd) {
|
||||
Write-Host ""
|
||||
Write-Host "ERROR: sqlcmd not found on PATH." -ForegroundColor Red
|
||||
Write-Host "Install it from: https://learn.microsoft.com/en-us/sql/tools/sqlcmd/sqlcmd-utility" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
Write-Host "Alternatively, copy fix_passwords.sql to the Azure Portal Query Editor manually." -ForegroundColor Yellow
|
||||
Write-Host "(Be careful that + and / characters are not changed during copy-paste.)" -ForegroundColor Yellow
|
||||
$useSqlCmd = $false
|
||||
} else {
|
||||
$useSqlCmd = $true
|
||||
}
|
||||
|
||||
# ---- Generate hashes ---------------------------------------------------------
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Generating password hashes..." -ForegroundColor Cyan
|
||||
$hash1 = New-IdentityPasswordHash "SuperAdmin123!"
|
||||
$hash2 = New-IdentityPasswordHash "CompanyAdmin123!"
|
||||
$stamp1 = [Guid]::NewGuid().ToString()
|
||||
$stamp2 = [Guid]::NewGuid().ToString()
|
||||
Write-Host "Done." -ForegroundColor Cyan
|
||||
|
||||
# ---- Build SQL ---------------------------------------------------------------
|
||||
|
||||
$sql = @"
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- Reset password hash for superadmin@powdercoating.com / SuperAdmin123!
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
PasswordHash = '$hash1',
|
||||
SecurityStamp = '$stamp1'
|
||||
WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM';
|
||||
|
||||
PRINT 'Updated: superadmin@powdercoating.com';
|
||||
|
||||
-- Reset password hash for admin@demo.com / CompanyAdmin123!
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
PasswordHash = '$hash2',
|
||||
SecurityStamp = '$stamp2'
|
||||
WHERE NormalizedEmail = 'ADMIN@DEMO.COM';
|
||||
|
||||
PRINT 'Updated: admin@demo.com';
|
||||
|
||||
-- Verify both users exist and have non-null hashes
|
||||
SELECT Email, LEN(PasswordHash) AS HashLength, IsActive
|
||||
FROM AspNetUsers
|
||||
WHERE NormalizedEmail IN ('SUPERADMIN@POWDERCOATING.COM', 'ADMIN@DEMO.COM');
|
||||
"@
|
||||
|
||||
# ---- Write SQL file (always, as backup) --------------------------------------
|
||||
|
||||
$sqlFile = Join-Path $PSScriptRoot "fix_passwords.sql"
|
||||
$sql | Set-Content -Path $sqlFile -Encoding UTF8
|
||||
Write-Host "SQL written to: $sqlFile" -ForegroundColor Gray
|
||||
|
||||
# ---- Execute via sqlcmd (avoids copy-paste corruption) -----------------------
|
||||
|
||||
if ($useSqlCmd) {
|
||||
Write-Host ""
|
||||
Write-Host "Running via sqlcmd (no copy-paste, Base64 characters preserved)..." -ForegroundColor Cyan
|
||||
|
||||
$result = & sqlcmd `
|
||||
-S $Server `
|
||||
-d $Database `
|
||||
-U $User `
|
||||
-P $Password `
|
||||
-Q $sql `
|
||||
-l 30 2>&1
|
||||
|
||||
Write-Host ""
|
||||
$result | ForEach-Object { Write-Host $_ }
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host ""
|
||||
Write-Host "SUCCESS. Try logging in now:" -ForegroundColor Green
|
||||
Write-Host " superadmin@powdercoating.com / SuperAdmin123!" -ForegroundColor White
|
||||
Write-Host " admin@demo.com / CompanyAdmin123!" -ForegroundColor White
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "sqlcmd returned exit code $LASTEXITCODE. Check the output above." -ForegroundColor Red
|
||||
Write-Host "You can also run fix_passwords.sql manually in the Azure Portal Query Editor." -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "Run fix_passwords.sql in Azure Portal > SQL Database > Query Editor." -ForegroundColor Yellow
|
||||
Write-Host "TIP: If + or / appear as spaces after pasting, the hash will be corrupted again." -ForegroundColor Yellow
|
||||
Write-Host " Use sqlcmd instead to be safe." -ForegroundColor Yellow
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Fix Company Admin Permissions
|
||||
-- This script updates all existing Company Admins to have all permissions
|
||||
|
||||
-- Update all users with CompanyRole = 'CompanyAdmin' to have all permissions
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
CanManageJobs = 1,
|
||||
CanManageInventory = 1,
|
||||
CanManageCustomers = 1,
|
||||
CanCreateQuotes = 1,
|
||||
CanApproveQuotes = 1,
|
||||
CanManageCalendar = 1,
|
||||
CanViewCalendar = 1,
|
||||
CanManageProducts = 1,
|
||||
CanViewProducts = 1,
|
||||
CanManageEquipment = 1,
|
||||
CanManageSuppliers = 1,
|
||||
CanManageMaintenance = 1
|
||||
WHERE CompanyRole = 'CompanyAdmin';
|
||||
|
||||
-- Show how many users were updated
|
||||
SELECT
|
||||
Id,
|
||||
Email,
|
||||
FirstName,
|
||||
LastName,
|
||||
CompanyRole,
|
||||
CanManageCalendar,
|
||||
CanManageProducts,
|
||||
CanManageEquipment
|
||||
FROM AspNetUsers
|
||||
WHERE CompanyRole = 'CompanyAdmin';
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Fix IsSystemDefined flag for Job Priorities
|
||||
-- Only "NORMAL" should be system-defined, the rest should be editable
|
||||
|
||||
PRINT 'Fixing Job Priority system-defined flags...';
|
||||
|
||||
UPDATE JobPriorityLookups
|
||||
SET IsSystemDefined = 0
|
||||
WHERE PriorityCode IN ('LOW', 'HIGH', 'URGENT', 'RUSH');
|
||||
|
||||
PRINT 'Fixed. Only NORMAL priority is now system-defined.';
|
||||
|
||||
-- Verify
|
||||
SELECT
|
||||
PriorityCode,
|
||||
DisplayName,
|
||||
IsSystemDefined,
|
||||
CASE WHEN IsSystemDefined = 1 THEN 'Protected' ELSE 'Editable' END AS Status
|
||||
FROM JobPriorityLookups
|
||||
WHERE IsDeleted = 0
|
||||
ORDER BY DisplayOrder;
|
||||
@@ -0,0 +1,264 @@
|
||||
# Generate-SeedSql.ps1
|
||||
# Generates seed_admins.sql with properly hashed passwords for ASP.NET Core Identity.
|
||||
#
|
||||
# Usage:
|
||||
# .\Generate-SeedSql.ps1
|
||||
#
|
||||
# Requires: PowerShell 5.1+ on .NET Framework 4.7.2+ OR PowerShell 7+
|
||||
|
||||
function New-IdentityPasswordHash {
|
||||
param([string]$Password)
|
||||
|
||||
# 16-byte random salt
|
||||
$salt = [byte[]]::new(16)
|
||||
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
||||
$rng.GetBytes($salt)
|
||||
$rng.Dispose()
|
||||
|
||||
# PBKDF2-HMAC-SHA256, 100,000 iterations, 32-byte output
|
||||
$pbkdf2 = [System.Security.Cryptography.Rfc2898DeriveBytes]::new(
|
||||
$Password, $salt, 100000,
|
||||
[System.Security.Cryptography.HashAlgorithmName]::SHA256)
|
||||
$subkey = $pbkdf2.GetBytes(32)
|
||||
$pbkdf2.Dispose()
|
||||
|
||||
# ASP.NET Core Identity v3 wire format (61 bytes):
|
||||
# [0] = 0x01 (version marker)
|
||||
# [1-4] = 0x00000001 (PRF = HMACSHA256, big-endian uint32)
|
||||
# [5-8] = 0x000186A0 (iterations = 100000, big-endian uint32)
|
||||
# [9-12] = 0x00000010 (salt length = 16, big-endian uint32)
|
||||
# [13-28] = salt bytes
|
||||
# [29-60] = subkey bytes
|
||||
$buf = [byte[]]::new(61)
|
||||
$buf[0] = 0x01
|
||||
$buf[1] = 0x00; $buf[2] = 0x00; $buf[3] = 0x00; $buf[4] = 0x01
|
||||
$buf[5] = 0x00; $buf[6] = 0x01; $buf[7] = 0x86; $buf[8] = 0xA0
|
||||
$buf[9] = 0x00; $buf[10] = 0x00; $buf[11] = 0x00; $buf[12] = 0x10
|
||||
[Array]::Copy($salt, 0, $buf, 13, 16)
|
||||
[Array]::Copy($subkey, 0, $buf, 29, 32)
|
||||
|
||||
return [Convert]::ToBase64String($buf)
|
||||
}
|
||||
|
||||
# ---- Generate GUIDs and hashes -----------------------------------------------
|
||||
|
||||
$roleSuperAdmin = [Guid]::NewGuid().ToString()
|
||||
$roleAdmin = [Guid]::NewGuid().ToString()
|
||||
$roleManager = [Guid]::NewGuid().ToString()
|
||||
$roleEmployee = [Guid]::NewGuid().ToString()
|
||||
$roleShopFloor = [Guid]::NewGuid().ToString()
|
||||
$roleReadOnly = [Guid]::NewGuid().ToString()
|
||||
|
||||
$user1Id = [Guid]::NewGuid().ToString()
|
||||
$user2Id = [Guid]::NewGuid().ToString()
|
||||
$user1Stamp = [Guid]::NewGuid().ToString()
|
||||
$user2Stamp = [Guid]::NewGuid().ToString()
|
||||
$user1Security = [Guid]::NewGuid().ToString()
|
||||
$user2Security = [Guid]::NewGuid().ToString()
|
||||
|
||||
$now = (Get-Date).ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
Write-Host "Hashing SuperAdmin123! ..." -ForegroundColor Cyan
|
||||
$hash1 = New-IdentityPasswordHash "SuperAdmin123!"
|
||||
Write-Host "Hashing Admin123! ..." -ForegroundColor Cyan
|
||||
$hash2 = New-IdentityPasswordHash "Admin123!"
|
||||
Write-Host "Done. Writing SQL file..." -ForegroundColor Cyan
|
||||
|
||||
# ---- Build SQL ---------------------------------------------------------------
|
||||
|
||||
$outFile = Join-Path $PSScriptRoot "seed_admins.sql"
|
||||
|
||||
$lines = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
$lines.Add("-- =============================================================================")
|
||||
$lines.Add("-- PowderCoating App - Initial Seed SQL")
|
||||
$lines.Add("-- Generated: $now UTC")
|
||||
$lines.Add("--")
|
||||
$lines.Add("-- Run this against your Azure SQL database when startup seeding fails.")
|
||||
$lines.Add("-- All inserts are guarded with IF NOT EXISTS and are safe to re-run.")
|
||||
$lines.Add("-- =============================================================================")
|
||||
$lines.Add("")
|
||||
$lines.Add("SET NOCOUNT ON;")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- 1. Default Company")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM Companies WHERE CompanyCode = 'DEMO')")
|
||||
$lines.Add("BEGIN")
|
||||
$lines.Add(" SET IDENTITY_INSERT Companies ON;")
|
||||
$lines.Add("")
|
||||
$lines.Add(" INSERT INTO Companies (")
|
||||
$lines.Add(" Id, CompanyId, CompanyName, CompanyCode,")
|
||||
$lines.Add(" PrimaryContactName, PrimaryContactEmail, Phone,")
|
||||
$lines.Add(" Address, City, State, ZipCode,")
|
||||
$lines.Add(" IsActive, SubscriptionPlan, SubscriptionStatus,")
|
||||
$lines.Add(" SubscriptionStartDate, TimeZone,")
|
||||
$lines.Add(" CreatedAt, IsDeleted")
|
||||
$lines.Add(" ) VALUES (")
|
||||
$lines.Add(" 1, 1, 'Demo Company', 'DEMO',")
|
||||
$lines.Add(" 'Admin User', 'admin@demo.com', '(555) 123-4567',")
|
||||
$lines.Add(" '123 Demo Street', 'Demo City', 'CA', '90210',")
|
||||
$lines.Add(" 1, 2, 0,")
|
||||
$lines.Add(" '$now', 'America/New_York',")
|
||||
$lines.Add(" '$now', 0")
|
||||
$lines.Add(" );")
|
||||
$lines.Add("")
|
||||
$lines.Add(" SET IDENTITY_INSERT Companies OFF;")
|
||||
$lines.Add(" PRINT 'Company inserted.';")
|
||||
$lines.Add("END")
|
||||
$lines.Add("ELSE")
|
||||
$lines.Add(" PRINT 'Company already exists - skipped.';")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- 2. Roles")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'SUPERADMIN')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleSuperAdmin', 'SuperAdmin', 'SUPERADMIN', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'ADMINISTRATOR')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleAdmin', 'Administrator', 'ADMINISTRATOR', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'MANAGER')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleManager', 'Manager', 'MANAGER', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'EMPLOYEE')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleEmployee', 'Employee', 'EMPLOYEE', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'SHOPFLOOR')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleShopFloor', 'ShopFloor', 'SHOPFLOOR', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'READONLY')")
|
||||
$lines.Add(" INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)")
|
||||
$lines.Add(" VALUES ('$roleReadOnly', 'ReadOnly', 'READONLY', NEWID());")
|
||||
$lines.Add("")
|
||||
$lines.Add("PRINT 'Roles inserted.';")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- 3. SuperAdmin Users")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- User 1: superadmin@powdercoating.com / SuperAdmin123!")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetUsers WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM')")
|
||||
$lines.Add("BEGIN")
|
||||
$lines.Add(" INSERT INTO AspNetUsers (")
|
||||
$lines.Add(" Id, UserName, NormalizedUserName, Email, NormalizedEmail,")
|
||||
$lines.Add(" EmailConfirmed, PasswordHash, SecurityStamp, ConcurrencyStamp,")
|
||||
$lines.Add(" PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled,")
|
||||
$lines.Add(" LockoutEnd, LockoutEnabled, AccessFailedCount,")
|
||||
$lines.Add(" CompanyId, CompanyRole,")
|
||||
$lines.Add(" FirstName, LastName, EmployeeNumber,")
|
||||
$lines.Add(" HireDate, IsActive,")
|
||||
$lines.Add(" Department, Position, HourlyRate,")
|
||||
$lines.Add(" Theme, DateFormat, TimeZone, SidebarColor,")
|
||||
$lines.Add(" CanViewShopFloor, CanManageJobs, CanManageInventory, CanManageCustomers,")
|
||||
$lines.Add(" CanCreateQuotes, CanApproveQuotes, CanManageCalendar, CanViewCalendar,")
|
||||
$lines.Add(" CanManageProducts, CanViewProducts, CanManageEquipment,")
|
||||
$lines.Add(" CanManageSuppliers, CanManageMaintenance,")
|
||||
$lines.Add(" CreatedAt")
|
||||
$lines.Add(" ) VALUES (")
|
||||
$lines.Add(" '$user1Id',")
|
||||
$lines.Add(" 'superadmin@powdercoating.com', 'SUPERADMIN@POWDERCOATING.COM',")
|
||||
$lines.Add(" 'superadmin@powdercoating.com', 'SUPERADMIN@POWDERCOATING.COM',")
|
||||
$lines.Add(" 1, '$hash1', '$user1Security', '$user1Stamp',")
|
||||
$lines.Add(" NULL, 0, 0, NULL, 1, 0,")
|
||||
$lines.Add(" 1, NULL,")
|
||||
$lines.Add(" 'Super', 'Admin', 'SA-001',")
|
||||
$lines.Add(" '$now', 1,")
|
||||
$lines.Add(" 'Platform', 'Super Administrator', 0,")
|
||||
$lines.Add(" 'light', 'MM/dd/yyyy', 'America/New_York', 'ocean',")
|
||||
$lines.Add(" 1, 1, 1, 1,")
|
||||
$lines.Add(" 1, 1, 1, 1,")
|
||||
$lines.Add(" 1, 1, 1,")
|
||||
$lines.Add(" 1, 1,")
|
||||
$lines.Add(" '$now'")
|
||||
$lines.Add(" );")
|
||||
$lines.Add(" PRINT 'User superadmin@powdercoating.com inserted.';")
|
||||
$lines.Add("END")
|
||||
$lines.Add("ELSE")
|
||||
$lines.Add(" PRINT 'User superadmin@powdercoating.com already exists - skipped.';")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- User 2: admin@powdercoating.com / Admin123!")
|
||||
$lines.Add("IF NOT EXISTS (SELECT 1 FROM AspNetUsers WHERE NormalizedEmail = 'ADMIN@POWDERCOATING.COM')")
|
||||
$lines.Add("BEGIN")
|
||||
$lines.Add(" INSERT INTO AspNetUsers (")
|
||||
$lines.Add(" Id, UserName, NormalizedUserName, Email, NormalizedEmail,")
|
||||
$lines.Add(" EmailConfirmed, PasswordHash, SecurityStamp, ConcurrencyStamp,")
|
||||
$lines.Add(" PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled,")
|
||||
$lines.Add(" LockoutEnd, LockoutEnabled, AccessFailedCount,")
|
||||
$lines.Add(" CompanyId, CompanyRole,")
|
||||
$lines.Add(" FirstName, LastName, EmployeeNumber,")
|
||||
$lines.Add(" HireDate, IsActive,")
|
||||
$lines.Add(" Department, Position, HourlyRate,")
|
||||
$lines.Add(" Theme, DateFormat, TimeZone, SidebarColor,")
|
||||
$lines.Add(" CanViewShopFloor, CanManageJobs, CanManageInventory, CanManageCustomers,")
|
||||
$lines.Add(" CanCreateQuotes, CanApproveQuotes, CanManageCalendar, CanViewCalendar,")
|
||||
$lines.Add(" CanManageProducts, CanViewProducts, CanManageEquipment,")
|
||||
$lines.Add(" CanManageSuppliers, CanManageMaintenance,")
|
||||
$lines.Add(" CreatedAt")
|
||||
$lines.Add(" ) VALUES (")
|
||||
$lines.Add(" '$user2Id',")
|
||||
$lines.Add(" 'admin@powdercoating.com', 'ADMIN@POWDERCOATING.COM',")
|
||||
$lines.Add(" 'admin@powdercoating.com', 'ADMIN@POWDERCOATING.COM',")
|
||||
$lines.Add(" 1, '$hash2', '$user2Security', '$user2Stamp',")
|
||||
$lines.Add(" NULL, 0, 0, NULL, 1, 0,")
|
||||
$lines.Add(" 1, NULL,")
|
||||
$lines.Add(" 'Admin', 'User', 'SA-002',")
|
||||
$lines.Add(" '$now', 1,")
|
||||
$lines.Add(" 'Platform', 'Platform Administrator', 0,")
|
||||
$lines.Add(" 'light', 'MM/dd/yyyy', 'America/New_York', 'ocean',")
|
||||
$lines.Add(" 1, 1, 1, 1,")
|
||||
$lines.Add(" 1, 1, 1, 1,")
|
||||
$lines.Add(" 1, 1, 1,")
|
||||
$lines.Add(" 1, 1,")
|
||||
$lines.Add(" '$now'")
|
||||
$lines.Add(" );")
|
||||
$lines.Add(" PRINT 'User admin@powdercoating.com inserted.';")
|
||||
$lines.Add("END")
|
||||
$lines.Add("ELSE")
|
||||
$lines.Add(" PRINT 'User admin@powdercoating.com already exists - skipped.';")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("-- 4. Assign SuperAdmin role to both users")
|
||||
$lines.Add("")
|
||||
$lines.Add("DECLARE @roleId NVARCHAR(450) = (SELECT Id FROM AspNetRoles WHERE NormalizedName = 'SUPERADMIN');")
|
||||
$lines.Add("DECLARE @u1Id NVARCHAR(450) = (SELECT Id FROM AspNetUsers WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM');")
|
||||
$lines.Add("DECLARE @u2Id NVARCHAR(450) = (SELECT Id FROM AspNetUsers WHERE NormalizedEmail = 'ADMIN@POWDERCOATING.COM');")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF @u1Id IS NOT NULL AND @roleId IS NOT NULL")
|
||||
$lines.Add(" AND NOT EXISTS (SELECT 1 FROM AspNetUserRoles WHERE UserId = @u1Id AND RoleId = @roleId)")
|
||||
$lines.Add("BEGIN")
|
||||
$lines.Add(" INSERT INTO AspNetUserRoles (UserId, RoleId) VALUES (@u1Id, @roleId);")
|
||||
$lines.Add(" PRINT 'Role assigned to superadmin@powdercoating.com.';")
|
||||
$lines.Add("END")
|
||||
$lines.Add("")
|
||||
$lines.Add("IF @u2Id IS NOT NULL AND @roleId IS NOT NULL")
|
||||
$lines.Add(" AND NOT EXISTS (SELECT 1 FROM AspNetUserRoles WHERE UserId = @u2Id AND RoleId = @roleId)")
|
||||
$lines.Add("BEGIN")
|
||||
$lines.Add(" INSERT INTO AspNetUserRoles (UserId, RoleId) VALUES (@u2Id, @roleId);")
|
||||
$lines.Add(" PRINT 'Role assigned to admin@powdercoating.com.';")
|
||||
$lines.Add("END")
|
||||
$lines.Add("GO")
|
||||
$lines.Add("")
|
||||
$lines.Add("PRINT '================================================';")
|
||||
$lines.Add("PRINT 'Seed complete.';")
|
||||
$lines.Add("PRINT 'Login 1: superadmin@powdercoating.com / SuperAdmin123!';")
|
||||
$lines.Add("PRINT 'Login 2: admin@powdercoating.com / Admin123!';")
|
||||
$lines.Add("PRINT '================================================';")
|
||||
$lines.Add("GO")
|
||||
|
||||
$lines | Set-Content -Path $outFile -Encoding UTF8
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "SQL file written to: $outFile" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Run it against your Azure SQL database using:" -ForegroundColor Yellow
|
||||
Write-Host " sqlcmd -S <server>.database.windows.net -d PowderCoatingDb -U <user> -P <password> -i seed_admins.sql" -ForegroundColor White
|
||||
Write-Host " OR paste it into Azure Portal > Query Editor" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "Login 1: superadmin@powdercoating.com / SuperAdmin123!" -ForegroundColor Cyan
|
||||
Write-Host "Login 2: admin@powdercoating.com / Admin123!" -ForegroundColor Cyan
|
||||
@@ -0,0 +1,211 @@
|
||||
-- ============================================================================
|
||||
-- HARD DELETE ALL COMPANY DATA - Demo Company Only
|
||||
-- WARNING: This permanently deletes all data for the specified company!
|
||||
-- ============================================================================
|
||||
|
||||
SET NOCOUNT ON;
|
||||
DECLARE @CompanyId INT = 1; -- Demo Company (change if needed)
|
||||
DECLARE @CompanyName NVARCHAR(255);
|
||||
DECLARE @RowsDeleted INT = 0;
|
||||
|
||||
-- Get company name for confirmation
|
||||
SELECT @CompanyName = CompanyName FROM Companies WHERE Id = @CompanyId;
|
||||
|
||||
PRINT '============================================================================';
|
||||
PRINT 'HARD DELETE - Company: ' + ISNULL(@CompanyName, 'NOT FOUND');
|
||||
PRINT 'Company ID: ' + CAST(@CompanyId AS NVARCHAR(10));
|
||||
PRINT 'Started at: ' + CONVERT(NVARCHAR(30), GETDATE(), 120);
|
||||
PRINT '============================================================================';
|
||||
PRINT '';
|
||||
|
||||
IF @CompanyName IS NULL
|
||||
BEGIN
|
||||
PRINT 'ERROR: Company not found!';
|
||||
RETURN;
|
||||
END
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
BEGIN TRY
|
||||
-- ========================================================================
|
||||
-- LEVEL 1: Most dependent tables (delete first)
|
||||
-- ========================================================================
|
||||
|
||||
PRINT 'Deleting Level 1: Most dependent tables...';
|
||||
|
||||
-- JobStatusHistory (references Jobs)
|
||||
DELETE FROM JobStatusHistory WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobStatusHistory: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- JobPhotos (references Jobs)
|
||||
DELETE FROM JobPhotos WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobPhotos: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- JobNotes (references Jobs)
|
||||
DELETE FROM JobNotes WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobNotes: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- Appointments (references Jobs, Customers)
|
||||
DELETE FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Appointments: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- JobItems (references Jobs)
|
||||
DELETE FROM JobItems WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobItems: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- QuoteItems (references Quotes)
|
||||
DELETE FROM QuoteItems WHERE QuoteId IN (SELECT Id FROM Quotes WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ QuoteItems: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- InventoryTransactions (references InventoryItems)
|
||||
DELETE FROM InventoryTransactions WHERE InventoryItemId IN (SELECT Id FROM InventoryItems WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ InventoryTransactions: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- MaintenanceRecords (references Equipment)
|
||||
DELETE FROM MaintenanceRecords WHERE EquipmentId IN (SELECT Id FROM Equipment WHERE CompanyId = @CompanyId);
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ MaintenanceRecords: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ========================================================================
|
||||
-- LEVEL 2: Mid-level dependencies
|
||||
-- ========================================================================
|
||||
|
||||
PRINT 'Deleting Level 2: Mid-level dependencies...';
|
||||
|
||||
-- Jobs (references Customers, Quotes)
|
||||
DELETE FROM Jobs WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Jobs: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- Quotes (references Customers)
|
||||
DELETE FROM Quotes WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Quotes: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- CatalogItems (references CatalogCategories, Customers)
|
||||
DELETE FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ CatalogItems: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ========================================================================
|
||||
-- LEVEL 3: Base entities
|
||||
-- ========================================================================
|
||||
|
||||
PRINT 'Deleting Level 3: Base entities...';
|
||||
|
||||
-- Customers
|
||||
DELETE FROM Customers WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Customers: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- InventoryItems
|
||||
DELETE FROM InventoryItems WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ InventoryItems: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- Equipment
|
||||
DELETE FROM Equipment WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Equipment: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- CatalogCategories
|
||||
DELETE FROM CatalogCategories WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ CatalogCategories: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- ShopWorkers
|
||||
DELETE FROM ShopWorkers WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ ShopWorkers: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- PricingTiers
|
||||
DELETE FROM PricingTiers WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ PricingTiers: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- CompanyOperatingCosts
|
||||
DELETE FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ CompanyOperatingCosts: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- Suppliers
|
||||
DELETE FROM Suppliers WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ Suppliers: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ========================================================================
|
||||
-- LEVEL 4: Lookup tables (delete last)
|
||||
-- ========================================================================
|
||||
|
||||
PRINT 'Deleting Level 4: Lookup tables...';
|
||||
|
||||
-- JobStatusLookup
|
||||
DELETE FROM JobStatusLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobStatusLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- JobPriorityLookup
|
||||
DELETE FROM JobPriorityLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ JobPriorityLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- QuoteStatusLookup
|
||||
DELETE FROM QuoteStatusLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ QuoteStatusLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- AppointmentStatusLookup
|
||||
DELETE FROM AppointmentStatusLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ AppointmentStatusLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- AppointmentTypeLookup
|
||||
DELETE FROM AppointmentTypeLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ AppointmentTypeLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
-- InventoryCategoryLookup
|
||||
DELETE FROM InventoryCategoryLookup WHERE CompanyId = @CompanyId;
|
||||
SET @RowsDeleted = @@ROWCOUNT;
|
||||
PRINT ' ✓ InventoryCategoryLookup: ' + CAST(@RowsDeleted AS NVARCHAR(10)) + ' rows';
|
||||
|
||||
PRINT '';
|
||||
PRINT '============================================================================';
|
||||
PRINT 'SUCCESS! All company data deleted.';
|
||||
PRINT 'Completed at: ' + CONVERT(NVARCHAR(30), GETDATE(), 120);
|
||||
PRINT '============================================================================';
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
PRINT '';
|
||||
PRINT 'Transaction committed successfully.';
|
||||
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
ROLLBACK TRANSACTION;
|
||||
|
||||
PRINT '';
|
||||
PRINT '============================================================================';
|
||||
PRINT 'ERROR! Rolling back transaction...';
|
||||
PRINT '';
|
||||
PRINT 'Error Message: ' + ERROR_MESSAGE();
|
||||
PRINT 'Error Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10));
|
||||
PRINT '============================================================================';
|
||||
|
||||
-- Re-throw the error
|
||||
THROW;
|
||||
END CATCH;
|
||||
|
||||
SET NOCOUNT OFF;
|
||||
@@ -0,0 +1,27 @@
|
||||
-- Find what's blocking the delete
|
||||
SELECT
|
||||
session_id,
|
||||
blocking_session_id,
|
||||
wait_type,
|
||||
wait_time,
|
||||
last_wait_type,
|
||||
command,
|
||||
status
|
||||
FROM sys.dm_exec_requests
|
||||
WHERE blocking_session_id <> 0;
|
||||
|
||||
-- See all active sessions on this database
|
||||
SELECT
|
||||
s.session_id,
|
||||
s.login_name,
|
||||
s.host_name,
|
||||
s.program_name,
|
||||
s.status,
|
||||
r.command,
|
||||
r.blocking_session_id
|
||||
FROM sys.dm_exec_sessions s
|
||||
LEFT JOIN sys.dm_exec_requests r ON s.session_id = r.session_id
|
||||
WHERE s.database_id = DB_ID('PowderCoatingDb');
|
||||
|
||||
-- To kill a specific session (replace XX with session_id):
|
||||
-- KILL XX;
|
||||
@@ -0,0 +1,67 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [AccountingOverride] bit NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [OnlinePaymentsOverride] bit NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142151Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142159Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142161Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412174157_AddCompanyFeatureOverrides', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [IsAnnualBilling] bit NOT NULL DEFAULT CAST(0 AS bit);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093047Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093054Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093055Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412183411_AddIsAnnualBilling', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Key', N'Value', N'Label', N'Description', N'GroupName') AND [object_id] = OBJECT_ID(N'[PlatformSettings]'))
|
||||
SET IDENTITY_INSERT [PlatformSettings] ON;
|
||||
EXEC(N'INSERT INTO [PlatformSettings] ([Id], [Key], [Value], [Label], [Description], [GroupName])
|
||||
VALUES (8, N''MaxTenants'', N'''', N''Max Tenants'', N''Maximum number of tenant companies allowed to self-register. Leave blank or 0 for unlimited. Once the limit is reached, the signup page and login signup link are hidden.'', N''Access Control'')');
|
||||
IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Key', N'Value', N'Label', N'Description', N'GroupName') AND [object_id] = OBJECT_ID(N'[PlatformSettings]'))
|
||||
SET IDENTITY_INSERT [PlatformSettings] OFF;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071531Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071537Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071538Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412005228_AddMaxTenantsSetting', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
CREATE TABLE [PendingRegistrationSessions] (
|
||||
[Id] int NOT NULL IDENTITY,
|
||||
[Token] nvarchar(max) NOT NULL,
|
||||
[CompanyName] nvarchar(max) NOT NULL,
|
||||
[CompanyPhone] nvarchar(max) NULL,
|
||||
[FirstName] nvarchar(max) NOT NULL,
|
||||
[LastName] nvarchar(max) NOT NULL,
|
||||
[Email] nvarchar(max) NOT NULL,
|
||||
[Plan] int NOT NULL,
|
||||
[IsAnnual] bit NOT NULL,
|
||||
[CreatedAt] datetime2 NOT NULL,
|
||||
[IsCompleted] bit NOT NULL,
|
||||
CONSTRAINT [PK_PendingRegistrationSessions] PRIMARY KEY ([Id])
|
||||
);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916607Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916613Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916614Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260414135810_AddPendingRegistrationSession', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedAt] datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedByName] nvarchar(max) NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedByUserId] nvarchar(max) NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083161Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083167Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083169Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260415010203_AddSetupWizardCompletionTracking', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Key', N'Value', N'Label', N'Description', N'GroupName') AND [object_id] = OBJECT_ID(N'[PlatformSettings]'))
|
||||
SET IDENTITY_INSERT [PlatformSettings] ON;
|
||||
EXEC(N'INSERT INTO [PlatformSettings] ([Id], [Key], [Value], [Label], [Description], [GroupName])
|
||||
VALUES (8, N''MaxTenants'', N'''', N''Max Tenants'', N''Maximum number of tenant companies allowed to self-register. Leave blank or 0 for unlimited. Once the limit is reached, the signup page and login signup link are hidden.'', N''Access Control'')');
|
||||
IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Key', N'Value', N'Label', N'Description', N'GroupName') AND [object_id] = OBJECT_ID(N'[PlatformSettings]'))
|
||||
SET IDENTITY_INSERT [PlatformSettings] OFF;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071531Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071537Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T00:52:25.3071538Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412005228_AddMaxTenantsSetting'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412005228_AddMaxTenantsSetting', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [AccountingOverride] bit NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [OnlinePaymentsOverride] bit NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142151Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142159Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T17:41:53.0142161Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412174157_AddCompanyFeatureOverrides'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412174157_AddCompanyFeatureOverrides', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [Companies] ADD [IsAnnualBilling] bit NOT NULL DEFAULT CAST(0 AS bit);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093047Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093054Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-12T18:34:08.9093055Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260412183411_AddIsAnnualBilling'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260412183411_AddIsAnnualBilling', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
CREATE TABLE [PendingRegistrationSessions] (
|
||||
[Id] int NOT NULL IDENTITY,
|
||||
[Token] nvarchar(max) NOT NULL,
|
||||
[CompanyName] nvarchar(max) NOT NULL,
|
||||
[CompanyPhone] nvarchar(max) NULL,
|
||||
[FirstName] nvarchar(max) NOT NULL,
|
||||
[LastName] nvarchar(max) NOT NULL,
|
||||
[Email] nvarchar(max) NOT NULL,
|
||||
[Plan] int NOT NULL,
|
||||
[IsAnnual] bit NOT NULL,
|
||||
[CreatedAt] datetime2 NOT NULL,
|
||||
[IsCompleted] bit NOT NULL,
|
||||
CONSTRAINT [PK_PendingRegistrationSessions] PRIMARY KEY ([Id])
|
||||
);
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916607Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916613Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-14T13:58:07.0916614Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260414135810_AddPendingRegistrationSession'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260414135810_AddPendingRegistrationSession', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedAt] datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedByName] nvarchar(max) NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [CompanyPreferences] ADD [SetupWizardCompletedByUserId] nvarchar(max) NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083161Z''
|
||||
WHERE [Id] = 1;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083167Z''
|
||||
WHERE [Id] = 2;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-15T01:02:00.3083169Z''
|
||||
WHERE [Id] = 3;
|
||||
SELECT @@ROWCOUNT');
|
||||
END;
|
||||
GO
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT * FROM [__EFMigrationsHistory]
|
||||
WHERE [MigrationId] = N'20260415010203_AddSetupWizardCompletionTracking'
|
||||
)
|
||||
BEGIN
|
||||
INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
|
||||
VALUES (N'20260415010203_AddSetupWizardCompletionTracking', N'8.0.11');
|
||||
END;
|
||||
GO
|
||||
|
||||
COMMIT;
|
||||
GO
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
-- ============================================================================
|
||||
-- NUCLEAR DELETE - Delete ALL data for a company (including fresh seeds)
|
||||
-- Run this, then immediately seed WITHOUT restarting the app
|
||||
-- ============================================================================
|
||||
|
||||
DECLARE @CompanyId INT = 1;
|
||||
|
||||
PRINT 'NUCLEAR DELETE - Deleting EVERYTHING for Company ID: ' + CAST(@CompanyId AS NVARCHAR(10));
|
||||
PRINT '';
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
BEGIN TRY
|
||||
-- Delete in reverse order (most dependent first)
|
||||
DELETE FROM JobStatusHistory WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM JobPhotos WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM JobNotes WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM JobItems WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM QuoteItems WHERE QuoteId IN (SELECT Id FROM Quotes WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM InventoryTransactions WHERE InventoryItemId IN (SELECT Id FROM InventoryItems WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM MaintenanceRecords WHERE EquipmentId IN (SELECT Id FROM Equipment WHERE CompanyId = @CompanyId);
|
||||
|
||||
DELETE FROM Jobs WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Quotes WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CatalogCategories WHERE CompanyId = @CompanyId;
|
||||
|
||||
DELETE FROM Customers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM InventoryItems WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Equipment WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM ShopWorkers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM PricingTiers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Suppliers WHERE CompanyId = @CompanyId;
|
||||
|
||||
DELETE FROM JobStatusLookup WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM JobPriorityLookup WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM QuoteStatusLookup WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM AppointmentStatusLookup WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM AppointmentTypeLookup WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM InventoryCategoryLookup WHERE CompanyId = @CompanyId;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
PRINT 'SUCCESS! All data deleted. NOW SEED IMMEDIATELY (do not restart app)!';
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
ROLLBACK TRANSACTION;
|
||||
PRINT 'ERROR: ' + ERROR_MESSAGE();
|
||||
THROW;
|
||||
END CATCH;
|
||||
@@ -0,0 +1,230 @@
|
||||
-- =============================================
|
||||
-- Preview Company Data Reset
|
||||
-- =============================================
|
||||
-- This script shows what WOULD be deleted without actually deleting anything.
|
||||
-- Use this to verify before running the actual ResetCompanyData.sql script.
|
||||
-- =============================================
|
||||
|
||||
-- CONFIGURATION: Set ONE of these (comment out the other)
|
||||
DECLARE @CompanyCode NVARCHAR(50) = 'DEMO'; -- Use company code
|
||||
-- DECLARE @CompanyId INT = 1; -- OR use company ID directly
|
||||
|
||||
-- Get CompanyId from CompanyCode if needed
|
||||
IF @CompanyCode IS NOT NULL AND EXISTS(SELECT 1 FROM Companies WHERE CompanyCode = @CompanyCode)
|
||||
BEGIN
|
||||
SELECT @CompanyId = Id FROM Companies WHERE CompanyCode = @CompanyCode;
|
||||
END
|
||||
|
||||
-- Validate company exists
|
||||
IF @CompanyId IS NULL OR NOT EXISTS(SELECT 1 FROM Companies WHERE Id = @CompanyId)
|
||||
BEGIN
|
||||
RAISERROR('Company not found! Please check @CompanyId or @CompanyCode', 16, 1);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- Display company information
|
||||
DECLARE @CompanyName NVARCHAR(200);
|
||||
SELECT @CompanyName = CompanyName, @CompanyCode = CompanyCode FROM Companies WHERE Id = @CompanyId;
|
||||
|
||||
PRINT '=============================================';
|
||||
PRINT 'PREVIEW: DATA TO BE DELETED FOR COMPANY:';
|
||||
PRINT ' Company ID: ' + CAST(@CompanyId AS NVARCHAR(10));
|
||||
PRINT ' Company Code: ' + @CompanyCode;
|
||||
PRINT ' Company Name: ' + @CompanyName;
|
||||
PRINT '=============================================';
|
||||
PRINT '';
|
||||
PRINT 'NOTE: This is a PREVIEW only. No data will be deleted.';
|
||||
PRINT '';
|
||||
|
||||
DECLARE @Count INT;
|
||||
DECLARE @TotalCount INT = 0;
|
||||
|
||||
-- Job Photos
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM JobPhotos
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Job Photos: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Job Notes
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM JobNotes
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Job Notes: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Job Items
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM JobItems
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Job Items: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Jobs
|
||||
SELECT @Count = COUNT(*) FROM Jobs WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Jobs: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Quote Items
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM QuoteItems
|
||||
WHERE QuoteId IN (SELECT Id FROM Quotes WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Quote Items: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Quotes
|
||||
SELECT @Count = COUNT(*) FROM Quotes WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Quotes: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Appointments
|
||||
IF OBJECT_ID('Appointments', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @Count = COUNT(*) FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Appointments: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
END
|
||||
|
||||
-- Inventory Transactions
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM InventoryTransactions
|
||||
WHERE InventoryItemId IN (SELECT Id FROM InventoryItems WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Inventory Transactions: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Inventory Items
|
||||
SELECT @Count = COUNT(*) FROM InventoryItems WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Inventory Items: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Maintenance Records
|
||||
SELECT @Count = COUNT(*)
|
||||
FROM MaintenanceRecords
|
||||
WHERE EquipmentId IN (SELECT Id FROM Equipment WHERE CompanyId = @CompanyId);
|
||||
PRINT 'Maintenance Records: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Equipment
|
||||
SELECT @Count = COUNT(*) FROM Equipment WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Equipment: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Catalog Items
|
||||
IF OBJECT_ID('CatalogItems', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @Count = COUNT(*) FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Catalog Items: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
END
|
||||
|
||||
-- Catalog Categories
|
||||
IF OBJECT_ID('CatalogCategories', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @Count = COUNT(*) FROM CatalogCategories WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Catalog Categories: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
END
|
||||
|
||||
-- Suppliers
|
||||
IF OBJECT_ID('Suppliers', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @Count = COUNT(*) FROM Suppliers WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Suppliers: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
END
|
||||
|
||||
-- Customers
|
||||
SELECT @Count = COUNT(*) FROM Customers WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Customers: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Pricing Tiers
|
||||
SELECT @Count = COUNT(*) FROM PricingTiers WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Pricing Tiers: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Company Operating Costs
|
||||
SELECT @Count = COUNT(*) FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Company Operating Costs: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
|
||||
-- Company Preferences
|
||||
IF OBJECT_ID('CompanyPreferences', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
SELECT @Count = COUNT(*) FROM CompanyPreferences WHERE CompanyId = @CompanyId;
|
||||
PRINT 'Company Preferences: ' + CAST(@Count AS NVARCHAR(10));
|
||||
SET @TotalCount = @TotalCount + @Count;
|
||||
END
|
||||
|
||||
PRINT '';
|
||||
PRINT '=============================================';
|
||||
PRINT 'TOTAL RECORDS TO BE DELETED: ' + CAST(@TotalCount AS NVARCHAR(10));
|
||||
PRINT '=============================================';
|
||||
PRINT '';
|
||||
PRINT 'PRESERVED DATA (will NOT be deleted):';
|
||||
PRINT ' - Company record: ' + @CompanyName;
|
||||
PRINT ' - Users associated with company';
|
||||
PRINT ' - Job Status Lookups';
|
||||
PRINT ' - Job Priority Lookups';
|
||||
PRINT ' - Quote Status Lookups';
|
||||
PRINT ' - Inventory Category Lookups';
|
||||
PRINT ' - Appointment Status Lookups';
|
||||
PRINT ' - Appointment Type Lookups';
|
||||
PRINT '';
|
||||
|
||||
-- Show sample data for verification
|
||||
PRINT '=============================================';
|
||||
PRINT 'SAMPLE DATA (first 5 of each type):';
|
||||
PRINT '=============================================';
|
||||
PRINT '';
|
||||
|
||||
-- Sample Customers
|
||||
IF EXISTS(SELECT 1 FROM Customers WHERE CompanyId = @CompanyId)
|
||||
BEGIN
|
||||
PRINT 'CUSTOMERS:';
|
||||
SELECT TOP 5
|
||||
Id,
|
||||
CompanyName,
|
||||
ISNULL(ContactFirstName + ' ' + ContactLastName, 'N/A') AS ContactName,
|
||||
Email
|
||||
FROM Customers
|
||||
WHERE CompanyId = @CompanyId
|
||||
ORDER BY Id;
|
||||
PRINT '';
|
||||
END
|
||||
|
||||
-- Sample Jobs
|
||||
IF EXISTS(SELECT 1 FROM Jobs WHERE CompanyId = @CompanyId)
|
||||
BEGIN
|
||||
PRINT 'JOBS:';
|
||||
SELECT TOP 5
|
||||
j.Id,
|
||||
j.JobNumber,
|
||||
c.CompanyName AS CustomerName,
|
||||
j.Status,
|
||||
j.CreatedAt
|
||||
FROM Jobs j
|
||||
LEFT JOIN Customers c ON j.CustomerId = c.Id
|
||||
WHERE j.CompanyId = @CompanyId
|
||||
ORDER BY j.Id;
|
||||
PRINT '';
|
||||
END
|
||||
|
||||
-- Sample Inventory
|
||||
IF EXISTS(SELECT 1 FROM InventoryItems WHERE CompanyId = @CompanyId)
|
||||
BEGIN
|
||||
PRINT 'INVENTORY ITEMS:';
|
||||
SELECT TOP 5
|
||||
Id,
|
||||
SKU,
|
||||
Name,
|
||||
QuantityOnHand,
|
||||
UnitOfMeasure
|
||||
FROM InventoryItems
|
||||
WHERE CompanyId = @CompanyId
|
||||
ORDER BY Id;
|
||||
PRINT '';
|
||||
END
|
||||
|
||||
PRINT '';
|
||||
PRINT 'To proceed with deletion, run: ResetCompanyData.sql';
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Quick delete without transaction - use if NuclearDelete hangs
|
||||
DECLARE @CompanyId INT = 1;
|
||||
|
||||
-- Most dependent first
|
||||
DELETE FROM JobStatusHistory WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM JobPhotos WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM JobNotes WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM JobItems WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM QuoteItems WHERE QuoteId IN (SELECT Id FROM Quotes WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM InventoryTransactions WHERE InventoryItemId IN (SELECT Id FROM InventoryItems WHERE CompanyId = @CompanyId);
|
||||
DELETE FROM MaintenanceRecords WHERE EquipmentId IN (SELECT Id FROM Equipment WHERE CompanyId = @CompanyId);
|
||||
|
||||
DELETE FROM Jobs WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Quotes WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CatalogCategories WHERE CompanyId = @CompanyId;
|
||||
|
||||
DELETE FROM Customers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM InventoryItems WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Equipment WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM ShopWorkers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM PricingTiers WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM Suppliers WHERE CompanyId = @CompanyId;
|
||||
|
||||
DELETE FROM JobStatusLookups WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM JobPriorityLookups WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM QuoteStatusLookups WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM AppointmentStatusLookups WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM AppointmentTypeLookups WHERE CompanyId = @CompanyId;
|
||||
DELETE FROM InventoryCategoryLookups WHERE CompanyId = @CompanyId;
|
||||
|
||||
PRINT 'Done!';
|
||||
@@ -0,0 +1,187 @@
|
||||
# Company Data Reset Scripts
|
||||
|
||||
These scripts allow you to completely reset a company's transactional data for testing purposes while preserving lookup tables and the company structure.
|
||||
|
||||
## Files
|
||||
|
||||
- **`PreviewCompanyDataReset.sql`** - Shows what would be deleted without making changes (safe to run)
|
||||
- **`ResetCompanyData.sql`** - Actually deletes the data (use with caution!)
|
||||
|
||||
## What Gets Deleted
|
||||
|
||||
The scripts remove ALL transactional data for the specified company:
|
||||
|
||||
- ✓ Jobs, Job Items, Job Notes, Job Photos
|
||||
- ✓ Quotes and Quote Items
|
||||
- ✓ Customers
|
||||
- ✓ Inventory Items and Inventory Transactions
|
||||
- ✓ Equipment and Maintenance Records
|
||||
- ✓ Catalog Categories and Catalog Items
|
||||
- ✓ Suppliers
|
||||
- ✓ Appointments
|
||||
- ✓ Pricing Tiers
|
||||
- ✓ Company Operating Costs
|
||||
- ✓ Company Preferences
|
||||
|
||||
## What Gets Preserved
|
||||
|
||||
The scripts preserve all lookup tables and structure:
|
||||
|
||||
- ✓ The Company record itself
|
||||
- ✓ Users associated with the company
|
||||
- ✓ Job Status Lookups
|
||||
- ✓ Job Priority Lookups
|
||||
- ✓ Quote Status Lookups
|
||||
- ✓ Inventory Category Lookups
|
||||
- ✓ Appointment Status Lookups
|
||||
- ✓ Appointment Type Lookups
|
||||
|
||||
## Usage
|
||||
|
||||
### Step 1: Preview (Recommended)
|
||||
|
||||
First, run the preview script to see what will be deleted:
|
||||
|
||||
1. Open `PreviewCompanyDataReset.sql` in SQL Server Management Studio (SSMS) or Azure Data Studio
|
||||
2. Set the company identifier at the top of the script:
|
||||
```sql
|
||||
-- Option 1: Use company code
|
||||
DECLARE @CompanyCode NVARCHAR(50) = 'DEMO';
|
||||
|
||||
-- Option 2: OR use company ID (comment out the line above)
|
||||
-- DECLARE @CompanyId INT = 1;
|
||||
```
|
||||
3. Execute the script
|
||||
4. Review the output showing:
|
||||
- Count of records to be deleted per table
|
||||
- Total records to be deleted
|
||||
- Sample data from each table
|
||||
- What will be preserved
|
||||
|
||||
### Step 2: Reset (Actual Deletion)
|
||||
|
||||
After reviewing the preview, run the reset script:
|
||||
|
||||
1. Open `ResetCompanyData.sql`
|
||||
2. Set the same company identifier:
|
||||
```sql
|
||||
DECLARE @CompanyCode NVARCHAR(50) = 'DEMO';
|
||||
```
|
||||
3. **IMPORTANT:** Make a database backup if needed
|
||||
4. Execute the script
|
||||
5. Review the output showing:
|
||||
- Deleted record counts per table
|
||||
- Total deleted records
|
||||
- Preserved data
|
||||
|
||||
### Step 3: Re-seed (Optional)
|
||||
|
||||
After resetting, you can re-populate the company with fresh demo data:
|
||||
|
||||
1. Log in to the application as a SuperAdmin
|
||||
2. Navigate to: **Platform Management** > **Seed Data**
|
||||
3. Click "Seed Data" for the company you just reset
|
||||
4. Review the seeding results and any warnings
|
||||
|
||||
## Safety Features
|
||||
|
||||
### Transaction Rollback
|
||||
Both scripts use transactions. If any error occurs during deletion, ALL changes are rolled back automatically.
|
||||
|
||||
### Validation
|
||||
- Scripts validate that the company exists before attempting deletion
|
||||
- Clear error messages if company is not found
|
||||
|
||||
### Detailed Logging
|
||||
- Each deletion step is logged with record counts
|
||||
- Final summary shows exactly what was deleted
|
||||
- Preserved data is clearly listed
|
||||
|
||||
## Examples
|
||||
|
||||
### Reset the Demo Company
|
||||
```sql
|
||||
DECLARE @CompanyCode NVARCHAR(50) = 'DEMO';
|
||||
```
|
||||
|
||||
### Reset a Specific Company by ID
|
||||
```sql
|
||||
-- DECLARE @CompanyCode NVARCHAR(50) = 'DEMO'; -- Comment this out
|
||||
DECLARE @CompanyId INT = 5;
|
||||
```
|
||||
|
||||
### Reset Multiple Companies
|
||||
To reset multiple companies, execute the script once for each company (change the company code/ID between runs).
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Testing Fresh Import
|
||||
```
|
||||
1. Run ResetCompanyData.sql for company "ABC"
|
||||
2. Import CSV data for company ABC
|
||||
3. Test the import results
|
||||
```
|
||||
|
||||
### Testing Seed Data Changes
|
||||
```
|
||||
1. Modify seed data routines in code
|
||||
2. Run ResetCompanyData.sql for "DEMO"
|
||||
3. Re-seed via UI to test new seed data
|
||||
4. Verify results
|
||||
```
|
||||
|
||||
### Clean Slate for Demo
|
||||
```
|
||||
1. Preview what will be deleted
|
||||
2. Run ResetCompanyData.sql
|
||||
3. Re-seed with fresh demo data
|
||||
4. Demo is ready with clean data
|
||||
```
|
||||
|
||||
## Warnings
|
||||
|
||||
⚠️ **IMPORTANT:** These scripts permanently delete data!
|
||||
|
||||
- Always run the **preview script first**
|
||||
- Make a **database backup** before running reset on production data
|
||||
- Cannot be undone (unless you restore from backup)
|
||||
- Users are NOT deleted (they will exist but have no data)
|
||||
- Company configuration is NOT deleted
|
||||
|
||||
## Database Permissions
|
||||
|
||||
To run these scripts, you need:
|
||||
- `db_datawriter` role (to delete data)
|
||||
- `db_datareader` role (to read company info)
|
||||
- Execute permissions on the database
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Foreign Key Constraint Error
|
||||
If you get a foreign key violation, the script may need updating for new tables. Check the error message for the table name and add it to the script in the correct order.
|
||||
|
||||
### Company Not Found
|
||||
```
|
||||
Company not found! Please check @CompanyId or @CompanyCode
|
||||
```
|
||||
**Solution:** Verify the company code or ID is correct:
|
||||
```sql
|
||||
SELECT Id, CompanyCode, CompanyName FROM Companies;
|
||||
```
|
||||
|
||||
### Transaction Rollback
|
||||
If the script fails and rolls back:
|
||||
1. Check the error message in the output
|
||||
2. Fix the issue (usually a missing table or new FK constraint)
|
||||
3. Re-run the script
|
||||
|
||||
## Support
|
||||
|
||||
For questions or issues with these scripts:
|
||||
1. Check the script comments for usage details
|
||||
2. Review the CLAUDE.md file for database structure
|
||||
3. Contact the development team
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-02-16
|
||||
@@ -0,0 +1,243 @@
|
||||
-- =============================================
|
||||
-- Reset Company Data for Testing
|
||||
-- =============================================
|
||||
-- This script completely removes all transactional data for a company
|
||||
-- while preserving lookup tables (statuses, priorities, categories, etc.)
|
||||
--
|
||||
-- Usage:
|
||||
-- 1. Set the @CompanyId or @CompanyCode variable below
|
||||
-- 2. Review the data to be deleted (uncomment SELECT statements)
|
||||
-- 3. Execute the script
|
||||
-- =============================================
|
||||
|
||||
-- CONFIGURATION: Set ONE of these (comment out the other)
|
||||
DECLARE @CompanyCode NVARCHAR(50) = 'DEMO'; -- Use company code
|
||||
-- DECLARE @CompanyId INT = 1; -- OR use company ID directly
|
||||
|
||||
-- Get CompanyId from CompanyCode if needed
|
||||
IF @CompanyCode IS NOT NULL AND EXISTS(SELECT 1 FROM Companies WHERE CompanyCode = @CompanyCode)
|
||||
BEGIN
|
||||
SELECT @CompanyId = Id FROM Companies WHERE CompanyCode = @CompanyCode;
|
||||
END
|
||||
|
||||
-- Validate company exists
|
||||
IF @CompanyId IS NULL OR NOT EXISTS(SELECT 1 FROM Companies WHERE Id = @CompanyId)
|
||||
BEGIN
|
||||
RAISERROR('Company not found! Please check @CompanyId or @CompanyCode', 16, 1);
|
||||
RETURN;
|
||||
END
|
||||
|
||||
-- Display company information
|
||||
DECLARE @CompanyName NVARCHAR(200);
|
||||
SELECT @CompanyName = CompanyName, @CompanyCode = CompanyCode FROM Companies WHERE Id = @CompanyId;
|
||||
|
||||
PRINT '=============================================';
|
||||
PRINT 'RESETTING DATA FOR COMPANY:';
|
||||
PRINT ' Company ID: ' + CAST(@CompanyId AS NVARCHAR(10));
|
||||
PRINT ' Company Code: ' + @CompanyCode;
|
||||
PRINT ' Company Name: ' + @CompanyName;
|
||||
PRINT '=============================================';
|
||||
PRINT '';
|
||||
|
||||
-- Begin transaction for safety
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
BEGIN TRY
|
||||
DECLARE @RowCount INT = 0;
|
||||
DECLARE @TotalDeleted INT = 0;
|
||||
|
||||
-- =============================================
|
||||
-- DELETE ORDER: Child tables first to avoid FK violations
|
||||
-- =============================================
|
||||
|
||||
-- 1. Job Photos
|
||||
PRINT 'Deleting Job Photos...';
|
||||
DELETE FROM JobPhotos
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' job photo(s)';
|
||||
|
||||
-- 2. Job Notes
|
||||
PRINT 'Deleting Job Notes...';
|
||||
DELETE FROM JobNotes
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' job note(s)';
|
||||
|
||||
-- 3. Job Items
|
||||
PRINT 'Deleting Job Items...';
|
||||
DELETE FROM JobItems
|
||||
WHERE JobId IN (SELECT Id FROM Jobs WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' job item(s)';
|
||||
|
||||
-- 4. Jobs
|
||||
PRINT 'Deleting Jobs...';
|
||||
DELETE FROM Jobs WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' job(s)';
|
||||
|
||||
-- 5. Quote Items
|
||||
PRINT 'Deleting Quote Items...';
|
||||
DELETE FROM QuoteItems
|
||||
WHERE QuoteId IN (SELECT Id FROM Quotes WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' quote item(s)';
|
||||
|
||||
-- 6. Quotes
|
||||
PRINT 'Deleting Quotes...';
|
||||
DELETE FROM Quotes WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' quote(s)';
|
||||
|
||||
-- 7. Appointments (if table exists)
|
||||
IF OBJECT_ID('Appointments', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
PRINT 'Deleting Appointments...';
|
||||
DELETE FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' appointment(s)';
|
||||
END
|
||||
|
||||
-- 8. Inventory Transactions
|
||||
PRINT 'Deleting Inventory Transactions...';
|
||||
DELETE FROM InventoryTransactions
|
||||
WHERE InventoryItemId IN (SELECT Id FROM InventoryItems WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' inventory transaction(s)';
|
||||
|
||||
-- 9. Inventory Items
|
||||
PRINT 'Deleting Inventory Items...';
|
||||
DELETE FROM InventoryItems WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' inventory item(s)';
|
||||
|
||||
-- 10. Maintenance Records
|
||||
PRINT 'Deleting Maintenance Records...';
|
||||
DELETE FROM MaintenanceRecords
|
||||
WHERE EquipmentId IN (SELECT Id FROM Equipment WHERE CompanyId = @CompanyId);
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' maintenance record(s)';
|
||||
|
||||
-- 11. Equipment
|
||||
PRINT 'Deleting Equipment...';
|
||||
DELETE FROM Equipment WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' equipment record(s)';
|
||||
|
||||
-- 12. Catalog Items (if table exists)
|
||||
IF OBJECT_ID('CatalogItems', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
PRINT 'Deleting Catalog Items...';
|
||||
DELETE FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' catalog item(s)';
|
||||
END
|
||||
|
||||
-- 13. Catalog Categories (if table exists)
|
||||
IF OBJECT_ID('CatalogCategories', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
PRINT 'Deleting Catalog Categories...';
|
||||
DELETE FROM CatalogCategories WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' catalog categor(y/ies)';
|
||||
END
|
||||
|
||||
-- 14. Suppliers (if table exists)
|
||||
IF OBJECT_ID('Suppliers', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
PRINT 'Deleting Suppliers...';
|
||||
DELETE FROM Suppliers WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' supplier(s)';
|
||||
END
|
||||
|
||||
-- 15. Customers
|
||||
PRINT 'Deleting Customers...';
|
||||
DELETE FROM Customers WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' customer(s)';
|
||||
|
||||
-- 16. Pricing Tiers
|
||||
PRINT 'Deleting Pricing Tiers...';
|
||||
DELETE FROM PricingTiers WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' pricing tier(s)';
|
||||
|
||||
-- 17. Company Operating Costs
|
||||
PRINT 'Deleting Company Operating Costs...';
|
||||
DELETE FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' operating cost record(s)';
|
||||
|
||||
-- 18. Company Preferences (if table exists)
|
||||
IF OBJECT_ID('CompanyPreferences', 'U') IS NOT NULL
|
||||
BEGIN
|
||||
PRINT 'Deleting Company Preferences...';
|
||||
DELETE FROM CompanyPreferences WHERE CompanyId = @CompanyId;
|
||||
SET @RowCount = @@ROWCOUNT;
|
||||
SET @TotalDeleted = @TotalDeleted + @RowCount;
|
||||
PRINT ' Deleted ' + CAST(@RowCount AS NVARCHAR(10)) + ' preference record(s)';
|
||||
END
|
||||
|
||||
-- =============================================
|
||||
-- PRESERVED DATA (NOT DELETED)
|
||||
-- =============================================
|
||||
PRINT '';
|
||||
PRINT 'PRESERVED DATA (not deleted):';
|
||||
PRINT ' - Company record';
|
||||
PRINT ' - Users associated with company';
|
||||
PRINT ' - Job Status Lookups';
|
||||
PRINT ' - Job Priority Lookups';
|
||||
PRINT ' - Quote Status Lookups';
|
||||
PRINT ' - Inventory Category Lookups';
|
||||
PRINT ' - Appointment Status Lookups (if exists)';
|
||||
PRINT ' - Appointment Type Lookups (if exists)';
|
||||
PRINT '';
|
||||
|
||||
-- Summary
|
||||
PRINT '=============================================';
|
||||
PRINT 'RESET SUMMARY:';
|
||||
PRINT ' Total records deleted: ' + CAST(@TotalDeleted AS NVARCHAR(10));
|
||||
PRINT ' Company preserved: ' + @CompanyName + ' (' + @CompanyCode + ')';
|
||||
PRINT '=============================================';
|
||||
PRINT '';
|
||||
|
||||
-- Commit the transaction
|
||||
PRINT 'Committing transaction...';
|
||||
COMMIT TRANSACTION;
|
||||
PRINT 'SUCCESS! Company data has been reset.';
|
||||
PRINT '';
|
||||
PRINT 'You can now re-seed the company data via:';
|
||||
PRINT ' Platform Management > Seed Data > Seed Data for ' + @CompanyName;
|
||||
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
-- Rollback on error
|
||||
PRINT '';
|
||||
PRINT 'ERROR! Rolling back transaction...';
|
||||
ROLLBACK TRANSACTION;
|
||||
|
||||
PRINT 'Error Message: ' + ERROR_MESSAGE();
|
||||
PRINT 'Error Line: ' + CAST(ERROR_LINE() AS NVARCHAR(10));
|
||||
|
||||
-- Re-throw the error
|
||||
THROW;
|
||||
END CATCH;
|
||||
@@ -0,0 +1,70 @@
|
||||
-- ============================================================================
|
||||
-- VERIFY COMPANY DATA - Check what records exist for a company
|
||||
-- ============================================================================
|
||||
|
||||
DECLARE @CompanyId INT = 1; -- Demo Company
|
||||
|
||||
PRINT '============================================================================';
|
||||
PRINT 'DATA VERIFICATION - Company ID: ' + CAST(@CompanyId AS NVARCHAR(10));
|
||||
PRINT '============================================================================';
|
||||
PRINT '';
|
||||
|
||||
-- Lookup Tables
|
||||
PRINT 'LOOKUP TABLES:';
|
||||
SELECT 'JobStatusLookup' AS TableName, COUNT(*) AS TotalRows, SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END) AS SoftDeleted
|
||||
FROM JobStatusLookups WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'JobPriorityLookup', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM JobPriorityLookups WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'QuoteStatusLookup', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM QuoteStatusLookups WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'InventoryCategoryLookup', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM InventoryCategoryLookups WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'AppointmentStatusLookup', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM AppointmentStatusLookups WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'AppointmentTypeLookup', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM AppointmentTypeLookups WHERE CompanyId = @CompanyId;
|
||||
|
||||
PRINT '';
|
||||
PRINT 'BASE ENTITIES:';
|
||||
SELECT 'Customers' AS TableName, COUNT(*) AS TotalRows, SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END) AS SoftDeleted
|
||||
FROM Customers WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'InventoryItems', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM InventoryItems WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'Equipment', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM Equipment WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'PricingTiers', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM PricingTiers WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'CompanyOperatingCosts', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM CompanyOperatingCosts WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'CatalogCategories', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM CatalogCategories WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'CatalogItems', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM CatalogItems WHERE CompanyId = @CompanyId;
|
||||
|
||||
PRINT '';
|
||||
PRINT 'TRANSACTIONAL DATA:';
|
||||
SELECT 'Quotes' AS TableName, COUNT(*) AS TotalRows, SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END) AS SoftDeleted
|
||||
FROM Quotes WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'Jobs', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM Jobs WHERE CompanyId = @CompanyId
|
||||
UNION ALL
|
||||
SELECT 'Appointments', COUNT(*), SUM(CASE WHEN IsDeleted = 1 THEN 1 ELSE 0 END)
|
||||
FROM Appointments WHERE CompanyId = @CompanyId;
|
||||
|
||||
PRINT '';
|
||||
PRINT '============================================================================';
|
||||
PRINT 'If TotalRows > 0, the hard delete did not work correctly!';
|
||||
PRINT 'If SoftDeleted > 0, there are soft-deleted records blocking seeding!';
|
||||
PRINT '============================================================================';
|
||||
@@ -0,0 +1,269 @@
|
||||
-- ============================================================
|
||||
-- Lookup Table Migration Verification Script
|
||||
-- ============================================================
|
||||
-- This script verifies that the enum-to-lookup migration was successful
|
||||
-- and that all data integrity has been preserved.
|
||||
--
|
||||
-- Run this against the PowderCoatingDb database
|
||||
-- ============================================================
|
||||
|
||||
PRINT '========================================';
|
||||
PRINT 'LOOKUP TABLE MIGRATION VERIFICATION';
|
||||
PRINT '========================================';
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 1: Verify Lookup Tables Exist
|
||||
-- ============================================================
|
||||
PRINT '1. CHECKING LOOKUP TABLES EXIST...';
|
||||
PRINT '';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'JobStatusLookups')
|
||||
PRINT ' ✓ JobStatusLookups table exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: JobStatusLookups table NOT found!';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'JobPriorityLookups')
|
||||
PRINT ' ✓ JobPriorityLookups table exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: JobPriorityLookups table NOT found!';
|
||||
|
||||
IF EXISTS (SELECT * FROM sys.tables WHERE name = 'QuoteStatusLookups')
|
||||
PRINT ' ✓ QuoteStatusLookups table exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: QuoteStatusLookups table NOT found!';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 2: Verify Lookup Data Counts
|
||||
-- ============================================================
|
||||
PRINT '2. CHECKING LOOKUP DATA COUNTS...';
|
||||
PRINT '';
|
||||
|
||||
DECLARE @JobStatusCount INT;
|
||||
DECLARE @JobPriorityCount INT;
|
||||
DECLARE @QuoteStatusCount INT;
|
||||
DECLARE @CompanyCount INT;
|
||||
|
||||
SELECT @CompanyCount = COUNT(*) FROM Companies WHERE IsDeleted = 0;
|
||||
SELECT @JobStatusCount = COUNT(*) FROM JobStatusLookups WHERE IsDeleted = 0;
|
||||
SELECT @JobPriorityCount = COUNT(*) FROM JobPriorityLookups WHERE IsDeleted = 0;
|
||||
SELECT @QuoteStatusCount = COUNT(*) FROM QuoteStatusLookups WHERE IsDeleted = 0;
|
||||
|
||||
PRINT ' Companies: ' + CAST(@CompanyCount AS VARCHAR(10));
|
||||
PRINT ' Job Statuses: ' + CAST(@JobStatusCount AS VARCHAR(10)) + ' (Expected: ' + CAST(@CompanyCount * 16 AS VARCHAR(10)) + ')';
|
||||
PRINT ' Job Priorities: ' + CAST(@JobPriorityCount AS VARCHAR(10)) + ' (Expected: ' + CAST(@CompanyCount * 5 AS VARCHAR(10)) + ')';
|
||||
PRINT ' Quote Statuses: ' + CAST(@QuoteStatusCount AS VARCHAR(10)) + ' (Expected: ' + CAST(@CompanyCount * 7 AS VARCHAR(10)) + ')';
|
||||
|
||||
IF @JobStatusCount = @CompanyCount * 16
|
||||
PRINT ' ✓ Job Status count correct'
|
||||
ELSE
|
||||
PRINT ' ⚠ Job Status count mismatch (may be normal if custom statuses added)';
|
||||
|
||||
IF @JobPriorityCount = @CompanyCount * 5
|
||||
PRINT ' ✓ Job Priority count correct'
|
||||
ELSE
|
||||
PRINT ' ⚠ Job Priority count mismatch (may be normal if custom priorities added)';
|
||||
|
||||
IF @QuoteStatusCount = @CompanyCount * 7
|
||||
PRINT ' ✓ Quote Status count correct'
|
||||
ELSE
|
||||
PRINT ' ⚠ Quote Status count mismatch (may be normal if custom statuses added)';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 3: Verify Foreign Key Relationships
|
||||
-- ============================================================
|
||||
PRINT '3. CHECKING FOREIGN KEY RELATIONSHIPS...';
|
||||
PRINT '';
|
||||
|
||||
-- Check Jobs table has FK to JobStatusLookup
|
||||
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE name = 'FK_Jobs_JobStatusLookups_JobStatusId')
|
||||
PRINT ' ✓ Jobs.JobStatusId FK exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: Jobs.JobStatusId FK NOT found!';
|
||||
|
||||
-- Check Jobs table has FK to JobPriorityLookup
|
||||
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE name = 'FK_Jobs_JobPriorityLookups_JobPriorityId')
|
||||
PRINT ' ✓ Jobs.JobPriorityId FK exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: Jobs.JobPriorityId FK NOT found!';
|
||||
|
||||
-- Check Quotes table has FK to QuoteStatusLookup
|
||||
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE name = 'FK_Quotes_QuoteStatusLookups_QuoteStatusId')
|
||||
PRINT ' ✓ Quotes.QuoteStatusId FK exists'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: Quotes.QuoteStatusId FK NOT found!';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 4: Verify Data Integrity - No Orphaned Records
|
||||
-- ============================================================
|
||||
PRINT '4. CHECKING DATA INTEGRITY...';
|
||||
PRINT '';
|
||||
|
||||
-- Check for jobs with NULL status
|
||||
DECLARE @JobsWithNullStatus INT;
|
||||
SELECT @JobsWithNullStatus = COUNT(*) FROM Jobs WHERE JobStatusId IS NULL;
|
||||
|
||||
IF @JobsWithNullStatus = 0
|
||||
PRINT ' ✓ All Jobs have valid JobStatusId'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@JobsWithNullStatus AS VARCHAR(10)) + ' Jobs have NULL JobStatusId!';
|
||||
|
||||
-- Check for jobs with NULL priority
|
||||
DECLARE @JobsWithNullPriority INT;
|
||||
SELECT @JobsWithNullPriority = COUNT(*) FROM Jobs WHERE JobPriorityId IS NULL;
|
||||
|
||||
IF @JobsWithNullPriority = 0
|
||||
PRINT ' ✓ All Jobs have valid JobPriorityId'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@JobsWithNullPriority AS VARCHAR(10)) + ' Jobs have NULL JobPriorityId!';
|
||||
|
||||
-- Check for quotes with NULL status
|
||||
DECLARE @QuotesWithNullStatus INT;
|
||||
SELECT @QuotesWithNullStatus = COUNT(*) FROM Quotes WHERE QuoteStatusId IS NULL;
|
||||
|
||||
IF @QuotesWithNullStatus = 0
|
||||
PRINT ' ✓ All Quotes have valid QuoteStatusId'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@QuotesWithNullStatus AS VARCHAR(10)) + ' Quotes have NULL QuoteStatusId!';
|
||||
|
||||
-- Check for orphaned job status references
|
||||
DECLARE @OrphanedJobStatuses INT;
|
||||
SELECT @OrphanedJobStatuses = COUNT(*)
|
||||
FROM Jobs j
|
||||
LEFT JOIN JobStatusLookups s ON j.JobStatusId = s.Id
|
||||
WHERE j.JobStatusId IS NOT NULL AND s.Id IS NULL;
|
||||
|
||||
IF @OrphanedJobStatuses = 0
|
||||
PRINT ' ✓ No orphaned JobStatus references'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@OrphanedJobStatuses AS VARCHAR(10)) + ' Jobs reference non-existent statuses!';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 5: Verify Unique Constraints
|
||||
-- ============================================================
|
||||
PRINT '5. CHECKING UNIQUE CONSTRAINTS...';
|
||||
PRINT '';
|
||||
|
||||
-- Check for duplicate status codes per company
|
||||
DECLARE @DuplicateJobStatuses INT;
|
||||
SELECT @DuplicateJobStatuses = COUNT(*)
|
||||
FROM (
|
||||
SELECT CompanyId, StatusCode, COUNT(*) as cnt
|
||||
FROM JobStatusLookups
|
||||
WHERE IsDeleted = 0
|
||||
GROUP BY CompanyId, StatusCode
|
||||
HAVING COUNT(*) > 1
|
||||
) AS dupes;
|
||||
|
||||
IF @DuplicateJobStatuses = 0
|
||||
PRINT ' ✓ No duplicate JobStatus codes per company'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@DuplicateJobStatuses AS VARCHAR(10)) + ' duplicate JobStatus codes found!';
|
||||
|
||||
-- Check for duplicate priority codes per company
|
||||
DECLARE @DuplicateJobPriorities INT;
|
||||
SELECT @DuplicateJobPriorities = COUNT(*)
|
||||
FROM (
|
||||
SELECT CompanyId, PriorityCode, COUNT(*) as cnt
|
||||
FROM JobPriorityLookups
|
||||
WHERE IsDeleted = 0
|
||||
GROUP BY CompanyId, PriorityCode
|
||||
HAVING COUNT(*) > 1
|
||||
) AS dupes;
|
||||
|
||||
IF @DuplicateJobPriorities = 0
|
||||
PRINT ' ✓ No duplicate JobPriority codes per company'
|
||||
ELSE
|
||||
PRINT ' ✗ ERROR: ' + CAST(@DuplicateJobPriorities AS VARCHAR(10)) + ' duplicate JobPriority codes found!';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 6: Verify Business Logic Flags
|
||||
-- ============================================================
|
||||
PRINT '6. CHECKING BUSINESS LOGIC FLAGS...';
|
||||
PRINT '';
|
||||
|
||||
-- Check that each company has exactly one "Approved" quote status
|
||||
DECLARE @CompaniesWithMultipleApproved INT;
|
||||
SELECT @CompaniesWithMultipleApproved = COUNT(*)
|
||||
FROM (
|
||||
SELECT CompanyId, COUNT(*) as cnt
|
||||
FROM QuoteStatusLookups
|
||||
WHERE IsApprovedStatus = 1 AND IsDeleted = 0
|
||||
GROUP BY CompanyId
|
||||
HAVING COUNT(*) > 1
|
||||
) AS dupes;
|
||||
|
||||
IF @CompaniesWithMultipleApproved = 0
|
||||
PRINT ' ✓ Each company has exactly one Approved quote status'
|
||||
ELSE
|
||||
PRINT ' ⚠ WARNING: ' + CAST(@CompaniesWithMultipleApproved AS VARCHAR(10)) + ' companies have multiple Approved statuses!';
|
||||
|
||||
-- Check that system-defined statuses exist
|
||||
DECLARE @SystemJobStatuses INT;
|
||||
SELECT @SystemJobStatuses = COUNT(DISTINCT StatusCode)
|
||||
FROM JobStatusLookups
|
||||
WHERE IsSystemDefined = 1 AND StatusCode IN ('PENDING', 'COMPLETED', 'CANCELLED');
|
||||
|
||||
IF @SystemJobStatuses = 3
|
||||
PRINT ' ✓ System-defined job statuses exist (PENDING, COMPLETED, CANCELLED)'
|
||||
ELSE
|
||||
PRINT ' ⚠ WARNING: Missing system-defined job statuses';
|
||||
|
||||
PRINT '';
|
||||
|
||||
-- ============================================================
|
||||
-- SECTION 7: Sample Data Display
|
||||
-- ============================================================
|
||||
PRINT '7. SAMPLE LOOKUP DATA (First Company)...';
|
||||
PRINT '';
|
||||
|
||||
-- Get first company ID
|
||||
DECLARE @FirstCompanyId INT;
|
||||
SELECT TOP 1 @FirstCompanyId = Id FROM Companies WHERE IsDeleted = 0 ORDER BY Id;
|
||||
|
||||
PRINT ' Job Statuses for Company ' + CAST(@FirstCompanyId AS VARCHAR(10)) + ':';
|
||||
SELECT
|
||||
' ' + CAST(DisplayOrder AS VARCHAR(3)) + '. ' +
|
||||
DisplayName + ' (' + StatusCode + ') - ' +
|
||||
ColorClass +
|
||||
CASE WHEN IsSystemDefined = 1 THEN ' [SYSTEM]' ELSE '' END AS StatusInfo
|
||||
FROM JobStatusLookups
|
||||
WHERE CompanyId = @FirstCompanyId AND IsDeleted = 0
|
||||
ORDER BY DisplayOrder;
|
||||
|
||||
PRINT '';
|
||||
PRINT ' Job Priorities for Company ' + CAST(@FirstCompanyId AS VARCHAR(10)) + ':';
|
||||
SELECT
|
||||
' ' + CAST(DisplayOrder AS VARCHAR(3)) + '. ' +
|
||||
DisplayName + ' (' + PriorityCode + ') - ' +
|
||||
ColorClass AS PriorityInfo
|
||||
FROM JobPriorityLookups
|
||||
WHERE CompanyId = @FirstCompanyId AND IsDeleted = 0
|
||||
ORDER BY DisplayOrder;
|
||||
|
||||
PRINT '';
|
||||
PRINT ' Quote Statuses for Company ' + CAST(@FirstCompanyId AS VARCHAR(10)) + ':';
|
||||
SELECT
|
||||
' ' + CAST(DisplayOrder AS VARCHAR(3)) + '. ' +
|
||||
DisplayName + ' (' + StatusCode + ') - ' +
|
||||
ColorClass +
|
||||
CASE WHEN IsApprovedStatus = 1 THEN ' [APPROVED]' ELSE '' END +
|
||||
CASE WHEN IsConvertedStatus = 1 THEN ' [CONVERTED]' ELSE '' END AS StatusInfo
|
||||
FROM QuoteStatusLookups
|
||||
WHERE CompanyId = @FirstCompanyId AND IsDeleted = 0
|
||||
ORDER BY DisplayOrder;
|
||||
|
||||
PRINT '';
|
||||
PRINT '========================================';
|
||||
PRINT 'VERIFICATION COMPLETE';
|
||||
PRINT '========================================';
|
||||
@@ -0,0 +1,36 @@
|
||||
@echo off
|
||||
echo ========================================
|
||||
echo Apply Database Migrations
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
cd ..\src\PowderCoating.Web
|
||||
|
||||
echo WARNING: This will modify your database!
|
||||
echo Database: PowderCoatingDb on .\SQLEXPRESS
|
||||
echo.
|
||||
set /p confirm="Continue? (Y/N): "
|
||||
if /i not "%confirm%"=="Y" (
|
||||
echo Cancelled.
|
||||
pause
|
||||
exit /b
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Applying migrations...
|
||||
dotnet ef database update --project ..\PowderCoating.Infrastructure
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
echo ========================================
|
||||
echo SUCCESS: Migrations applied
|
||||
echo ========================================
|
||||
) else (
|
||||
echo.
|
||||
echo ========================================
|
||||
echo ERROR: Migration failed
|
||||
echo ========================================
|
||||
)
|
||||
|
||||
echo.
|
||||
pause
|
||||
@@ -0,0 +1,23 @@
|
||||
@echo off
|
||||
echo ========================================
|
||||
echo Checking Database Migrations
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
cd ..\src\PowderCoating.Web
|
||||
|
||||
echo Current migrations in project:
|
||||
echo ----------------------------------------
|
||||
dotnet ef migrations list --project ..\PowderCoating.Infrastructure
|
||||
echo.
|
||||
|
||||
echo Database connection info:
|
||||
echo ----------------------------------------
|
||||
dotnet ef dbcontext info --project ..\PowderCoating.Infrastructure
|
||||
echo.
|
||||
|
||||
echo.
|
||||
echo To apply pending migrations, run:
|
||||
echo .\scripts\apply-migrations.bat
|
||||
echo.
|
||||
pause
|
||||
@@ -0,0 +1,135 @@
|
||||
# Deployment Script for Development Server
|
||||
# Run this script to deploy code changes and apply database migrations
|
||||
|
||||
param(
|
||||
[switch]$SkipBuild,
|
||||
[switch]$SkipMigrations,
|
||||
[switch]$WhatIf
|
||||
)
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Powder Coating App - Dev Deployment" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
||||
$webProject = Join-Path $projectRoot "src\PowderCoating.Web"
|
||||
$infraProject = Join-Path $projectRoot "src\PowderCoating.Infrastructure"
|
||||
|
||||
# Step 1: Check current location
|
||||
Write-Host "[1/5] Checking environment..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path $webProject)) {
|
||||
Write-Host "ERROR: Web project not found at $webProject" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host "✓ Project structure verified" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# Step 2: Build the solution
|
||||
if (-not $SkipBuild) {
|
||||
Write-Host "[2/5] Building solution..." -ForegroundColor Yellow
|
||||
if ($WhatIf) {
|
||||
Write-Host "WHAT-IF: Would run: dotnet build --configuration Release" -ForegroundColor Gray
|
||||
} else {
|
||||
Push-Location $projectRoot
|
||||
dotnet build --configuration Release
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "ERROR: Build failed" -ForegroundColor Red
|
||||
Pop-Location
|
||||
exit 1
|
||||
}
|
||||
Pop-Location
|
||||
Write-Host "✓ Build successful" -ForegroundColor Green
|
||||
}
|
||||
} else {
|
||||
Write-Host "[2/5] Skipping build (--SkipBuild specified)" -ForegroundColor Gray
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# Step 3: Check for pending migrations
|
||||
Write-Host "[3/5] Checking database migrations..." -ForegroundColor Yellow
|
||||
if ($WhatIf) {
|
||||
Write-Host "WHAT-IF: Would check migrations with: dotnet ef migrations list" -ForegroundColor Gray
|
||||
} else {
|
||||
Push-Location $webProject
|
||||
|
||||
# List all migrations
|
||||
Write-Host "Listing all migrations:" -ForegroundColor Cyan
|
||||
dotnet ef migrations list --project $infraProject --no-build
|
||||
|
||||
Pop-Location
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# Step 4: Apply migrations
|
||||
if (-not $SkipMigrations) {
|
||||
Write-Host "[4/5] Applying database migrations..." -ForegroundColor Yellow
|
||||
|
||||
if ($WhatIf) {
|
||||
Write-Host "WHAT-IF: Would run: dotnet ef database update" -ForegroundColor Gray
|
||||
} else {
|
||||
$confirm = Read-Host "Apply migrations to database? (y/N)"
|
||||
if ($confirm -eq 'y' -or $confirm -eq 'Y') {
|
||||
Push-Location $webProject
|
||||
|
||||
dotnet ef database update --project $infraProject --no-build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "ERROR: Migration failed" -ForegroundColor Red
|
||||
Pop-Location
|
||||
exit 1
|
||||
}
|
||||
|
||||
Pop-Location
|
||||
Write-Host "✓ Migrations applied successfully" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "⊘ Migrations skipped by user" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Host "[4/5] Skipping migrations (--SkipMigrations specified)" -ForegroundColor Gray
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# Step 5: Summary
|
||||
Write-Host "[5/5] Deployment Summary" -ForegroundColor Yellow
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Gray
|
||||
Write-Host "Recent Changes Deployed:" -ForegroundColor Cyan
|
||||
Write-Host " • Security Headers Added (CSP, HSTS, X-Frame-Options)" -ForegroundColor White
|
||||
Write-Host " • Password Policy Strengthened (12 chars, special chars required)" -ForegroundColor White
|
||||
Write-Host " • CORS Policy Restricted (config-based whitelist)" -ForegroundColor White
|
||||
Write-Host " • Path Traversal Protection Enhanced" -ForegroundColor White
|
||||
Write-Host " • IDOR Protection on Profile Photos" -ForegroundColor White
|
||||
Write-Host " • Session Cookies Hardened (Secure, SameSite=Strict)" -ForegroundColor White
|
||||
Write-Host " • JWT Expiration Reduced (15 minutes)" -ForegroundColor White
|
||||
Write-Host " • File Upload Names Use GUIDs" -ForegroundColor White
|
||||
Write-Host " • Input Validation (SecurityHelper class)" -ForegroundColor White
|
||||
Write-Host " • AppConstants.Policies Updated (CompanyAdminOnly added)" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "Configuration Files:" -ForegroundColor Cyan
|
||||
Write-Host " ✓ appsettings.Development.json - Dev configuration active" -ForegroundColor Green
|
||||
Write-Host " ✓ appsettings.json - Production placeholders only" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Security Documentation:" -ForegroundColor Cyan
|
||||
Write-Host " → SECURITY_FIXES_SUMMARY.md - Complete fix list" -ForegroundColor White
|
||||
Write-Host " → DEPLOYMENT_CONFIGURATION.md - Production deployment guide" -ForegroundColor White
|
||||
Write-Host ""
|
||||
|
||||
if (-not $WhatIf) {
|
||||
Write-Host "✓ DEPLOYMENT COMPLETE" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "⊘ WHAT-IF MODE - No changes made" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Next Steps:" -ForegroundColor Cyan
|
||||
Write-Host " 1. Test the application: https://localhost:58461" -ForegroundColor White
|
||||
Write-Host " 2. Verify Data Lookups tab loads without CSP errors" -ForegroundColor White
|
||||
Write-Host " 3. Test password policy (12 chars, special char required)" -ForegroundColor White
|
||||
Write-Host " 4. Review SECURITY_FIXES_SUMMARY.md for all changes" -ForegroundColor White
|
||||
Write-Host ""
|
||||
Write-Host "Rollback (if needed):" -ForegroundColor Yellow
|
||||
Write-Host " git log --oneline # Find commit hash before deployment" -ForegroundColor Gray
|
||||
Write-Host " git reset --hard <commit-hash>" -ForegroundColor Gray
|
||||
Write-Host " dotnet ef database update <migration-name> --project src/PowderCoating.Infrastructure" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,60 @@
|
||||
# Fix Company Admin Permissions
|
||||
# Run this from the project root directory
|
||||
|
||||
$connectionString = "Server=.\SQLEXPRESS;Database=PowderCoatingDb;Trusted_Connection=true;MultipleActiveResultSets=true;TrustServerCertificate=true"
|
||||
|
||||
Write-Host "Fixing Company Admin permissions..." -ForegroundColor Yellow
|
||||
|
||||
$sqlQuery = @"
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
CanManageJobs = 1,
|
||||
CanManageInventory = 1,
|
||||
CanManageCustomers = 1,
|
||||
CanCreateQuotes = 1,
|
||||
CanApproveQuotes = 1,
|
||||
CanManageCalendar = 1,
|
||||
CanViewCalendar = 1,
|
||||
CanManageProducts = 1,
|
||||
CanViewProducts = 1,
|
||||
CanManageEquipment = 1,
|
||||
CanManageSuppliers = 1,
|
||||
CanManageMaintenance = 1
|
||||
WHERE CompanyRole = 'CompanyAdmin';
|
||||
|
||||
SELECT
|
||||
Email,
|
||||
FirstName + ' ' + LastName AS Name,
|
||||
CompanyRole,
|
||||
CanManageCalendar,
|
||||
CanManageProducts
|
||||
FROM AspNetUsers
|
||||
WHERE CompanyRole = 'CompanyAdmin';
|
||||
"@
|
||||
|
||||
try {
|
||||
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
|
||||
$connection.Open()
|
||||
|
||||
$command = $connection.CreateCommand()
|
||||
$command.CommandText = $sqlQuery
|
||||
|
||||
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $command
|
||||
$dataset = New-Object System.Data.DataSet
|
||||
$adapter.Fill($dataset) | Out-Null
|
||||
|
||||
Write-Host "`nCompany Admins Updated:" -ForegroundColor Green
|
||||
$dataset.Tables[0] | Format-Table -AutoSize
|
||||
|
||||
$connection.Close()
|
||||
Write-Host "`nPermissions updated successfully!" -ForegroundColor Green
|
||||
Write-Host "Company Admins need to log out and log back in for changes to take effect." -ForegroundColor Yellow
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error: $_" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
if ($connection.State -eq 'Open') {
|
||||
$connection.Close()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
SET NOCOUNT ON;
|
||||
|
||||
-- Reset password hash for superadmin@powdercoating.com / SuperAdmin123!
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
PasswordHash = 'AQAAAAEAAYagAAAAEOl+bhp4wG2EHbbmOqWK7xV09ve7JBpy85sH4ZPufbO36VoVDm/TwvphXES6t+DteQ==',
|
||||
SecurityStamp = '3a23c797-6ae3-4d36-9bd0-5977bb51411a'
|
||||
WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM';
|
||||
|
||||
PRINT 'Updated: superadmin@powdercoating.com';
|
||||
|
||||
-- Reset password hash for admin@powdercoating.com / Admin123!
|
||||
UPDATE AspNetUsers
|
||||
SET
|
||||
PasswordHash = 'AQAAAAEAAYagAAAAEDRofgA5DOrEQ6MI/4qiifCcdqnWXBtrWhsqWDucXsB2QIvTrGaAAXQDXuZsQgnj3g==',
|
||||
SecurityStamp = '27fa18d3-dd94-46fc-bfe5-bb00950d531b'
|
||||
WHERE NormalizedEmail = 'ADMIN@POWDERCOATING.COM';
|
||||
|
||||
PRINT 'Updated: admin@powdercoating.com';
|
||||
|
||||
-- Verify both users exist and have non-null hashes
|
||||
SELECT Email, LEN(PasswordHash) AS HashLength, IsActive
|
||||
FROM AspNetUsers
|
||||
WHERE NormalizedEmail IN ('SUPERADMIN@POWDERCOATING.COM', 'ADMIN@POWDERCOATING.COM');
|
||||
@@ -0,0 +1,134 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates a full idempotent EF Core migration SQL script and drops it into Y:\pcc\deployment.
|
||||
|
||||
.DESCRIPTION
|
||||
Runs `dotnet ef migrations script --idempotent` against the PowderCoating database and writes
|
||||
the output to Y:\pcc\deployment\migrations_YYYYMMDD_HHmmss.sql. The script is safe to run on
|
||||
any database state: EF wraps each migration in an IF NOT EXISTS check against
|
||||
__EFMigrationsHistory, so already-applied migrations are skipped.
|
||||
|
||||
.NOTES
|
||||
Run from any directory — paths are resolved relative to this script's location.
|
||||
Requires: .NET 8 SDK, dotnet-ef tool (dotnet tool install --global dotnet-ef)
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$OutputDir = "Y:\pcc\Deployments"
|
||||
)
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$displayStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$outputFile = "migrations_$timestamp.sql"
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Split-Path -Parent $scriptDir
|
||||
$webProject = Join-Path $repoRoot "src\PowderCoating.Web"
|
||||
$infraProject = Join-Path $repoRoot "src\PowderCoating.Infrastructure"
|
||||
$outputPath = Join-Path $OutputDir $outputFile
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== EF Migration Script Generator ===" -ForegroundColor Cyan
|
||||
Write-Host " Web project : $webProject"
|
||||
Write-Host " Output : $outputPath"
|
||||
Write-Host ""
|
||||
|
||||
# ── Preflight checks ──────────────────────────────────────────────────────────
|
||||
|
||||
if (!(Test-Path $webProject)) {
|
||||
Write-Host "ERROR: Web project not found at: $webProject" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (!(Test-Path $OutputDir)) {
|
||||
Write-Host "Creating output directory: $OutputDir" -ForegroundColor Yellow
|
||||
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$efVersion = dotnet ef --version 2>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "ERROR: dotnet-ef tool not found. Install with: dotnet tool install --global dotnet-ef" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host "dotnet-ef : $($efVersion | Select-Object -First 1)" -ForegroundColor DarkGray
|
||||
|
||||
# ── Generate script ───────────────────────────────────────────────────────────
|
||||
|
||||
Write-Host "Generating idempotent migration script..." -ForegroundColor Cyan
|
||||
|
||||
$savedLocation = Get-Location
|
||||
Set-Location $webProject
|
||||
|
||||
# Capture stderr to a temp file so EF's structured log noise (INF/WRN lines and
|
||||
# the "Ignoring added logger provider" design-time message) don't trigger
|
||||
# PowerShell 5.1's NativeCommandError handling.
|
||||
$tempErr = [System.IO.Path]::GetTempFileName()
|
||||
try {
|
||||
# Temporarily silence $ErrorActionPreference so PS 5.1 doesn't surface EF's
|
||||
# design-time stderr lines (structured log noise) as NativeCommandError objects.
|
||||
$prevEAP = $ErrorActionPreference
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
$null = dotnet ef migrations script `
|
||||
--idempotent `
|
||||
--context ApplicationDbContext `
|
||||
--project $infraProject `
|
||||
--output $outputPath `
|
||||
--no-build 2>$tempErr
|
||||
$exitCode = $LASTEXITCODE
|
||||
$ErrorActionPreference = $prevEAP
|
||||
|
||||
# Surface any genuine errors — filter out EF's routine log/diagnostic lines
|
||||
if (Test-Path $tempErr) {
|
||||
Get-Content $tempErr |
|
||||
Where-Object {
|
||||
$_ -notmatch "^\s*$" -and
|
||||
$_ -notmatch "\[.*\]\s+(INF|WRN)" -and
|
||||
$_ -notmatch "Ignoring added logger provider" -and
|
||||
$_ -notmatch "NativeCommandError"
|
||||
} |
|
||||
ForEach-Object { Write-Host " $_" -ForegroundColor DarkYellow }
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Set-Location $savedLocation
|
||||
if (Test-Path $tempErr) { Remove-Item $tempErr -Force }
|
||||
}
|
||||
|
||||
if ($exitCode -ne 0) {
|
||||
Write-Host "ERROR: dotnet ef migrations script failed (exit code $exitCode)" -ForegroundColor Red
|
||||
exit $exitCode
|
||||
}
|
||||
|
||||
if (!(Test-Path $outputPath)) {
|
||||
Write-Host "ERROR: Output file was not created: $outputPath" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── Stamp header ──────────────────────────────────────────────────────────────
|
||||
|
||||
$header = @"
|
||||
-- =============================================================================
|
||||
-- Idempotent EF Core migration script
|
||||
-- Project : PowderCoating.Web
|
||||
-- Generated: $displayStamp
|
||||
-- Safe to run on any database state (checks __EFMigrationsHistory per migration)
|
||||
-- =============================================================================
|
||||
|
||||
"@
|
||||
|
||||
$existing = Get-Content $outputPath -Raw
|
||||
Set-Content -Path $outputPath -Value ($header + $existing) -Encoding UTF8
|
||||
|
||||
# ── Report ────────────────────────────────────────────────────────────────────
|
||||
|
||||
$size = [math]::Round((Get-Item $outputPath).Length / 1KB, 1)
|
||||
$lineCount = (Get-Content $outputPath).Count
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done." -ForegroundColor Green
|
||||
Write-Host " File : $outputPath"
|
||||
Write-Host " Size : $size KB ($lineCount lines)"
|
||||
Write-Host " Stamp : $displayStamp"
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,134 @@
|
||||
#Requires -Version 5.1
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generates a full idempotent EF Core migration SQL script and drops it into Y:\pcc\deployment.
|
||||
|
||||
.DESCRIPTION
|
||||
Runs `dotnet ef migrations script --idempotent` against the PowderCoating database and writes
|
||||
the output to Y:\pcc\deployment\migrations_YYYYMMDD_HHmmss.sql. The script is safe to run on
|
||||
any database state: EF wraps each migration in an IF NOT EXISTS check against
|
||||
__EFMigrationsHistory, so already-applied migrations are skipped.
|
||||
|
||||
.NOTES
|
||||
Run from any directory — paths are resolved relative to this script's location.
|
||||
Requires: .NET 8 SDK, dotnet-ef tool (dotnet tool install --global dotnet-ef)
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[string]$OutputDir = "Y:\pcc\deployment"
|
||||
)
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$displayStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$outputFile = "migrations_$timestamp.sql"
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Split-Path -Parent $scriptDir
|
||||
$webProject = Join-Path $repoRoot "src\PowderCoating.Web"
|
||||
$infraProject = Join-Path $repoRoot "src\PowderCoating.Infrastructure"
|
||||
$outputPath = Join-Path $OutputDir $outputFile
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "=== EF Migration Script Generator ===" -ForegroundColor Cyan
|
||||
Write-Host " Web project : $webProject"
|
||||
Write-Host " Output : $outputPath"
|
||||
Write-Host ""
|
||||
|
||||
# ── Preflight checks ──────────────────────────────────────────────────────────
|
||||
|
||||
if (!(Test-Path $webProject)) {
|
||||
Write-Host "ERROR: Web project not found at: $webProject" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (!(Test-Path $OutputDir)) {
|
||||
Write-Host "Creating output directory: $OutputDir" -ForegroundColor Yellow
|
||||
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$efVersion = dotnet ef --version 2>$null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "ERROR: dotnet-ef tool not found. Install with: dotnet tool install --global dotnet-ef" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
Write-Host "dotnet-ef : $($efVersion | Select-Object -First 1)" -ForegroundColor DarkGray
|
||||
|
||||
# ── Generate script ───────────────────────────────────────────────────────────
|
||||
|
||||
Write-Host "Generating idempotent migration script..." -ForegroundColor Cyan
|
||||
|
||||
$savedLocation = Get-Location
|
||||
Set-Location $webProject
|
||||
|
||||
# Capture stderr to a temp file so EF's structured log noise (INF/WRN lines and
|
||||
# the "Ignoring added logger provider" design-time message) don't trigger
|
||||
# PowerShell 5.1's NativeCommandError handling.
|
||||
$tempErr = [System.IO.Path]::GetTempFileName()
|
||||
try {
|
||||
# Temporarily silence $ErrorActionPreference so PS 5.1 doesn't surface EF's
|
||||
# design-time stderr lines (structured log noise) as NativeCommandError objects.
|
||||
$prevEAP = $ErrorActionPreference
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
$null = dotnet ef migrations script `
|
||||
--idempotent `
|
||||
--context ApplicationDbContext `
|
||||
--project $infraProject `
|
||||
--output $outputPath `
|
||||
--no-build 2>$tempErr
|
||||
$exitCode = $LASTEXITCODE
|
||||
$ErrorActionPreference = $prevEAP
|
||||
|
||||
# Surface any genuine errors — filter out EF's routine log/diagnostic lines
|
||||
if (Test-Path $tempErr) {
|
||||
Get-Content $tempErr |
|
||||
Where-Object {
|
||||
$_ -notmatch "^\s*$" -and
|
||||
$_ -notmatch "\[.*\]\s+(INF|WRN)" -and
|
||||
$_ -notmatch "Ignoring added logger provider" -and
|
||||
$_ -notmatch "NativeCommandError"
|
||||
} |
|
||||
ForEach-Object { Write-Host " $_" -ForegroundColor DarkYellow }
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Set-Location $savedLocation
|
||||
if (Test-Path $tempErr) { Remove-Item $tempErr -Force }
|
||||
}
|
||||
|
||||
if ($exitCode -ne 0) {
|
||||
Write-Host "ERROR: dotnet ef migrations script failed (exit code $exitCode)" -ForegroundColor Red
|
||||
exit $exitCode
|
||||
}
|
||||
|
||||
if (!(Test-Path $outputPath)) {
|
||||
Write-Host "ERROR: Output file was not created: $outputPath" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── Stamp header ──────────────────────────────────────────────────────────────
|
||||
|
||||
$header = @"
|
||||
-- =============================================================================
|
||||
-- Idempotent EF Core migration script
|
||||
-- Project : PowderCoating.Web
|
||||
-- Generated: $displayStamp
|
||||
-- Safe to run on any database state (checks __EFMigrationsHistory per migration)
|
||||
-- =============================================================================
|
||||
|
||||
"@
|
||||
|
||||
$existing = Get-Content $outputPath -Raw
|
||||
Set-Content -Path $outputPath -Value ($header + $existing) -Encoding UTF8
|
||||
|
||||
# ── Report ────────────────────────────────────────────────────────────────────
|
||||
|
||||
$size = [math]::Round((Get-Item $outputPath).Length / 1KB, 1)
|
||||
$lineCount = (Get-Content $outputPath).Count
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done." -ForegroundColor Green
|
||||
Write-Host " File : $outputPath"
|
||||
Write-Host " Size : $size KB ($lineCount lines)"
|
||||
Write-Host " Stamp : $displayStamp"
|
||||
Write-Host ""
|
||||
@@ -0,0 +1,180 @@
|
||||
-- =============================================================================
|
||||
-- PowderCoating App - Initial Seed SQL
|
||||
-- Generated: 2026-02-28 04:10:44 UTC
|
||||
--
|
||||
-- Run this against your Azure SQL database when startup seeding fails.
|
||||
-- All inserts are guarded with IF NOT EXISTS and are safe to re-run.
|
||||
-- =============================================================================
|
||||
|
||||
SET NOCOUNT ON;
|
||||
GO
|
||||
|
||||
-- 1. Default Company
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM Companies WHERE CompanyCode = 'DEMO')
|
||||
BEGIN
|
||||
SET IDENTITY_INSERT Companies ON;
|
||||
|
||||
INSERT INTO Companies (
|
||||
Id, CompanyId, CompanyName, CompanyCode,
|
||||
PrimaryContactName, PrimaryContactEmail, Phone,
|
||||
Address, City, State, ZipCode,
|
||||
IsActive, SubscriptionPlan, SubscriptionStatus,
|
||||
SubscriptionStartDate, TimeZone,
|
||||
CreatedAt, IsDeleted
|
||||
) VALUES (
|
||||
1, 1, 'Demo Company', 'DEMO',
|
||||
'Admin User', 'admin@demo.com', '(555) 123-4567',
|
||||
'123 Demo Street', 'Demo City', 'CA', '90210',
|
||||
1, 2, 0,
|
||||
'2026-02-28 04:10:44', 'America/New_York',
|
||||
'2026-02-28 04:10:44', 0
|
||||
);
|
||||
|
||||
SET IDENTITY_INSERT Companies OFF;
|
||||
PRINT 'Company inserted.';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'Company already exists - skipped.';
|
||||
GO
|
||||
|
||||
-- 2. Roles
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'SUPERADMIN')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('208a163b-c303-4083-856e-4060bc5a923d', 'SuperAdmin', 'SUPERADMIN', NEWID());
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'ADMINISTRATOR')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('bf92ab6a-0d14-42d5-b19d-b5f1a5da8df0', 'Administrator', 'ADMINISTRATOR', NEWID());
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'MANAGER')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('c12dcd9d-408a-4d38-ba55-ef2a3f88ccb5', 'Manager', 'MANAGER', NEWID());
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'EMPLOYEE')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('1ab093d9-f1fb-439e-9194-5fe76be041dc', 'Employee', 'EMPLOYEE', NEWID());
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'SHOPFLOOR')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('13ba213d-3118-4d93-ac5d-d0674369f409', 'ShopFloor', 'SHOPFLOOR', NEWID());
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetRoles WHERE NormalizedName = 'READONLY')
|
||||
INSERT INTO AspNetRoles (Id, Name, NormalizedName, ConcurrencyStamp)
|
||||
VALUES ('1181bde5-0034-439b-8351-c7821faf6c28', 'ReadOnly', 'READONLY', NEWID());
|
||||
|
||||
PRINT 'Roles inserted.';
|
||||
GO
|
||||
|
||||
-- 3. SuperAdmin Users
|
||||
|
||||
-- User 1: superadmin@powdercoating.com / SuperAdmin123!
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetUsers WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM')
|
||||
BEGIN
|
||||
INSERT INTO AspNetUsers (
|
||||
Id, UserName, NormalizedUserName, Email, NormalizedEmail,
|
||||
EmailConfirmed, PasswordHash, SecurityStamp, ConcurrencyStamp,
|
||||
PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled,
|
||||
LockoutEnd, LockoutEnabled, AccessFailedCount,
|
||||
CompanyId, CompanyRole,
|
||||
FirstName, LastName, EmployeeNumber,
|
||||
HireDate, IsActive,
|
||||
Department, Position, HourlyRate,
|
||||
Theme, DateFormat, TimeZone, SidebarColor,
|
||||
CanViewShopFloor, CanManageJobs, CanManageInventory, CanManageCustomers,
|
||||
CanCreateQuotes, CanApproveQuotes, CanManageCalendar, CanViewCalendar,
|
||||
CanManageProducts, CanViewProducts, CanManageEquipment,
|
||||
CanManageSuppliers, CanManageMaintenance,
|
||||
CreatedAt
|
||||
) VALUES (
|
||||
'd0a94944-60ab-49dd-aff5-3f9ae6a351bc',
|
||||
'superadmin@powdercoating.com', 'SUPERADMIN@POWDERCOATING.COM',
|
||||
'superadmin@powdercoating.com', 'SUPERADMIN@POWDERCOATING.COM',
|
||||
1, 'AQAAAAEAAYagAAAAEFSYOADLy+Inki1mJqCFcIUyEsv584NI8kyE5rorPABmuphaRajTEFbXK/VoCGQrJA==', '3b604497-51cd-401f-92cd-eda6c7c60ff2', '7f532e9b-89ee-4a6c-ab12-ab16b69f45a9',
|
||||
NULL, 0, 0, NULL, 1, 0,
|
||||
1, NULL,
|
||||
'Super', 'Admin', 'SA-001',
|
||||
'2026-02-28 04:10:44', 1,
|
||||
'Platform', 'Super Administrator', 0,
|
||||
'light', 'MM/dd/yyyy', 'America/New_York', 'ocean',
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1,
|
||||
1, 1,
|
||||
'2026-02-28 04:10:44'
|
||||
);
|
||||
PRINT 'User superadmin@powdercoating.com inserted.';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'User superadmin@powdercoating.com already exists - skipped.';
|
||||
GO
|
||||
|
||||
-- User 2: admin@powdercoating.com / Admin123!
|
||||
IF NOT EXISTS (SELECT 1 FROM AspNetUsers WHERE NormalizedEmail = 'ADMIN@POWDERCOATING.COM')
|
||||
BEGIN
|
||||
INSERT INTO AspNetUsers (
|
||||
Id, UserName, NormalizedUserName, Email, NormalizedEmail,
|
||||
EmailConfirmed, PasswordHash, SecurityStamp, ConcurrencyStamp,
|
||||
PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled,
|
||||
LockoutEnd, LockoutEnabled, AccessFailedCount,
|
||||
CompanyId, CompanyRole,
|
||||
FirstName, LastName, EmployeeNumber,
|
||||
HireDate, IsActive,
|
||||
Department, Position, HourlyRate,
|
||||
Theme, DateFormat, TimeZone, SidebarColor,
|
||||
CanViewShopFloor, CanManageJobs, CanManageInventory, CanManageCustomers,
|
||||
CanCreateQuotes, CanApproveQuotes, CanManageCalendar, CanViewCalendar,
|
||||
CanManageProducts, CanViewProducts, CanManageEquipment,
|
||||
CanManageSuppliers, CanManageMaintenance,
|
||||
CreatedAt
|
||||
) VALUES (
|
||||
'6b0f44fe-8547-41ac-81b7-6451a49077c3',
|
||||
'admin@powdercoating.com', 'ADMIN@POWDERCOATING.COM',
|
||||
'admin@powdercoating.com', 'ADMIN@POWDERCOATING.COM',
|
||||
1, 'AQAAAAEAAYagAAAAENnCYaYw3wsQ5ZW8u7tHBs6eEtAcFKJ+35B93yDq0TMiP524/UcWO8qEoTcfyiAJnw==', '7d2b9944-345a-4819-af02-2f7f56e28120', 'b4b820d5-3168-465a-b107-2a0e99e3eab1',
|
||||
NULL, 0, 0, NULL, 1, 0,
|
||||
1, NULL,
|
||||
'Admin', 'User', 'SA-002',
|
||||
'2026-02-28 04:10:44', 1,
|
||||
'Platform', 'Platform Administrator', 0,
|
||||
'light', 'MM/dd/yyyy', 'America/New_York', 'ocean',
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1,
|
||||
1, 1,
|
||||
'2026-02-28 04:10:44'
|
||||
);
|
||||
PRINT 'User admin@powdercoating.com inserted.';
|
||||
END
|
||||
ELSE
|
||||
PRINT 'User admin@powdercoating.com already exists - skipped.';
|
||||
GO
|
||||
|
||||
-- 4. Assign SuperAdmin role to both users
|
||||
|
||||
DECLARE @roleId NVARCHAR(450) = (SELECT Id FROM AspNetRoles WHERE NormalizedName = 'SUPERADMIN');
|
||||
DECLARE @u1Id NVARCHAR(450) = (SELECT Id FROM AspNetUsers WHERE NormalizedEmail = 'SUPERADMIN@POWDERCOATING.COM');
|
||||
DECLARE @u2Id NVARCHAR(450) = (SELECT Id FROM AspNetUsers WHERE NormalizedEmail = 'ADMIN@POWDERCOATING.COM');
|
||||
|
||||
IF @u1Id IS NOT NULL AND @roleId IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM AspNetUserRoles WHERE UserId = @u1Id AND RoleId = @roleId)
|
||||
BEGIN
|
||||
INSERT INTO AspNetUserRoles (UserId, RoleId) VALUES (@u1Id, @roleId);
|
||||
PRINT 'Role assigned to superadmin@powdercoating.com.';
|
||||
END
|
||||
|
||||
IF @u2Id IS NOT NULL AND @roleId IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM AspNetUserRoles WHERE UserId = @u2Id AND RoleId = @roleId)
|
||||
BEGIN
|
||||
INSERT INTO AspNetUserRoles (UserId, RoleId) VALUES (@u2Id, @roleId);
|
||||
PRINT 'Role assigned to admin@powdercoating.com.';
|
||||
END
|
||||
GO
|
||||
|
||||
PRINT '================================================';
|
||||
PRINT 'Seed complete.';
|
||||
PRINT 'Login 1: superadmin@powdercoating.com / SuperAdmin123!';
|
||||
PRINT 'Login 2: admin@powdercoating.com / Admin123!';
|
||||
PRINT '================================================';
|
||||
GO
|
||||
Reference in New Issue
Block a user