Fix Reset Demo Company: full wipe mode + missing removal categories

Root cause: fingerprint-based removal failed on databases seeded with
older code (different emails/SKUs); plus Vendors, Named Ovens, and
Appointments had no removal path at all.

- Add ForceRemoveAll flag to RemoveSeedDataOptions: when true, all
  removal blocks delete by CompanyId instead of fingerprint matching
- Customers block: ForceRemoveAll deletes all company customers
- Workers block: ForceRemoveAll deletes all users with CompanyRole=Worker
- New Vendors block (triggered by options.Vendors || ForceRemoveAll)
- New NamedOvens (OvenCost) block (triggered by options.NamedOvens || ForceRemoveAll)
- New Appointments block (triggered by options.Appointments || ForceRemoveAll)
- ResetDemoCompany: set ForceRemoveAll=true and enable all new flags so
  every re-seedable table is wiped clean before re-seeding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 22:49:30 -04:00
parent c0e4a66126
commit 249128e852
3 changed files with 97 additions and 9 deletions
@@ -123,11 +123,15 @@ public partial class SeedDataService
// --- Customers (+ their jobs, quotes, and related items) ---
if (options.Customers)
{
var seededCustomerIds = await _context.Customers
.IgnoreQueryFilters()
.Where(c => c.CompanyId == companyId && SeededCustomerEmails.Contains(c.Email))
.Select(c => c.Id)
.ToListAsync();
// ForceRemoveAll: wipe every customer for the company (demo reset path).
// Normal mode: fingerprint-match by seeded email addresses (selective removal).
var seededCustomerIds = options.ForceRemoveAll
? await _context.Customers.IgnoreQueryFilters()
.Where(c => c.CompanyId == companyId)
.Select(c => c.Id).ToListAsync()
: await _context.Customers.IgnoreQueryFilters()
.Where(c => c.CompanyId == companyId && SeededCustomerEmails.Contains(c.Email))
.Select(c => c.Id).ToListAsync();
if (seededCustomerIds.Any())
{
@@ -452,12 +456,81 @@ public partial class SeedDataService
}
}
// --- Vendors ---
if (options.Vendors || options.ForceRemoveAll)
{
var vendors = await _context.Set<Core.Entities.Vendor>()
.IgnoreQueryFilters()
.Where(v => v.CompanyId == companyId)
.ToListAsync();
if (vendors.Any())
{
_context.Set<Core.Entities.Vendor>().RemoveRange(vendors);
totalRemoved += vendors.Count;
details.Add($"✓ Removed {vendors.Count} vendor(s)");
await _context.SaveChangesAsync();
}
else
{
details.Add("• No vendors found");
}
}
// --- Named Ovens (OvenCost) ---
if (options.NamedOvens || options.ForceRemoveAll)
{
var ovens = await _context.Set<Core.Entities.OvenCost>()
.IgnoreQueryFilters()
.Where(o => o.CompanyId == companyId)
.ToListAsync();
if (ovens.Any())
{
_context.Set<Core.Entities.OvenCost>().RemoveRange(ovens);
totalRemoved += ovens.Count;
details.Add($"✓ Removed {ovens.Count} named oven(s)");
await _context.SaveChangesAsync();
}
else
{
details.Add("• No named ovens found");
}
}
// --- Appointments ---
if (options.Appointments || options.ForceRemoveAll)
{
var appointments = await _context.Set<Core.Entities.Appointment>()
.IgnoreQueryFilters()
.Where(a => a.CompanyId == companyId)
.ToListAsync();
if (appointments.Any())
{
_context.Set<Core.Entities.Appointment>().RemoveRange(appointments);
totalRemoved += appointments.Count;
details.Add($"✓ Removed {appointments.Count} appointment(s)");
await _context.SaveChangesAsync();
}
else
{
details.Add("• No appointments found");
}
}
// --- Shop Workers ---
if (options.Workers)
{
var workerUsers = await _userManager.Users
.Where(u => SeededWorkerEmails.Contains(u.Email) && u.CompanyId == companyId)
.ToListAsync();
// ForceRemoveAll: remove all non-admin company users (role = Worker/Employee).
// Normal mode: fingerprint-match by @pcldemo.com email domain.
var workerUsers = options.ForceRemoveAll
? await _userManager.Users
.Where(u => u.CompanyId == companyId && u.CompanyRole == "Worker")
.ToListAsync()
: await _userManager.Users
.Where(u => SeededWorkerEmails.Contains(u.Email) && u.CompanyId == companyId)
.ToListAsync();
if (workerUsers.Any())
{