Aug 17 - Update for customer inspector contract scope

This commit is contained in:
2026-08-17 16:07:50 -04:00
parent 1921f95faf
commit 5a01d141f4
4 changed files with 78 additions and 4 deletions
+47
View File
@@ -513,6 +513,53 @@ class SyncManager: ObservableObject {
try await upsertAreas(for: apiFacility.id, facility: localFacility, context: context)
}
// Prune facilities the server no longer returns
// /api/v1/facilities is already scoped to what this user may see,
// but cached rows were never removed so a facility survived
// locally after the inspector's contract was unassigned, after it
// was deactivated, or after a different user signed in on the same
// iPad. Every picker derives its CONTRACT list from these rows, so
// one stale facility keeps a whole contract in the Start
// Inspection picker forever. (Templates were already pruned this
// way below; facilities were the gap.)
//
// Safe because we only reach here after BOTH requests succeeded
// a failed sync throws before this point and deletes nothing.
let returnedFacilityIds = Set(uniqueFacilities.map { $0.id })
// Work that has not reached the server yet still needs its
// facility row: ExecuteInspectionView and MyInspectionsView resolve
// the name by serverId and would otherwise show "Unknown Facility"
// on a draft the inspector is midway through. Keep those rows but
// mark them unavailable so no NEW work can be started against them;
// they are pruned on a later sync once the work has been submitted.
let localInspections = try context.fetch(FetchDescriptor<LocalInspection>())
let localIssues = try context.fetch(FetchDescriptor<LocalIssue>())
var inUseFacilityIds = Set(
localInspections
.filter { $0.syncStatus != "synced" }
.map { $0.facilityServerId }
)
// Device-created issues too: their facilityNameCache is nil until
// the server round-trips, and the issue DETAIL view has no cache
// fallback it would read "Unknown Facility" outright.
inUseFacilityIds.formUnion(
localIssues
.filter { $0.syncStatus != "synced" }
.map { $0.facilityServerId }
)
for existing in existingFacilities {
guard !returnedFacilityIds.contains(existing.serverId) else { continue }
if inUseFacilityIds.contains(existing.serverId) {
// Retained for display only. The pickers filter on
// isActive, so it cannot be chosen for new work.
existing.isActive = false
} else {
context.delete(existing) // cascades to its areas
}
}
let existingTemplates = try context.fetch(FetchDescriptor<LocalTemplate>())
let templateMap = Dictionary(
existingTemplates.map { ($0.serverId, $0) },