# LeanderShiftPlannerV2 – Technische Dokumentation **Version:** 2.0.1 **Sprache:** C# / .NET 9.0 **Framework:** Avalonia UI 11.2.1 **Repository:** https://gitea.leanderserver.de/leander19961/LeanderShiftPlannerV2 --- ## Inhaltsverzeichnis 1. [Projektübersicht](#1-projektübersicht) 2. [Systemvoraussetzungen & Abhängigkeiten](#2-systemvoraussetzungen--abhängigkeiten) 3. [Projektstruktur](#3-projektstruktur) 4. [Datenmodell](#4-datenmodell) - 4.1 [Person](#41-person) - 4.2 [Shift (Schicht)](#42-shift-schicht) - 4.3 [ShiftPlan](#43-shiftplan) - 4.4 [Timesheet (Stundenzettel)](#44-timesheet-stundenzettel) - 4.5 [Filter](#45-filter) 5. [Services (Geschäftslogik)](#5-services-geschäftslogik) - 5.1 [AppService](#51-appservice) - 5.2 [PersonService](#52-personservice) - 5.3 [ShiftService](#53-shiftservice) - 5.4 [ShiftPlanService](#54-shiftplanservice) - 5.5 [FilterService](#55-filterservice) - 5.6 [TimesheetService](#56-timesheetservice) - 5.7 [ViewService](#57-viewservice) 6. [Berechnungsalgorithmus](#6-berechnungsalgorithmus) 7. [Datei-I/O (FileIO)](#7-datei-io-fileio) 8. [Benutzeroberfläche (View)](#8-benutzeroberfläche-view) 9. [Ausgabedateien](#9-ausgabedateien) 10. [Hinweise & Einschränkungen](#10-hinweise--einschränkungen) --- ## 1. Projektübersicht LeanderShiftPlannerV2 ist eine Desktop-Anwendung zur **automatisierten Erstellung von Schichtplänen**. Das Tool erlaubt das Anlegen, Verwalten und Verknüpfen von Personen und Schichten, die anschließende Berechnung aller validen Schichtplankombinationen sowie die automatische Generierung von Stundenzetteln. **Kernfunktionen:** - Verwaltung von Personen (inkl. Wochenstunden und Stundenzettel-Quelle) - Verwaltung von Schichten (Wochentag, Uhrzeit, Min./Max.-Personenanzahl) - Bidirektionale Verknüpfung von Personen und Schichten - Parallele Berechnung aller gültigen Schichtplankombinationen - Filterung der berechneten Pläne - Ausgabe als PNG und/oder Textdatei - Automatische Generierung von Stundenzetteln (Excel-Format, optional Google Calendar-Integration) > **Wichtig:** Alle Stundenangaben müssen im **Wochenstunden-Format** erfolgen! --- ## 2. Systemvoraussetzungen & Abhängigkeiten | Anforderung | Details | |---|---| | .NET-Version | .NET 9.0 | | UI-Framework | Avalonia UI 11.2.1 (Cross-Platform: Windows, Linux, macOS) | | Ausgabetyp | WinExe (Desktop-Anwendung) | **NuGet-Pakete:** | Paket | Version | Verwendung | |---|---|---| | Avalonia | 11.2.1 | UI-Framework | | Avalonia.Desktop | 11.2.1 | Desktop-Unterstützung | | Avalonia.Themes.Fluent | 11.2.1 | Fluent Design Theme | | Avalonia.Fonts.Inter | 11.2.1 | Schriftarten | | EPPlus | 7.5.3 | Excel-Generierung (Stundenzettel) | | Google.Apis.Calendar.v3 | 1.69.0.3746 | Google Calendar Integration | | SkiaSharp | 3.118.0-preview | PNG-Rendering der Schichtpläne | --- ## 3. Projektstruktur ``` LeanderShiftPlannerV2/ ├── LeanderShiftPlannerV2.sln └── LeanderShiftPlannerV2/ ├── App.axaml / App.axaml.cs # Avalonia-Anwendungseinstieg ├── Program.cs # Main-Einstiegspunkt ├── app.manifest ├── LeanderShiftPlannerV2.csproj ├── Model/ # Datenmodelle (POCOs) │ ├── Person.cs │ ├── Shift.cs │ ├── ShiftPlan.cs │ ├── Timesheet.cs │ └── Filter.cs ├── Service/ # Geschäftslogik / Services │ ├── AppService.cs # Zentraler Service-Container │ ├── PersonService.cs │ ├── ShiftService.cs │ ├── ShiftPlanService.cs # Kernalgorithmus │ ├── FilterService.cs │ ├── TimesheetService.cs │ └── ViewService.cs ├── FileIO/ # Laden/Speichern von Daten ├── View/ # Avalonia AXAML Views ├── Util/ # Hilfsfunktionen └── Resources/ ├── icon/ # Anwendungssymbol ├── font/ # Eingebettete Schriften (Emblem.ttf, Roboto.ttf) └── templates/ ├── timesheet.xlsx # Excel-Vorlage für Stundenzettel └── googleapi.txt # Google API-Konfiguration ``` --- ## 4. Datenmodell ### 4.1 Person **Datei:** `Model/Person.cs` **Implementiert:** `INotifyPropertyChanged`, `[Serializable]` Repräsentiert eine Person, die in Schichten eingeteilt werden kann. | Eigenschaft | Typ | Beschreibung | |---|---|---| | `FirstName` | `string` | Vorname der Person | | `Surname` | `string` | Nachname der Person | | `Hours` | `int` | Wöchentliche Arbeitsstunden (Pflichtangabe) | | `TimeSheetSource` | `string` | Pfad/Quelle für die Stundenzettel-Daten | | `Shifts` | `ObservableCollection` | Liste verknüpfter Schichten (nicht serialisiert) | | `Timesheets` | `List` | Liste der Stundenzettel (nicht serialisiert) | | `ShiftsString` | `string` (readonly) | Lesbare Darstellung aller Schichten für die UI | **Methoden:** - `AddShift(Shift)` / `RemoveShift(Shift)` / `ClearShifts()` – Verwaltung der Schichtverknüpfungen (lösen jeweils `PropertyChanged` aus) - `GetTimeSheetByMonth(string month)` – Gibt den Stundenzettel für einen bestimmten Monat zurück --- ### 4.2 Shift (Schicht) **Datei:** `Model/Shift.cs` **Implementiert:** `INotifyPropertyChanged`, `[Serializable]` Repräsentiert eine einzelne Schicht an einem bestimmten Wochentag. | Eigenschaft | Typ | Beschreibung | |---|---|---| | `Day` | `string` | Wochentag (z.B. "Montag") | | `StartTime` | `string` | Startzeit (Format: "HH:mm") | | `EndTime` | `string` | Endzeit (Format: "HH:mm") | | `Duration` | `int` | Dauer der Schicht in Stunden | | `ShiftInDay` | `int` | Position der Schicht am Tag (1 = erste, 2 = zweite, 3 = dritte) | | `MaxPersonCount` | `int` | Maximale Anzahl Personen pro Schicht | | `MinPersonCount` | `int` | Minimale Anzahl Personen pro Schicht | | `Persons` | `ObservableCollection` | Verknüpfte Personen (nicht serialisiert) | | `PersonsString` | `string` (readonly) | Lesbare Darstellung aller Personen für die UI | | `Time` | `string` (readonly) | Formatierte Zeitanzeige "HH:MM - HH:MM" | **Methoden:** - `AddPerson(Person)` / `RemovePerson(Person)` / `ClearPersons()` – Verwaltung der Personenverknüpfungen --- ### 4.3 ShiftPlan **Datei:** `Model/ShiftPlan.cs` Repräsentiert einen vollständigen Schichtplan, der aus einer Kombination von Schichten über alle konfigurierten Wochentage besteht. Wird vom `ShiftPlanService` erzeugt. --- ### 4.4 Timesheet (Stundenzettel) **Datei:** `Model/Timesheet.cs` Repräsentiert den Stundenzettel einer Person für einen bestimmten Monat. Beinhaltet die Arbeitszeiterfassung und wird wahlweise aus einer lokalen Quelle oder über Google Calendar bezogen. | Eigenschaft | Typ | Beschreibung | |---|---|---| | `Month` | `string` | Monat im Format "MM.YYYY" o.ä. | --- ### 4.5 Filter **Datei:** `Model/Filter.cs` Hält die aktuell gesetzten Filterkriterien für die Anzeige der berechneten Schichtpläne. --- ## 5. Services (Geschäftslogik) ### 5.1 AppService **Datei:** `Service/AppService.cs` Zentraler Service-Container, der alle anderen Services hält und als einheitlicher Zugriffspunkt dient. Wird bei Anwendungsstart instanziiert und an alle abhängigen Komponenten weitergegeben. **Hält Referenzen auf:** `PersonService`, `ShiftService`, `ShiftPlanService`, `FilterService`, `TimesheetService`, `ViewService` --- ### 5.2 PersonService **Datei:** `Service/PersonService.cs` Verwaltet die Liste aller Personen sowie deren Stundenzettel-Daten. **Kernfunktionen:** - CRUD-Operationen für `Person`-Objekte - Laden von Stundenzetteln aus einer externen Quelle (`TimeSheetSource`) - `GenerateRandomTimeSheets()` – Generiert zufällige Stundenzettel auf Basis von Google Calendar-Einträgen und passt sie an die Wochenstunden der Person an --- ### 5.3 ShiftService **Datei:** `Service/ShiftService.cs` Verwaltet die Liste aller Schichten. **Kernfunktionen:** - CRUD-Operationen für `Shift`-Objekte - `GetShiftList()` – Gibt alle konfigurierten Schichten zurück --- ### 5.4 ShiftPlanService **Datei:** `Service/ShiftPlanService.cs` Der **Kernservice** der Anwendung. Enthält den gesamten Berechnungsalgorithmus für Schichtpläne. **Wichtige Methoden:** | Methode | Beschreibung | |---|---| | `StartCalculation(CancellationToken)` | Startet die vollständige Berechnung (Listen initialisieren → Schichten generieren → Pläne berechnen) | | `GeneratePlannedShifts()` | Erzeugt alle möglichen Schichtkombinationen (Permutationen nach Personenanzahl: 1, 2 oder 3 Personen pro Schicht) | | `CalculateShiftPlans(CancellationToken)` | Kombiniert die Tagesschichten zu vollständigen Wochenplänen, **parallel** mit `Parallel.ForEach` | | `CheckShift(List<...>)` | Prüft, ob ein Schichtplan die Wochenstunden aller Personen exakt erfüllt (nicht über- oder unterschreitet) | | `GetShiftPlans()` | Gibt die aktuell gefilterte `ObservableCollection` zurück | | `GetAllShiftPlans()` | Gibt alle berechneten Pläne (ungefiltert) zurück | | `ResetShiftPlanList()` | Setzt den gefilterten Stand auf alle berechneten Pläne zurück | --- ### 5.5 FilterService **Datei:** `Service/FilterService.cs` Wendet einen `Filter` auf die Liste der Schichtpläne an und reduziert die angezeigte Liste entsprechend. --- ### 5.6 TimesheetService **Datei:** `Service/TimesheetService.cs` Generiert Stundenzettel-Dateien im Excel-Format (`Data/Output/Timesheets`) auf Basis der `Timesheet`-Daten der Personen und der bereitgestellten `timesheet.xlsx`-Vorlage. --- ### 5.7 ViewService **Datei:** `Service/ViewService.cs` Übernimmt die Darstellung eines `ShiftPlan` als PNG-Bild mittels **SkiaSharp**. Speichert alle Pläne im Verzeichnis `Data/Output/ShiftPlans`. --- ## 6. Berechnungsalgorithmus Der Algorithmus in `ShiftPlanService` arbeitet in drei Phasen: ### Phase 1 – Generierung der Einzelschicht-Varianten (`GeneratePlannedShifts`) Für jede konfigurierte Schicht werden alle Kombinationen von Personen gebildet, die dieser Schicht zugewiesen sind – von `MinPersonCount` bis `MaxPersonCount`. Das Ergebnis ist eine Liste aller möglichen "besetzten Einzelschichten": - **1 Person:** Jede Person einzeln - **2 Personen:** Alle Zweier-Kombinationen (ohne Wiederholung) - **3 Personen:** Alle Dreier-Kombinationen (ohne Wiederholung) ### Phase 2 – Aufbau der Tagesschichten (`GenerateDayShifts`) Die Einzelschicht-Varianten werden nach Wochentag gruppiert. Innerhalb eines Tages werden die bis zu 3 möglichen Positionen (ShiftInDay = 1, 2, 3) zu Dreier-Tupeln `(Shift, Shift, Shift)` kombiniert. Fehlt eine Position, wird `null` eingesetzt. ### Phase 3 – Kombination zum Wochenplan & Validierung (`CalculateShiftPlans`) Alle Tagesschichten werden durch **verschachtelte `Parallel.ForEach`-Schleifen** kombiniert (je ein Level pro Wochentag). Für jede entstehende Wochenplan-Kombination wird `CheckShift` aufgerufen: - Es werden die kumulierten Stunden aller Personen berechnet. - Ein Plan gilt nur dann als **gültig**, wenn jede Person **exakt** ihre konfigurierten Wochenstunden arbeitet (weder mehr noch weniger). - Gültige Pläne werden in die `ObservableCollection` eingetragen. > **Hinweis zur Performance:** Die Berechnung ist exponentiell in der Anzahl der Schichten und Personen. Die parallele Ausführung über `Parallel.ForEach` nutzt alle verfügbaren CPU-Kerne. Bei komplexen Konfigurationen kann die Berechnung dennoch mehrere Sekunden bis Minuten dauern. --- ## 7. Datei-I/O (FileIO) Das `FileIO`-Verzeichnis enthält Klassen zum Laden und Speichern des Anwendungszustands (Personen, Schichten) als XML-Dateien mittels `XmlSerializer`. - Personen und Schichten werden als XML serialisiert/deserialisiert. - Verknüpfungen (`Shifts` in Person, `Persons` in Shift) sind mit `[XmlIgnore]` dekoriert und werden nach dem Laden aus dem XML neu aufgebaut, um zirkuläre Referenzen zu vermeiden. --- ## 8. Benutzeroberfläche (View) Die Oberfläche ist mit **Avalonia UI** und dem **Fluent Theme** umgesetzt. Sie besteht aus mehreren Tabs: | Tab | Funktion | |---|---| | **Persons** | Personen anlegen (`Add new person`), bearbeiten (`Edit person`), löschen (`Delete person`), Schichten zuweisen (`Select shifts for person`) | | **Shifts** | Schichten anlegen (`Add new shift`), bearbeiten (`Edit shift`), löschen (`Delete shift`), Personen zuweisen (`Select persons for shift`) | | **ShiftPlans** | Berechnung starten (`Start calculation`), Pläne filtern (`Filter shiftplans`), Filter zurücksetzen (`Reset all filters`), Plan öffnen (`Open selected ShiftPlan`), alle Pläne speichern (`Save all ShiftPlans`), Stundenzettel generieren (`Generate Timesheets`) | **Bidirektionale Verknüpfung:** Die Zuweisung von Personen und Schichten erfolgt stets automatisch in beide Richtungen. Eine über "Select shifts for person" hinzugefügte Schicht wird der Person **und** gleichzeitig der Person zur Schicht hinzugefügt – ohne manuellen Zweischritt. --- ## 9. Ausgabedateien | Pfad | Inhalt | |---|---| | `Data/Output/ShiftPlans/` | Alle Schichtpläne als `.png`-Dateien (via `Save all ShiftPlans`) | | `Data/Output/Timesheets/` | Stundenzettel als `.xlsx`-Dateien (via `Generate Timesheets`) | --- ## 10. Hinweise & Einschränkungen - **Stundenformat:** Alle Stunden müssen als **Wochenstunden** angegeben werden. Tagesstunden oder Monatsstunden werden nicht unterstützt. - **Personenanzahl pro Schicht:** Der Algorithmus unterstützt aktuell maximal **3 Personen** pro Schicht. Höhere Werte für `MaxPersonCount` werden im Code nicht berücksichtigt. - **Anzahl Wochentage:** Der Algorithmus ist für genau **5 Wochentage** ausgelegt (die Schleifentiefe in `CalculateShiftPlans` ist auf 5 verschachtelt). - **Stundenzettel-Quelle:** Die `TimeSheetSource` einer Person verweist auf eine externe Datenquelle (Google Calendar oder lokale Datei), die vor der Generierung verfügbar sein muss. - **Google API:** Die Konfiguration der Google Calendar API erfolgt über die eingebettete Ressource `Resources/templates/googleapi.txt`. - **Performance:** Bei sehr vielen Personen und Schichten kann die Anzahl der Permutationen exponentiell wachsen. Eine Abbruchmöglichkeit via `CancellationToken` ist implementiert. --- *Dokumentation erstellt am 22.04.2026*