- Make residence_id nullable in contractor model - Add created_by_id field to track contractor creator - Update access control: personal contractors visible only to creator, residence contractors visible to all residence users - Add database migration for schema changes - Update admin panel DTOs and handlers for optional residence - Fix test utilities for new model structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
166 lines
4.9 KiB
Go
166 lines
4.9 KiB
Go
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
|
|
func (r *ContractorRepository) Update(contractor *models.Contractor) error {
|
|
return r.db.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
|
|
}
|