diff --git a/internal/service/week_service.go b/internal/service/week_service.go index ede4063..365d489 100644 --- a/internal/service/week_service.go +++ b/internal/service/week_service.go @@ -197,9 +197,11 @@ func (s *WeekService) CloseWeek(ctx context.Context, weekKey string) (*domain.Cl // Compute expected ms for the week (from settings frozen at week start) expectedMs := int64(set.HoursPerWeek * 3_600_000) - // Verify all past workdays that have entries are closed; collect worked ms. - // Past workdays with no entries at all are skipped (they contribute 0h). - // Future workdays in the week are always skipped. + // Collect worked ms across all workdays in the week. + // A workday is included if it has a closed_days record — whether past or future + // (future days can be pre-marked as holiday/vacation before closing the week early). + // Past workdays with open entries require an explicit close before the week can close. + // Past workdays with no record and no entries contribute 0h. todayKey := s.clock.Now().In(s.tz).Format("2006-01-02") var totalWorkedMs int64 for _, dk := range dayKeys { @@ -207,18 +209,20 @@ func (s *WeekService) CloseWeek(ctx context.Context, weekKey string) (*domain.Cl if !set.IsWorkday(int(t.Weekday())) { continue // weekend or non-workday — skip } - if dk > todayKey { - continue // future workday — skip - } cd, err := s.closedDays.GetByDayKey(ctx, dk) if err != nil && !errors.Is(err, sql.ErrNoRows) { return nil, err } if cd != nil { + // Already closed (past or pre-marked future) — count it. totalWorkedMs += cd.WorkedMs continue } - // No closed_days record — check whether the day has any entries. + if dk > todayKey { + // Future workday with no closed record — entries are impossible, skip. + continue + } + // Past/today workday with no closed record — check whether entries exist. dayEntries, err := s.entries.ListByDayKey(ctx, dk) if err != nil { return nil, err diff --git a/internal/service/week_service_test.go b/internal/service/week_service_test.go index b7e8e23..7da6db8 100644 --- a/internal/service/week_service_test.go +++ b/internal/service/week_service_test.go @@ -246,7 +246,7 @@ func TestCloseWeekMidWeek(t *testing.T) { t.Errorf("expected_ms: want %s, got %s", wantExpected, time.Duration(cw.ExpectedMs)*time.Millisecond) } const wantDelta = wantWorked - wantExpected // −11.5h - if cw.DeltaMs != int64(wantDelta) { + if cw.DeltaMs != int64(wantDelta/time.Millisecond) { t.Errorf("delta_ms: want %s, got %s", wantDelta, time.Duration(cw.DeltaMs)*time.Millisecond) } }