Features: - Add task action buttons to push notifications (complete, view, cancel, etc.) - Add button types logic for different task states (overdue, in_progress, etc.) - Implement Chain of Responsibility pattern for task categorization - Add comprehensive kanban categorization documentation Fixes: - Reset recurring task status to Pending after completion so tasks appear in correct kanban column (was staying in "In Progress") - Fix PostgreSQL EXTRACT function error in overdue notifications query - Update seed data to properly set next_due_date for recurring tasks Admin: - Add tasks list to residence detail page - Fix task edit page to properly handle all fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
157 lines
4.9 KiB
Go
157 lines
4.9 KiB
Go
package repositories
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/treytartt/casera-api/internal/models"
|
|
)
|
|
|
|
// DocumentRepository handles database operations for documents
|
|
type DocumentRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewDocumentRepository creates a new document repository
|
|
func NewDocumentRepository(db *gorm.DB) *DocumentRepository {
|
|
return &DocumentRepository{db: db}
|
|
}
|
|
|
|
// FindByID finds a document by ID with preloaded relations
|
|
func (r *DocumentRepository) FindByID(id uint) (*models.Document, error) {
|
|
var document models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Task").
|
|
Preload("Images").
|
|
Where("id = ? AND is_active = ?", id, true).
|
|
First(&document).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &document, nil
|
|
}
|
|
|
|
// FindByResidence finds all documents for a residence
|
|
func (r *DocumentRepository) FindByResidence(residenceID uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Images").
|
|
Where("residence_id = ? AND is_active = ?", residenceID, true).
|
|
Order("created_at DESC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindByUser finds all documents accessible to a user
|
|
func (r *DocumentRepository) FindByUser(residenceIDs []uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ?", residenceIDs, true).
|
|
Order("created_at DESC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindWarranties finds all warranty documents
|
|
func (r *DocumentRepository) FindWarranties(residenceIDs []uint) ([]models.Document, error) {
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ? AND document_type = ?",
|
|
residenceIDs, true, models.DocumentTypeWarranty).
|
|
Order("expiry_date ASC NULLS LAST").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// FindExpiringWarranties finds warranties expiring within the specified days
|
|
func (r *DocumentRepository) FindExpiringWarranties(residenceIDs []uint, days int) ([]models.Document, error) {
|
|
threshold := time.Now().UTC().AddDate(0, 0, days)
|
|
now := time.Now().UTC()
|
|
|
|
var documents []models.Document
|
|
err := r.db.Preload("CreatedBy").
|
|
Preload("Residence").
|
|
Preload("Images").
|
|
Where("residence_id IN ? AND is_active = ? AND document_type = ? AND expiry_date > ? AND expiry_date <= ?",
|
|
residenceIDs, true, models.DocumentTypeWarranty, now, threshold).
|
|
Order("expiry_date ASC").
|
|
Find(&documents).Error
|
|
return documents, err
|
|
}
|
|
|
|
// Create creates a new document
|
|
func (r *DocumentRepository) Create(document *models.Document) error {
|
|
return r.db.Create(document).Error
|
|
}
|
|
|
|
// Update updates a document
|
|
// Uses Omit to exclude associations that could interfere with Save
|
|
func (r *DocumentRepository) Update(document *models.Document) error {
|
|
return r.db.Omit("CreatedBy", "Task", "Images", "Residence").Save(document).Error
|
|
}
|
|
|
|
// Delete soft-deletes a document
|
|
func (r *DocumentRepository) Delete(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", false).Error
|
|
}
|
|
|
|
// Activate activates a document
|
|
func (r *DocumentRepository) Activate(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", true).Error
|
|
}
|
|
|
|
// Deactivate deactivates a document
|
|
func (r *DocumentRepository) Deactivate(id uint) error {
|
|
return r.db.Model(&models.Document{}).
|
|
Where("id = ?", id).
|
|
Update("is_active", false).Error
|
|
}
|
|
|
|
// CountByResidence counts documents in a residence
|
|
func (r *DocumentRepository) CountByResidence(residenceID uint) (int64, error) {
|
|
var count int64
|
|
err := r.db.Model(&models.Document{}).
|
|
Where("residence_id = ? AND is_active = ?", residenceID, true).
|
|
Count(&count).Error
|
|
return count, err
|
|
}
|
|
|
|
// FindByIDIncludingInactive finds a document by ID including inactive ones
|
|
func (r *DocumentRepository) FindByIDIncludingInactive(id uint, document *models.Document) error {
|
|
return r.db.Preload("CreatedBy").Preload("Images").First(document, id).Error
|
|
}
|
|
|
|
// CreateDocumentImage creates a new document image
|
|
func (r *DocumentRepository) CreateDocumentImage(image *models.DocumentImage) error {
|
|
return r.db.Create(image).Error
|
|
}
|
|
|
|
// DeleteDocumentImage deletes a document image
|
|
func (r *DocumentRepository) DeleteDocumentImage(id uint) error {
|
|
return r.db.Delete(&models.DocumentImage{}, id).Error
|
|
}
|
|
|
|
// DeleteDocumentImages deletes all images for a document
|
|
func (r *DocumentRepository) DeleteDocumentImages(documentID uint) error {
|
|
return r.db.Where("document_id = ?", documentID).Delete(&models.DocumentImage{}).Error
|
|
}
|
|
|
|
// FindImageByID finds a document image by ID
|
|
func (r *DocumentRepository) FindImageByID(id uint) (*models.DocumentImage, error) {
|
|
var image models.DocumentImage
|
|
err := r.db.First(&image, id).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &image, nil
|
|
}
|