fix: allow closing current week when future workdays are not yet closed

CloseWeek was requiring every workday in the ISO week to have a
closed_days record, including days in the future. Now only workdays
up to and including today are checked; future workdays are skipped.

Adds TestCloseWeekMidWeek regression test.
This commit is contained in:
2026-04-30 17:54:38 +02:00
parent 9d6233b116
commit 6fceda46b5
2 changed files with 38 additions and 4 deletions

View File

@@ -88,13 +88,18 @@ func (s *WeekService) CloseWeek(ctx context.Context, weekKey string) (*domain.Cl
// Compute expected ms for the week (from settings frozen at week start) // Compute expected ms for the week (from settings frozen at week start)
expectedMs := int64(set.HoursPerWeek * 3_600_000) expectedMs := int64(set.HoursPerWeek * 3_600_000)
// Verify all workdays are closed; collect worked ms // Verify all workdays up to and including today are closed; collect worked ms.
// Future workdays in the week (not yet reached) are skipped.
todayKey := time.Now().In(s.tz).Format("2006-01-02")
var totalWorkedMs int64 var totalWorkedMs int64
for _, dk := range dayKeys { for _, dk := range dayKeys {
t, _ := time.ParseInLocation("2006-01-02", dk, s.tz) t, _ := time.ParseInLocation("2006-01-02", dk, s.tz)
if !set.IsWorkday(int(t.Weekday())) { if !set.IsWorkday(int(t.Weekday())) {
continue // weekend or non-workday — skip continue // weekend or non-workday — skip
} }
if dk > todayKey {
continue // future workday — skip
}
cd, err := s.closedDays.GetByDayKey(ctx, dk) cd, err := s.closedDays.GetByDayKey(ctx, dk)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {

View File

@@ -54,7 +54,6 @@ func TestCloseWeekBasic(t *testing.T) {
ctx := context.Background() ctx := context.Background()
entrySvc, daySvc, weekSvc, _ := newFullServices(t) entrySvc, daySvc, weekSvc, _ := newFullServices(t)
// Find the ISO week for "this week" using a known Mon-Fri
// Use a fixed Monday to make the test deterministic // Use a fixed Monday to make the test deterministic
monday := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC) // 2024-W03, Monday monday := time.Date(2024, 1, 15, 10, 0, 0, 0, time.UTC) // 2024-W03, Monday
weekKey := "2024-W03" weekKey := "2024-W03"
@@ -92,7 +91,7 @@ func TestCloseWeekMissingDayFails(t *testing.T) {
ctx := context.Background() ctx := context.Background()
_, daySvc, weekSvc, _ := newFullServices(t) _, daySvc, weekSvc, _ := newFullServices(t)
// Only close Mon-Thu, leave Friday open // Only close Mon-Thu, leave Friday open — all are in the past
monday := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC) monday := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
dk := monday.AddDate(0, 0, i).Format("2006-01-02") dk := monday.AddDate(0, 0, i).Format("2006-01-02")
@@ -101,7 +100,7 @@ func TestCloseWeekMissingDayFails(t *testing.T) {
_, err := weekSvc.CloseWeek(ctx, "2024-W03") _, err := weekSvc.CloseWeek(ctx, "2024-W03")
if err == nil { if err == nil {
t.Fatal("expected error closing week with unclosed day") t.Fatal("expected error closing week with unclosed past day")
} }
} }
@@ -142,3 +141,33 @@ func TestReopenWeek(t *testing.T) {
t.Fatalf("CloseWeek after reopen: %v", err) t.Fatalf("CloseWeek after reopen: %v", err)
} }
} }
func TestCloseWeekMidWeek(t *testing.T) {
// Regression: closing the current week mid-week should succeed when all
// past workdays are closed and future workdays are left open.
ctx := context.Background()
_, daySvc, weekSvc, _ := newFullServices(t)
tz, _ := time.LoadLocation("UTC")
now := time.Now().In(tz)
isoYear, isoWeek := now.ISOWeek()
weekKey := fmt.Sprintf("%d-W%02d", isoYear, isoWeek)
// Close every workday from Monday up to and including today.
monday := now.AddDate(0, 0, -int(now.Weekday()-time.Monday))
today := now.Format("2006-01-02")
for d := monday; d.Format("2006-01-02") <= today; d = d.AddDate(0, 0, 1) {
wd := d.Weekday()
if wd == time.Saturday || wd == time.Sunday {
continue
}
dk := d.Format("2006-01-02")
if _, err := daySvc.MarkDay(ctx, dk, domain.DayKindHoliday); err != nil {
t.Fatalf("MarkDay %s: %v", dk, err)
}
}
if _, err := weekSvc.CloseWeek(ctx, weekKey); err != nil {
t.Fatalf("CloseWeek mid-week: %v", err)
}
}