365 lines
16 KiB
Markdown
365 lines
16 KiB
Markdown
# LeanderShiftPlannerV2 – Technische Dokumentation
|
||
|
||
**Version:** 2.0.2
|
||
**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<Shift>` | Liste verknüpfter Schichten (nicht serialisiert) |
|
||
| `Timesheets` | `List<Timesheet>` | 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<Person>` | 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<ShiftPlan>` 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<ShiftPlan>` 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.
|
||
|
||
---
|
||
|
||
**Google API Zugangsdaten einrichten**
|
||
Da das Programm über deinen persönlichen Google Account auf den Kalender zugreift, musst du einmalig eigene OAuth-Zugangsdaten in der Google Cloud Console erstellen und in der Datei Resources/templates/googleapi.txt hinterlegen.
|
||
|
||
Schritt-für-Schritt-Anleitung:
|
||
|
||
- Öffne die [Google Cloud Console](https://console.cloud.google.com/) und melde dich mit deinem Google-Konto an.
|
||
- Erstelle ein neues Projekt oder wähle ein bestehendes aus.
|
||
- Navigiere zu „APIs & Dienste" → „Bibliothek" und suche nach „Google Calendar API". Klicke auf „Aktivieren".
|
||
- Gehe zu „APIs & Dienste" → „Anmeldedaten" und klicke auf „Anmeldedaten erstellen" → „OAuth-Client-ID".
|
||
- Falls noch nicht geschehen, wirst du aufgefordert, den OAuth-Zustimmungsbildschirm zu konfigurieren. Wähle dort „Extern" und fülle die Pflichtfelder (App-Name, Support-E-Mail) aus. Alle anderen Felder können leer bleiben.
|
||
- Als Anwendungstyp wähle „Desktop-App" und vergib einen beliebigen Namen.
|
||
- Nach der Erstellung siehst du deine Client-ID und dein Client-Secret. Trage beide Werte in die Datei Resources/templates/googleapi.txt ein:
|
||
|
||
`ClientId=deine-client-id`
|
||
`ClientSecret=dein-client-secret`
|
||
|
||
Beim ersten Programmstart öffnet sich einmalig ein Browser-Fenster, in dem du den Zugriff für dein Google-Konto bestätigst. Danach wird das Zugriffstoken lokal gespeichert und der Dialog erscheint nicht mehr.
|
||
|
||
*Dokumentation erstellt am 28.04.2026*
|