From 1d55f04a1ff9acfc40bb79535a443f88ba92ee06 Mon Sep 17 00:00:00 2001 From: Joakim Date: Mon, 5 Jan 2026 11:18:43 +0100 Subject: [PATCH] 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. --- opal-task/cmd/add.go | 85 +++++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/opal-task/cmd/add.go b/opal-task/cmd/add.go index a8f626f..4e7a9b6 100644 --- a/opal-task/cmd/add.go +++ b/opal-task/cmd/add.go @@ -4,8 +4,10 @@ import ( "fmt" "os" "strings" + "time" "git.jnss.me/joakim/opal/internal/engine" + "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -115,53 +117,78 @@ func addRecurringTask(description string, mod *engine.Modifier) error { return fmt.Errorf("invalid recurrence pattern: %w", err) } - // Create template task - template, err := engine.CreateTask(description) - if err != nil { - return fmt.Errorf("failed to create template: %w", err) + // Create template task (without saving yet) + now := time.Now() + template := &engine.Task{ + UUID: uuid.New(), + Status: engine.StatusRecurring, + Description: description, + Priority: engine.PriorityDefault, + Created: now, + Modified: now, + RecurrenceDuration: &duration, + Tags: []string{}, } - template.Status = engine.StatusRecurring - template.RecurrenceDuration = &duration + // Create modifier without the recur attribute + tempMod := &engine.Modifier{ + SetAttributes: make(map[string]*string), + AttributeOrder: []string{}, + AddTags: mod.AddTags, + RemoveTags: mod.RemoveTags, + } - // Apply other modifiers to template (except recur) - if mod != nil { - tempMod := &engine.Modifier{ - SetAttributes: make(map[string]*string), - AddTags: mod.AddTags, - RemoveTags: mod.RemoveTags, + // Copy all attributes except recur + for _, key := range mod.AttributeOrder { + if key != "recur" { + val := mod.SetAttributes[key] + tempMod.SetAttributes[key] = val + tempMod.AttributeOrder = append(tempMod.AttributeOrder, key) } + } - // Copy all attributes except recur - for key, val := range mod.SetAttributes { - if key != "recur" { - tempMod.SetAttributes[key] = val - } - } + // Apply modifiers to template before first save + if err := tempMod.ApplyToNew(template); err != nil { + return fmt.Errorf("failed to apply modifiers to template: %w", err) + } - if err := tempMod.Apply(template); err != nil { - return fmt.Errorf("failed to apply modifiers to template: %w", err) + // Save template + 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 - instance, err := engine.CreateTask(description) - if err != nil { - return fmt.Errorf("failed to create first instance: %w", err) + instance := &engine.Task{ + UUID: uuid.New(), + 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 { return fmt.Errorf("failed to save first instance: %w", err) } // Copy tags to instance 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)