fix: skip untracked workdays when closing a week
Previously any past workday without a closed_days record blocked week close. Now only days that actually have entries require an explicit close. Empty workdays count as 0h worked, which is reflected in the weekly delta automatically. - WeekService.CloseWeek: after finding no closed_days record, check whether the day has any entries; only error if it does - NewWeekService: takes EntryStore to support the above check - Updated TestCloseWeekMissingDayFails to reflect the new semantic (test now creates entries on Friday but leaves it unclosed)
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
type WeekService struct {
|
||||
closedDays *store.ClosedDayStore
|
||||
closedWeeks *store.ClosedWeekStore
|
||||
entries *store.EntryStore
|
||||
settings *store.SettingsStore
|
||||
db interface {
|
||||
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
|
||||
@@ -26,6 +27,7 @@ type WeekService struct {
|
||||
func NewWeekService(
|
||||
closedDays *store.ClosedDayStore,
|
||||
closedWeeks *store.ClosedWeekStore,
|
||||
entries *store.EntryStore,
|
||||
settings *store.SettingsStore,
|
||||
rawDB *sql.DB,
|
||||
tz *time.Location,
|
||||
@@ -33,6 +35,7 @@ func NewWeekService(
|
||||
return &WeekService{
|
||||
closedDays: closedDays,
|
||||
closedWeeks: closedWeeks,
|
||||
entries: entries,
|
||||
settings: settings,
|
||||
rawDB: rawDB,
|
||||
tz: tz,
|
||||
@@ -88,8 +91,9 @@ 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 workdays up to and including today are closed; collect worked ms.
|
||||
// Future workdays in the week (not yet reached) are skipped.
|
||||
// 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.
|
||||
todayKey := time.Now().In(s.tz).Format("2006-01-02")
|
||||
var totalWorkedMs int64
|
||||
for _, dk := range dayKeys {
|
||||
@@ -101,13 +105,23 @@ func (s *WeekService) CloseWeek(ctx context.Context, weekKey string) (*domain.Cl
|
||||
continue // future workday — skip
|
||||
}
|
||||
cd, err := s.closedDays.GetByDayKey(ctx, dk)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("%w: %s", ErrWeekHasUnclosedDays, dk)
|
||||
}
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
totalWorkedMs += cd.WorkedMs
|
||||
if cd != nil {
|
||||
totalWorkedMs += cd.WorkedMs
|
||||
continue
|
||||
}
|
||||
// No closed_days record — check whether the day has any entries.
|
||||
dayEntries, err := s.entries.ListByDayKey(ctx, dk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(dayEntries) > 0 {
|
||||
// Day has tracked time but was never closed — require explicit close.
|
||||
return nil, fmt.Errorf("%w: %s", ErrWeekHasUnclosedDays, dk)
|
||||
}
|
||||
// No entries, no closed record → untracked day, counts as 0h.
|
||||
}
|
||||
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
Reference in New Issue
Block a user