diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index c8a9ea0..b369143 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -171,7 +171,8 @@
"PowerShell(Select-String *)",
"Bash(Select-Object -First 20)",
"PowerShell(node -e \"require\\('fs'\\).existsSync\\(require\\('path'\\).join\\(process.cwd\\(\\), 'node_modules', 'sharp'\\)\\) ? console.log\\('sharp ok'\\) : console.log\\('no sharp'\\)\")",
- "WebFetch(domain:www.powdercoatinglogix.com)"
+ "WebFetch(domain:www.powdercoatinglogix.com)",
+ "PowerShell($bytes = [System.IO.File]::ReadAllBytes\\('src/PowderCoating.Web/Views/Jobs/Details.cshtml'\\); $text = [System.Text.Encoding]::UTF8.GetString\\($bytes\\); $idx = $text.IndexOf\\('hasPowderData'\\); $snippet = $text.Substring\\($idx - 20, 250\\); [System.Text.Encoding]::Unicode.GetBytes\\($snippet\\) | Format-Hex | Select-Object -First 30)"
]
}
}
diff --git a/TODO.txt b/TODO.txt
index fd1a85d..0db5bd0 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,5 +1,11 @@
Shop Management App TO DO List
==============================
+-Inventory Lookup not always finding price for Columbia Coatings
+-Logging powder usage and choosing a job doesn't record properly in the activity section of the powder itself
+-Need to allow deleting of powder usage entries, or at least editing in case of a goof up
+-Still random weird characters on a bunch of pages. Intake button for example on the jobs screen shows: Intake ✓
+
+
-Google review request email after a job
-Check my ChatGPT chat about surface area for a few solid ideas for the system
-Fix up approve/decline messages between customer and user on quote approval feature
diff --git a/TODO.txt.bak b/TODO.txt.bak
index fd36605..da255c5 100644
--- a/TODO.txt.bak
+++ b/TODO.txt.bak
@@ -1,9 +1,9 @@
Shop Management App TO DO List
==============================
--Lookup not working 100% correct. If I type columbia as the manufacturer and a color name....it's finding blackmamba from prismatic incorrectly.
--Lookup Modal not showing ALL matches. Maybe make scrollable
--Pickup cure information from TDS Sheet if not found by AI Search
--ON AI Photo Quote page, when the AI info comes back we should scroll the modal window down so it's visible. It's not clear that new info has been added to the modal for all customers
+-Inventory Lookup not always finding price for Columbia Coatings
+-Logging powder usage and choosing a job doesn't record properly in the activity section of the powder itself
+-Need to allow deleting of powder usage entries, or at least editing in case of a goof up
+
-Google review request email after a job
-Check my ChatGPT chat about surface area for a few solid ideas for the system
-Fix up approve/decline messages between customer and user on quote approval feature
@@ -181,6 +181,10 @@ AI Agent item where we upload a picture and it will calculate the approximate sq
-When scanning inventory QR Code, there is no cancel button
-Bug: When scanning Inventory QR Code, if not logged in...it takes you to the dashboard after login, not our inventory scanning screen
-Add SMS capabilities
+-Lookup not working 100% correct. If I type columbia as the manufacturer and a color name....it's finding blackmamba from prismatic incorrectly.
+-Lookup Modal not showing ALL matches. Maybe make scrollable
+-Pickup cure information from TDS Sheet if not found by AI Search
+-ON AI Photo Quote page, when the AI info comes back we should scroll the modal window down so it's visible. It's not clear that new info has been added to the modal for all customers
Ideas Removed
=======================
diff --git a/docs/Sales Tax Report.txt b/docs/Sales Tax Report.txt
new file mode 100644
index 0000000..969c075
--- /dev/null
+++ b/docs/Sales Tax Report.txt
@@ -0,0 +1,47 @@
+ I looked through the reporting code, and I do not see a dedicated sales tax report.
+
+ What exists today:
+
+ - The report menu has Financial Summary, AR Aging, Balance Sheet, and Sales & Income, but no sales-tax-specific report card in /Y:/PCC/
+ PowderCoatingApp/src/PowderCoating.Web/Views/Reports/Landing.cshtml:147 and /Y:/PCC/PowderCoatingApp/src/PowderCoating.Web/Views/Reports/
+ Landing.cshtml:211.
+ - The reporting service only exposes four finance reports in /Y:/PCC/PowderCoatingApp/src/PowderCoating.Application/Interfaces/
+ IFinancialReportService.cs:13 through /Y:/PCC/PowderCoatingApp/src/PowderCoating.Application/Interfaces/IFinancialReportService.cs:22.
+ - Sales & Income does surface tax, but only as a total and per-invoice column in /Y:/PCC/PowderCoatingApp/src/PowderCoating.Infrastructure/
+ Services/FinancialReportService.cs:406, /Y:/PCC/PowderCoatingApp/src/PowderCoating.Infrastructure/Services/FinancialReportService.cs:419,
+ and /Y:/PCC/PowderCoatingApp/src/PowderCoating.Web/Views/Reports/SalesAndIncome.cshtml:268.
+ - The good news is the underlying data is already there: invoices store TaxAmount and SalesTaxAccountId in /Y:/PCC/PowderCoatingApp/src/
+ PowderCoating.Core/Entities/Invoice.cs:23 and /Y:/PCC/PowderCoatingApp/src/PowderCoating.Core/Entities/Invoice.cs:55, and the chart of
+ accounts seeds 2200 Sales Tax Payable in /Y:/PCC/PowderCoatingApp/src/PowderCoating.Infrastructure/Services/SeedDataService.Accounts.cs:59.
+
+ Plan I’d recommend:
+
+ 1. Add a new Sales Tax report under Reports > Finance with date range filters, matching the existing report pattern in /Y:/PCC/PowderCoatingApp/
+ src/PowderCoating.Web/Controllers/ReportsController.cs:993.
+ 2. Build a SalesTaxReportDto plus GetSalesTaxReportAsync(...) in the reporting interface/service.
+ 3. Phase 1 report contents:
+
+ - Total taxable sales
+ - Total non-taxed sales
+ - Total sales tax billed
+ - Breakdown by sales-tax liability account
+ - Breakdown by month
+ - Invoice detail grid: invoice date, invoice #, customer, subtotal, tax %, tax amount, total, amount paid, balance due, status, tax account
+
+ 4. Add PDF export, and probably CSV too, since this is the kind of report people hand to accountants.
+ 5. Put a report card on the Reports landing page and gate it the same way as the other accounting reports.
+
+ Two important caveats before we build it:
+
+ - The biggest decision is whether this should be invoice-basis or cash-basis. The current model naturally supports invoice-basis first.
+ - Refunds and credit memos do not appear to store a separate tax adjustment value in /Y:/PCC/PowderCoatingApp/src/PowderCoating.Core/Entities/
+ Refund.cs:1 and /Y:/PCC/PowderCoatingApp/src/PowderCoating.Core/Entities/CreditMemo.cs:1, so phase 1 would be best framed as “sales tax billed
+ on invoices,” with net tax adjustments as a phase 2 enhancement.
+
+ My recommendation is:
+
+ - Build phase 1 as an invoice-basis Sales Tax Liability report
+ - Label zero-tax invoices as non-taxed sales
+ - Add cash-basis and refund/credit tax adjustments later if you need filing-grade accuracy for more complex cases
+
+ If you want, I can turn this plan into the actual report next.
\ No newline at end of file
diff --git a/scripts/migrate-prod.sql b/scripts/migrate-prod.sql
new file mode 100644
index 0000000..2a4a116
--- /dev/null
+++ b/scripts/migrate-prod.sql
@@ -0,0 +1,7122 @@
+IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
+BEGIN
+ CREATE TABLE [__EFMigrationsHistory] (
+ [MigrationId] nvarchar(150) NOT NULL,
+ [ProductVersion] nvarchar(32) NOT NULL,
+ CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
+ );
+END;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260316155002_Baseline'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-16T15:49:58.7377851Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260316155002_Baseline'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-16T15:49:58.7377856Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260316155002_Baseline'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-16T15:49:58.7377858Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260316155002_Baseline'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260316155002_Baseline', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317121938_AddAiContextProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [AiContextProfile] nvarchar(2000) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317121938_AddAiContextProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T12:19:34.4894690Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317121938_AddAiContextProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T12:19:34.4894696Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317121938_AddAiContextProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T12:19:34.4894698Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317121938_AddAiContextProfile'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260317121938_AddAiContextProfile', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ DECLARE @var0 sysname;
+ SELECT @var0 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[QuoteItems]') AND [c].[name] = N'Quantity');
+ IF @var0 IS NOT NULL EXEC(N'ALTER TABLE [QuoteItems] DROP CONSTRAINT [' + @var0 + '];');
+ ALTER TABLE [QuoteItems] ALTER COLUMN [Quantity] decimal(18,2) NOT NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ DECLARE @var1 sysname;
+ SELECT @var1 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[JobItems]') AND [c].[name] = N'Quantity');
+ IF @var1 IS NOT NULL EXEC(N'ALTER TABLE [JobItems] DROP CONSTRAINT [' + @var1 + '];');
+ ALTER TABLE [JobItems] ALTER COLUMN [Quantity] decimal(18,2) NOT NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T20:59:24.2463737Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T20:59:24.2463746Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-17T20:59:24.2463748Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260317205927_FixLaborItemQuantityDecimal'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260317205927_FixLaborItemQuantityDecimal', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ CREATE TABLE [JobTimeEntries] (
+ [Id] int NOT NULL IDENTITY,
+ [JobId] int NOT NULL,
+ [ShopWorkerId] int NOT NULL,
+ [WorkDate] datetime2 NOT NULL,
+ [HoursWorked] decimal(18,2) NOT NULL,
+ [Stage] nvarchar(max) NULL,
+ [Notes] nvarchar(max) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_JobTimeEntries] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_JobTimeEntries_Jobs_JobId] FOREIGN KEY ([JobId]) REFERENCES [Jobs] ([Id]) ON DELETE CASCADE,
+ CONSTRAINT [FK_JobTimeEntries_ShopWorkers_ShopWorkerId] FOREIGN KEY ([ShopWorkerId]) REFERENCES [ShopWorkers] ([Id]) ON DELETE CASCADE
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T12:48:44.7462691Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T12:48:44.7462697Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T12:48:44.7462699Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ CREATE INDEX [IX_JobTimeEntries_JobId] ON [JobTimeEntries] ([JobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ CREATE INDEX [IX_JobTimeEntries_ShopWorkerId] ON [JobTimeEntries] ([ShopWorkerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318124847_AddJobTimeEntries'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260318124847_AddJobTimeEntries', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [ShopAccessCode] uniqueidentifier NOT NULL DEFAULT (NEWID());
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:14:57.2203832Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:14:57.2203838Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:14:57.2203839Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_Jobs_ShopAccessCode] ON [Jobs] ([ShopAccessCode]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318131500_AddJobShopAccessCode'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260318131500_AddJobShopAccessCode', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ CREATE TABLE [ShopWorkerRoleCosts] (
+ [Id] int NOT NULL IDENTITY,
+ [Role] int NOT NULL,
+ [HourlyRate] decimal(18,2) NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_ShopWorkerRoleCosts] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:28:54.6854802Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:28:54.6854849Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:28:54.6854851Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_ShopWorkerRoleCosts_CompanyId_Role] ON [ShopWorkerRoleCosts] ([CompanyId], [Role]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318132857_AddShopWorkerRoleCosts'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260318132857_AddShopWorkerRoleCosts', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IsReworkJob] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [OriginalJobId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ CREATE TABLE [ReworkRecords] (
+ [Id] int NOT NULL IDENTITY,
+ [JobId] int NOT NULL,
+ [JobItemId] int NULL,
+ [ReworkJobId] int NULL,
+ [ReworkType] int NOT NULL,
+ [Reason] int NOT NULL,
+ [DefectDescription] nvarchar(max) NOT NULL,
+ [DiscoveredBy] int NOT NULL,
+ [DiscoveredDate] datetime2 NOT NULL,
+ [ReportedByName] nvarchar(max) NULL,
+ [EstimatedReworkCost] decimal(18,2) NOT NULL,
+ [ActualReworkCost] decimal(18,2) NOT NULL,
+ [IsBillableToCustomer] bit NOT NULL,
+ [BillingNotes] nvarchar(max) NULL,
+ [Status] int NOT NULL,
+ [Resolution] int NULL,
+ [ResolvedDate] datetime2 NULL,
+ [ResolutionNotes] nvarchar(max) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_ReworkRecords] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_ReworkRecords_JobItems_JobItemId] FOREIGN KEY ([JobItemId]) REFERENCES [JobItems] ([Id]),
+ CONSTRAINT [FK_ReworkRecords_Jobs_JobId] FOREIGN KEY ([JobId]) REFERENCES [Jobs] ([Id]) ON DELETE NO ACTION,
+ CONSTRAINT [FK_ReworkRecords_Jobs_ReworkJobId] FOREIGN KEY ([ReworkJobId]) REFERENCES [Jobs] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:42:32.9092998Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:42:32.9093003Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T13:42:32.9093005Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ CREATE INDEX [IX_Jobs_OriginalJobId] ON [Jobs] ([OriginalJobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ CREATE INDEX [IX_ReworkRecords_JobId] ON [ReworkRecords] ([JobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ CREATE INDEX [IX_ReworkRecords_JobItemId] ON [ReworkRecords] ([JobItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ CREATE INDEX [IX_ReworkRecords_ReworkJobId] ON [ReworkRecords] ([ReworkJobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD CONSTRAINT [FK_Jobs_Jobs_OriginalJobId] FOREIGN KEY ([OriginalJobId]) REFERENCES [Jobs] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318134236_AddReworkTracking'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260318134236_AddReworkTracking', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [CreditApplied] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ ALTER TABLE [Customers] ADD [CreditBalance] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE TABLE [CreditMemos] (
+ [Id] int NOT NULL IDENTITY,
+ [MemoNumber] nvarchar(450) NOT NULL,
+ [CustomerId] int NOT NULL,
+ [OriginalInvoiceId] int NULL,
+ [ReworkRecordId] int NULL,
+ [Amount] decimal(18,2) NOT NULL,
+ [AmountApplied] decimal(18,2) NOT NULL,
+ [IssueDate] datetime2 NOT NULL,
+ [ExpiryDate] datetime2 NULL,
+ [Reason] nvarchar(max) NOT NULL,
+ [Notes] nvarchar(max) NULL,
+ [Status] int NOT NULL,
+ [IssuedById] nvarchar(450) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_CreditMemos] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_CreditMemos_AspNetUsers_IssuedById] FOREIGN KEY ([IssuedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_CreditMemos_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE NO ACTION,
+ CONSTRAINT [FK_CreditMemos_Invoices_OriginalInvoiceId] FOREIGN KEY ([OriginalInvoiceId]) REFERENCES [Invoices] ([Id]),
+ CONSTRAINT [FK_CreditMemos_ReworkRecords_ReworkRecordId] FOREIGN KEY ([ReworkRecordId]) REFERENCES [ReworkRecords] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE TABLE [Refunds] (
+ [Id] int NOT NULL IDENTITY,
+ [InvoiceId] int NOT NULL,
+ [PaymentId] int NULL,
+ [Amount] decimal(18,2) NOT NULL,
+ [RefundDate] datetime2 NOT NULL,
+ [RefundMethod] int NOT NULL,
+ [Reason] nvarchar(max) NOT NULL,
+ [Reference] nvarchar(max) NULL,
+ [Notes] nvarchar(max) NULL,
+ [Status] int NOT NULL,
+ [IssuedDate] datetime2 NULL,
+ [IssuedById] nvarchar(450) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_Refunds] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_Refunds_AspNetUsers_IssuedById] FOREIGN KEY ([IssuedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_Refunds_Invoices_InvoiceId] FOREIGN KEY ([InvoiceId]) REFERENCES [Invoices] ([Id]) ON DELETE NO ACTION,
+ CONSTRAINT [FK_Refunds_Payments_PaymentId] FOREIGN KEY ([PaymentId]) REFERENCES [Payments] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE TABLE [CreditMemoApplications] (
+ [Id] int NOT NULL IDENTITY,
+ [CreditMemoId] int NOT NULL,
+ [InvoiceId] int NOT NULL,
+ [AmountApplied] decimal(18,2) NOT NULL,
+ [AppliedDate] datetime2 NOT NULL,
+ [AppliedById] nvarchar(450) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_CreditMemoApplications] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_CreditMemoApplications_AspNetUsers_AppliedById] FOREIGN KEY ([AppliedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_CreditMemoApplications_CreditMemos_CreditMemoId] FOREIGN KEY ([CreditMemoId]) REFERENCES [CreditMemos] ([Id]) ON DELETE NO ACTION,
+ CONSTRAINT [FK_CreditMemoApplications_Invoices_InvoiceId] FOREIGN KEY ([InvoiceId]) REFERENCES [Invoices] ([Id]) ON DELETE NO ACTION
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T22:26:44.9349567Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T22:26:44.9349573Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-18T22:26:44.9349575Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemoApplications_AppliedById] ON [CreditMemoApplications] ([AppliedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemoApplications_CreditMemoId] ON [CreditMemoApplications] ([CreditMemoId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemoApplications_InvoiceId] ON [CreditMemoApplications] ([InvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_CreditMemos_CompanyId_MemoNumber] ON [CreditMemos] ([CompanyId], [MemoNumber]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemos_CustomerId] ON [CreditMemos] ([CustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemos_IssuedById] ON [CreditMemos] ([IssuedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemos_OriginalInvoiceId] ON [CreditMemos] ([OriginalInvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_CreditMemos_ReworkRecordId] ON [CreditMemos] ([ReworkRecordId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_Refunds_InvoiceId] ON [Refunds] ([InvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_Refunds_IssuedById] ON [Refunds] ([IssuedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ CREATE INDEX [IX_Refunds_PaymentId] ON [Refunds] ([PaymentId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260318222648_AddRefundsAndCreditMemos'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260318222648_AddRefundsAndCreditMemos', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE TABLE [JobTemplates] (
+ [Id] int NOT NULL IDENTITY,
+ [Name] nvarchar(max) NOT NULL,
+ [Description] nvarchar(max) NULL,
+ [CustomerId] int NULL,
+ [SpecialInstructions] nvarchar(max) NULL,
+ [IsActive] bit NOT NULL,
+ [UsageCount] int NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_JobTemplates] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_JobTemplates_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE TABLE [JobTemplateItems] (
+ [Id] int NOT NULL IDENTITY,
+ [JobTemplateId] int NOT NULL,
+ [Description] nvarchar(max) NOT NULL,
+ [Quantity] decimal(18,2) NOT NULL,
+ [SurfaceAreaSqFt] decimal(18,2) NOT NULL,
+ [CatalogItemId] int NULL,
+ [IsGenericItem] bit NOT NULL,
+ [IsLaborItem] bit NOT NULL,
+ [ManualUnitPrice] decimal(18,2) NULL,
+ [RequiresSandblasting] bit NOT NULL,
+ [RequiresMasking] bit NOT NULL,
+ [IncludePrepCost] bit NOT NULL,
+ [EstimatedMinutes] int NOT NULL,
+ [Complexity] nvarchar(max) NULL,
+ [Notes] nvarchar(max) NULL,
+ [DisplayOrder] int NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_JobTemplateItems] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_JobTemplateItems_CatalogItems_CatalogItemId] FOREIGN KEY ([CatalogItemId]) REFERENCES [CatalogItems] ([Id]),
+ CONSTRAINT [FK_JobTemplateItems_JobTemplates_JobTemplateId] FOREIGN KEY ([JobTemplateId]) REFERENCES [JobTemplates] ([Id]) ON DELETE CASCADE
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE TABLE [JobTemplateItemCoats] (
+ [Id] int NOT NULL IDENTITY,
+ [JobTemplateItemId] int NOT NULL,
+ [CoatName] nvarchar(max) NOT NULL,
+ [Sequence] int NOT NULL,
+ [InventoryItemId] int NULL,
+ [ColorName] nvarchar(max) NULL,
+ [VendorId] int NULL,
+ [ColorCode] nvarchar(max) NULL,
+ [Finish] nvarchar(max) NULL,
+ [CoverageSqFtPerLb] decimal(18,2) NOT NULL,
+ [TransferEfficiency] decimal(18,2) NOT NULL,
+ [PowderCostPerLb] decimal(18,2) NULL,
+ [Notes] nvarchar(max) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_JobTemplateItemCoats] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_JobTemplateItemCoats_InventoryItems_InventoryItemId] FOREIGN KEY ([InventoryItemId]) REFERENCES [InventoryItems] ([Id]),
+ CONSTRAINT [FK_JobTemplateItemCoats_JobTemplateItems_JobTemplateItemId] FOREIGN KEY ([JobTemplateItemId]) REFERENCES [JobTemplateItems] ([Id]) ON DELETE CASCADE,
+ CONSTRAINT [FK_JobTemplateItemCoats_Vendors_VendorId] FOREIGN KEY ([VendorId]) REFERENCES [Vendors] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE TABLE [JobTemplateItemPrepServices] (
+ [Id] int NOT NULL IDENTITY,
+ [JobTemplateItemId] int NOT NULL,
+ [PrepServiceId] int NOT NULL,
+ [EstimatedMinutes] int NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_JobTemplateItemPrepServices] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_JobTemplateItemPrepServices_JobTemplateItems_JobTemplateItemId] FOREIGN KEY ([JobTemplateItemId]) REFERENCES [JobTemplateItems] ([Id]) ON DELETE CASCADE,
+ CONSTRAINT [FK_JobTemplateItemPrepServices_PrepServices_PrepServiceId] FOREIGN KEY ([PrepServiceId]) REFERENCES [PrepServices] ([Id]) ON DELETE CASCADE
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T02:38:23.4195291Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T02:38:23.4195296Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T02:38:23.4195298Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItemCoats_InventoryItemId] ON [JobTemplateItemCoats] ([InventoryItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItemCoats_JobTemplateItemId] ON [JobTemplateItemCoats] ([JobTemplateItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItemCoats_VendorId] ON [JobTemplateItemCoats] ([VendorId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItemPrepServices_JobTemplateItemId] ON [JobTemplateItemPrepServices] ([JobTemplateItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItemPrepServices_PrepServiceId] ON [JobTemplateItemPrepServices] ([PrepServiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItems_CatalogItemId] ON [JobTemplateItems] ([CatalogItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplateItems_JobTemplateId] ON [JobTemplateItems] ([JobTemplateId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ CREATE INDEX [IX_JobTemplates_CustomerId] ON [JobTemplates] ([CustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319023827_AddJobTemplates'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260319023827_AddJobTemplates', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [GiftCertificateRedeemed] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE TABLE [GiftCertificates] (
+ [Id] int NOT NULL IDENTITY,
+ [CertificateCode] nvarchar(max) NOT NULL,
+ [OriginalAmount] decimal(18,2) NOT NULL,
+ [RedeemedAmount] decimal(18,2) NOT NULL,
+ [RecipientCustomerId] int NULL,
+ [RecipientName] nvarchar(max) NULL,
+ [RecipientEmail] nvarchar(max) NULL,
+ [IssuedReason] int NOT NULL,
+ [PurchasePrice] decimal(18,2) NULL,
+ [PurchasingCustomerId] int NULL,
+ [Status] int NOT NULL,
+ [IssueDate] datetime2 NOT NULL,
+ [ExpiryDate] datetime2 NULL,
+ [Notes] nvarchar(max) NULL,
+ [IssuedById] nvarchar(450) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_GiftCertificates] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_GiftCertificates_AspNetUsers_IssuedById] FOREIGN KEY ([IssuedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_GiftCertificates_Customers_PurchasingCustomerId] FOREIGN KEY ([PurchasingCustomerId]) REFERENCES [Customers] ([Id]),
+ CONSTRAINT [FK_GiftCertificates_Customers_RecipientCustomerId] FOREIGN KEY ([RecipientCustomerId]) REFERENCES [Customers] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE TABLE [GiftCertificateRedemptions] (
+ [Id] int NOT NULL IDENTITY,
+ [GiftCertificateId] int NOT NULL,
+ [InvoiceId] int NOT NULL,
+ [AmountRedeemed] decimal(18,2) NOT NULL,
+ [RedeemedDate] datetime2 NOT NULL,
+ [RedeemedById] nvarchar(450) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_GiftCertificateRedemptions] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_GiftCertificateRedemptions_AspNetUsers_RedeemedById] FOREIGN KEY ([RedeemedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_GiftCertificateRedemptions_GiftCertificates_GiftCertificateId] FOREIGN KEY ([GiftCertificateId]) REFERENCES [GiftCertificates] ([Id]) ON DELETE CASCADE,
+ CONSTRAINT [FK_GiftCertificateRedemptions_Invoices_InvoiceId] FOREIGN KEY ([InvoiceId]) REFERENCES [Invoices] ([Id]) ON DELETE CASCADE
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T15:45:03.1454465Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T15:45:03.1454472Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-19T15:45:03.1454474Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificateRedemptions_GiftCertificateId] ON [GiftCertificateRedemptions] ([GiftCertificateId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificateRedemptions_InvoiceId] ON [GiftCertificateRedemptions] ([InvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificateRedemptions_RedeemedById] ON [GiftCertificateRedemptions] ([RedeemedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificates_IssuedById] ON [GiftCertificates] ([IssuedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificates_PurchasingCustomerId] ON [GiftCertificates] ([PurchasingCustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ CREATE INDEX [IX_GiftCertificates_RecipientCustomerId] ON [GiftCertificates] ([RecipientCustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260319154506_AddGiftCertificates'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260319154506_AddGiftCertificates', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ ALTER TABLE [Refunds] ADD [CreditMemoId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:24:47.3611509Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:24:47.3611518Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:24:47.3611521Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ CREATE INDEX [IX_Refunds_CreditMemoId] ON [Refunds] ([CreditMemoId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ ALTER TABLE [Refunds] ADD CONSTRAINT [FK_Refunds_CreditMemos_CreditMemoId] FOREIGN KEY ([CreditMemoId]) REFERENCES [CreditMemos] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320002450_AddRefundStoreCreditLink'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260320002450_AddRefundStoreCreditLink', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320005106_AddQuoteItemIsAiItem'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [IsAiItem] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320005106_AddQuoteItemIsAiItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:51:03.2423766Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320005106_AddQuoteItemIsAiItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:51:03.2423772Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320005106_AddQuoteItemIsAiItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T00:51:03.2423774Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320005106_AddQuoteItemIsAiItem'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260320005106_AddQuoteItemIsAiItem', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [ItemsSubtotal] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [OvenBatchCost] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [OverheadAmount] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [OverheadPercent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [ProfitMargin] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [ProfitPercent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [ShopSuppliesAmount] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [ShopSuppliesPercent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T01:10:54.1468159Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T01:10:54.1468166Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T01:10:54.1468176Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320011057_AddQuotePricingSnapshot'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260320011057_AddQuotePricingSnapshot', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowOnlinePayments] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [DepositPercent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [RequiresDeposit] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [OnlineAmountPaid] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [OnlinePaymentStatus] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [OnlineSurchargeCollected] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [PaymentLinkExpiresAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [PaymentLinkToken] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [StripePaymentIntentId] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [OnlinePaymentSurchargeType] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [OnlinePaymentSurchargeValue] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [OnlineSurchargeAcknowledged] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [StripeAccountId] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [StripeConnectStatus] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T23:15:05.6886302Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T23:15:05.6886308Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-20T23:15:05.6886310Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260320231509_AddStripeConnectAndOnlinePayments'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260320231509_AddStripeConnectAndOnlinePayments', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [MaxQuotePhotos] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [MaxQuotePhotosOverride] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-26T23:04:35.1353265Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-26T23:04:35.1353273Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-26T23:04:35.1353275Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260326230438_AddQuotePhotoSubscriptionLimits'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260326230438_AddQuotePhotoSubscriptionLimits', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260328133627_AddJobPhotoIsAiAnalysisPhoto'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-28T13:36:24.1548411Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260328133627_AddJobPhotoIsAiAnalysisPhoto'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-28T13:36:24.1548419Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260328133627_AddJobPhotoIsAiAnalysisPhoto'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-28T13:36:24.1548421Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260328133627_AddJobPhotoIsAiAnalysisPhoto'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260328133627_AddJobPhotoIsAiAnalysisPhoto', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [DiscountReason] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [DiscountType] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [DiscountValue] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IsRushJob] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ ALTER TABLE [JobPhotos] ADD [IsAiAnalysisPhoto] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:32:56.7368710Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:32:56.7368717Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:32:56.7368718Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329003300_AddJobDiscountRushFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260329003300_AddJobDiscountRushFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE TABLE [Deposits] (
+ [Id] int NOT NULL IDENTITY,
+ [ReceiptNumber] nvarchar(max) NOT NULL,
+ [CustomerId] int NOT NULL,
+ [JobId] int NULL,
+ [QuoteId] int NULL,
+ [Amount] decimal(18,2) NOT NULL,
+ [PaymentMethod] int NOT NULL,
+ [ReceivedDate] datetime2 NOT NULL,
+ [Reference] nvarchar(max) NULL,
+ [Notes] nvarchar(max) NULL,
+ [RecordedById] nvarchar(450) NULL,
+ [AppliedToInvoiceId] int NULL,
+ [AppliedDate] datetime2 NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_Deposits] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_Deposits_AspNetUsers_RecordedById] FOREIGN KEY ([RecordedById]) REFERENCES [AspNetUsers] ([Id]),
+ CONSTRAINT [FK_Deposits_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE CASCADE,
+ CONSTRAINT [FK_Deposits_Invoices_AppliedToInvoiceId] FOREIGN KEY ([AppliedToInvoiceId]) REFERENCES [Invoices] ([Id]) ON DELETE SET NULL,
+ CONSTRAINT [FK_Deposits_Jobs_JobId] FOREIGN KEY ([JobId]) REFERENCES [Jobs] ([Id]),
+ CONSTRAINT [FK_Deposits_Quotes_QuoteId] FOREIGN KEY ([QuoteId]) REFERENCES [Quotes] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:58:35.7576949Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:58:35.7576955Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T00:58:35.7576957Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE INDEX [IX_Deposits_AppliedToInvoiceId] ON [Deposits] ([AppliedToInvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE INDEX [IX_Deposits_CustomerId] ON [Deposits] ([CustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE INDEX [IX_Deposits_JobId] ON [Deposits] ([JobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE INDEX [IX_Deposits_QuoteId] ON [Deposits] ([QuoteId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ CREATE INDEX [IX_Deposits_RecordedById] ON [Deposits] ([RecordedById]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329005838_AddDeposits'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260329005838_AddDeposits', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ DROP INDEX [IX_Invoices_CompanyId_JobId] ON [Invoices];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ DROP INDEX [IX_Invoices_JobId] ON [Invoices];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ DECLARE @var2 sysname;
+ SELECT @var2 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[Invoices]') AND [c].[name] = N'JobId');
+ IF @var2 IS NOT NULL EXEC(N'ALTER TABLE [Invoices] DROP CONSTRAINT [' + @var2 + '];');
+ ALTER TABLE [Invoices] ALTER COLUMN [JobId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [CatalogItemId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [CatalogItems] ADD [InventoryItemId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [CatalogItems] ADD [IsMerchandise] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [CatalogCategories] ADD [IsMerchandise] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T13:47:49.4176542Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T13:47:49.4176549Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T13:47:49.4176551Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ EXEC(N'CREATE UNIQUE INDEX [IX_Invoices_CompanyId_JobId] ON [Invoices] ([CompanyId], [JobId]) WHERE [JobId] IS NOT NULL');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ EXEC(N'CREATE UNIQUE INDEX [IX_Invoices_JobId] ON [Invoices] ([JobId]) WHERE [JobId] IS NOT NULL');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ CREATE INDEX [IX_InvoiceItems_CatalogItemId] ON [InvoiceItems] ([CatalogItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ CREATE INDEX [IX_CatalogItems_InventoryItemId] ON [CatalogItems] ([InventoryItemId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [CatalogItems] ADD CONSTRAINT [FK_CatalogItems_InventoryItems_InventoryItemId] FOREIGN KEY ([InventoryItemId]) REFERENCES [InventoryItems] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD CONSTRAINT [FK_InvoiceItems_CatalogItems_CatalogItemId] FOREIGN KEY ([CatalogItemId]) REFERENCES [CatalogItems] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329134753_AddMerchandise'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260329134753_AddMerchandise', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [GcExpiryDate] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [GcRecipientEmail] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [GcRecipientName] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [GeneratedGiftCertificateId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD [IsGiftCertificate] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [GiftCertificates] ADD [SourceInvoiceItemId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T14:11:34.2305437Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T14:11:34.2305443Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-29T14:11:34.2305445Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ CREATE INDEX [IX_InvoiceItems_GeneratedGiftCertificateId] ON [InvoiceItems] ([GeneratedGiftCertificateId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ ALTER TABLE [InvoiceItems] ADD CONSTRAINT [FK_InvoiceItems_GiftCertificates_GeneratedGiftCertificateId] FOREIGN KEY ([GeneratedGiftCertificateId]) REFERENCES [GiftCertificates] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260329141137_AddGiftCertificateInvoiceItems'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260329141137_AddGiftCertificateInvoiceItems', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [IsSalesItem] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [Sku] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ ALTER TABLE [JobItems] ADD [IsSalesItem] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ ALTER TABLE [JobItems] ADD [Sku] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-30T23:40:30.1483162Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-30T23:40:30.1483168Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-03-30T23:40:30.1483170Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260330234034_AddSalesItemFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260330234034_AddSalesItemFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [DepositAmountPaid] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [DepositPaymentIntentId] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [DepositPaymentLinkExpiresAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [DepositPaymentLinkToken] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T12:56:27.1808248Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T12:56:27.1808254Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T12:56:27.1808255Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401125630_AddQuoteDepositPaymentFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260401125630_AddQuoteDepositPaymentFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ DECLARE @var3 sysname;
+ SELECT @var3 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[Bills]') AND [c].[name] = N'BalanceDue');
+ IF @var3 IS NOT NULL EXEC(N'ALTER TABLE [Bills] DROP CONSTRAINT [' + @var3 + '];');
+ ALTER TABLE [Bills] DROP COLUMN [BalanceDue];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ DECLARE @var4 sysname;
+ SELECT @var4 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[GiftCertificates]') AND [c].[name] = N'CertificateCode');
+ IF @var4 IS NOT NULL EXEC(N'ALTER TABLE [GiftCertificates] DROP CONSTRAINT [' + @var4 + '];');
+ ALTER TABLE [GiftCertificates] ALTER COLUMN [CertificateCode] nvarchar(450) NOT NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T13:17:21.8121883Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T13:17:21.8121891Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T13:17:21.8121893Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_GiftCertificates_CertificateCode] ON [GiftCertificates] ([CertificateCode]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401131724_AddUniqueDocumentNumberConstraints'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260401131724_AddUniqueDocumentNumberConstraints', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ DROP INDEX [IX_GiftCertificates_CertificateCode] ON [GiftCertificates];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T14:16:49.2887180Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T14:16:49.2887185Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-01T14:16:49.2887186Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_GiftCertificates_CompanyId_CertificateCode] ON [GiftCertificates] ([CompanyId], [CertificateCode]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260401141653_FixGiftCertificateUniqueIndexPerCompany'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260401141653_FixGiftCertificateUniqueIndexPerCompany', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ ALTER TABLE [Invoices] ADD [ExternalReference] nvarchar(450) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T01:54:18.8649199Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T01:54:18.8649205Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T01:54:18.8649206Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ CREATE INDEX [IX_Invoices_CompanyId_ExternalReference] ON [Invoices] ([CompanyId], [ExternalReference]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402015422_AddInvoiceExternalReference'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402015422_AddInvoiceExternalReference', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402032156_AddMigratingFromQuickBooks'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [MigratingFromQuickBooks] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402032156_AddMigratingFromQuickBooks'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T03:21:53.0005398Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402032156_AddMigratingFromQuickBooks'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T03:21:53.0005405Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402032156_AddMigratingFromQuickBooks'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T03:21:53.0005406Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402032156_AddMigratingFromQuickBooks'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402032156_AddMigratingFromQuickBooks', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402165758_AddQbMigrationStateJson'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [QbMigrationStateJson] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402165758_AddQbMigrationStateJson'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T16:57:55.0246999Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402165758_AddQbMigrationStateJson'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T16:57:55.0247004Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402165758_AddQbMigrationStateJson'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T16:57:55.0247006Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402165758_AddQbMigrationStateJson'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402165758_AddQbMigrationStateJson', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ DROP INDEX [IX_InventoryItems_SKU] ON [InventoryItems];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:47:18.8788284Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:47:18.8788291Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:47:18.8788292Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_InventoryItems_CompanyId_SKU] ON [InventoryItems] ([CompanyId], [SKU]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402184721_FixInventorySkuUniqueIndex'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402184721_FixInventorySkuUniqueIndex', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ DROP INDEX [IX_Jobs_ShopAccessCode] ON [Jobs];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:52:13.7857008Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:52:13.7857015Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T18:52:13.7857016Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_Jobs_CompanyId_ShopAccessCode] ON [Jobs] ([CompanyId], [ShopAccessCode]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402185216_FixJobShopAccessCodeUniqueIndex'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402185216_FixJobShopAccessCodeUniqueIndex', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402224949_AddDashboardTips'
+)
+BEGIN
+ CREATE TABLE [DashboardTips] (
+ [Id] int NOT NULL IDENTITY,
+ [TipText] nvarchar(max) NOT NULL,
+ [IsActive] bit NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ CONSTRAINT [PK_DashboardTips] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402224949_AddDashboardTips'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T22:49:46.0354841Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402224949_AddDashboardTips'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T22:49:46.0354847Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402224949_AddDashboardTips'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-02T22:49:46.0354849Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260402224949_AddDashboardTips'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260402224949_AddDashboardTips', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260403000650_AddStripeWebhookEvents'
+)
+BEGIN
+ CREATE TABLE [StripeWebhookEvents] (
+ [Id] bigint NOT NULL IDENTITY,
+ [EventId] nvarchar(max) NOT NULL,
+ [EventType] nvarchar(max) NOT NULL,
+ [CompanyId] int NULL,
+ [RawJson] nvarchar(max) NOT NULL,
+ [Status] int NOT NULL,
+ [ErrorMessage] nvarchar(max) NULL,
+ [ReceivedAt] datetime2 NOT NULL,
+ [ProcessedAt] datetime2 NULL,
+ CONSTRAINT [PK_StripeWebhookEvents] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260403000650_AddStripeWebhookEvents'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-03T00:06:46.7783905Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260403000650_AddStripeWebhookEvents'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-03T00:06:46.7783912Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260403000650_AddStripeWebhookEvents'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-03T00:06:46.7783913Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260403000650_AddStripeWebhookEvents'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260403000650_AddStripeWebhookEvents', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404151636_AddAllowAccountingToPlan'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowAccounting] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404151636_AddAllowAccountingToPlan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T15:16:32.2541952Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404151636_AddAllowAccountingToPlan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T15:16:32.2541958Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404151636_AddAllowAccountingToPlan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T15:16:32.2541968Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404151636_AddAllowAccountingToPlan'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260404151636_AddAllowAccountingToPlan', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404194126_AddBillReceiptFilePath'
+)
+BEGIN
+ ALTER TABLE [Bills] ADD [ReceiptFilePath] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404194126_AddBillReceiptFilePath'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T19:41:22.8540290Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404194126_AddBillReceiptFilePath'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T19:41:22.8540296Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404194126_AddBillReceiptFilePath'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-04T19:41:22.8540297Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260404194126_AddBillReceiptFilePath'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260404194126_AddBillReceiptFilePath', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T00:33:47.2862744Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T00:33:47.2862750Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T00:33:47.2862752Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_InventoryTransactions_TransactionType_TransactionDate] ON [InventoryTransactions] ([TransactionType], [TransactionDate]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_InventoryItems_CompanyId_IsActive] ON [InventoryItems] ([CompanyId], [IsActive]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_InventoryItems_IsActive] ON [InventoryItems] ([IsActive]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_Bills_CompanyId_Status] ON [Bills] ([CompanyId], [Status]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_Bills_DueDate] ON [Bills] ([DueDate]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_Bills_Status] ON [Bills] ([Status]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ CREATE INDEX [IX_Appointments_ScheduledStartTime] ON [Appointments] ([ScheduledStartTime]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405003350_AddPerformanceIndexes'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260405003350_AddPerformanceIndexes', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ CREATE TABLE [PlatformSettings] (
+ [Id] int NOT NULL IDENTITY,
+ [Key] nvarchar(200) NOT NULL,
+ [Value] nvarchar(max) NULL,
+ [Label] nvarchar(max) NULL,
+ [Description] nvarchar(max) NULL,
+ [GroupName] nvarchar(max) NULL,
+ [UpdatedAt] datetime2 NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_PlatformSettings] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_PlatformSettings_Key] ON [PlatformSettings] ([Key]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+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 (1, N''AdminNotificationEmail'', NULL, N''Admin Notification Email'', N''Email address that receives platform event notifications (new signups, bug reports, subscription events). Leave blank to disable.'', N''Notifications'')');
+ 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'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T15:56:49.8180443Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T15:56:49.8180449Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T15:56:49.8180450Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405155653_AddPlatformSettings'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260405155653_AddPlatformSettings', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405161241_AddPlatformSettingsV2'
+)
+BEGIN
+ DECLARE @var5 sysname;
+ SELECT @var5 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[PlatformSettings]') AND [c].[name] = N'Key');
+ IF @var5 IS NOT NULL EXEC(N'ALTER TABLE [PlatformSettings] DROP CONSTRAINT [' + @var5 + '];');
+ ALTER TABLE [PlatformSettings] ALTER COLUMN [Key] nvarchar(200) NOT NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405161241_AddPlatformSettingsV2'
+)
+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 (2, N''BaseUrl'', NULL, N''Base URL'', N''Public URL of this application (e.g. https://app.powdercoatinglogix.com). Used in email links. Falls back to the current request URL if blank.'', N''General''),
+ (3, N''TrialPeriodDays'', N''7'', N''Trial Period (days)'', N''Number of days a new company gets on the free trial before their subscription expires.'', N''Subscriptions''),
+ (4, N''QuoteApprovalTokenDays'', N''30'', N''Quote Approval Token Validity (days)'', N''How many days a customer quote-approval link remains valid before expiring.'', N''Quotes''),
+ (5, N''AuditLogRetentionDays'', N''365'', N''Audit Log Retention (days)'', N''Audit log entries older than this many days are automatically purged by the nightly job.'', N''Data Retention''),
+ (6, N''StripeWebhookRetentionDays'', N''90'', N''Stripe Webhook Retention (days)'', N''Processed Stripe webhook events older than this many days are automatically purged.'', N''Data Retention'')');
+ 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'20260405161241_AddPlatformSettingsV2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:12:38.5900904Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405161241_AddPlatformSettingsV2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:12:38.5900913Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405161241_AddPlatformSettingsV2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:12:38.5900914Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405161241_AddPlatformSettingsV2'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260405161241_AddPlatformSettingsV2', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405162137_UpdateAdminEmailDescription'
+)
+BEGIN
+ EXEC(N'UPDATE [PlatformSettings] SET [Description] = N''Email address(es) that receive platform event notifications (new signups, bug reports, subscription events). Separate multiple addresses with commas. Leave blank to disable.''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405162137_UpdateAdminEmailDescription'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:21:34.4700837Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405162137_UpdateAdminEmailDescription'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:21:34.4700844Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405162137_UpdateAdminEmailDescription'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-05T16:21:34.4700846Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260405162137_UpdateAdminEmailDescription'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260405162137_UpdateAdminEmailDescription', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260406191501_MakeBillLineItemAccountIdNullable'
+)
+BEGIN
+ DECLARE @var6 sysname;
+ SELECT @var6 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[BillLineItems]') AND [c].[name] = N'AccountId');
+ IF @var6 IS NOT NULL EXEC(N'ALTER TABLE [BillLineItems] DROP CONSTRAINT [' + @var6 + '];');
+ ALTER TABLE [BillLineItems] ALTER COLUMN [AccountId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260406191501_MakeBillLineItemAccountIdNullable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-06T19:14:56.7157942Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260406191501_MakeBillLineItemAccountIdNullable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-06T19:14:56.7157953Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260406191501_MakeBillLineItemAccountIdNullable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-06T19:14:56.7157955Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260406191501_MakeBillLineItemAccountIdNullable'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260406191501_MakeBillLineItemAccountIdNullable', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IntakeCheckedByUserId] nvarchar(450) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IntakeConditionNotes] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IntakeDate] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [IntakePartCount] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-08T20:53:42.2947842Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-08T20:53:42.2947847Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-08T20:53:42.2947849Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ CREATE INDEX [IX_Jobs_IntakeCheckedByUserId] ON [Jobs] ([IntakeCheckedByUserId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD CONSTRAINT [FK_Jobs_AspNetUsers_IntakeCheckedByUserId] FOREIGN KEY ([IntakeCheckedByUserId]) REFERENCES [AspNetUsers] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260408205345_AddJobIntakeFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260408205345_AddJobIntakeFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ CREATE TABLE [InAppNotifications] (
+ [Id] int NOT NULL IDENTITY,
+ [Title] nvarchar(max) NOT NULL,
+ [Message] nvarchar(max) NOT NULL,
+ [Link] nvarchar(max) NULL,
+ [NotificationType] nvarchar(max) NOT NULL,
+ [IsRead] bit NOT NULL,
+ [ReadAt] datetime2 NULL,
+ [QuoteId] int NULL,
+ [InvoiceId] int NULL,
+ [CustomerId] int NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_InAppNotifications] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_InAppNotifications_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]),
+ CONSTRAINT [FK_InAppNotifications_Invoices_InvoiceId] FOREIGN KEY ([InvoiceId]) REFERENCES [Invoices] ([Id]),
+ CONSTRAINT [FK_InAppNotifications_Quotes_QuoteId] FOREIGN KEY ([QuoteId]) REFERENCES [Quotes] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-09T01:38:18.3630787Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-09T01:38:18.3630794Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-09T01:38:18.3630795Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ CREATE INDEX [IX_InAppNotifications_CustomerId] ON [InAppNotifications] ([CustomerId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ CREATE INDEX [IX_InAppNotifications_InvoiceId] ON [InAppNotifications] ([InvoiceId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ CREATE INDEX [IX_InAppNotifications_QuoteId] ON [InAppNotifications] ([QuoteId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260409013822_AddInAppNotifications'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260409013822_AddInAppNotifications', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [MarketingEmailOptOut] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [MarketingUnsubscribeToken] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+
+ UPDATE Companies
+ SET MarketingUnsubscribeToken = LOWER(REPLACE(NEWID(), '-', ''))
+ WHERE MarketingUnsubscribeToken IS NULL
+
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ DECLARE @var7 sysname;
+ SELECT @var7 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[Companies]') AND [c].[name] = N'MarketingUnsubscribeToken');
+ IF @var7 IS NOT NULL EXEC(N'ALTER TABLE [Companies] DROP CONSTRAINT [' + @var7 + '];');
+ ALTER TABLE [Companies] ALTER COLUMN [MarketingUnsubscribeToken] nvarchar(max) NOT NULL;
+ ALTER TABLE [Companies] ADD DEFAULT N'' FOR [MarketingUnsubscribeToken];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ CREATE TABLE [TermsAcceptances] (
+ [Id] int NOT NULL IDENTITY,
+ [UserId] nvarchar(max) NOT NULL,
+ [CompanyId] int NOT NULL,
+ [TosVersion] nvarchar(max) NOT NULL,
+ [AcceptedAt] datetime2 NOT NULL,
+ [IpAddress] nvarchar(max) NULL,
+ [UserAgent] nvarchar(max) NULL,
+ CONSTRAINT [PK_TermsAcceptances] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:19:30.4105127Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:19:30.4105133Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:19:30.4105135Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410021934_AddLegalCompliance'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260410021934_AddLegalCompliance', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowAiInventoryAssist] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowAiPhotoQuotes] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:53:49.5828243Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:53:49.5828250Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T02:53:49.5828252Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410025353_AddAiFeaturesToPlanConfig'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260410025353_AddAiFeaturesToPlanConfig', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410032027_AddTrialsEnabledSetting'
+)
+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 (7, N''TrialsEnabled'', N''true'', N''Free Trials Enabled'', N''When true (default), new signups start with a free trial period. When false, a credit card is required at signup — registrants are sent through Stripe Checkout before their account is created.'', N''Subscriptions'')');
+ 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'20260410032027_AddTrialsEnabledSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T03:20:23.1587184Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410032027_AddTrialsEnabledSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T03:20:23.1587190Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410032027_AddTrialsEnabledSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-10T03:20:23.1587191Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260410032027_AddTrialsEnabledSetting'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260410032027_AddTrialsEnabledSetting', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+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
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260415120000_AddAuditLogTable'
+)
+BEGIN
+ CREATE TABLE [AuditLogs] (
+ [Id] bigint NOT NULL IDENTITY,
+ [UserId] nvarchar(max) NULL,
+ [UserName] nvarchar(max) NOT NULL,
+ [CompanyId] int NULL,
+ [CompanyName] nvarchar(max) NULL,
+ [Action] nvarchar(max) NOT NULL,
+ [EntityType] nvarchar(450) NOT NULL,
+ [EntityId] nvarchar(450) NULL,
+ [EntityDescription] nvarchar(max) NULL,
+ [OldValues] nvarchar(max) NULL,
+ [NewValues] nvarchar(max) NULL,
+ [IpAddress] nvarchar(max) NULL,
+ [Timestamp] datetime2 NOT NULL,
+ CONSTRAINT [PK_AuditLogs] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260415120000_AddAuditLogTable'
+)
+BEGIN
+ CREATE INDEX [IX_AuditLogs_CompanyId_Timestamp] ON [AuditLogs] ([CompanyId], [Timestamp]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260415120000_AddAuditLogTable'
+)
+BEGIN
+ CREATE INDEX [IX_AuditLogs_EntityType_EntityId] ON [AuditLogs] ([EntityType], [EntityId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260415120000_AddAuditLogTable'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260415120000_AddAuditLogTable', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ ALTER TABLE [InventoryTransactions] ADD [JobId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T04:42:51.1510259Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T04:42:51.1510266Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T04:42:51.1510268Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ CREATE INDEX [IX_InventoryTransactions_JobId] ON [InventoryTransactions] ([JobId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ ALTER TABLE [InventoryTransactions] ADD CONSTRAINT [FK_InventoryTransactions_Jobs_JobId] FOREIGN KEY ([JobId]) REFERENCES [Jobs] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417044255_AddInventoryTransactionJobId'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260417044255_AddInventoryTransactionJobId', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [HasSamplePanel] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [IsCoating] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [SamplePanelNotes] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+
+ UPDATE ii
+ SET ii.IsCoating = 1
+ FROM InventoryItems ii
+ INNER JOIN InventoryCategoryLookups icl ON icl.Id = ii.InventoryCategoryId
+ WHERE icl.IsCoating = 1 AND icl.IsDeleted = 0
+
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T16:06:41.3326769Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T16:06:41.3326775Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T16:06:41.3326776Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417160645_AddSamplePanel'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260417160645_AddSamplePanel', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417170543_DropItemIsCoatingColumn'
+)
+BEGIN
+ DECLARE @var8 sysname;
+ SELECT @var8 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[InventoryItems]') AND [c].[name] = N'IsCoating');
+ IF @var8 IS NOT NULL EXEC(N'ALTER TABLE [InventoryItems] DROP CONSTRAINT [' + @var8 + '];');
+ ALTER TABLE [InventoryItems] DROP COLUMN [IsCoating];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417170543_DropItemIsCoatingColumn'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T17:05:40.0377587Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417170543_DropItemIsCoatingColumn'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T17:05:40.0377592Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417170543_DropItemIsCoatingColumn'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-17T17:05:40.0377594Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260417170543_DropItemIsCoatingColumn'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260417170543_DropItemIsCoatingColumn', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ DECLARE @var9 sysname;
+ SELECT @var9 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[InventoryItems]') AND [c].[name] = N'SamplePanelNotes');
+ IF @var9 IS NOT NULL EXEC(N'ALTER TABLE [InventoryItems] DROP CONSTRAINT [' + @var9 + '];');
+ ALTER TABLE [InventoryItems] DROP COLUMN [SamplePanelNotes];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [WoAccentColor] nvarchar(max) NOT NULL DEFAULT N'';
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [WoTerms] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-18T22:08:33.1580643Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-18T22:08:33.1580650Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-18T22:08:33.1580651Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260418220837_AddWorkOrderTemplate'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260418220837_AddWorkOrderTemplate', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419201818_AddInvoiceNumberPrefix'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [InvoiceNumberPrefix] nvarchar(max) NOT NULL DEFAULT N'';
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419201818_AddInvoiceNumberPrefix'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T20:18:14.2123100Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419201818_AddInvoiceNumberPrefix'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T20:18:14.2123106Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419201818_AddInvoiceNumberPrefix'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T20:18:14.2123107Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419201818_AddInvoiceNumberPrefix'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260419201818_AddInvoiceNumberPrefix', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419215302_AddContactSubmissions'
+)
+BEGIN
+ CREATE TABLE [ContactSubmissions] (
+ [Id] int NOT NULL IDENTITY,
+ [SenderName] nvarchar(max) NOT NULL,
+ [SenderEmail] nvarchar(max) NOT NULL,
+ [CompanyName] nvarchar(max) NOT NULL,
+ [Category] nvarchar(max) NOT NULL,
+ [Subject] nvarchar(max) NOT NULL,
+ [Message] nvarchar(max) NOT NULL,
+ [IsRead] bit NOT NULL,
+ [ReadAt] datetime2 NULL,
+ [ReadByUserId] nvarchar(max) NULL,
+ [ReadByUserName] nvarchar(max) NULL,
+ [AdminNotes] nvarchar(max) NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_ContactSubmissions] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419215302_AddContactSubmissions'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T21:52:59.4160772Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419215302_AddContactSubmissions'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T21:52:59.4160778Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419215302_AddContactSubmissions'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-19T21:52:59.4160779Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260419215302_AddContactSubmissions'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260419215302_AddContactSubmissions', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [BlastNozzleSize] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [BlastRateSqFtPerHourOverride] decimal(18,2) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [BlastSetupType] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [CoatingGunType] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [CoatingRateSqFtPerHourOverride] decimal(18,2) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [CompressorCfm] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [PrimaryBlastSubstrate] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [ShopCapabilityTier] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-20T23:36:06.0440591Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-20T23:36:06.0440597Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-20T23:36:06.0440599Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260420233610_AddShopCapabilityProfile'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260420233610_AddShopCapabilityProfile', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421003346_AddCompanyBlastSetups'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T00:33:43.0886131Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421003346_AddCompanyBlastSetups'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T00:33:43.0886138Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421003346_AddCompanyBlastSetups'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T00:33:43.0886139Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421003346_AddCompanyBlastSetups'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260421003346_AddCompanyBlastSetups', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ ALTER TABLE [QuoteItemPrepServices] ADD [BlastSetupId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ ALTER TABLE [PrepServices] ADD [RequiresBlastSetup] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ ALTER TABLE [JobItemPrepServices] ADD [BlastSetupId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ CREATE TABLE [CompanyBlastSetups] (
+ [Id] int NOT NULL IDENTITY,
+ [Name] nvarchar(100) NOT NULL,
+ [SetupType] int NOT NULL,
+ [CompressorCfm] decimal(18,2) NOT NULL,
+ [BlastNozzleSize] int NOT NULL,
+ [PrimarySubstrate] int NOT NULL,
+ [BlastRateSqFtPerHourOverride] decimal(18,2) NULL,
+ [IsDefault] bit NOT NULL,
+ [IsActive] bit NOT NULL,
+ [DisplayOrder] int NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_CompanyBlastSetups] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_CompanyBlastSetups_Companies_CompanyId] FOREIGN KEY ([CompanyId]) REFERENCES [Companies] ([Id]) ON DELETE NO ACTION
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T01:24:05.9835208Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T01:24:05.9835215Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T01:24:05.9835220Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ CREATE INDEX [IX_QuoteItemPrepServices_BlastSetupId] ON [QuoteItemPrepServices] ([BlastSetupId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ CREATE INDEX [IX_JobItemPrepServices_BlastSetupId] ON [JobItemPrepServices] ([BlastSetupId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ CREATE INDEX [IX_CompanyBlastSetups_CompanyId] ON [CompanyBlastSetups] ([CompanyId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ ALTER TABLE [JobItemPrepServices] ADD CONSTRAINT [FK_JobItemPrepServices_CompanyBlastSetups_BlastSetupId] FOREIGN KEY ([BlastSetupId]) REFERENCES [CompanyBlastSetups] ([Id]) ON DELETE SET NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ ALTER TABLE [QuoteItemPrepServices] ADD CONSTRAINT [FK_QuoteItemPrepServices_CompanyBlastSetups_BlastSetupId] FOREIGN KEY ([BlastSetupId]) REFERENCES [CompanyBlastSetups] ([Id]) ON DELETE SET NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421012409_AddCompanyBlastSetupsTable'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260421012409_AddCompanyBlastSetupsTable', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [EquipmentCosts] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [LaborCosts] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [MaterialCosts] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T12:59:53.4982640Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T12:59:53.4982651Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T12:59:53.4982653Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421125956_AddQuoteCostBreakdownColumns2'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260421125956_AddQuoteCostBreakdownColumns2', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [ItemEquipmentCost] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [ItemLaborCost] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ ALTER TABLE [QuoteItems] ADD [ItemMaterialCost] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:21:19.6983052Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:21:19.6983058Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:21:19.6983060Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421132123_AddQuoteItemCostSnapshot'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260421132123_AddQuoteItemCostSnapshot', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ ALTER TABLE [Quotes] ADD [HideDiscountFromCustomer] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [PricingMode] int NOT NULL DEFAULT 0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [TargetMarginPercent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:59:19.8113639Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:59:19.8113645Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-21T13:59:19.8113646Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260421135923_AddPricingModeAndHideDiscount'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260421135923_AddPricingModeAndHideDiscount', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ ALTER TABLE [AspNetUsers] ADD [IsBanned] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ ALTER TABLE [AspNetUsers] ADD [BannedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ ALTER TABLE [AspNetUsers] ADD [BanReason] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ ALTER TABLE [AspNetUsers] ADD [BannedByUserId] nvarchar(450) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:08:40.9182111Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:08:40.9182118Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:08:40.9182119Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422040844_AddUserBan'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260422040844_AddUserBan', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ CREATE TABLE [BannedIps] (
+ [Id] int NOT NULL IDENTITY,
+ [IpAddress] nvarchar(45) NOT NULL,
+ [Reason] nvarchar(500) NULL,
+ [BannedByUserId] nvarchar(450) NULL,
+ [BannedAt] datetime2 NOT NULL,
+ [ExpiresAt] datetime2 NULL,
+ [IsActive] bit NOT NULL,
+ CONSTRAINT [PK_BannedIps] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ CREATE INDEX [IX_BannedIps_IpAddress_IsActive] ON [BannedIps] ([IpAddress], [IsActive]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:21:26.9627092Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:21:26.9627100Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-22T04:21:26.9627102Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260422042129_AddBannedIps'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260422042129_AddBannedIps', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ CREATE TABLE [AiUsageLogs] (
+ [Id] bigint NOT NULL IDENTITY,
+ [CompanyId] int NOT NULL,
+ [UserId] nvarchar(max) NOT NULL,
+ [Feature] nvarchar(max) NOT NULL,
+ [Success] bit NOT NULL,
+ [InputLength] int NOT NULL,
+ [CalledAt] datetime2 NOT NULL,
+ CONSTRAINT [PK_AiUsageLogs] PRIMARY KEY ([Id]),
+ CONSTRAINT [FK_AiUsageLogs_Companies_CompanyId] FOREIGN KEY ([CompanyId]) REFERENCES [Companies] ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:19:33.1714669Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:19:33.1714674Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:19:33.1714676Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ CREATE INDEX [IX_AiUsageLogs_CompanyId_CalledAt] ON [AiUsageLogs] ([CompanyId], [CalledAt]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423011936_AddAiUsageLog'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260423011936_AddAiUsageLog', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423015446_AddSmsOptedOutAt'
+)
+BEGIN
+ ALTER TABLE [Customers] ADD [SmsOptedOutAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423015446_AddSmsOptedOutAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:54:43.1815272Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423015446_AddSmsOptedOutAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:54:43.1815281Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423015446_AddSmsOptedOutAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-23T01:54:43.1815283Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260423015446_AddSmsOptedOutAt'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260423015446_AddSmsOptedOutAt', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [MonthlyBillableHours] int NOT NULL DEFAULT 160;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [MonthlyRent] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ ALTER TABLE [CompanyOperatingCosts] ADD [MonthlyUtilities] decimal(18,2) NOT NULL DEFAULT 0.0;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-24T23:28:22.1047155Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-24T23:28:22.1047162Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-24T23:28:22.1047164Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260424232825_AddFacilityOverheadFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260424232825_AddFacilityOverheadFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ ALTER TABLE [CatalogItems] ADD [ImagePath] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ ALTER TABLE [CatalogItems] ADD [ThumbnailPath] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T12:32:52.2955147Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T12:32:52.2955155Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T12:32:52.2955156Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425123256_AddCatalogItemImages'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260425123256_AddCatalogItemImages', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ CREATE TABLE [UserPasskeys] (
+ [Id] int NOT NULL IDENTITY,
+ [UserId] nvarchar(max) NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CredentialId] varbinary(900) NOT NULL,
+ [PublicKey] varbinary(max) NOT NULL,
+ [UserHandle] varbinary(max) NOT NULL,
+ [SignCount] bigint NOT NULL,
+ [DeviceFriendlyName] nvarchar(max) NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [LastUsedAt] datetime2 NULL,
+ CONSTRAINT [PK_UserPasskeys] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T18:27:08.5374555Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T18:27:08.5374562Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T18:27:08.5374563Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_UserPasskeys_CredentialId] ON [UserPasskeys] ([CredentialId]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425182712_AddUserPasskeys'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260425182712_AddUserPasskeys', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425223454_AddCatalogPriceCheckReport'
+)
+BEGIN
+ CREATE TABLE [CatalogPriceCheckReports] (
+ [Id] int NOT NULL IDENTITY,
+ [RunAt] datetime2 NOT NULL,
+ [ItemsChecked] int NOT NULL,
+ [ResultsJson] nvarchar(max) NOT NULL,
+ [OperatingCostsSummary] nvarchar(max) NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_CatalogPriceCheckReports] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425223454_AddCatalogPriceCheckReport'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T22:34:50.0016987Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425223454_AddCatalogPriceCheckReport'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T22:34:50.0016993Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425223454_AddCatalogPriceCheckReport'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-25T22:34:50.0016994Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260425223454_AddCatalogPriceCheckReport'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260425223454_AddCatalogPriceCheckReport', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowAiCatalogPriceCheck] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [AiCatalogPriceCheckEnabled] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ IF NOT EXISTS (SELECT 1 FROM [PlatformSettings] WHERE [Key] = N'AiCatalogPriceCheckEnabled')
+ BEGIN
+ INSERT INTO [PlatformSettings] ([Key], [Value], [Label], [Description], [GroupName])
+ VALUES (N'AiCatalogPriceCheckEnabled', N'true', N'AI Catalog Price Check Enabled',
+ N'When true (default), the AI Catalog Price Check feature is available to companies on qualifying plans. Set to false to disable it platform-wide.',
+ N'AI Features');
+ END
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T12:26:21.7275012Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T12:26:21.7275018Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T12:26:21.7275020Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426122625_AddAiCatalogPriceCheckGating'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260426122625_AddAiCatalogPriceCheckGating', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426142825_AddPasskeyPromptDismissed'
+)
+BEGIN
+ ALTER TABLE [AspNetUsers] ADD [PasskeyPromptDismissed] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426142825_AddPasskeyPromptDismissed'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T14:28:21.4545921Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426142825_AddPasskeyPromptDismissed'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T14:28:21.4545931Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426142825_AddPasskeyPromptDismissed'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-26T14:28:21.4545932Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260426142825_AddPasskeyPromptDismissed'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260426142825_AddPasskeyPromptDismissed', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [FirstInvoiceCreatedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [FirstJobCreatedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [FirstQuoteCreatedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [FirstWorkflowCompleted] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [FirstWorkflowCompletedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [GuidedActivationDismissedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ ALTER TABLE [CompanyPreferences] ADD [OnboardingPath] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-28T16:40:22.3595055Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-28T16:40:22.3595063Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-28T16:40:22.3595065Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260428164026_AddGuidedActivationFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260428164026_AddGuidedActivationFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429220019_AddJobQuoteSnapshotUpdatedAt'
+)
+BEGIN
+ ALTER TABLE [Jobs] ADD [QuoteSnapshotUpdatedAt] datetime2 NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429220019_AddJobQuoteSnapshotUpdatedAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:00:14.7474877Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429220019_AddJobQuoteSnapshotUpdatedAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:00:14.7474884Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429220019_AddJobQuoteSnapshotUpdatedAt'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:00:14.7474886Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429220019_AddJobQuoteSnapshotUpdatedAt'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260429220019_AddJobQuoteSnapshotUpdatedAt', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429221217_AddInventoryItemImageUrl'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [ImageUrl] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429221217_AddInventoryItemImageUrl'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:12:13.9939171Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429221217_AddInventoryItemImageUrl'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:12:13.9939177Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429221217_AddInventoryItemImageUrl'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-04-29T22:12:13.9939179Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260429221217_AddInventoryItemImageUrl'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260429221217_AddInventoryItemImageUrl', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ ALTER TABLE [SubscriptionPlanConfigs] ADD [AllowSms] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [SmsDisabledByAdmin] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ ALTER TABLE [Companies] ADD [SmsEnabled] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:08:58.8800523Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:08:58.8800529Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:08:58.8800531Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502000902_AddSmsGating'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260502000902_AddSmsGating', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502002653_AddCompanySmsAgreement'
+)
+BEGIN
+ CREATE TABLE [CompanySmsAgreements] (
+ [Id] int NOT NULL IDENTITY,
+ [AgreedByUserId] nvarchar(max) NOT NULL,
+ [AgreedByUserName] nvarchar(max) NOT NULL,
+ [AgreedAt] datetime2 NOT NULL,
+ [IpAddress] nvarchar(max) NULL,
+ [UserAgent] nvarchar(max) NULL,
+ [TermsVersion] nvarchar(max) NOT NULL,
+ [CompanyId] int NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [CreatedBy] nvarchar(max) NULL,
+ [UpdatedBy] nvarchar(max) NULL,
+ [IsDeleted] bit NOT NULL,
+ [DeletedAt] datetime2 NULL,
+ [DeletedBy] nvarchar(max) NULL,
+ CONSTRAINT [PK_CompanySmsAgreements] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502002653_AddCompanySmsAgreement'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:26:49.3814933Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502002653_AddCompanySmsAgreement'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:26:49.3814939Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502002653_AddCompanySmsAgreement'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-02T00:26:49.3814941Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260502002653_AddCompanySmsAgreement'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260502002653_AddCompanySmsAgreement', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [SdsUrl] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ ALTER TABLE [InventoryItems] ADD [TdsUrl] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ CREATE TABLE [PowderCatalogItems] (
+ [Id] int NOT NULL IDENTITY,
+ [VendorName] nvarchar(450) NOT NULL,
+ [Sku] nvarchar(450) NOT NULL,
+ [ColorName] nvarchar(450) NOT NULL,
+ [Description] nvarchar(max) NULL,
+ [UnitPrice] decimal(18,2) NOT NULL,
+ [PriceTiersJson] nvarchar(max) NULL,
+ [ImageUrl] nvarchar(max) NULL,
+ [SdsUrl] nvarchar(max) NULL,
+ [TdsUrl] nvarchar(max) NULL,
+ [ApplicationGuideUrl] nvarchar(max) NULL,
+ [ProductUrl] nvarchar(max) NULL,
+ [IsDiscontinued] bit NOT NULL,
+ [CreatedAt] datetime2 NOT NULL,
+ [UpdatedAt] datetime2 NULL,
+ [LastSyncedAt] datetime2 NULL,
+ CONSTRAINT [PK_PowderCatalogItems] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T16:59:39.5543667Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T16:59:39.5543674Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T16:59:39.5543675Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ CREATE INDEX [IX_PowderCatalogItems_ColorName] ON [PowderCatalogItems] ([ColorName]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ CREATE UNIQUE INDEX [IX_PowderCatalogItems_Vendor_Sku] ON [PowderCatalogItems] ([VendorName], [Sku]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503165943_AddPowderCatalogItem'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260503165943_AddPowderCatalogItem', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [ColorFamilies] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [CoverageSqFtPerLb] decimal(18,2) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [CureTemperatureF] decimal(18,2) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [CureTimeMinutes] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [Finish] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [IsUserContributed] bit NOT NULL DEFAULT CAST(0 AS bit);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [RequiresClearCoat] bit NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ ALTER TABLE [PowderCatalogItems] ADD [TransferEfficiency] decimal(18,2) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T20:30:44.9555184Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T20:30:44.9555189Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-03T20:30:44.9555191Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260503203048_AddPowderCatalogSpecFields'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260503203048_AddPowderCatalogSpecFields', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505161757_AddGracePeriodDaysSetting'
+)
+BEGIN
+ IF NOT EXISTS (SELECT 1 FROM [PlatformSettings] WHERE [Key] = N'GracePeriodDays')
+ BEGIN
+ INSERT INTO [PlatformSettings] ([Key], [Value], [Label], [Description], [GroupName])
+ VALUES (N'GracePeriodDays', N'14', N'Grace Period (days)',
+ N'Number of days a company can continue to log in after their subscription expires before being fully locked out.',
+ N'Subscriptions');
+ END
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505161757_AddGracePeriodDaysSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T16:17:52.7808281Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505161757_AddGracePeriodDaysSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T16:17:52.7808287Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505161757_AddGracePeriodDaysSetting'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T16:17:52.7808289Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505161757_AddGracePeriodDaysSetting'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260505161757_AddGracePeriodDaysSetting', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505191333_AddGracePeriodAppliesToTrials'
+)
+BEGIN
+ IF NOT EXISTS (SELECT 1 FROM [PlatformSettings] WHERE [Key] = N'GracePeriodAppliesToTrials')
+ BEGIN
+ INSERT INTO [PlatformSettings] ([Key], [Value], [Label], [Description], [GroupName])
+ VALUES (N'GracePeriodAppliesToTrials', N'false', N'Grace Period Applies to Trials',
+ N'When false (default), trial accounts (no Stripe subscription) are locked out immediately when their trial expires — no grace period. Enable to give trial accounts the same grace period as paid accounts.',
+ N'Subscriptions');
+ END
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505191333_AddGracePeriodAppliesToTrials'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T19:13:29.5374011Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505191333_AddGracePeriodAppliesToTrials'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T19:13:29.5374019Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505191333_AddGracePeriodAppliesToTrials'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T19:13:29.5374021Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505191333_AddGracePeriodAppliesToTrials'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260505191333_AddGracePeriodAppliesToTrials', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ ALTER TABLE [JobTimeEntries] DROP CONSTRAINT [FK_JobTimeEntries_ShopWorkers_ShopWorkerId];
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ DECLARE @var10 sysname;
+ SELECT @var10 = [d].[name]
+ FROM [sys].[default_constraints] [d]
+ INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
+ WHERE ([d].[parent_object_id] = OBJECT_ID(N'[JobTimeEntries]') AND [c].[name] = N'ShopWorkerId');
+ IF @var10 IS NOT NULL EXEC(N'ALTER TABLE [JobTimeEntries] DROP CONSTRAINT [' + @var10 + '];');
+ ALTER TABLE [JobTimeEntries] ALTER COLUMN [ShopWorkerId] int NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ ALTER TABLE [JobTimeEntries] ADD [UserDisplayName] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ ALTER TABLE [JobTimeEntries] ADD [UserId] nvarchar(max) NULL;
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T23:10:14.7638603Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T23:10:14.7638610Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-05T23:10:14.7638612Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ ALTER TABLE [JobTimeEntries] ADD CONSTRAINT [FK_JobTimeEntries_ShopWorkers_ShopWorkerId] FOREIGN KEY ([ShopWorkerId]) REFERENCES [ShopWorkers] ([Id]);
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260505231018_MigrateTimeEntriesToUserId'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260505231018_MigrateTimeEntriesToUserId', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
+BEGIN TRANSACTION;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260506020726_AddDataProtectionKeys'
+)
+BEGIN
+ CREATE TABLE [DataProtectionKeys] (
+ [Id] int NOT NULL IDENTITY,
+ [FriendlyName] nvarchar(max) NULL,
+ [Xml] nvarchar(max) NULL,
+ CONSTRAINT [PK_DataProtectionKeys] PRIMARY KEY ([Id])
+ );
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260506020726_AddDataProtectionKeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-06T02:07:22.6252199Z''
+ WHERE [Id] = 1;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260506020726_AddDataProtectionKeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-06T02:07:22.6252206Z''
+ WHERE [Id] = 2;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260506020726_AddDataProtectionKeys'
+)
+BEGIN
+ EXEC(N'UPDATE [PricingTiers] SET [CreatedAt] = ''2026-05-06T02:07:22.6252208Z''
+ WHERE [Id] = 3;
+ SELECT @@ROWCOUNT');
+END;
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM [__EFMigrationsHistory]
+ WHERE [MigrationId] = N'20260506020726_AddDataProtectionKeys'
+)
+BEGIN
+ INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
+ VALUES (N'20260506020726_AddDataProtectionKeys', N'8.0.11');
+END;
+GO
+
+COMMIT;
+GO
+
diff --git a/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs b/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
index 2166558..85bc65c 100644
--- a/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
+++ b/src/PowderCoating.Application/DTOs/AI/AiQuickQuoteDtos.cs
@@ -65,6 +65,8 @@ public class SaveQuickQuoteRequest
public decimal EstimatedUnitPrice { get; set; }
public decimal MaterialCost { get; set; }
public decimal LaborCost { get; set; }
+ public decimal OvenBatchCost { get; set; }
+ public int OvenCycleMinutes { get; set; }
}
/// Internal JSON schema returned by Claude for quick quote analysis.
diff --git a/src/PowderCoating.Application/DTOs/Accounting/FinancialReportDtos.cs b/src/PowderCoating.Application/DTOs/Accounting/FinancialReportDtos.cs
index b9d4bc9..44901d7 100644
--- a/src/PowderCoating.Application/DTOs/Accounting/FinancialReportDtos.cs
+++ b/src/PowderCoating.Application/DTOs/Accounting/FinancialReportDtos.cs
@@ -159,3 +159,65 @@ public class SalesInvoiceLineDto
public decimal AmountPaid { get; set; }
public decimal BalanceDue { get; set; }
}
+
+// ============================================================
+// SALES TAX REPORT
+// ============================================================
+
+public class SalesTaxReportDto
+{
+ public DateTime From { get; set; }
+ public DateTime To { get; set; }
+ public string CompanyName { get; set; } = string.Empty;
+
+ /// Subtotal of invoices where TaxAmount > 0.
+ public decimal TotalTaxableSales { get; set; }
+ /// Subtotal of invoices where TaxAmount == 0.
+ public decimal TotalNonTaxableSales { get; set; }
+ /// Sum of all TaxAmount values across the period.
+ public decimal TotalTaxBilled { get; set; }
+ public int TaxableInvoiceCount { get; set; }
+ public int NonTaxableInvoiceCount { get; set; }
+ public decimal EffectiveTaxRate => TotalTaxableSales == 0 ? 0
+ : Math.Round(TotalTaxBilled / TotalTaxableSales * 100, 2);
+
+ public List ByAccount { get; set; } = new();
+ public List ByMonth { get; set; } = new();
+ public List Invoices { get; set; } = new();
+}
+
+public class SalesTaxByAccountDto
+{
+ public int? AccountId { get; set; }
+ public string AccountName { get; set; } = string.Empty;
+ public string AccountNumber { get; set; } = string.Empty;
+ public decimal TaxableSales { get; set; }
+ public decimal TaxBilled { get; set; }
+ public int InvoiceCount { get; set; }
+}
+
+public class SalesTaxByMonthDto
+{
+ public int Year { get; set; }
+ public int Month { get; set; }
+ public string Label { get; set; } = string.Empty;
+ public decimal TaxableSales { get; set; }
+ public decimal TaxBilled { get; set; }
+ public int InvoiceCount { get; set; }
+}
+
+public class SalesTaxInvoiceLineDto
+{
+ public int InvoiceId { get; set; }
+ public string InvoiceNumber { get; set; } = string.Empty;
+ public string CustomerName { get; set; } = string.Empty;
+ public DateTime InvoiceDate { get; set; }
+ public string Status { get; set; } = string.Empty;
+ public decimal SubTotal { get; set; }
+ public decimal TaxPercent { get; set; }
+ public decimal TaxAmount { get; set; }
+ public decimal Total { get; set; }
+ public decimal AmountPaid { get; set; }
+ public decimal BalanceDue { get; set; }
+ public string TaxAccountName { get; set; } = string.Empty;
+}
diff --git a/src/PowderCoating.Application/DTOs/Inventory/InventoryDtos.cs b/src/PowderCoating.Application/DTOs/Inventory/InventoryDtos.cs
index 846703d..624d1ec 100644
--- a/src/PowderCoating.Application/DTOs/Inventory/InventoryDtos.cs
+++ b/src/PowderCoating.Application/DTOs/Inventory/InventoryDtos.cs
@@ -17,6 +17,7 @@ public class InventoryItemDto
public string? Manufacturer { get; set; }
public string? ManufacturerPartNumber { get; set; }
public decimal? CoverageSqFtPerLb { get; set; }
+ public decimal? SpecificGravity { get; set; }
public decimal? TransferEfficiency { get; set; }
public int? CureTemperatureF { get; set; }
public int? CureTimeMinutes { get; set; }
@@ -125,6 +126,10 @@ public class CreateInventoryItemDto
[Display(Name = "Coverage (Sq Ft/Lb)")]
public decimal? CoverageSqFtPerLb { get; set; }
+ [Range(0, 100, ErrorMessage = "Specific gravity must be between 0 and 100")]
+ [Display(Name = "Specific Gravity")]
+ public decimal? SpecificGravity { get; set; }
+
[Range(0, 100, ErrorMessage = "Transfer efficiency must be between 0 and 100%")]
[Display(Name = "Transfer Efficiency (%)")]
public decimal? TransferEfficiency { get; set; }
diff --git a/src/PowderCoating.Application/DTOs/Inventory/PowderCatalogDtos.cs b/src/PowderCoating.Application/DTOs/Inventory/PowderCatalogDtos.cs
index f0b8994..03e5480 100644
--- a/src/PowderCoating.Application/DTOs/Inventory/PowderCatalogDtos.cs
+++ b/src/PowderCoating.Application/DTOs/Inventory/PowderCatalogDtos.cs
@@ -23,6 +23,7 @@ public class PowderCatalogLookupResult
public string? ColorFamilies { get; set; }
public bool? RequiresClearCoat { get; set; }
public decimal? CoverageSqFtPerLb { get; set; }
+ public decimal? SpecificGravity { get; set; }
public decimal? TransferEfficiency { get; set; }
}
diff --git a/src/PowderCoating.Application/Interfaces/IFinancialReportService.cs b/src/PowderCoating.Application/Interfaces/IFinancialReportService.cs
index 7537dc1..59eea64 100644
--- a/src/PowderCoating.Application/Interfaces/IFinancialReportService.cs
+++ b/src/PowderCoating.Application/Interfaces/IFinancialReportService.cs
@@ -20,4 +20,7 @@ public interface IFinancialReportService
/// Returns a Sales & Income report for the given company and date range.
Task GetSalesAndIncomeAsync(int companyId, DateTime from, DateTime to);
+
+ /// Returns an invoice-basis Sales Tax Liability report for the given company and date range.
+ Task GetSalesTaxReportAsync(int companyId, DateTime from, DateTime to);
}
diff --git a/src/PowderCoating.Application/Interfaces/IInventoryAiLookupService.cs b/src/PowderCoating.Application/Interfaces/IInventoryAiLookupService.cs
index 92da698..97ea248 100644
--- a/src/PowderCoating.Application/Interfaces/IInventoryAiLookupService.cs
+++ b/src/PowderCoating.Application/Interfaces/IInventoryAiLookupService.cs
@@ -20,6 +20,7 @@ public class InventoryAiLookupResult
public bool? RequiresClearCoat { get; set; }
// Application properties
+ public decimal? SpecificGravity { get; set; } // used to derive theoretical coverage when docs omit coverage
public decimal? CoverageSqFtPerLb { get; set; } // typical ~80-120 sq ft/lb
public decimal? TransferEfficiency { get; set; } // typical 50-75%
public decimal? UnitCostPerLb { get; set; } // price per lb/unit if found in search results
diff --git a/src/PowderCoating.Application/Interfaces/IPdfService.cs b/src/PowderCoating.Application/Interfaces/IPdfService.cs
index 0bb48f1..c59cbe4 100644
--- a/src/PowderCoating.Application/Interfaces/IPdfService.cs
+++ b/src/PowderCoating.Application/Interfaces/IPdfService.cs
@@ -41,6 +41,7 @@ public interface IPdfService
Task GenerateBalanceSheetPdfAsync(BalanceSheetDto dto);
Task GenerateArAgingPdfAsync(ArAgingReportDto dto);
Task GenerateSalesAndIncomePdfAsync(SalesIncomeReportDto dto);
+ Task GenerateSalesTaxReportPdfAsync(SalesTaxReportDto dto);
Task GenerateGiftCertificatePdfAsync(
GiftCertificateDto cert,
diff --git a/src/PowderCoating.Application/Services/PdfService.cs b/src/PowderCoating.Application/Services/PdfService.cs
index d0402c0..e42a196 100644
--- a/src/PowderCoating.Application/Services/PdfService.cs
+++ b/src/PowderCoating.Application/Services/PdfService.cs
@@ -1,3 +1,4 @@
+using PowderCoating.Application.DTOs.Accounting;
using PowderCoating.Application.DTOs.Company;
using PowderCoating.Application.DTOs.GiftCertificate;
using PowderCoating.Application.DTOs.Invoice;
@@ -2122,4 +2123,202 @@ public class PdfService : IPdfService
});
});
}
+
+ // ─── Sales Tax Report ─────────────────────────────────────────────────────
+
+ ///
+ /// Generates a letter-sized PDF for the Sales Tax Liability report. Sections: summary KPI
+ /// row, breakdown by tax account, breakdown by month, then a full invoice detail table.
+ /// Intended for handing to an accountant or attaching to a tax filing.
+ ///
+ public Task GenerateSalesTaxReportPdfAsync(SalesTaxReportDto dto)
+ {
+ QuestPDF.Settings.License = LicenseType.Community;
+ const string accent = "#1e3a5f";
+
+ return Task.Run(() =>
+ {
+ var document = Document.Create(container =>
+ {
+ container.Page(page =>
+ {
+ page.Size(PageSizes.Letter);
+ page.Margin(0.65f, Unit.Inch);
+ page.PageColor(Colors.White);
+ page.DefaultTextStyle(x => x.FontSize(9).FontFamily("Arial"));
+
+ page.Header().Column(col =>
+ {
+ col.Item().Row(row =>
+ {
+ row.RelativeItem().Column(c =>
+ {
+ c.Item().Text(dto.CompanyName).FontSize(14).Bold().FontColor(accent);
+ c.Item().Text("Sales Tax Liability Report").FontSize(10).FontColor(Colors.Grey.Darken1);
+ c.Item().Text($"{dto.From:MMMM d, yyyy} – {dto.To:MMMM d, yyyy}").FontSize(9).FontColor(Colors.Grey.Darken1);
+ });
+ row.RelativeItem().AlignRight().Column(c =>
+ {
+ c.Item().Text("Invoice-Basis Report").FontSize(8).Italic().FontColor(Colors.Grey.Medium);
+ c.Item().Text($"Generated {DateTime.Today:MMM d, yyyy}").FontSize(8).FontColor(Colors.Grey.Medium);
+ });
+ });
+ col.Item().PaddingTop(4).BorderBottom(1.5f).BorderColor(accent);
+ });
+
+ page.Content().PaddingTop(12).Column(col =>
+ {
+ // KPI summary row
+ col.Item().PaddingBottom(12).Row(row =>
+ {
+ void KpiBox(RowDescriptor r, string label, string value, string bg)
+ {
+ r.RelativeItem().Background(bg).Padding(8).Column(c =>
+ {
+ c.Item().Text(label).FontSize(7.5f).FontColor(Colors.Grey.Darken2);
+ c.Item().Text(value).FontSize(13).Bold().FontColor(accent);
+ });
+ }
+ KpiBox(row, "Total Tax Billed", $"{dto.TotalTaxBilled:C}", "#e8f4fd");
+ row.ConstantItem(6);
+ KpiBox(row, "Taxable Sales", $"{dto.TotalTaxableSales:C}", "#f0fdf4");
+ row.ConstantItem(6);
+ KpiBox(row, "Non-Taxable Sales", $"{dto.TotalNonTaxableSales:C}", "#fafafa");
+ row.ConstantItem(6);
+ KpiBox(row, "Effective Tax Rate", $"{dto.EffectiveTaxRate:F2}%", "#fff7ed");
+ });
+
+ // By account
+ if (dto.ByAccount.Any())
+ {
+ col.Item().PaddingBottom(4).Text("Tax by Liability Account").FontSize(10).Bold().FontColor(accent);
+ col.Item().PaddingBottom(10).Table(table =>
+ {
+ table.ColumnsDefinition(c =>
+ {
+ c.RelativeColumn(3);
+ c.RelativeColumn(2);
+ c.RelativeColumn(2);
+ c.ConstantColumn(40);
+ });
+ void AHdr(string t) => table.Header(h => h.Cell().Background("#e8f4fd").Padding(4).Text(t).Bold().FontSize(8));
+ table.Header(h =>
+ {
+ h.Cell().Background("#e8f4fd").Padding(4).Text("Account").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignRight().Text("Taxable Sales").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignRight().Text("Tax Billed").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignCenter().Text("Invoices").Bold().FontSize(8);
+ });
+ foreach (var a in dto.ByAccount)
+ {
+ var label = string.IsNullOrEmpty(a.AccountNumber) ? a.AccountName : $"{a.AccountNumber} {a.AccountName}";
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).Text(label).FontSize(8);
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignRight().Text($"{a.TaxableSales:C}").FontSize(8);
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignRight().Text($"{a.TaxBilled:C}").FontSize(8).Bold();
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignCenter().Text(a.InvoiceCount.ToString()).FontSize(8);
+ }
+ // Totals row
+ table.Cell().Background("#f8fafc").Padding(4).Text("Total").Bold().FontSize(8);
+ table.Cell().Background("#f8fafc").Padding(4).AlignRight().Text($"{dto.ByAccount.Sum(a => a.TaxableSales):C}").Bold().FontSize(8);
+ table.Cell().Background("#f8fafc").Padding(4).AlignRight().Text($"{dto.ByAccount.Sum(a => a.TaxBilled):C}").Bold().FontSize(8);
+ table.Cell().Background("#f8fafc").Padding(4).AlignCenter().Text(dto.ByAccount.Sum(a => a.InvoiceCount).ToString()).Bold().FontSize(8);
+ });
+ }
+
+ // By month
+ if (dto.ByMonth.Any())
+ {
+ col.Item().PaddingBottom(4).Text("Tax by Month").FontSize(10).Bold().FontColor(accent);
+ col.Item().PaddingBottom(10).Table(table =>
+ {
+ table.ColumnsDefinition(c =>
+ {
+ c.RelativeColumn(2);
+ c.RelativeColumn(2);
+ c.RelativeColumn(2);
+ c.ConstantColumn(40);
+ });
+ table.Header(h =>
+ {
+ h.Cell().Background("#e8f4fd").Padding(4).Text("Month").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignRight().Text("Taxable Sales").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignRight().Text("Tax Billed").Bold().FontSize(8);
+ h.Cell().Background("#e8f4fd").Padding(4).AlignCenter().Text("Invoices").Bold().FontSize(8);
+ });
+ foreach (var m in dto.ByMonth)
+ {
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).Text(m.Label).FontSize(8);
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignRight().Text($"{m.TaxableSales:C}").FontSize(8);
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignRight().Text($"{m.TaxBilled:C}").FontSize(8).Bold();
+ table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(4).AlignCenter().Text(m.InvoiceCount.ToString()).FontSize(8);
+ }
+ });
+ }
+
+ // Invoice detail
+ col.Item().PaddingBottom(4).Text("Invoice Detail").FontSize(10).Bold().FontColor(accent);
+ col.Item().Table(table =>
+ {
+ table.ColumnsDefinition(c =>
+ {
+ c.ConstantColumn(70); // Invoice #
+ c.RelativeColumn(2.5f); // Customer
+ c.ConstantColumn(58); // Date
+ c.ConstantColumn(48); // Status
+ c.ConstantColumn(52); // SubTotal
+ c.ConstantColumn(34); // Tax %
+ c.ConstantColumn(52); // Tax $
+ c.ConstantColumn(52); // Total
+ c.RelativeColumn(2); // Tax Account
+ });
+ table.Header(h =>
+ {
+ string bg = "#e8f4fd";
+ h.Cell().Background(bg).Padding(3).Text("Invoice #").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).Text("Customer").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).Text("Date").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).Text("Status").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).AlignRight().Text("Subtotal").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).AlignRight().Text("Tax %").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).AlignRight().Text("Tax $").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).AlignRight().Text("Total").Bold().FontSize(7.5f);
+ h.Cell().Background(bg).Padding(3).Text("Tax Account").Bold().FontSize(7.5f);
+ });
+ foreach (var inv in dto.Invoices)
+ {
+ var rowBg = inv.TaxAmount == 0 ? Colors.Grey.Lighten4 : Colors.White;
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(inv.InvoiceNumber).FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(inv.CustomerName).FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(inv.InvoiceDate.ToString("MM/dd/yyyy")).FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(inv.Status).FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text($"{inv.SubTotal:C}").FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text(inv.TaxAmount > 0 ? $"{inv.TaxPercent:F2}%" : "—").FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text(inv.TaxAmount > 0 ? $"{inv.TaxAmount:C}" : "—").FontSize(7.5f).Bold();
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).AlignRight().Text($"{inv.Total:C}").FontSize(7.5f);
+ table.Cell().Background(rowBg).BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(3).Text(inv.TaxAccountName).FontSize(7).FontColor(Colors.Grey.Darken1);
+ }
+ // Totals row
+ table.Cell().ColumnSpan(4).Background("#f0fdf4").Padding(3).AlignRight().Text("Totals").Bold().FontSize(7.5f);
+ table.Cell().Background("#f0fdf4").Padding(3).AlignRight().Text($"{dto.Invoices.Sum(i => i.SubTotal):C}").Bold().FontSize(7.5f);
+ table.Cell().Background("#f0fdf4").Padding(3);
+ table.Cell().Background("#f0fdf4").Padding(3).AlignRight().Text($"{dto.TotalTaxBilled:C}").Bold().FontSize(7.5f);
+ table.Cell().Background("#f0fdf4").Padding(3).AlignRight().Text($"{dto.Invoices.Sum(i => i.Total):C}").Bold().FontSize(7.5f);
+ table.Cell().Background("#f0fdf4").Padding(3);
+ });
+ });
+
+ page.Footer().AlignCenter().Text(text =>
+ {
+ text.DefaultTextStyle(s => s.FontSize(7.5f).FontColor(Colors.Grey.Medium));
+ text.Span("Sales Tax Liability Report | Invoice Basis | ");
+ text.CurrentPageNumber();
+ text.Span(" of ");
+ text.TotalPages();
+ });
+ });
+ });
+
+ return document.GeneratePdf();
+ });
+ }
}
diff --git a/src/PowderCoating.Application/Services/PricingCalculationService.cs b/src/PowderCoating.Application/Services/PricingCalculationService.cs
index 8c8e2a1..ff16d20 100644
--- a/src/PowderCoating.Application/Services/PricingCalculationService.cs
+++ b/src/PowderCoating.Application/Services/PricingCalculationService.cs
@@ -422,12 +422,14 @@ public class PricingCalculationService : IPricingCalculationService
else
{
// Non-catalog: derive base from first coat's material + labor + equipment + markup
+ decimal coatLaborCost = 0m; // coat-only labor, used for coating booth (not prep/sandblast)
if (item.Coats != null && item.Coats.Count > 0)
{
var firstCoatResult = await CalculateCoatPriceAsync(
item.Coats[0], item.SurfaceAreaSqFt, item.Quantity, 0, item.EstimatedMinutes, companyId);
totalMaterialCost = firstCoatResult.CoatMaterialCost;
- totalLaborCost = firstCoatResult.CoatLaborCost;
+ coatLaborCost = firstCoatResult.CoatLaborCost;
+ totalLaborCost = coatLaborCost;
}
// Prep service labor (done once per item batch)
@@ -443,9 +445,10 @@ public class PricingCalculationService : IPricingCalculationService
// Consumables surcharge (5% of material)
totalMaterialCost += totalMaterialCost * ConsumablesSurchargePercent;
- // Equipment cost: coating booth only (oven cost moved to quote-level batch calculation)
- var totalLaborHours = totalLaborCost / costs.StandardLaborRate;
- totalEquipmentCost = totalLaborHours * costs.CoatingBoothCostPerHour;
+ // Equipment cost: coating booth only — use coat labor hours, not prep/sandblast hours
+ // (sandblasting happens in a blast cabinet, not the powder coating booth)
+ var coatLaborHours = costs.StandardLaborRate > 0 ? coatLaborCost / costs.StandardLaborRate : 0m;
+ totalEquipmentCost = coatLaborHours * costs.CoatingBoothCostPerHour;
// Apply pricing mode: markup on material only, or target margin on total cost
if (costs.PricingMode == PowderCoating.Core.Enums.PricingMode.MarginOnTotalCost)
@@ -675,22 +678,24 @@ public class PricingCalculationService : IPricingCalculationService
var effectiveBatches = Math.Max(1, ovenBatches);
var fullOvenBatchCost = effectiveBatches * (effectiveCycleMinutes / 60m) * effectiveOvenRate;
- // Scale oven cost by the fraction of total surface area coming from non-AI items.
- // Use item count as a fallback when surface areas are all zero.
- var totalSqFt = items.Sum(i => i.SurfaceAreaSqFt * i.Quantity);
- var aiSqFt = items.Where(i => i.IsAiItem).Sum(i => i.SurfaceAreaSqFt * i.Quantity);
- var nonAiSqFt = totalSqFt - aiSqFt;
+ // Only items with coating layers go in the oven — sandblast/prep-only items (zero coats) don't.
+ // Of those coating items, AI items already have oven cost baked into their AI price.
+ var coatingItems = items.Where(i => i.Coats != null && i.Coats.Any()).ToList();
+ var nonAiCoatItems = coatingItems.Where(i => !i.IsAiItem).ToList();
decimal nonAiFraction;
- if (totalSqFt > 0)
+ if (!coatingItems.Any())
{
- nonAiFraction = nonAiSqFt / totalSqFt;
+ nonAiFraction = 0m; // No coated items — no oven charge
}
else
{
- var totalCount = items.Count;
- var aiCount = items.Count(i => i.IsAiItem);
- nonAiFraction = totalCount > 0 ? (decimal)(totalCount - aiCount) / totalCount : 1m;
+ var totalCoatSqFt = coatingItems.Sum(i => i.SurfaceAreaSqFt * i.Quantity);
+ var nonAiCoatSqFt = nonAiCoatItems.Sum(i => i.SurfaceAreaSqFt * i.Quantity);
+ if (totalCoatSqFt > 0)
+ nonAiFraction = nonAiCoatSqFt / totalCoatSqFt;
+ else
+ nonAiFraction = coatingItems.Count > 0 ? (decimal)nonAiCoatItems.Count / coatingItems.Count : 1m;
}
var ovenBatchCost = fullOvenBatchCost * nonAiFraction;
diff --git a/src/PowderCoating.Core/Entities/InventoryItem.cs b/src/PowderCoating.Core/Entities/InventoryItem.cs
index 40050e9..6f1f5e9 100644
--- a/src/PowderCoating.Core/Entities/InventoryItem.cs
+++ b/src/PowderCoating.Core/Entities/InventoryItem.cs
@@ -20,6 +20,7 @@ public class InventoryItem : BaseEntity
public string? Manufacturer { get; set; }
public string? ManufacturerPartNumber { get; set; }
public decimal? CoverageSqFtPerLb { get; set; } // Square feet coverage per pound (default 30)
+ public decimal? SpecificGravity { get; set; } // Powder specific gravity from the technical data sheet
public decimal? TransferEfficiency { get; set; } // Percentage of powder that sticks (default 65%)
public decimal? CureTemperatureF { get; set; } // Required cure temperature in °F (recommended for oven scheduling)
public int? CureTimeMinutes { get; set; } // Required hold time at cure temperature
diff --git a/src/PowderCoating.Core/Entities/PowderCatalogItem.cs b/src/PowderCoating.Core/Entities/PowderCatalogItem.cs
index faa04ce..22dab0c 100644
--- a/src/PowderCoating.Core/Entities/PowderCatalogItem.cs
+++ b/src/PowderCoating.Core/Entities/PowderCatalogItem.cs
@@ -52,6 +52,9 @@ public class PowderCatalogItem
/// Theoretical coverage in sq ft per pound. Typical 80–120.
public decimal? CoverageSqFtPerLb { get; set; }
+ /// Specific gravity from the TDS. Used to derive theoretical coverage when needed.
+ public decimal? SpecificGravity { get; set; }
+
/// Powder transfer efficiency percentage. Typical 60–75%.
public decimal? TransferEfficiency { get; set; }
diff --git a/src/PowderCoating.Core/Entities/QuoteItem.cs b/src/PowderCoating.Core/Entities/QuoteItem.cs
index 6d8e072..5c96706 100644
--- a/src/PowderCoating.Core/Entities/QuoteItem.cs
+++ b/src/PowderCoating.Core/Entities/QuoteItem.cs
@@ -32,6 +32,11 @@ public class QuoteItem : BaseEntity
public bool RequiresSandblasting { get; set; }
public bool RequiresMasking { get; set; }
public int EstimatedMinutes { get; set; }
+
+ // Whether to add prep service labor cost on top of this item's base price.
+ // Defaults to false for catalog items (catalog price assumed to include standard labor);
+ // true for calculated items where prep is a separate billable add-on.
+ public bool IncludePrepCost { get; set; }
public string? Notes { get; set; }
diff --git a/src/PowderCoating.Infrastructure/Migrations/20260506123541_AddSpecificGravityToPowderCatalogAndInventory.Designer.cs b/src/PowderCoating.Infrastructure/Migrations/20260506123541_AddSpecificGravityToPowderCatalogAndInventory.Designer.cs
new file mode 100644
index 0000000..c11fa9e
--- /dev/null
+++ b/src/PowderCoating.Infrastructure/Migrations/20260506123541_AddSpecificGravityToPowderCatalogAndInventory.Designer.cs
@@ -0,0 +1,9525 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using PowderCoating.Infrastructure.Data;
+
+#nullable disable
+
+namespace PowderCoating.Infrastructure.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20260506123541_AddSpecificGravityToPowderCatalogAndInventory")]
+ partial class AddSpecificGravityToPowderCatalogAndInventory
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("FriendlyName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Xml")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("DataProtectionKeys");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex")
+ .HasFilter("[NormalizedName] IS NOT NULL");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ClaimValue")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderKey")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("RoleId")
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("LoginProvider")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("Value")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Account", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AccountNumber")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("AccountSubType")
+ .HasColumnType("int");
+
+ b.Property("AccountType")
+ .HasColumnType("int");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CurrentBalance")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystem")
+ .HasColumnType("bit");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("OpeningBalance")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("OpeningBalanceDate")
+ .HasColumnType("datetime2");
+
+ b.Property("ParentAccountId")
+ .HasColumnType("int");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ParentAccountId");
+
+ b.ToTable("Accounts");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AiItemPrediction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AiTags")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("Confidence")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ConversationRounds")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("PredictedComplexity")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PredictedMinutes")
+ .HasColumnType("int");
+
+ b.Property("PredictedSurfaceAreaSqFt")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("PredictedUnitPrice")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("Reasoning")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserOverrodeEstimate")
+ .HasColumnType("bit");
+
+ b.HasKey("Id");
+
+ b.ToTable("AiItemPredictions");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AiUsageLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CalledAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("Feature")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("InputLength")
+ .HasColumnType("int");
+
+ b.Property("Success")
+ .HasColumnType("bit");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompanyId", "CalledAt")
+ .HasDatabaseName("IX_AiUsageLogs_CompanyId_CalledAt");
+
+ b.ToTable("AiUsageLogs");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Announcement", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedByUserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CreatedByUserName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("datetime2");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDismissible")
+ .HasColumnType("bit");
+
+ b.Property("Message")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("StartsAt")
+ .HasColumnType("datetime2");
+
+ b.Property("Target")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TargetCompanyId")
+ .HasColumnType("int");
+
+ b.Property("TargetPlan")
+ .HasColumnType("int");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.HasKey("Id");
+
+ b.ToTable("Announcements");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AnnouncementDismissal", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("AnnouncementId")
+ .HasColumnType("int");
+
+ b.Property("DismissedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AnnouncementId", "UserId")
+ .IsUnique();
+
+ b.ToTable("AnnouncementDismissals");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.ApplicationUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("int");
+
+ b.Property("Address")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BanReason")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("BannedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("BannedByUserId")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CanApproveQuotes")
+ .HasColumnType("bit");
+
+ b.Property("CanCreateQuotes")
+ .HasColumnType("bit");
+
+ b.Property("CanManageCalendar")
+ .HasColumnType("bit");
+
+ b.Property("CanManageCustomers")
+ .HasColumnType("bit");
+
+ b.Property("CanManageEquipment")
+ .HasColumnType("bit");
+
+ b.Property("CanManageInventory")
+ .HasColumnType("bit");
+
+ b.Property("CanManageInvoices")
+ .HasColumnType("bit");
+
+ b.Property("CanManageJobs")
+ .HasColumnType("bit");
+
+ b.Property("CanManageMaintenance")
+ .HasColumnType("bit");
+
+ b.Property("CanManageProducts")
+ .HasColumnType("bit");
+
+ b.Property("CanManageVendors")
+ .HasColumnType("bit");
+
+ b.Property("CanViewCalendar")
+ .HasColumnType("bit");
+
+ b.Property("CanViewProducts")
+ .HasColumnType("bit");
+
+ b.Property("CanViewReports")
+ .HasColumnType("bit");
+
+ b.Property("CanViewShopFloor")
+ .HasColumnType("bit");
+
+ b.Property("City")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CompanyRole")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DashboardLayout")
+ .HasColumnType("int");
+
+ b.Property("DateFormat")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Department")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("EmployeeNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("HireDate")
+ .HasColumnType("datetime2");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsBanned")
+ .HasColumnType("bit");
+
+ b.Property("LastLoginDate")
+ .HasColumnType("datetime2");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("bit");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("datetimeoffset");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("Notes")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PasskeyPromptDismissed")
+ .HasColumnType("bit");
+
+ b.Property("PasswordHash")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("bit");
+
+ b.Property("Position")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ProfilePictureFilePath")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("SidebarColor")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("State")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TerminationDate")
+ .HasColumnType("datetime2");
+
+ b.Property("Theme")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TimeZone")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("bit");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("nvarchar(256)");
+
+ b.Property("ZipCode")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompanyId");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex")
+ .HasFilter("[NormalizedUserName] IS NOT NULL");
+
+ b.ToTable("AspNetUsers", (string)null);
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.Appointment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ActualEndTime")
+ .HasColumnType("datetime2");
+
+ b.Property("ActualStartTime")
+ .HasColumnType("datetime2");
+
+ b.Property("AppointmentNumber")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("AppointmentStatusId")
+ .HasColumnType("int");
+
+ b.Property("AppointmentTypeId")
+ .HasColumnType("int");
+
+ b.Property("AssignedUserId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CustomerId")
+ .HasColumnType("int");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsAllDay")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsReminderEnabled")
+ .HasColumnType("bit");
+
+ b.Property("JobId")
+ .HasColumnType("int");
+
+ b.Property("Location")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Notes")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("ReminderMinutesBefore")
+ .HasColumnType("int");
+
+ b.Property("ScheduledEndTime")
+ .HasColumnType("datetime2");
+
+ b.Property("ScheduledStartTime")
+ .HasColumnType("datetime2");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AppointmentStatusId");
+
+ b.HasIndex("AppointmentTypeId");
+
+ b.HasIndex("AssignedUserId");
+
+ b.HasIndex("CustomerId");
+
+ b.HasIndex("JobId");
+
+ b.HasIndex("ScheduledStartTime");
+
+ b.HasIndex("CompanyId", "AppointmentStatusId")
+ .HasDatabaseName("IX_Appointments_CompanyId_AppointmentStatusId");
+
+ b.HasIndex("CompanyId", "ScheduledStartTime")
+ .HasDatabaseName("IX_Appointments_CompanyId_ScheduledStartTime");
+
+ b.ToTable("Appointments");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AppointmentStatusLookup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ColorClass")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayOrder")
+ .HasColumnType("int");
+
+ b.Property("IconClass")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystemDefined")
+ .HasColumnType("bit");
+
+ b.Property("IsTerminalStatus")
+ .HasColumnType("bit");
+
+ b.Property("StatusCode")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("AppointmentStatusLookups");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AppointmentTypeLookup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("ColorClass")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("CreatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DeletedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("DeletedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Description")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("DisplayOrder")
+ .HasColumnType("int");
+
+ b.Property("IconClass")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("IsActive")
+ .HasColumnType("bit");
+
+ b.Property("IsDeleted")
+ .HasColumnType("bit");
+
+ b.Property("IsSystemDefined")
+ .HasColumnType("bit");
+
+ b.Property("RequiresJobLink")
+ .HasColumnType("bit");
+
+ b.Property("TypeCode")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime2");
+
+ b.Property("UpdatedBy")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("AppointmentTypeLookups");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.AuditLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Action")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("CompanyId")
+ .HasColumnType("int");
+
+ b.Property("CompanyName")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("EntityDescription")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("EntityId")
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("EntityType")
+ .IsRequired()
+ .HasColumnType("nvarchar(450)");
+
+ b.Property("IpAddress")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("NewValues")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("OldValues")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("Timestamp")
+ .HasColumnType("datetime2");
+
+ b.Property("UserId")
+ .HasColumnType("nvarchar(max)");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CompanyId", "Timestamp");
+
+ b.HasIndex("EntityType", "EntityId");
+
+ b.ToTable("AuditLogs");
+ });
+
+ modelBuilder.Entity("PowderCoating.Core.Entities.BannedIp", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("BannedAt")
+ .HasColumnType("datetime2");
+
+ b.Property