Consolidate task logic into single source of truth (DRY refactor)
This refactor eliminates duplicate task logic across the codebase by creating a centralized task package with three layers: - predicates/: Pure Go functions defining task state logic (IsCompleted, IsOverdue, IsDueSoon, IsUpcoming, IsActive, IsInProgress, EffectiveDate) - scopes/: GORM scope functions mirroring predicates for database queries - categorization/: Chain of Responsibility pattern for kanban column assignment Key fixes: - Fixed PostgreSQL DATE vs TIMESTAMP comparison bug in scopes (added explicit ::timestamp casts) that caused summary/kanban count mismatches - Fixed models/task.go IsOverdue() and IsDueSoon() to use EffectiveDate (NextDueDate ?? DueDate) instead of only DueDate - Removed duplicate isTaskCompleted() helpers from task_repo.go and task_button_types.go Files refactored to use consolidated logic: - task_repo.go: Uses scopes for statistics, predicates for filtering - task_button_types.go: Uses predicates instead of inline logic - responses/task.go: Delegates to categorization package - dashboard_handler.go: Uses scopes for task statistics - residence_service.go: Uses predicates for report generation - worker/jobs/handler.go: Documented SQL with predicate references Added comprehensive tests: - predicates_test.go: Unit tests for all predicate functions - scopes_test.go: Integration tests verifying scopes match predicates - consistency_test.go: Three-layer consistency tests ensuring predicates, scopes, and categorization all return identical results 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/casera-api/internal/models"
|
||||
"github.com/treytartt/casera-api/internal/task/categorization"
|
||||
)
|
||||
|
||||
// TaskCategoryResponse represents a task category
|
||||
@@ -365,53 +366,8 @@ func NewTaskCompletionWithTaskResponse(c *models.TaskCompletion, task *models.Ta
|
||||
}
|
||||
|
||||
// DetermineKanbanColumn determines which kanban column a task belongs to.
|
||||
// This is a wrapper around the Chain of Responsibility implementation in
|
||||
// internal/task/categorization package. See that package for detailed
|
||||
// documentation on the categorization logic.
|
||||
//
|
||||
// Deprecated: Use categorization.DetermineKanbanColumn directly for new code.
|
||||
// Delegates to internal/task/categorization package which is the single source
|
||||
// of truth for task categorization logic.
|
||||
func DetermineKanbanColumn(task *models.Task, daysThreshold int) string {
|
||||
// Import would cause circular dependency, so we replicate the logic here
|
||||
// for backwards compatibility. The authoritative implementation is in
|
||||
// internal/task/categorization/chain.go
|
||||
if daysThreshold <= 0 {
|
||||
daysThreshold = 30 // Default
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
threshold := now.AddDate(0, 0, daysThreshold)
|
||||
|
||||
// Priority order (Chain of Responsibility):
|
||||
// 1. Cancelled (highest priority)
|
||||
if task.IsCancelled {
|
||||
return "cancelled_tasks"
|
||||
}
|
||||
|
||||
// 2. Completed (one-time task with nil next_due_date and has completions)
|
||||
if task.NextDueDate == nil && len(task.Completions) > 0 {
|
||||
return "completed_tasks"
|
||||
}
|
||||
|
||||
// 3. In Progress (status check)
|
||||
if task.Status != nil && task.Status.Name == "In Progress" {
|
||||
return "in_progress_tasks"
|
||||
}
|
||||
|
||||
// 4. Overdue (next_due_date or due_date is in the past)
|
||||
effectiveDate := task.NextDueDate
|
||||
if effectiveDate == nil {
|
||||
effectiveDate = task.DueDate
|
||||
}
|
||||
if effectiveDate != nil {
|
||||
if effectiveDate.Before(now) {
|
||||
return "overdue_tasks"
|
||||
}
|
||||
// 5. Due Soon (within threshold)
|
||||
if effectiveDate.Before(threshold) {
|
||||
return "due_soon_tasks"
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Upcoming (default/fallback)
|
||||
return "upcoming_tasks"
|
||||
return categorization.DetermineKanbanColumn(task, daysThreshold)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user