Remove remaining status_id references after in_progress migration
- Remove Preload("Status") from worker handler and repositories
- Update seeds to use in_progress boolean instead of status_id
- Remove task_taskstatus table creation from lookup seeds
- Update documentation to reflect in_progress boolean pattern
Fixes notification worker error:
"Status: unsupported relations for schema Task"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -82,14 +82,14 @@ if task.NextDueDate == nil && len(task.Completions) > 0 {
|
||||
|
||||
**Handler**: `InProgressHandler`
|
||||
|
||||
**Condition**: `task.Status != nil && task.Status.Name == "In Progress"`
|
||||
**Condition**: `task.InProgress == true`
|
||||
|
||||
**Result**: `in_progress_tasks`
|
||||
|
||||
Tasks with the "In Progress" status are grouped together regardless of their due date. This allows users to see what's actively being worked on.
|
||||
Tasks marked as "In Progress" are grouped together regardless of their due date. This allows users to see what's actively being worked on.
|
||||
|
||||
```go
|
||||
if task.Status != nil && task.Status.Name == "In Progress" {
|
||||
if task.InProgress {
|
||||
return "in_progress_tasks"
|
||||
}
|
||||
```
|
||||
@@ -188,15 +188,14 @@ return "upcoming_tasks"
|
||||
This is by design - "In Progress" indicates active work, so the task should be visible there. However, this means:
|
||||
|
||||
1. If a recurring task is marked "In Progress" and then completed
|
||||
2. The status MUST be reset to "Pending" after completion
|
||||
2. The `in_progress` flag MUST be reset to `false` after completion
|
||||
3. Otherwise, the task stays in "In Progress" instead of moving to "Upcoming"
|
||||
|
||||
This is handled automatically in `TaskService.CreateCompletion()`:
|
||||
|
||||
```go
|
||||
if isRecurringTask {
|
||||
pendingStatusID := uint(1)
|
||||
task.StatusID = &pendingStatusID
|
||||
task.InProgress = false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -246,8 +245,8 @@ Each column has associated metadata for UI rendering:
|
||||
import "github.com/treytartt/casera-api/internal/task/categorization"
|
||||
|
||||
task := &models.Task{
|
||||
DueDate: time.Now().AddDate(0, 0, 15), // 15 days from now
|
||||
Status: &models.TaskStatus{Name: "Pending"},
|
||||
DueDate: time.Now().AddDate(0, 0, 15), // 15 days from now
|
||||
InProgress: false,
|
||||
}
|
||||
|
||||
column := categorization.DetermineKanbanColumn(task, 30)
|
||||
@@ -291,14 +290,14 @@ Key test scenarios:
|
||||
|
||||
1. **Check `IsCancelled`**: Cancelled takes highest priority
|
||||
2. **Check `NextDueDate`**: For recurring tasks, this determines placement
|
||||
3. **Check `Status`**: "In Progress" overrides date-based categorization
|
||||
3. **Check `InProgress`**: `true` overrides date-based categorization
|
||||
4. **Check `Completions`**: Empty + nil NextDueDate = upcoming, not completed
|
||||
|
||||
### Recurring task not moving to Upcoming after completion?
|
||||
|
||||
Verify that `CreateCompletion` is:
|
||||
1. Setting `NextDueDate` correctly
|
||||
2. Resetting `StatusID` to Pending (1)
|
||||
2. Resetting `InProgress` to `false`
|
||||
|
||||
### Task showing overdue but due date looks correct?
|
||||
|
||||
|
||||
@@ -103,12 +103,12 @@ if len(task.Completions) > 0 {
|
||||
|
||||
### 3. In Progress
|
||||
```go
|
||||
if task.Status != nil && task.Status.Name == "In Progress" {
|
||||
if task.InProgress {
|
||||
inProgress = append(inProgress, task)
|
||||
continue
|
||||
}
|
||||
```
|
||||
- **Condition**: Task's status name is exactly `"In Progress"`
|
||||
- **Condition**: Task's `in_progress` boolean is `true`
|
||||
- **Button Types**: `edit`, `complete`, `cancel`
|
||||
- **Rationale**: In-progress tasks can be edited, marked complete, or cancelled if work is abandoned
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ predicates.IsCompleted(task) // NextDueDate == nil && len(Completions) > 0
|
||||
predicates.IsActive(task) // !IsCancelled && !IsArchived
|
||||
predicates.IsCancelled(task) // IsCancelled == true
|
||||
predicates.IsArchived(task) // IsArchived == true
|
||||
predicates.IsInProgress(task) // Status.Name == "In Progress"
|
||||
predicates.IsInProgress(task) // InProgress == true
|
||||
|
||||
// Date calculations
|
||||
predicates.EffectiveDate(task) // NextDueDate ?? DueDate
|
||||
@@ -105,7 +105,7 @@ These rules are defined in `predicates/predicates.go` and enforced everywhere:
|
||||
|---------|------------|----------------|
|
||||
| **Completed** | `NextDueDate == nil && len(Completions) > 0` | `next_due_date IS NULL AND EXISTS (SELECT 1 FROM task_taskcompletion WHERE task_id = ?)` |
|
||||
| **Active** | `!IsCancelled && !IsArchived` | `is_cancelled = false AND is_archived = false` |
|
||||
| **In Progress** | `Status.Name == "In Progress"` | `JOIN task_taskstatus WHERE name = 'In Progress'` |
|
||||
| **In Progress** | `InProgress == true` | `in_progress = true` |
|
||||
| **Effective Date** | `NextDueDate ?? DueDate` | `COALESCE(next_due_date, due_date)` |
|
||||
| **Overdue** | `Active && !Completed && EffectiveDate < now` | Active + NotCompleted + `COALESCE(...) < ?` |
|
||||
| **Due Soon** | `Active && !Completed && now <= EffectiveDate < threshold` | Active + NotCompleted + `COALESCE(...) >= ? AND COALESCE(...) < ?` |
|
||||
@@ -117,7 +117,7 @@ When categorizing a task, the chain evaluates in this priority order:
|
||||
|
||||
1. **Cancelled** (highest) - `IsCancelled == true`
|
||||
2. **Completed** - `NextDueDate == nil && len(Completions) > 0`
|
||||
3. **In Progress** - `Status.Name == "In Progress"`
|
||||
3. **In Progress** - `InProgress == true`
|
||||
4. **Overdue** - `EffectiveDate < now`
|
||||
5. **Due Soon** - `now <= EffectiveDate < threshold`
|
||||
6. **Upcoming** (lowest/default) - Everything else
|
||||
@@ -139,7 +139,7 @@ db.Model(&models.Task{}).
|
||||
```go
|
||||
// Load tasks with preloads
|
||||
var tasks []models.Task
|
||||
db.Preload("Status").Preload("Completions").
|
||||
db.Preload("Completions").
|
||||
Scopes(task.ScopeForResidence(residenceID)).
|
||||
Find(&tasks)
|
||||
|
||||
@@ -329,18 +329,14 @@ db.Preload("Completions").Find(&tasks)
|
||||
predicates.IsCompleted(task) // Correct result
|
||||
```
|
||||
|
||||
### Forgetting to Preload Status
|
||||
### In Progress Boolean Field
|
||||
|
||||
The `IsInProgress` predicate checks `task.Status.Name == "In Progress"`. Without preloading:
|
||||
The `IsInProgress` predicate now uses a simple boolean field instead of a Status relation:
|
||||
|
||||
```go
|
||||
// BAD: Status not loaded
|
||||
db.Find(&tasks)
|
||||
predicates.IsInProgress(task) // Nil pointer or always false
|
||||
|
||||
// GOOD: Preload status
|
||||
db.Preload("Status").Find(&tasks)
|
||||
predicates.IsInProgress(task) // Correct result
|
||||
// IsInProgress uses the in_progress boolean field directly
|
||||
// No preloading required for this check
|
||||
predicates.IsInProgress(task) // Checks task.InProgress boolean
|
||||
```
|
||||
|
||||
## Quick Reference Import
|
||||
|
||||
Reference in New Issue
Block a user