1a44133a63
Removes the ShopWorker and ShopWorkerRoleCost entities, all related DTOs, mappings, controllers, views, and import/export paths. Worker identity is now handled entirely through ApplicationUser with per-user LaborCostPerHour. ShopWorkerRoleCosts table remains in production pending manual data migration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
178 lines
16 KiB
C#
178 lines
16 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using PowderCoating.Core.Interfaces.Services;
|
|
using PowderCoating.Infrastructure.Data;
|
|
|
|
namespace PowderCoating.Infrastructure.Services;
|
|
|
|
/// <summary>
|
|
/// Implements <see cref="ICompanyDataPurgeService"/> via bulk <c>ExecuteDeleteAsync</c> against
|
|
/// <see cref="ApplicationDbContext"/> directly. This is an intentional exception to the
|
|
/// IUnitOfWork pattern — identical to the rationale for <c>DataPurgeController</c> in the
|
|
/// documented permanent exceptions list. Each <c>ExecuteDeleteAsync</c> call commits immediately
|
|
/// at the database level so no <c>SaveChangesAsync</c> is needed for the bulk tiers.
|
|
/// </summary>
|
|
public class CompanyDataPurgeService : ICompanyDataPurgeService
|
|
{
|
|
private readonly ApplicationDbContext _context;
|
|
|
|
public CompanyDataPurgeService(ApplicationDbContext context)
|
|
{
|
|
_context = context;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public async Task DeleteAllBusinessDataAsync(int companyId, IReadOnlyList<string> 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
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
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();
|
|
}
|
|
}
|
|
}
|