package repositories import ( "gorm.io/gorm" "github.com/treytartt/casera-api/internal/models" ) // ContractorRepository handles database operations for contractors type ContractorRepository struct { db *gorm.DB } // NewContractorRepository creates a new contractor repository func NewContractorRepository(db *gorm.DB) *ContractorRepository { return &ContractorRepository{db: db} } // FindByID finds a contractor by ID with preloaded relations func (r *ContractorRepository) FindByID(id uint) (*models.Contractor, error) { var contractor models.Contractor err := r.db.Preload("CreatedBy"). Preload("Specialties"). Preload("Tasks"). Where("id = ? AND is_active = ?", id, true). First(&contractor).Error if err != nil { return nil, err } return &contractor, nil } // FindByResidence finds all contractors for a residence func (r *ContractorRepository) FindByResidence(residenceID uint) ([]models.Contractor, error) { var contractors []models.Contractor err := r.db.Preload("CreatedBy"). Preload("Specialties"). Where("residence_id = ? AND is_active = ?", residenceID, true). Order("is_favorite DESC, name ASC"). Find(&contractors).Error return contractors, err } // FindByUser finds all contractors accessible to a user // Returns contractors that either: // 1. Have no residence (personal contractors) AND were created by the user // 2. Belong to a residence the user has access to func (r *ContractorRepository) FindByUser(userID uint, residenceIDs []uint) ([]models.Contractor, error) { var contractors []models.Contractor query := r.db.Preload("CreatedBy"). Preload("Specialties"). Preload("Residence"). Where("is_active = ?", true) if len(residenceIDs) > 0 { // Personal contractors (no residence, created by user) OR residence contractors query = query.Where( "(residence_id IS NULL AND created_by_id = ?) OR (residence_id IN ?)", userID, residenceIDs, ) } else { // Only personal contractors query = query.Where("residence_id IS NULL AND created_by_id = ?", userID) } err := query.Order("is_favorite DESC, name ASC").Find(&contractors).Error return contractors, err } // Create creates a new contractor func (r *ContractorRepository) Create(contractor *models.Contractor) error { return r.db.Create(contractor).Error } // Update updates a contractor // Uses Omit to exclude associations that could interfere with Save func (r *ContractorRepository) Update(contractor *models.Contractor) error { return r.db.Omit("CreatedBy", "Specialties", "Tasks", "Residence").Save(contractor).Error } // Delete soft-deletes a contractor func (r *ContractorRepository) Delete(id uint) error { return r.db.Model(&models.Contractor{}). Where("id = ?", id). Update("is_active", false).Error } // ToggleFavorite toggles the favorite status of a contractor func (r *ContractorRepository) ToggleFavorite(id uint) (bool, error) { var contractor models.Contractor if err := r.db.First(&contractor, id).Error; err != nil { return false, err } newStatus := !contractor.IsFavorite err := r.db.Model(&models.Contractor{}). Where("id = ?", id). Update("is_favorite", newStatus).Error return newStatus, err } // GetTasksForContractor gets all tasks associated with a contractor func (r *ContractorRepository) GetTasksForContractor(contractorID uint) ([]models.Task, error) { var tasks []models.Task err := r.db.Preload("Category"). Preload("Priority"). Preload("Status"). Where("contractor_id = ?", contractorID). Order("due_date ASC NULLS LAST"). Find(&tasks).Error return tasks, err } // SetSpecialties sets the specialties for a contractor func (r *ContractorRepository) SetSpecialties(contractorID uint, specialtyIDs []uint) error { var contractor models.Contractor if err := r.db.First(&contractor, contractorID).Error; err != nil { return err } // Clear existing specialties if err := r.db.Model(&contractor).Association("Specialties").Clear(); err != nil { return err } if len(specialtyIDs) == 0 { return nil } // Add new specialties var specialties []models.ContractorSpecialty if err := r.db.Where("id IN ?", specialtyIDs).Find(&specialties).Error; err != nil { return err } return r.db.Model(&contractor).Association("Specialties").Append(specialties) } // CountByResidence counts contractors in a residence func (r *ContractorRepository) CountByResidence(residenceID uint) (int64, error) { var count int64 err := r.db.Model(&models.Contractor{}). Where("residence_id = ? AND is_active = ?", residenceID, true). Count(&count).Error return count, err } // === Specialty Operations === // GetAllSpecialties returns all contractor specialties func (r *ContractorRepository) GetAllSpecialties() ([]models.ContractorSpecialty, error) { var specialties []models.ContractorSpecialty err := r.db.Order("display_order, name").Find(&specialties).Error return specialties, err } // FindSpecialtyByID finds a specialty by ID func (r *ContractorRepository) FindSpecialtyByID(id uint) (*models.ContractorSpecialty, error) { var specialty models.ContractorSpecialty err := r.db.First(&specialty, id).Error if err != nil { return nil, err } return &specialty, nil }