06/11 Fix some errors: submission datetime incorrect; inspection & issue not linked, etc

This commit is contained in:
Nguyen Ngo
2026-06-11 17:18:30 -04:00
parent 3643fb1828
commit 7b4496a456
7 changed files with 223 additions and 18 deletions
+44 -13
View File
@@ -310,25 +310,56 @@ class SyncManager: ObservableObject {
.filter { $0.syncStatus == "pending" }
.sorted { $0.createdAt < $1.createdAt }
// Pre-fetch all inspections to check parent sync status.
// Pre-fetch all inspections to check parent sync status AND to resolve
// the parent's serverId. The allInspections array is the same set of
// objects that processInspectionQueue updated (serverId written in-memory
// this same triggerSync pass), so looking up serverId here is reliable.
// issue.inspection?.serverId is NOT reliable it navigates a @Relationship
// that SwiftData may have loaded as a separate object instance before
// processInspectionQueue wrote the serverId back, leaving it nil even
// when the parent inspection already synced successfully this same pass.
let allInspections = (try? context.fetch(FetchDescriptor<LocalInspection>())) ?? []
for issue in pending {
// Guard: if the parent inspection permanently failed to sync,
// submitting this issue without an inspection_id would create an
// orphaned server record. Mark it failed immediately instead.
let parentId = issue.inspectionLocalId
let parent = allInspections.first(where: { $0.localId == parentId })
if parent?.syncStatus == "failed" {
issue.syncStatus = "failed"
issue.syncErrorMessage = "Parent inspection failed to sync — issue cannot be submitted."
try? context.save()
syncError = "Issue \(issue.localId.prefix(8))\u{2026} blocked: parent inspection did not sync."
continue
let parentLocalId = issue.inspectionLocalId
let parent = allInspections.first(where: { $0.localId == parentLocalId })
// Parent inspection status guards
if let parent {
switch parent.syncStatus {
case "failed":
// Parent permanently failed this issue can never be linked.
// Mark it failed immediately rather than creating an orphaned
// server record with no inspection_id.
issue.syncStatus = "failed"
issue.syncErrorMessage = "Parent inspection failed to sync — issue cannot be submitted."
try? context.save()
syncError = "Issue \(issue.localId.prefix(8))\u{2026} blocked: parent inspection did not sync."
continue
case "synced":
// Parent has a serverId proceed and link correctly.
break
default:
// Parent is still "pending" (draft or awaiting submission).
// Submitting now would create a server issue with no
// inspection_id the issue and inspection appear unlinked
// on the web. Defer until the next triggerSync() pass, by
// which point processInspectionQueue will have synced the
// parent and assigned it a serverId.
continue
}
}
// parent == nil means inspectionLocalId == "" (standalone issue) submit without inspection_id.
// Resolve the parent's server ID. Safe to force-unwrap serverId
// here the switch above guarantees parent.syncStatus == "synced"
// when parent is non-nil, so serverId is always set at this point.
let inspectionServerId = parent?.serverId
do {
let issueId = try await APIClient.shared.submitIssue(issue)
let issueId = try await APIClient.shared.submitIssue(issue, inspectionServerId: inspectionServerId)
issue.serverId = issueId
issue.syncStatus = "synced"
// Photos are now represented by photoServerPaths on the server.