Unterrichtsplanung: Serienerzeugung, Stundenraster, Aufsichten & Vertretung (Kapitel 4.2/4.3 Nachtrag)

Stunden serienweise aus dem Stundenplan erzeugen (4.2.5); Stundenraster
(Uhrzeiten je Stunde) in den Einstellungen mit Zeitbedarf-Rückmeldung im
Verlaufsplan-Editor; wiederkehrende Pausenaufsicht; neuer "Vertretung
eintragen"-Dialog für einmalige Vertretungsaufsicht, Vertretungsstunde,
Sondereinsätze (Ausflüge, Berufsmessen) und schlichten Stundenausfall.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 21:59:07 +02:00
co-authored by Claude Sonnet 5
parent 99b2de6519
commit d6dee72acc
30 changed files with 2743 additions and 48 deletions
+59
View File
@@ -143,6 +143,65 @@ public enum GermanState
BW, BY, BE, BB, HB, HH, HE, MV, NI, NW, RP, SL, SN, ST, SH, TH
}
/// <summary>
/// Wiederkehrende Pausenaufsicht (kein Bezug zu einer Lerngruppe, anders als
/// <see cref="TimetableSlot"/>). <see cref="AfterPeriod"/> = 0 bedeutet "vor der 1. Stunde"
/// (Frühaufsicht), sonst "in der Pause nach dieser Stunde". Pro Wochentag/Pause höchstens eine
/// Aufsicht (ein Lehrer kann nicht an zwei Orten gleichzeitig Aufsicht führen).
/// </summary>
public class SupervisionDuty
{
public Guid Id { get; set; } = Guid.NewGuid();
public DayOfWeek Weekday { get; set; }
public int AfterPeriod { get; set; }
public string Location { get; set; } = "";
}
public enum SubstitutionKind { Supervision, Lesson, SpecialAssignment, Cancelled }
/// <summary>
/// Einmalige Ausnahme vom regulären Plan an einem konkreten Datum: eine Vertretungsaufsicht
/// (zusätzlich zur oder anstelle einer regulären <see cref="SupervisionDuty"/>), eine
/// Vertretungsstunde in einer eigenen oder fremden Lerngruppe, ein Sondereinsatz (Ausflug,
/// Berufsmesse, Exkursion, ...), der einen Teil des Tages oder den ganzen Tag blockiert, oder ein
/// schlichter Stundenausfall ohne Ersatz (z.B. weil die betroffene Gruppe selbst auf Klassenfahrt
/// ist). Bewusst getrennt von <see cref="Lesson"/>: die meisten dieser Einträge sind nicht Teil der
/// durchgeplanten Einheiten-Reihenfolge (siehe TODO.md, Nachtrag zu 4.3) — nur bei
/// <see cref="SubstitutionKind.Lesson"/> kann beim Anlegen ausdrücklich "als Stunde in der Einheit
/// übernehmen" gewählt werden, was zusätzlich eine echte <see cref="Lesson"/> erzeugt; dieser
/// Eintrag hier bleibt trotzdem bestehen, da er (unabhängig von der Einheiten-Fortschrittsanzeige)
/// das, was tatsächlich im Plan an diesem Tag stattfand, festhält.
/// </summary>
public class SubstitutionEntry
{
public Guid Id { get; set; } = Guid.NewGuid();
public DateOnly Date { get; set; }
public SubstitutionKind Kind { get; set; }
/// Bei <see cref="SubstitutionKind.Lesson"/> und <see cref="SubstitutionKind.Cancelled"/>
/// gesetzt — bei Cancelled wird Gruppe/Fach zur Anzeige aus dem an dieser Stelle regulär
/// eingetragenen <see cref="TimetableSlot"/> abgeleitet, nicht hier gespeichert.
public int? PeriodNumber { get; set; }
/// Nur bei <see cref="SubstitutionKind.Supervision"/> gesetzt — wie bei
/// <see cref="SupervisionDuty.AfterPeriod"/>, 0 = vor der 1. Stunde.
public int? AfterPeriod { get; set; }
/// Nur bei <see cref="SubstitutionKind.SpecialAssignment"/> und nicht <see cref="IsAllDay"/>:
/// der belegte Stundenbereich (inklusive).
public int? FromPeriod { get; set; }
public int? ToPeriod { get; set; }
/// Nur bei <see cref="SubstitutionKind.SpecialAssignment"/>: blockiert den ganzen Tag statt
/// nur <see cref="FromPeriod"/><see cref="ToPeriod"/>.
public bool IsAllDay { get; set; }
/// Gesetzt, wenn es sich um eine eigene Lerngruppe handelt — bei fremden/unbekannten Gruppen
/// oder Sondereinsätzen ohne Gruppenbezug (z.B. Berufsmesse) bleibt es null und nur
/// <see cref="GroupLabel"/> beschreibt ggf., um welche Gruppe es ging.
public Guid? GroupId { get; set; }
public string GroupLabel { get; set; } = "";
/// Thema (bei Lesson), Grund/Ort (bei Supervision, z.B. "Vertretung für Hr. Müller") bzw.
/// Bezeichnung (bei SpecialAssignment, z.B. "Ausflug ins Museum", "Berufsmesse").
public string Description { get; set; } = "";
public string? Notes { get; set; }
}
/// <summary>
/// Zeugnisnote eines Schülers in einer Lerngruppe für einen Zeitraum (Halbjahr/Gesamtjahr).
/// <see cref="CalculatedValue"/> ist das zuletzt berechnete Ergebnis; <see cref="OverrideValue"/>