using Microsoft.EntityFrameworkCore; using PowderCoating.Core.Interfaces.Services; using PowderCoating.Infrastructure.Data; namespace PowderCoating.Infrastructure.Services; /// /// Implements via bulk ExecuteDeleteAsync against /// directly. This is an intentional exception to the /// IUnitOfWork pattern — identical to the rationale for DataPurgeController in the /// documented permanent exceptions list. Each ExecuteDeleteAsync call commits immediately /// at the database level so no SaveChangesAsync is needed for the bulk tiers. /// public class CompanyDataPurgeService : ICompanyDataPurgeService { private readonly ApplicationDbContext _context; public CompanyDataPurgeService(ApplicationDbContext context) { _context = context; } /// public async Task DeleteAllBusinessDataAsync(int companyId, IReadOnlyList companyUserIds) { // ── Tier 1: Leaf children ───────────────────────────────────────────── await _context.JobItemPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobItemCoats.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItemPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItemCoats.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); // Announcement dismissals referencing the company's users or company-targeted announcements var announcementIds = await _context.Announcements .Where(a => a.TargetCompanyId == companyId).Select(a => a.Id).ToListAsync(); await _context.AnnouncementDismissals.IgnoreQueryFilters() .Where(x => companyUserIds.Contains(x.UserId) || announcementIds.Contains(x.AnnouncementId)) .ExecuteDeleteAsync(); // ── Tier 2: Mid-level children ──────────────────────────────────────── await _context.JobItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobStatusHistory.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobChangeHistories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobPhotos.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobNotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobDailyPriorities.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteChangeHistories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CustomerNotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryTransactions.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.MaintenanceRecords.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.BillLineItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.BillPayments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Payments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InvoiceItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuotePrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); // ── Tier 3: Top-level company entities ─────────────────────────────── await _context.Invoices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Appointments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Jobs.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Quotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Customers.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Bills.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Expenses.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Vendors.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Equipment.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.OvenCosts.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CatalogItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Accounts.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.NotificationLogs.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.NotificationTemplates.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Announcements.Where(x => x.TargetCompanyId == companyId).ExecuteDeleteAsync(); await _context.BugReports.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); // ── Tier 4: Company configs and lookup tables ───────────────────────── await _context.CatalogCategories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryCategoryLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobStatusLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobPriorityLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteStatusLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.AppointmentStatusLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.AppointmentTypeLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PricingTiers.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CompanyOperatingCosts.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CompanyPreferences.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); // Note: company record and users are left for the caller to handle via UserManager and UnitOfWork } /// public async Task ResetBusinessDataAsync(int companyId) { // ── Tier 0: Grandchildren ───────────────────────────────────────────── await _context.JobTemplateItemPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobTemplateItemCoats.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobItemPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobItemCoats.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItemPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItemCoats.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.GiftCertificateRedemptions.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CreditMemoApplications.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.OvenBatchItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); var announcementIds = await _context.Announcements .Where(a => a.TargetCompanyId == companyId).Select(a => a.Id).ToListAsync(); if (announcementIds.Count > 0) await _context.AnnouncementDismissals.IgnoreQueryFilters() .Where(x => announcementIds.Contains(x.AnnouncementId)) .ExecuteDeleteAsync(); // ── Tier 1: Children ────────────────────────────────────────────────── await _context.JobTemplateItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobStatusHistory.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobChangeHistories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobPhotos.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobNotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobDailyPriorities.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobTimeEntries.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobPrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.ReworkRecords.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuoteChangeHistories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuotePrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.QuotePhotos.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CustomerNotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryTransactions.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.MaintenanceRecords.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.BillLineItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.BillPayments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Payments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Deposits.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InvoiceItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PurchaseOrderItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.AiItemPredictions.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PowderUsageLogs.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.OvenBatches.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Refunds.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CreditMemos.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.GiftCertificates.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); // ── Tier 2: Top-level business entities ────────────────────────────── await _context.Invoices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Appointments.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Jobs.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.JobTemplates.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Quotes.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Customers.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Bills.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PurchaseOrders.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Expenses.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Vendors.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CatalogItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryItems.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Equipment.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.OvenCosts.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Accounts.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.NotificationLogs.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.PrepServices.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.CatalogCategories.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.InventoryCategoryLookups.IgnoreQueryFilters().Where(x => x.CompanyId == companyId).ExecuteDeleteAsync(); await _context.Announcements.Where(x => x.TargetCompanyId == companyId).ExecuteDeleteAsync(); // Clear QuickBooks migration wizard progress (tracked update, not bulk delete) var prefs = await _context.CompanyPreferences .IgnoreQueryFilters() .FirstOrDefaultAsync(p => p.CompanyId == companyId); if (prefs?.QbMigrationStateJson != null) { prefs.QbMigrationStateJson = null; prefs.UpdatedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); } } }