Fix backend API parity: document filters, task days param, i18n locales, contract tests

- Add document list filter support (residence, type, category, contractor, is_active, expiring_soon, search) to handler/service/repo
- Add `days` query param parsing to ListTasks handler (matches ListTasksByResidence)
- Add `error.invalid_token` i18n key to all 9 non-English locale files
- Update contract test to include VerificationResponse mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-18 18:49:48 -06:00
parent bb7493f033
commit e7c23bdeb1
16 changed files with 139 additions and 11 deletions

View File

@@ -8,6 +8,18 @@ import (
"github.com/treytartt/casera-api/internal/models"
)
// DocumentFilter contains optional filter parameters for listing documents.
type DocumentFilter struct {
ResidenceID *uint
DocumentType string
Category string
ContractorID *uint
IsActive *bool
ExpiringSoon *int
Tags string
Search string
}
// DocumentRepository handles database operations for documents
type DocumentRepository struct {
db *gorm.DB
@@ -55,6 +67,45 @@ func (r *DocumentRepository) FindByUser(residenceIDs []uint) ([]models.Document,
return documents, err
}
// FindByUserFiltered finds documents accessible to a user with optional filters.
func (r *DocumentRepository) FindByUserFiltered(residenceIDs []uint, filter *DocumentFilter) ([]models.Document, error) {
query := r.db.Preload("CreatedBy").
Preload("Residence").
Preload("Images").
Where("residence_id IN ? AND is_active = ?", residenceIDs, true)
if filter != nil {
if filter.DocumentType != "" {
query = query.Where("document_type = ?", filter.DocumentType)
}
if filter.Category != "" {
query = query.Where("category = ?", filter.Category)
}
if filter.ContractorID != nil {
query = query.Where("contractor_id = ?", *filter.ContractorID)
}
if filter.IsActive != nil {
query = query.Where("is_active = ?", *filter.IsActive)
}
if filter.ExpiringSoon != nil {
now := time.Now().UTC()
threshold := now.AddDate(0, 0, *filter.ExpiringSoon)
query = query.Where("expiry_date IS NOT NULL AND expiry_date > ? AND expiry_date <= ?", now, threshold)
}
if filter.Tags != "" {
query = query.Where("tags LIKE ?", "%"+filter.Tags+"%")
}
if filter.Search != "" {
searchPattern := "%" + filter.Search + "%"
query = query.Where("(title ILIKE ? OR description ILIKE ?)", searchPattern, searchPattern)
}
}
var documents []models.Document
err := query.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