Fix sync errors for duplicate projects and add modal scrolling

Root cause: Projects with duplicate (code, variant) pairs fail to sync
due to unique constraint on server. Example: multiple "OPS-1934" projects
with variant="Dell" where one already exists on server.

Fixes:
1. Sync service now detects duplicate (code, variant) on server and links
   local project to existing server project instead of failing
2. Local repair checks for duplicate (code, variant) pairs and deduplicates
   by appending UUID suffix to variant
3. Modal now scrollable with fixed header/footer (max-h-90vh)

This allows users to sync projects that were created offline with
conflicting codes/variants without losing data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 21:25:22 +03:00
parent b153afbf51
commit 8508ee2921
3 changed files with 48 additions and 9 deletions

View File

@@ -856,10 +856,29 @@ func (s *Service) pushProjectChange(change *localdb.PendingChange) error {
}
}
if err := projectRepo.UpsertByUUID(&project); err != nil {
return fmt.Errorf("upsert project on server: %w", err)
// Try upsert by UUID first
err = projectRepo.UpsertByUUID(&project)
if err != nil {
// Check if it's a duplicate (code, variant) constraint violation
// In this case, find existing project with same (code, variant) and link to it
var existing models.Project
lookupErr := mariaDB.Where("code = ? AND variant = ?", project.Code, project.Variant).First(&existing).Error
if lookupErr == nil {
// Found duplicate - link local project to existing server project
slog.Info("project duplicate found, linking to existing",
"local_uuid", project.UUID,
"server_uuid", existing.UUID,
"server_id", existing.ID,
"code", project.Code,
"variant", project.Variant)
project.ID = existing.ID
} else {
// Not a duplicate issue, return original error
return fmt.Errorf("upsert project on server: %w", err)
}
}
// Update local project with server ID
localProject, localErr := s.localDB.GetProjectByUUID(project.UUID)
if localErr == nil {
if project.ID > 0 {