147 lines
5.6 KiB
C#
147 lines
5.6 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace timetracker.Data;
|
|
|
|
public class TimetrackerService(IDbContextFactory<TimetrackerDbContext> factory)
|
|
{
|
|
public async Task<List<WorkDay>> 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<AppSettings> 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<List<VacationDay>> 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<TimeSpan> 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<List<WorkDay>> 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();
|
|
}
|
|
}
|