diff --git a/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs b/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs
index 89b9e40..6b478e7 100644
--- a/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs
+++ b/LehrerApp.Desktop.Tests/PlanningTabViewModelTests.cs
@@ -294,4 +294,44 @@ public class PlanningTabViewModelTests
Assert.Single(sessions.GetByGroup(groupId));
Assert.Equal("Für diese Stunde existiert bereits eine Sitzung.", notified);
}
+
+ [Fact]
+ public void CreateParticipationSession_AndereStundeAmSelbenTagBereitsVerknuepft_LegtKeineZweiteSitzungAn()
+ {
+ // Nutzer-Feedback: eine dritte Stunde am selben Tag (z.B. Vertretung) soll die bereits
+ // bestehende Sitzung des Tages weiterverwenden statt eine zweite anzulegen.
+ var groupId = Guid.NewGuid();
+ var group = new LearningGroup { Id = groupId, Name = "Testgruppe" };
+ var groups = new FakeGroups([group]);
+ var units = new FakeUnits();
+ var lessons = new FakeLessons();
+ var sessions = new FakeSessions([]);
+ var vm = new PlanningTabViewModel(units, lessons, groups, new FakeSubjects([]),
+ new FakeCompetencyDomains(), TestSupport.BuildAiSettingsService(), sessions);
+ vm.Initialize(groupId);
+
+ var unit = new Unit { GroupId = groupId, Title = "Optik" };
+ units.Add(unit);
+ var date = new DateOnly(2025, 9, 1);
+ var doubleLesson = new Lesson
+ { UnitId = unit.Id, GroupId = groupId, Date = date, Topic = "Brechung" };
+ var thirdLesson = new Lesson
+ { UnitId = unit.Id, GroupId = groupId, Date = date, Topic = "Vertretung" };
+ lessons.Add(doubleLesson);
+ lessons.Add(thirdLesson);
+
+ vm.RefreshPlanning(unit.Id, doubleLesson.Id);
+ vm.SelectedLesson = vm.Lessons.Single(l => l.Id == doubleLesson.Id);
+ vm.CreateParticipationSessionCommand.Execute(null);
+
+ string? notified = null;
+ vm.OnNotify = m => notified = m;
+ vm.RefreshPlanning(unit.Id, thirdLesson.Id);
+ vm.SelectedLesson = vm.Lessons.Single(l => l.Id == thirdLesson.Id);
+ vm.CreateParticipationSessionCommand.Execute(null);
+
+ var created = Assert.Single(sessions.GetByGroup(groupId));
+ Assert.Equal(doubleLesson.Id, created.LessonId);
+ Assert.Equal("Für diesen Tag existiert bereits eine Sitzung.", notified);
+ }
}
diff --git a/LehrerApp.Desktop.Tests/SeatingPlanViewModelTests.cs b/LehrerApp.Desktop.Tests/SeatingPlanViewModelTests.cs
index 2f748be..475c2cd 100644
--- a/LehrerApp.Desktop.Tests/SeatingPlanViewModelTests.cs
+++ b/LehrerApp.Desktop.Tests/SeatingPlanViewModelTests.cs
@@ -256,6 +256,30 @@ public sealed class SeatingPlanViewModelTests
Assert.Single(sessions.GetByGroup(groupId));
}
+ [Fact]
+ public void SelectOrCreateSessionForLesson_AndereStundeAmSelbenTagBereitsVerknuepft_LegtKeineZweiteSitzungAn()
+ {
+ // Nutzer-Feedback: eine dritte Stunde am selben Tag (z.B. durch Vertretung, eigene Lesson-
+ // Id) soll die bereits bestehende Sitzung der Doppelstunde weiterverwenden statt eine
+ // zweite, unabhängige Sitzung für denselben Tag anzulegen.
+ var groupId = Guid.NewGuid();
+ var today = DateOnly.FromDateTime(DateTime.Today);
+ var doubleLesson = new Lesson { GroupId = groupId, Date = today, Topic = "Redox" };
+ var thirdLesson = new Lesson { GroupId = groupId, Date = today, Topic = "Vertretung" };
+ var existingSession = new ParticipationSession
+ { GroupId = groupId, Date = today, LessonId = doubleLesson.Id, Comment = "Redox" };
+ var sessions = new FakeSessions([existingSession]);
+ var plan = new SeatingPlan { GroupId = groupId, Name = "Standard", Rows = 1, Columns = 1 };
+ var vm = new SeatingPlanTabViewModel(new FakeSeatingPlans([plan]), new FakeStudents([]),
+ new FakeMemberships([]), sessions, new FakeEntries(), new FakeAspects());
+ vm.Initialize(groupId, isReadOnly: false);
+
+ vm.SelectOrCreateSessionForLesson(thirdLesson);
+
+ Assert.Equal(existingSession.Id, vm.SelectedSession?.Id);
+ Assert.Single(sessions.GetByGroup(groupId));
+ }
+
[Fact]
public void SelectOrCreateSessionForLesson_KeineSitzungVorhanden_LegtVerknuepfteAnUndWaehltSieAus()
{
diff --git a/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs
index f4365d1..deeec62 100644
--- a/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs
+++ b/LehrerApp.Desktop/ViewModels/Groups/PlanningViewModels.cs
@@ -364,16 +364,25 @@ public partial class PlanningTabViewModel : ObservableObject
/// Übernimmt Datum + Thema der Stunde in eine neue Mitarbeitssitzung (3.3.1) — verknüpft über
/// das bisher ungenutzte Lesson.LessonId-Feld auf ParticipationSession, damit ein zweiter Klick
/// auf dieselbe Stunde keine doppelte Sitzung anlegt, sondern nur darauf hinweist.
+ ///
+ /// Prüft dabei zusätzlich auf JEDE bereits an diesem Tag bestehende Sitzung, nicht nur eine
+ /// exakt mit `lesson.Id` verknüpfte (Nutzer-Feedback, analog
+ /// ): kommt neben einer
+ /// Doppelstunde noch eine dritte Stunde desselben Tages hinzu (eigene `Lesson`, z.B. durch
+ /// Vertretung), soll das nicht zu einer zweiten Mitarbeitssitzung für den Tag führen.
[RelayCommand(CanExecute = nameof(HasSelectedLesson))]
private void CreateParticipationSession()
{
if (SelectedLesson is null) return;
var lesson = SelectedLesson.Model;
- var existing = _participationSessions.GetByGroup(lesson.GroupId)
- .FirstOrDefault(s => s.LessonId == lesson.Id);
+ var sessionsForGroup = _participationSessions.GetByGroup(lesson.GroupId);
+ var existing = sessionsForGroup.FirstOrDefault(s => s.LessonId == lesson.Id)
+ ?? sessionsForGroup.FirstOrDefault(s => s.Date == lesson.Date);
if (existing is not null)
{
- OnNotify?.Invoke("Für diese Stunde existiert bereits eine Sitzung.");
+ OnNotify?.Invoke(existing.LessonId == lesson.Id
+ ? "Für diese Stunde existiert bereits eine Sitzung."
+ : "Für diesen Tag existiert bereits eine Sitzung.");
return;
}
diff --git a/LehrerApp.Desktop/ViewModels/Groups/SeatingPlanViewModels.cs b/LehrerApp.Desktop/ViewModels/Groups/SeatingPlanViewModels.cs
index e89ccc3..5a79c9e 100644
--- a/LehrerApp.Desktop/ViewModels/Groups/SeatingPlanViewModels.cs
+++ b/LehrerApp.Desktop/ViewModels/Groups/SeatingPlanViewModels.cs
@@ -141,10 +141,18 @@ public partial class SeatingPlanTabViewModel : ObservableObject
/// erlaubt: welche Stunde gemeint ist, steht durch die explizite Auswahl der Lehrkraft
/// (Klick auf "Unterrichtsmodus starten" für genau diese Stunde) bereits unzweideutig fest -
/// keine Geistersitzungs-Gefahr wie beim bloßen Öffnen eines Tabs.
+ ///
+ /// Fällt bewusst auf JEDE an diesem Tag bereits bestehende Sitzung zurück, nicht nur auf eine
+ /// exakt mit `lesson.Id` verknüpfte (Nutzer-Feedback): kommt neben einer Doppelstunde noch eine
+ /// dritte Stunde am selben Tag hinzu (z.B. Vertretung, eigene `Lesson` mit eigener Id), soll
+ /// keine zweite Mitarbeitssitzung für denselben Tag entstehen — die Lehrkraft passt stattdessen
+ /// die Einschätzung der bereits bestehenden Sitzung an. Das entspricht dem Verhalten von
+ /// , das schon immer pro Tag statt pro Stunde arbeitet.
///
public void SelectOrCreateSessionForLesson(Lesson lesson)
{
- var existing = TodaySessions.FirstOrDefault(s => s.LessonId == lesson.Id);
+ var existing = TodaySessions.FirstOrDefault(s => s.LessonId == lesson.Id)
+ ?? TodaySessions.FirstOrDefault(s => s.Date == lesson.Date);
if (existing is not null) { SelectedSession = existing; return; }
if (!IsEditable) return;
diff --git a/LehrerApp.Desktop/Views/Groups/WebUntisLessonAbsenceComparisonDialog.axaml b/LehrerApp.Desktop/Views/Groups/WebUntisLessonAbsenceComparisonDialog.axaml
index 7339d58..43ac676 100644
--- a/LehrerApp.Desktop/Views/Groups/WebUntisLessonAbsenceComparisonDialog.axaml
+++ b/LehrerApp.Desktop/Views/Groups/WebUntisLessonAbsenceComparisonDialog.axaml
@@ -12,9 +12,9 @@
FontSize="12" Opacity="0.65" TextWrapping="Wrap"/>
-
+
-
+
diff --git a/TODO.md b/TODO.md
index 867c946..1a89630 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1392,6 +1392,15 @@ Zugangsdaten; Schülerdaten, Fehlzeiten und der unverschlüsselte CSV-Report pas
eine lokale `ParticipationSession` dieses Kurses existiert, können als offen, entschuldigt oder
unentschuldigt übernommen werden; fremde/ganztägige Abwesenheiten erzeugen keine lokale Stunde.
+**Nachtrag (September 2026, Nutzer-Feedback) — Zeitraum-Datumsfelder ließen sich nicht ändern:**
+die beiden `DatePicker` oben im Dialog ([WebUntisLessonAbsenceComparisonDialog.axaml](LehrerApp.Desktop/Views/Groups/WebUntisLessonAbsenceComparisonDialog.axaml))
+sind Avalonias WinUI-artiger Spinner-`DatePicker` — Änderungen im Tag/Monat/Jahr-Flyout werden nur
+über einen separaten Häkchen-Button im Flyout übernommen, ein Klick daneben verwirft sie
+stillschweigend. Ersetzt durch `CalendarDatePicker` (gleiche `DateTimeOffset?`-Bindung, kein
+Typwechsel nötig) — bereits das etablierte Muster für frei wählbare Einzeldatumsfelder in dieser
+Codebasis (`WithdrawStudentDialog`, `CreateLetterDialog`, 7.2.3), dort ohne dieses Problem, da
+ein Klick auf einen Kalendertag sofort übernimmt statt einen Bestätigungsschritt zu verlangen.
+
**Nachtrag zu 4.3, Fehlzeiten je Unterricht (August 2026):** Der ursprüngliche Fehlzeitenabgleich
rief `getTimetableWithAbsences` ohne Element auf und bekam damit den kompletten Lehrer-Stundenplan
zurück (einmal pro Kursmitglied, siehe damalige Ineffizienz-Korrektur) — das erfordert mehr
@@ -2141,6 +2150,22 @@ folgenden Punkte gehören direkt in `LehrerApp.Desktop`:
den vollen Verlaufsplan-Editor öffnen müssen. Neue kleine
`TeachingModeHomeworkViewModel : ObservableObject`, da `TeachingModeViewModel` selbst (wie
`LessonViewerViewModel`) keine Bindable-Basisklasse hat.
+
+ **Nachtrag (September 2026, Nutzer-Feedback) — zweite Sitzung bei zusätzlicher Stunde am
+ selben Tag:** `SelectOrCreateSessionForLesson` prüfte bisher ausschließlich auf eine bereits
+ exakt mit `lesson.Id` verknüpfte Sitzung. Kam neben einer Doppelstunde noch eine dritte
+ Stunde desselben Tages hinzu (eigene `Lesson`-Id, z.B. durch Vertretung), fand die Methode
+ keinen Treffer und legte eine zweite, unabhängige Mitarbeitssitzung für denselben Tag an —
+ dadurch zeigten Unterrichtsmodus und der normale Mitarbeit-Tab der Gruppe (der über
+ `ParticipationTabViewModel.LoadSessions()` unabhängig davon die nach Datum jüngste Sitzung
+ wählt) je nach Reihenfolge unterschiedliche Sitzungen für denselben Tag. Nutzer-Entscheidung:
+ strikt nach Datum statt nach Stunde gehen — eine zusätzliche Stunde am selben Tag (Vertretung
+ o.ä.) bekommt keine eigene Sitzung, sondern die Lehrkraft passt die Einschätzung der bereits
+ bestehenden Sitzung des Tages an. `SelectOrCreateSessionForLesson` fällt jetzt, wenn keine
+ exakt verknüpfte Sitzung existiert, zusätzlich auf jede andere Sitzung desselben Tages
+ zurück (Verhalten analog zu `EnsureTodaySession`, das schon immer pro Tag statt pro Stunde
+ arbeitet). Dieselbe Lücke bestand in `PlanningTabViewModel.CreateParticipationSession`
+ ("Sitzung aus der Stunde erstellen", 3.3.1) — gleicher Fallback dort ergänzt.
- [x] **4.5.24** Popup-Menü im Wochenraster für Unterrichtsansicht/Sitzplan/Planung/Planungsviewer
(August 2026, Nutzer-Feedback, zweite Runde). Die erste Fassung hatte das Problem am
falschen Ort gelöst — ein Dropdown in der "Heute"-**Tagesliste** (unten angedockt), obwohl