Arbeitszeit: Auswertung nach Kategorie/Gruppe + Pflichtstunden-Abgleich (Kapitel 6.3)

Dritter Tab "Auswertung": Zeitraum wahlweise Monat oder Schuljahr, Balken je Kategorie
und Gruppe. Neuer WorkloadSettingsService (gleiches JSON-Muster wie PeriodScheduleService)
für die Pflichtstundenzahl pro Woche, bewusst direkt im Auswertungs-Tab editierbar statt
in den Einstellungen, da das Feld nur dort gebraucht wird. Export (6.3.3) bleibt offen,
da er an das noch fehlende Kapitel 11 (Export-Infrastruktur) hängt.
This commit is contained in:
2026-08-15 23:26:06 +02:00
parent d8b05702bc
commit 52bb9c2c2f
10 changed files with 405 additions and 13 deletions
@@ -1,4 +1,5 @@
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Workload;
using Xunit;
@@ -346,3 +347,102 @@ public sealed class AddTimeEntryDialogViewModelTests
Assert.NotEmpty(vm.TimeError);
}
}
public sealed class WorkloadEvaluationViewModelTests
{
private static WorkloadSettingsService BuildSettingsService()
{
var tempPath = System.IO.Path.Combine(
System.IO.Path.GetTempPath(), $"lehrerapp-workloadsettings-tests-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempPath);
return new WorkloadSettingsService(tempPath);
}
[Fact]
public void MonatModus_ZeigtNurEintraegeDesGewaehltenMonats()
{
var entries = new FakeTimeEntries();
entries.Add(new TimeEntry { Category = "Korrektur", Date = new DateOnly(2026, 3, 10), DurationMinutes = 30 });
entries.Add(new TimeEntry { Category = "Korrektur", Date = new DateOnly(2026, 4, 1), DurationMinutes = 45 });
var vm = new WorkloadEvaluationViewModel(entries, new FakeGroups([]), BuildSettingsService(), new SchoolYearService())
{
SelectedYear = 2026,
};
vm.SelectedMonth = vm.MonthOptions[2]; // März
Assert.Equal("30 min (0.5 h)", vm.TotalMinutesDisplay);
}
[Fact]
public void CategorySummaries_GruppiertProKategorie()
{
var entries = new FakeTimeEntries();
entries.Add(new TimeEntry { Category = "Korrektur", Date = new DateOnly(2026, 3, 5), DurationMinutes = 30 });
entries.Add(new TimeEntry { Category = "Verwaltung", Date = new DateOnly(2026, 3, 6), DurationMinutes = 20 });
var vm = new WorkloadEvaluationViewModel(entries, new FakeGroups([]), BuildSettingsService(), new SchoolYearService())
{
SelectedYear = 2026,
};
vm.SelectedMonth = vm.MonthOptions[2];
Assert.Equal(2, vm.CategorySummaries.Count);
Assert.Contains(vm.CategorySummaries, s => s.Category == "Korrektur" && s.MinutesDisplay == "30 min");
}
[Fact]
public void GroupSummaries_ZeigtNurEintraegeMitGruppe()
{
var group = new LearningGroup { Name = "8a" };
var entries = new FakeTimeEntries();
entries.Add(new TimeEntry { Category = "X", GroupId = group.Id, Date = new DateOnly(2026, 3, 5), DurationMinutes = 30 });
entries.Add(new TimeEntry { Category = "X", GroupId = null, Date = new DateOnly(2026, 3, 6), DurationMinutes = 20 });
var vm = new WorkloadEvaluationViewModel(entries, new FakeGroups([group]), BuildSettingsService(), new SchoolYearService())
{
SelectedYear = 2026,
};
vm.SelectedMonth = vm.MonthOptions[2];
var summary = Assert.Single(vm.GroupSummaries);
Assert.Equal("8a", summary.GroupName);
Assert.Equal("30 min", summary.MinutesDisplay);
}
[Fact]
public void SchuljahrModus_UmfasstAugustBisJuli()
{
var entries = new FakeTimeEntries();
entries.Add(new TimeEntry { Category = "X", Date = new DateOnly(2025, 9, 1), DurationMinutes = 30 }); // im Schuljahr 2025/26
entries.Add(new TimeEntry { Category = "X", Date = new DateOnly(2026, 7, 30), DurationMinutes = 15 }); // im Schuljahr 2025/26
entries.Add(new TimeEntry { Category = "X", Date = new DateOnly(2026, 8, 5), DurationMinutes = 99 }); // schon Schuljahr 2026/27
var vm = new WorkloadEvaluationViewModel(entries, new FakeGroups([]), BuildSettingsService(), new SchoolYearService())
{
SelectedYear = 2025,
PeriodMode = WorkloadEvaluationViewModel.SchoolYearMode,
};
Assert.Equal("45 min (0.8 h)", vm.TotalMinutesDisplay);
}
[Fact]
public void SaveRequiredWeeklyHours_AktualisiertSollIstVergleich()
{
var entries = new FakeTimeEntries();
entries.Add(new TimeEntry { Category = "X", Date = new DateOnly(2026, 3, 2), DurationMinutes = 60 });
var vm = new WorkloadEvaluationViewModel(entries, new FakeGroups([]), BuildSettingsService(), new SchoolYearService())
{
SelectedYear = 2026,
RequiredWeeklyHoursText = "10",
};
vm.SelectedMonth = vm.MonthOptions[2];
vm.SaveRequiredWeeklyHoursCommand.Execute(null);
Assert.Contains("Soll:", vm.RequiredVsActualDisplay);
Assert.Contains("Ist: 1 h", vm.RequiredVsActualDisplay);
}
}