Files
PowderCoatingLogix/scripts/purge_imported_data.sql
T
spouliot 82fb48f7a5 Patch export/import for missing fields; add CustomerContacts export
- DataExportController + AccountDataExportController: add ProjectName to
  Jobs, Quotes, Invoices (XLSX + CSV); add LeadSource + ShipTo fields to
  Customers (XLSX + CSV); add CustomerContacts sheet/CSV (new)
- Both export views: add Customer Contacts checkbox (checked by default)
- CustomerImportDto: add LeadSource + ShipTo* fields
- JobImportDto: add ProjectName
- QuoteImportDto: add ProjectName
- InvoiceImportDto: add Project Name (dual-name alias for round-trip)
- CsvImportService: wire all new import fields to entity creation;
  also patch invoice update path for ProjectName
- Add scripts/purge_imported_data.sql (dry-run T-SQL for data cleanup)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 15:14:27 -04:00

404 lines
16 KiB
Transact-SQL

-- =============================================================================
-- Company Data Purge Script
-- Removes financial, job, and quote data created before a cutoff date.
-- Customers and Vendors are always preserved.
--
-- WHAT THIS DELETES (for records where CreatedAt < @CutoffDate):
-- • Journal entries & bank reconciliations
-- • Bills, bill payments, purchase orders, vendor credits, expenses
-- • Invoices, invoice payments, credit memos, refunds, deposits
-- • Jobs and all child records (items, coats, prep, notes, photos, etc.)
-- • Quotes and all child records
--
-- WHAT THIS KEEPS (always):
-- • Customers, customer notes, customer contacts, preferred powders
-- • Vendors
-- • Inventory items and inventory transactions
-- • Equipment, catalog items, pricing tiers
-- • Company settings and configuration
-- • Any record with CreatedAt >= @CutoffDate
--
-- INSTRUCTIONS:
-- 1. Set @CompanyId — find it with: SELECT Id, Name FROM Companies
-- 2. Set @CutoffDate — records created BEFORE this date are deleted
-- 3. Run with @DryRun = 1 first and review the row counts printed
-- 4. Back up the database before setting @DryRun = 0
-- 5. Set @DryRun = 0 and run again to apply
-- =============================================================================
DECLARE @CutoffDate DATE = '2026-01-01'; -- Delete records created BEFORE this date
DECLARE @CompanyId INT = 0; -- !! Set to your company ID before running
DECLARE @DryRun BIT = 1; -- 1 = preview counts only | 0 = apply deletes
-- =============================================================================
SET NOCOUNT ON;
IF @CompanyId = 0
BEGIN
RAISERROR('ERROR: Set @CompanyId before running this script. Run: SELECT Id, Name FROM Companies', 16, 1);
RETURN;
END
PRINT '============================================================';
PRINT 'Purge run at: ' + CONVERT(NVARCHAR, GETDATE(), 120);
PRINT 'Company ID : ' + CAST(@CompanyId AS NVARCHAR);
PRINT 'Cutoff date : ' + CAST(@CutoffDate AS NVARCHAR) + ' (records BEFORE this date)';
PRINT 'Dry run : ' + CASE @DryRun WHEN 1 THEN 'YES — no changes will be made' ELSE 'NO — deletes will be applied' END;
PRINT '============================================================';
BEGIN TRANSACTION;
-- ===========================================================================
-- SECTION 1 — JOURNAL ENTRIES & GL
-- ===========================================================================
PRINT '';
PRINT '--- Section 1: Journal Entries & GL ---';
-- Null the self-referential ReversalOfId FK before deleting
UPDATE JournalEntries
SET ReversalOfId = NULL
WHERE CompanyId = @CompanyId
AND ReversalOfId IN (SELECT Id FROM JournalEntries WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
DELETE FROM JournalEntryLines
WHERE JournalEntryId IN (
SELECT Id FROM JournalEntries WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JournalEntryLines deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JournalEntries
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'JournalEntries deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM BankReconciliations
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'BankReconciliations deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ===========================================================================
-- SECTION 2 — BILLS, PURCHASE ORDERS & EXPENSES
-- ===========================================================================
PRINT '';
PRINT '--- Section 2: Bills, Purchase Orders & Expenses ---';
-- Vendor credits (must come before Bills because VendorCreditApplications references both)
DELETE FROM VendorCreditApplications
WHERE VendorCreditId IN (
SELECT Id FROM VendorCredits WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'VendorCreditApplications deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM VendorCreditLineItems
WHERE VendorCreditId IN (
SELECT Id FROM VendorCredits WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'VendorCreditLineItems deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM VendorCredits
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'VendorCredits deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Bills
DELETE FROM BillPayments
WHERE BillId IN (
SELECT Id FROM Bills WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'BillPayments deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM BillLineItems
WHERE BillId IN (
SELECT Id FROM Bills WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'BillLineItems deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM Bills
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Bills deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Purchase orders
DELETE FROM PurchaseOrderItems
WHERE PurchaseOrderId IN (
SELECT Id FROM PurchaseOrders WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'PurchaseOrderItems deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM PurchaseOrders
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'PurchaseOrders deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Expenses
DELETE FROM Expenses
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Expenses deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ===========================================================================
-- SECTION 3 — INVOICES, PAYMENTS & DEPOSITS
-- ===========================================================================
PRINT '';
PRINT '--- Section 3: Invoices, Payments & Deposits ---';
-- CreditMemos: NULL the OriginalInvoiceId FK before deleting the invoice it points to
UPDATE CreditMemos
SET OriginalInvoiceId = NULL
WHERE CompanyId = @CompanyId
AND OriginalInvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
DELETE FROM CreditMemoApplications
WHERE InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate)
OR CreditMemoId IN (
SELECT Id FROM CreditMemos WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'CreditMemoApplications deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM CreditMemos
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'CreditMemos deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Refunds and gift-cert redemptions tied to deleted invoices
DELETE FROM Refunds
WHERE InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'Refunds deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM GiftCertificateRedemptions
WHERE InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'GiftCertificateRedemptions deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Payments
DELETE FROM Payments
WHERE InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'Payments deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- InvoiceItems (NULL SourceJobItemId on any invoice items that survive but point at deleted jobs)
UPDATE InvoiceItems
SET SourceJobItemId = NULL
WHERE SourceJobItemId IN (
SELECT ji.Id FROM JobItems ji
INNER JOIN Jobs j ON ji.JobId = j.Id
WHERE j.CompanyId = @CompanyId AND j.CreatedAt < @CutoffDate);
DELETE FROM InvoiceItems
WHERE InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'InvoiceItems deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Deposits: clear the AppliedToInvoiceId FK before deleting the invoices
UPDATE Deposits
SET AppliedToInvoiceId = NULL,
AppliedDate = NULL
WHERE CompanyId = @CompanyId
AND AppliedToInvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- Now delete deposits that were created before the cutoff
DELETE FROM Deposits
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Deposits deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Notification logs referencing deleted invoices
UPDATE NotificationLogs
SET InvoiceId = NULL
WHERE CompanyId = @CompanyId
AND InvoiceId IN (
SELECT Id FROM Invoices WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
DELETE FROM Invoices
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Invoices deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ===========================================================================
-- SECTION 4 — JOBS
-- ===========================================================================
PRINT '';
PRINT '--- Section 4: Jobs ---';
-- NULL FKs in other tables that point to jobs/job-items being deleted --
-- BillLineItems.JobId (bill survived but referenced a deleted job)
UPDATE BillLineItems
SET JobId = NULL
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- Expenses.JobId
UPDATE Expenses
SET JobId = NULL
WHERE CompanyId = @CompanyId
AND JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- Appointments.JobId
UPDATE Appointments
SET JobId = NULL
WHERE CompanyId = @CompanyId
AND JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- Deposits.JobId (NoAction FK — must NULL before deleting job)
UPDATE Deposits
SET JobId = NULL
WHERE CompanyId = @CompanyId
AND JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- NotificationLogs.JobId
UPDATE NotificationLogs
SET JobId = NULL
WHERE CompanyId = @CompanyId
AND JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- OvenBatchItems: delete before OvenBatches and before JobItems
DELETE FROM OvenBatchItems
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'OvenBatchItems deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Clean up now-empty OvenBatches (batches belonging to this company with no remaining items)
DELETE FROM OvenBatches
WHERE CompanyId = @CompanyId
AND CreatedAt < @CutoffDate
AND Id NOT IN (SELECT DISTINCT OvenBatchId FROM OvenBatchItems);
PRINT 'OvenBatches deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ReworkRecords (JobId required FK; ReworkJobId optional)
DELETE FROM ReworkRecords
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate)
OR ReworkJobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'ReworkRecords deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- JobItem children (coats and prep services)
DELETE FROM JobItemCoats
WHERE JobItemId IN (
SELECT Id FROM JobItems WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate));
PRINT 'JobItemCoats deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobItemPrepServices
WHERE JobItemId IN (
SELECT Id FROM JobItems WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate));
PRINT 'JobItemPrepServices deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobItems
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobItems deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- Job metadata tables
DELETE FROM JobChangeHistories
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobChangeHistories deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobTimeEntries
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobTimeEntries deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobPhotos
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobPhotos deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobNotes
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobNotes deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobStatusHistory
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobStatusHistory deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM JobDailyPriorities
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'JobDailyPriorities deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM PowderUsageLogs
WHERE JobId IN (
SELECT Id FROM Jobs WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'PowderUsageLogs deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM AiItemPredictions
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'AiItemPredictions deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM Jobs
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Jobs deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ===========================================================================
-- SECTION 5 — QUOTES
-- ===========================================================================
PRINT '';
PRINT '--- Section 5: Quotes ---';
-- NULL FKs that point to quotes being deleted
UPDATE Deposits
SET QuoteId = NULL
WHERE CompanyId = @CompanyId
AND QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
UPDATE NotificationLogs
SET QuoteId = NULL
WHERE CompanyId = @CompanyId
AND QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
-- QuoteItem children
DELETE FROM QuoteItemCoats
WHERE QuoteItemId IN (
SELECT Id FROM QuoteItems WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate));
PRINT 'QuoteItemCoats deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM QuoteItemPrepServices
WHERE QuoteItemId IN (
SELECT Id FROM QuoteItems WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate));
PRINT 'QuoteItemPrepServices deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM QuoteItems
WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'QuoteItems deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM QuoteChangeHistories
WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'QuoteChangeHistories deleted: ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM QuotePhotos
WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'QuotePhotos deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- QuotePrepServices are scoped to the quote's company via the quote FK, not directly
DELETE FROM QuotePrepServices
WHERE QuoteId IN (
SELECT Id FROM Quotes WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate);
PRINT 'QuotePrepServices deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
DELETE FROM Quotes
WHERE CompanyId = @CompanyId AND CreatedAt < @CutoffDate;
PRINT 'Quotes deleted : ' + CAST(@@ROWCOUNT AS NVARCHAR);
-- ===========================================================================
-- SUMMARY & COMMIT / ROLLBACK
-- ===========================================================================
PRINT '';
PRINT '============================================================';
IF @DryRun = 1
BEGIN
PRINT 'DRY RUN complete — rolling back. No data was changed.';
PRINT 'Set @DryRun = 0 and run again to apply the deletes.';
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
PRINT 'Purge complete — committing.';
COMMIT TRANSACTION;
END