From 0add620b477edd6ad19f72b1170ea90182ac4150 Mon Sep 17 00:00:00 2001 From: James Salt Date: Wed, 27 May 2026 11:50:28 +0100 Subject: [PATCH] BCH-1156: Filter My Tasks by effective due date using COALESCE Step executions store their due_date on step_executions.due_date while workflow_executions.due_date is NULL. The previous filter queried only the workflow execution column, so every date range returned zero results. COALESCE(step_executions.due_date, workflow_executions.due_date) mirrors the UI's getEffectiveDueDate logic and correctly includes tasks. Co-Authored-By: Claude Sonnet 4.6 --- .../workflows/step_execution_service.go | 7 +-- .../workflows/step_execution_service_test.go | 43 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/internal/service/relational/workflows/step_execution_service.go b/internal/service/relational/workflows/step_execution_service.go index 98be1e1f..61b4c1a6 100644 --- a/internal/service/relational/workflows/step_execution_service.go +++ b/internal/service/relational/workflows/step_execution_service.go @@ -404,12 +404,13 @@ func (s *StepExecutionService) GetMyAssignments(userID, userEmail string, filter query = query.Where("step_executions.status = ?", filter.Status) } - // Apply due date filters (on workflow execution) + // Filter on effective due date: step's own due_date takes precedence over the + // workflow execution's due_date, mirroring getEffectiveDueDate in the UI. if filter.DueBefore != nil { - query = query.Where("workflow_executions.due_date <= ?", filter.DueBefore) + query = query.Where("COALESCE(step_executions.due_date, workflow_executions.due_date) <= ?", filter.DueBefore) } if filter.DueAfter != nil { - query = query.Where("workflow_executions.due_date >= ?", filter.DueAfter) + query = query.Where("COALESCE(step_executions.due_date, workflow_executions.due_date) >= ?", filter.DueAfter) } // Apply workflow definition filter diff --git a/internal/service/relational/workflows/step_execution_service_test.go b/internal/service/relational/workflows/step_execution_service_test.go index d6b70af1..48de61f6 100644 --- a/internal/service/relational/workflows/step_execution_service_test.go +++ b/internal/service/relational/workflows/step_execution_service_test.go @@ -3,6 +3,7 @@ package workflows import ( "context" "testing" + "time" "github.com/compliance-framework/api/internal/service/relational" "github.com/google/uuid" @@ -870,3 +871,45 @@ func TestStepExecutionService_Integration(t *testing.T) { require.NoError(t, err) assert.Len(t, completedSteps, 2) } + +// TestGetMyAssignments_FiltersOnStepDueDate covers BCH-1156: when a step execution +// has its own due_date but the parent workflow_execution.due_date is NULL, a date +// range filter must still match using the step's due_date (the effective due date +// shown by the UI). +func TestGetMyAssignments_FiltersOnStepDueDate(t *testing.T) { + db := setupTestDB(t) + service := NewStepExecutionService(db, nil) + + workflowDef := createTestWorkflowDefinition() + require.NoError(t, db.Create(workflowDef).Error) + + instance := createTestWorkflowInstance(workflowDef.ID) + require.NoError(t, db.Create(instance).Error) + + // Execution has no due_date — mirrors real data where due_date lives on the step + execution := createTestWorkflowExecution(instance.ID) + execution.Status = "in_progress" + require.NoError(t, db.Create(execution).Error) + + stepDef := createTestWorkflowStepDefinition(workflowDef.ID) + require.NoError(t, db.Create(stepDef).Error) + + may28 := time.Date(2026, 5, 28, 10, 0, 0, 0, time.UTC) + stepExec := createTestStepExecution(execution.ID, stepDef.ID) + stepExec.DueDate = &may28 + stepExec.AssignedToType = "user" + stepExec.AssignedToID = "test-user-abc" + require.NoError(t, db.Create(stepExec).Error) + + after := time.Date(2026, 5, 26, 0, 0, 0, 0, time.UTC) + before := time.Date(2026, 5, 28, 23, 59, 59, 0, time.UTC) + + results, total, err := service.GetMyAssignments("test-user-abc", "", MyAssignmentsFilter{ + DueAfter: &after, + DueBefore: &before, + }, 10, 0) + + require.NoError(t, err) + assert.Equal(t, int64(1), total, "step with due_date on step_executions (not workflow_executions) must be returned") + assert.Len(t, results, 1) +}