Fix all FK constraint violations when purging soft-deleted Jobs
Before deleting Jobs, now: - Deletes non-nullable child rows: ReworkRecords, PowderUsageLogs, OvenBatchItems - Nulls out nullable FK refs: Invoices, Deposits, Appointments, BillLineItems, Expenses, InventoryTransactions DB-cascade / SET NULL relationships (JobChangeHistory, JobStatusHistory, JobTimeEntry, JobItems, JobNotes, JobPhotos, KioskSession, NotificationLog) are excluded — the DB handles them automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -293,16 +293,48 @@ public class DataPurgeController : Controller
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "Jobs":
|
case "Jobs":
|
||||||
// Appointments.JobId is a nullable FK — null it out first so the DELETE
|
// Collect IDs first so all FK cleanup targets the exact same set of jobs.
|
||||||
// doesn't violate FK_Appointments_Jobs_JobId.
|
|
||||||
var purgingJobIds = await _db.Jobs.IgnoreQueryFilters()
|
var purgingJobIds = await _db.Jobs.IgnoreQueryFilters()
|
||||||
.Where(e => e.IsDeleted && e.DeletedAt <= cutoff)
|
.Where(e => e.IsDeleted && e.DeletedAt <= cutoff)
|
||||||
.Select(e => e.Id)
|
.Select(e => e.Id)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
if (purgingJobIds.Count > 0)
|
if (purgingJobIds.Count > 0)
|
||||||
|
{
|
||||||
|
// Non-nullable FK children must be deleted before the parent row can go.
|
||||||
|
// ReworkRecord.JobId (Restrict), PowderUsageLog.JobId (NoAction),
|
||||||
|
// OvenBatchItem.JobId (NoAction) — cannot be nulled, so rows are removed.
|
||||||
|
await _db.ReworkRecords.IgnoreQueryFilters()
|
||||||
|
.Where(r => purgingJobIds.Contains(r.JobId))
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
await _db.PowderUsageLogs.IgnoreQueryFilters()
|
||||||
|
.Where(l => purgingJobIds.Contains(l.JobId))
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
await _db.OvenBatchItems.IgnoreQueryFilters()
|
||||||
|
.Where(i => purgingJobIds.Contains(i.JobId))
|
||||||
|
.ExecuteDeleteAsync();
|
||||||
|
|
||||||
|
// Nullable FKs with NO ACTION / RESTRICT — null them out so the DELETE
|
||||||
|
// does not violate the constraint. KioskSession and NotificationLog are
|
||||||
|
// excluded here because their FKs use SET NULL and the DB handles them.
|
||||||
|
await _db.Invoices.IgnoreQueryFilters()
|
||||||
|
.Where(i => i.JobId.HasValue && purgingJobIds.Contains(i.JobId.Value))
|
||||||
|
.ExecuteUpdateAsync(s => s.SetProperty(i => i.JobId, (int?)null));
|
||||||
|
await _db.Deposits.IgnoreQueryFilters()
|
||||||
|
.Where(d => d.JobId.HasValue && purgingJobIds.Contains(d.JobId.Value))
|
||||||
|
.ExecuteUpdateAsync(s => s.SetProperty(d => d.JobId, (int?)null));
|
||||||
await _db.Appointments.IgnoreQueryFilters()
|
await _db.Appointments.IgnoreQueryFilters()
|
||||||
.Where(a => a.JobId.HasValue && purgingJobIds.Contains(a.JobId.Value))
|
.Where(a => a.JobId.HasValue && purgingJobIds.Contains(a.JobId.Value))
|
||||||
.ExecuteUpdateAsync(s => s.SetProperty(a => a.JobId, (int?)null));
|
.ExecuteUpdateAsync(s => s.SetProperty(a => a.JobId, (int?)null));
|
||||||
|
await _db.BillLineItems.IgnoreQueryFilters()
|
||||||
|
.Where(b => b.JobId.HasValue && purgingJobIds.Contains(b.JobId.Value))
|
||||||
|
.ExecuteUpdateAsync(s => s.SetProperty(b => b.JobId, (int?)null));
|
||||||
|
await _db.Expenses.IgnoreQueryFilters()
|
||||||
|
.Where(e => e.JobId.HasValue && purgingJobIds.Contains(e.JobId.Value))
|
||||||
|
.ExecuteUpdateAsync(s => s.SetProperty(e => e.JobId, (int?)null));
|
||||||
|
await _db.InventoryTransactions.IgnoreQueryFilters()
|
||||||
|
.Where(t => t.JobId.HasValue && purgingJobIds.Contains(t.JobId.Value))
|
||||||
|
.ExecuteUpdateAsync(s => s.SetProperty(t => t.JobId, (int?)null));
|
||||||
|
}
|
||||||
count = await _db.Jobs.IgnoreQueryFilters()
|
count = await _db.Jobs.IgnoreQueryFilters()
|
||||||
.Where(e => e.IsDeleted && e.DeletedAt <= cutoff).ExecuteDeleteAsync();
|
.Where(e => e.IsDeleted && e.DeletedAt <= cutoff).ExecuteDeleteAsync();
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user