Fix recurring task modifier application bug

When creating recurring tasks, modifiers (due, wait, priority, etc) were
not being applied because AttributeOrder was not copied to the temporary
modifier. This caused all date attributes to be ignored.

Refactored addRecurringTask to:
- Create task structs directly instead of using CreateTask (avoiding
  premature saves)
- Use ApplyToNew() instead of Apply() for modifiers before first save
- Properly copy AttributeOrder when building the temporary modifier
- Save template and instance once with all fields correctly set

This ensures recurring tasks now properly have due dates, wait dates,
and other modifiers applied when created via 'opal add' or batch import.
This commit is contained in:
2026-01-05 11:18:43 +01:00
parent 79eb3bb62a
commit 1d55f04a1f
+56 -29
View File
@@ -4,8 +4,10 @@ import (
"fmt" "fmt"
"os" "os"
"strings" "strings"
"time"
"git.jnss.me/joakim/opal/internal/engine" "git.jnss.me/joakim/opal/internal/engine"
"github.com/google/uuid"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -115,53 +117,78 @@ func addRecurringTask(description string, mod *engine.Modifier) error {
return fmt.Errorf("invalid recurrence pattern: %w", err) return fmt.Errorf("invalid recurrence pattern: %w", err)
} }
// Create template task // Create template task (without saving yet)
template, err := engine.CreateTask(description) now := time.Now()
if err != nil { template := &engine.Task{
return fmt.Errorf("failed to create template: %w", err) UUID: uuid.New(),
Status: engine.StatusRecurring,
Description: description,
Priority: engine.PriorityDefault,
Created: now,
Modified: now,
RecurrenceDuration: &duration,
Tags: []string{},
} }
template.Status = engine.StatusRecurring // Create modifier without the recur attribute
template.RecurrenceDuration = &duration tempMod := &engine.Modifier{
SetAttributes: make(map[string]*string),
AttributeOrder: []string{},
AddTags: mod.AddTags,
RemoveTags: mod.RemoveTags,
}
// Apply other modifiers to template (except recur) // Copy all attributes except recur
if mod != nil { for _, key := range mod.AttributeOrder {
tempMod := &engine.Modifier{ if key != "recur" {
SetAttributes: make(map[string]*string), val := mod.SetAttributes[key]
AddTags: mod.AddTags, tempMod.SetAttributes[key] = val
RemoveTags: mod.RemoveTags, tempMod.AttributeOrder = append(tempMod.AttributeOrder, key)
} }
}
// Copy all attributes except recur // Apply modifiers to template before first save
for key, val := range mod.SetAttributes { if err := tempMod.ApplyToNew(template); err != nil {
if key != "recur" { return fmt.Errorf("failed to apply modifiers to template: %w", err)
tempMod.SetAttributes[key] = val }
}
}
if err := tempMod.Apply(template); err != nil { // Save template
return fmt.Errorf("failed to apply modifiers to template: %w", err) if err := template.Save(); err != nil {
return fmt.Errorf("failed to save template: %w", err)
}
// Add tags to template (requires task.ID from save)
for _, tag := range mod.AddTags {
if err := template.AddTag(tag); err != nil {
return fmt.Errorf("failed to add tag to template: %w", err)
} }
} }
// Create first instance // Create first instance
instance, err := engine.CreateTask(description) instance := &engine.Task{
if err != nil { UUID: uuid.New(),
return fmt.Errorf("failed to create first instance: %w", err) Status: engine.StatusPending,
Description: description,
Priority: template.Priority,
Created: now,
Modified: now,
ParentUUID: &template.UUID,
Due: template.Due,
Wait: template.Wait,
Scheduled: template.Scheduled,
Project: template.Project,
Tags: []string{},
} }
instance.ParentUUID = &template.UUID
instance.Due = template.Due
instance.Project = template.Project
instance.Priority = template.Priority
if err := instance.Save(); err != nil { if err := instance.Save(); err != nil {
return fmt.Errorf("failed to save first instance: %w", err) return fmt.Errorf("failed to save first instance: %w", err)
} }
// Copy tags to instance // Copy tags to instance
for _, tag := range template.Tags { for _, tag := range template.Tags {
instance.AddTag(tag) if err := instance.AddTag(tag); err != nil {
return fmt.Errorf("failed to add tag to instance: %w", err)
}
} }
fmt.Printf("Created recurring task %s\n", template.UUID) fmt.Printf("Created recurring task %s\n", template.UUID)