diff --git a/internal/api/notify_test.go b/internal/api/notify_test.go index fce410f..5b9e8b3 100644 --- a/internal/api/notify_test.go +++ b/internal/api/notify_test.go @@ -158,3 +158,33 @@ func TestDailySummary(t *testing.T) { t.Fatal("only one summary per day") } } + +func TestDailySummaryIncludesGroupSharedLists(t *testing.T) { + srv, sent := notifyServer(t) + store := srv.store + owner, _ := newTestUser(t, store, "chef@example.com") + me, _ := newTestUser(t, store, "team@example.com") + team, err := store.CreateTeam("", "Arbeit", "") + if err != nil { + t.Fatal(err) + } + for _, uid := range []string{owner.ID, me.ID} { // the owner must see the group to share with it + if err := store.AddTeamMember(team.ID, uid, "member"); err != nil { + t.Fatal(err) + } + } + list, _ := store.CreateTaskList(owner.ID, "", "", "Projekt", "📋", "#000000") + if _, err := store.AddTaskListShare(list.ID, "team", team.ID, "editor", owner.ID); err != nil { + t.Fatal(err) + } + th, _ := store.CreateThoughtWithDetails(owner.ID, "Bericht abgeben", "normal", "manual", "2026-10-30T10:00", list.ID, "todo", "") + _, _ = store.UpdateThoughtTriage(owner.ID, th.ID, "keep", "task", "", "2026-10-30T10:00", list.ID) + + set := store.NotificationSettingsOf(me.ID) + set.DailySummary = domain.DailySummary{Enabled: true, Time: "07:00", Email: true} + _ = store.SetNotificationSettings(me.ID, set) + srv.sendDailySummaries(time.Date(2026, 10, 30, 7, 1, 0, 0, time.Local)) + if len(*sent) != 1 || !strings.Contains((*sent)[0].text, "Bericht abgeben (Projekt)") { + t.Fatalf("group-shared task missing: %+v", *sent) + } +} diff --git a/internal/storage/notifications.go b/internal/storage/notifications.go index ce6c5ee..0892750 100644 --- a/internal/storage/notifications.go +++ b/internal/storage/notifications.go @@ -208,15 +208,20 @@ func (s *Store) ActiveUserIDs() []string { return out } -// OpenTasksDueBefore lists the user's open tasks (own ones and those of shared task lists) due before +// OpenTasksDueBefore lists the user's open tasks (own ones, those of own lists and of lists shared with +// the user directly or through a group) due before // the given day (YYYY-MM-DD, exclusive), oldest first. func (s *Store) OpenTasksDueBefore(userID, beforeDay string) ([]SummaryTask, error) { rows, err := s.db.Query(`SELECT t.content, t.due_date, COALESCE(l.title, '') FROM thoughts t LEFT JOIN task_lists l ON l.id = t.task_list_id WHERE (t.status = 'kept' OR (t.status = 'converted' AND t.converted_type = 'task')) AND t.is_completed = 0 AND t.due_date != '' AND substr(t.due_date, 1, 10) < ? - AND (t.user_id = ? OR (t.task_list_id != '' AND t.task_list_id IN (SELECT task_list_id FROM task_list_shares WHERE target_type = 'user' AND target_id = ?))) - ORDER BY t.due_date`, beforeDay, userID, userID) + AND (t.user_id = ? OR (t.task_list_id != '' AND t.task_list_id IN ( + SELECT id FROM task_lists WHERE user_id = ? + UNION SELECT task_list_id FROM task_list_shares WHERE target_type = 'user' AND target_id = ? + UNION SELECT task_list_id FROM task_list_shares WHERE target_type = 'team' + AND target_id IN (SELECT team_id FROM team_members WHERE user_id = ?)))) + ORDER BY t.due_date`, beforeDay, userID, userID, userID, userID) if err != nil { return nil, err }