using Microsoft.EntityFrameworkCore; namespace timetracker.Data; public class TimetrackerService(IDbContextFactory factory) { public async Task> GetWeekAsync(DateOnly monday) { await using var db = await factory.CreateDbContextAsync(); return await db.WorkDays .Include(w => w.Breaks) .Where(w => w.Date >= monday && w.Date < monday.AddDays(7)) .OrderBy(w => w.Date) .ToListAsync(); } public async Task UpsertWorkDayAsync(WorkDay workDay) { await using var db = await factory.CreateDbContextAsync(); var existing = await db.WorkDays .Include(w => w.Breaks) .FirstOrDefaultAsync(w => w.Date == workDay.Date); if (existing == null) { workDay.Id = 0; foreach (var b in workDay.Breaks) b.Id = 0; db.WorkDays.Add(workDay); } else { existing.StartTime = workDay.StartTime; existing.EndTime = workDay.EndTime; db.BreakEntries.RemoveRange(existing.Breaks); existing.Breaks.Clear(); foreach (var b in workDay.Breaks) existing.Breaks.Add(new BreakEntry { WorkDayId = existing.Id, StartTime = b.StartTime, EndTime = b.EndTime }); } await db.SaveChangesAsync(); } public async Task GetSettingsAsync() { await using var db = await factory.CreateDbContextAsync(); return await db.AppSettings.FindAsync(1) ?? new AppSettings { Id = 1 }; } public async Task SaveSettingsAsync(AppSettings settings) { await using var db = await factory.CreateDbContextAsync(); settings.Id = 1; var existing = await db.AppSettings.FindAsync(1); if (existing == null) db.AppSettings.Add(settings); else { existing.DailyTargetHours = settings.DailyTargetHours; existing.MinimumBreakMinutes = settings.MinimumBreakMinutes; existing.VacationDaysPerYear = settings.VacationDaysPerYear; existing.WorkMonday = settings.WorkMonday; existing.WorkTuesday = settings.WorkTuesday; existing.WorkWednesday = settings.WorkWednesday; existing.WorkThursday = settings.WorkThursday; existing.WorkFriday = settings.WorkFriday; existing.WorkSaturday = settings.WorkSaturday; existing.WorkSunday = settings.WorkSunday; } await db.SaveChangesAsync(); } // ── Urlaub ──────────────────────────────────────────────────────────── public async Task> GetVacationDaysAsync(int year) { await using var db = await factory.CreateDbContextAsync(); return await db.VacationDays .Where(v => v.Date.Year == year) .OrderBy(v => v.Date) .ToListAsync(); } public async Task AddVacationDayAsync(VacationDay vacationDay) { await using var db = await factory.CreateDbContextAsync(); var exists = await db.VacationDays.AnyAsync(v => v.Date == vacationDay.Date); if (!exists) { vacationDay.Id = 0; db.VacationDays.Add(vacationDay); await db.SaveChangesAsync(); } } public async Task RemoveVacationDayAsync(int id) { await using var db = await factory.CreateDbContextAsync(); var v = await db.VacationDays.FindAsync(id); if (v != null) { db.VacationDays.Remove(v); await db.SaveChangesAsync(); } } // ── Gleitzeitkonto ─────────────────────────────────────────────────── public async Task GetTotalOvertimeAsync(AppSettings settings) { await using var db = await factory.CreateDbContextAsync(); var allDays = await db.WorkDays .Include(w => w.Breaks) .Where(w => w.StartTime != null && w.EndTime != null) .ToListAsync(); var total = TimeSpan.Zero; foreach (var wd in allDays) { if (!settings.IsWorkDay(wd.Date.DayOfWeek)) continue; var gross = wd.EndTime!.Value.ToTimeSpan() - wd.StartTime!.Value.ToTimeSpan(); if (gross <= TimeSpan.Zero) continue; var breakTotal = wd.Breaks .Where(b => b.StartTime.HasValue && b.EndTime.HasValue && b.EndTime > b.StartTime) .Aggregate(TimeSpan.Zero, (s, b) => s + (b.EndTime!.Value.ToTimeSpan() - b.StartTime!.Value.ToTimeSpan())); total += gross - breakTotal - TimeSpan.FromHours(settings.DailyTargetHours); } return total; } // ── Monatsübersicht ─────────────────────────────────────────────────── public async Task> GetMonthAsync(int year, int month) { await using var db = await factory.CreateDbContextAsync(); var from = new DateOnly(year, month, 1); var to = from.AddMonths(1); return await db.WorkDays .Include(w => w.Breaks) .Where(w => w.Date >= from && w.Date < to) .OrderBy(w => w.Date) .ToListAsync(); } }