QuellCode/CentronERP war nur als Gitlink (Submodul-Referenz auf 79c1142) getrackt, ohne .gitmodules und ohne erreichbares Remote. Der Untersuchungsgegenstand der Versuchsreihe war damit nicht reproduzierbar gesichert: Ein Klon haette ein leeres Verzeichnis erhalten, und die Belege der 3.287 Anforderungen waeren nicht ueberpruefbar gewesen. Umstellung: - Historie nach c:\DEV\CentronERP_git_snapshot_79c1142 ausgelagert (vollstaendig lesbar, enthaelt 79c1142 und Vorgaenger 89ccfd6) - Gitlink aus dem Index entfernt - Dateiinhalt aufgenommen: 24.557 Dateien, rund 333 MB Die verschachtelte .gitignore der Codebasis gilt weiter, Build-Artefakte bleiben ausgeschlossen. Details in Versuche/Versuch_01/_Codebasis-Nachweis.md
430 lines
20 KiB
Markdown
430 lines
20 KiB
Markdown
# Exchange Sync – QS-Protokoll
|
||
|
||
> **Erstellt:** 2026-03-10
|
||
> **Abgeschlossen:** 2026-03-11
|
||
> **Status:** ✅ Vollständig implementiert
|
||
> **Bearbeiter:** Entwicklung (VÖD)
|
||
> **Branch:** `vöd/Exchange_Sync`
|
||
|
||
---
|
||
|
||
## Geänderte Dateien
|
||
|
||
| Datei | Änderungsart |
|
||
|-------|-------------|
|
||
| `src/backend/Centron.BL/Sales/Calendar/ScheduleBL.cs` | Bugfixes + Refactoring |
|
||
| `src/nexus/CentronNexus/ServiceBoard/Scheduler/SchedulerPage.razor` | Frontend-Fix Wochenanzeige |
|
||
| `src/centron/Centron.WPF.UI/Modules/MyCentron/CentronInspectors/Inspectors/Schedule/OrphanedSeriesScheduleInspector.cs` | Neu – Inspector für verwaiste Serientermine |
|
||
| `src/centron/Centron.WPF.UI/Modules/MyCentron/CentronInspectors/Inspectors/InspectorManager.cs` | Inspector registriert |
|
||
| `src/centron/Centron.WPF.UI/Modules/MyCentron/CentronInspectors/Inspectors/Schedule/TimeChangedByGraphInspector.cs` | Namespace-Umbenennung (Time → Schedule) |
|
||
| `src/centron/Centron.WPF.UI/Modules/MyCentron/CentronInspectors/Inspectors/Schedule/DuplicateMailEntryIdInspector.cs` | Namespace-Umbenennung (Time → Schedule) |
|
||
|
||
---
|
||
|
||
## Übersicht
|
||
|
||
| # | Ticket | Problem | Schweregrad | Status |
|
||
|---|--------|---------|-------------|--------|
|
||
| 1 | [164020](#ticket-164020) | CenSU überschreibt fremde Outlook-Termine | 🔴 Kritisch | ✅ Implementiert |
|
||
| 2 | [163184](#ticket-163184) | Doppelter Inhalt in Exchange-Termineinladung | 🔴 Kritisch | ✅ Implementiert |
|
||
| 3 | [164121](#ticket-164121) | Gelöschte Serientermin-Instanzen bleiben in Nexus | 🟠 Hoch | ✅ Implementiert |
|
||
| 4 | [160145](#ticket-160145) | Ganztages-Events um +1 Tag verschoben | 🟠 Hoch | ✅ Implementiert |
|
||
| 5 | [158813](#ticket-158813) | Geplante Zeiten mit falschem Datum nach Sync | 🟠 Hoch | ✅ Implementiert |
|
||
| 6 | [157036](#ticket-157036) | Fehlerhafte Wochenanzeige in Nexus | 🟡 Mittel | ✅ Implementiert |
|
||
| 7 | [162826](#ticket-162826) | Outlook-Erinnerungsflut bei synchronisierten Zeiten | 🟡 Mittel | ✅ Implementiert |
|
||
| 8 | [156991](#ticket-156991) | Doppelte Feiertage / falsches Datum in Nexus | 🟡 Mittel | ✅ Implementiert |
|
||
| + | [Zusatz](#zusatz-serientermin-kaskade) | Gelöschter Serienmaster löscht Instanzen nicht | 🟠 Hoch | ✅ Implementiert |
|
||
| + | [Inspector](#zusatz-inspector) | Manueller Cleanup verwaister Serientermine | — | ✅ Implementiert |
|
||
|
||
---
|
||
|
||
## Ticket 164020
|
||
|
||
**Betreff:** CenSU überschreibt fremde Outlook-Termine mit alten Zeitdaten
|
||
|
||
### Root Cause
|
||
|
||
`SyncOldSchedule()` (Nexus → Outlook) hatte keinen Filter auf den Termintyp. Auch Helpdesk-Zeiterfassungs-Schedules (`ObjectType = HelpdeskTimerClass / HelpdeskClass`) wurden über diesen Pfad nach Exchange geschrieben – obwohl diese bereits über `CreateOrUpdateTimeSchedule()` verwaltet werden. Parallel lief `UpdateScheduleByGraph()` (Outlook → Nexus) und schrieb Outlook-Daten in Helpdesk-Schedules zurück (Datum/Uhrzeit), was zu einem Teufelskreis führte.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`SyncOldSchedule()` – Helpdesktypen ausschließen:**
|
||
```csharp
|
||
baseExpression = baseExpression.And(x =>
|
||
x.ObjectType != (int)CentronObjectKindNumeric.HelpdeskTimerClass &&
|
||
x.ObjectType != (int)CentronObjectKindNumeric.HelpdeskClass);
|
||
```
|
||
|
||
**`UpdateScheduleByGraph()` – Datum-Schutz für Helpdesk-Schedules:**
|
||
```csharp
|
||
if (!this.IsHelpdeskSchedule(schedule))
|
||
{
|
||
// Datum nur für reine Outlook-Termine zurückschreiben
|
||
schedule.DateStart = this.GetDateTimeFromGraphDateTime(graphEvent.Start, localTimeZone);
|
||
schedule.DateEnd = this.GetDateTimeFromGraphDateTime(graphEvent.End, localTimeZone);
|
||
}
|
||
```
|
||
|
||
**Neue Hilfsmethode `IsHelpdeskSchedule()`:**
|
||
```csharp
|
||
private bool IsHelpdeskSchedule(Schedule schedule)
|
||
=> schedule.ObjectType == (int)CentronObjectKindNumeric.HelpdeskTimerClass
|
||
|| schedule.ObjectType == (int)CentronObjectKindNumeric.HelpdeskClass;
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. Helpdeskzeit in Nexus anlegen (z.B. 11.03.2026, 09:00–10:00) | Zeit wird angelegt |
|
||
| 2. Webservice-Sync abwarten (60s) | Zeit erscheint als Termin in Outlook mit korrektem Datum |
|
||
| 3. Termin in Outlook manuell bearbeiten (Betreff ändern) | — |
|
||
| 4. Webservice-Sync abwarten | **Datum der Nexus-Zeit bleibt 11.03.2026, 09:00–10:00** (kein Überschreiben durch Outlook) |
|
||
| 5. Techniker legt weiteren eigenen Outlook-Termin an | Exchange-Termin bleibt unverändert – CenSU schreibt nicht in ihn |
|
||
|
||
---
|
||
|
||
## Ticket 163184
|
||
|
||
**Betreff:** Doppelter Inhalt in Exchange-Termineinladung (Body enthält Text 3× und altes Datum)
|
||
|
||
### Root Cause
|
||
|
||
In `CreateOrUpdateTimeSchedule()` wurde `schedule.Caption = timer.ExternalNote` gesetzt, bevor `ReplaceVariables(body, schedule)` aufgerufen wurde. Das Body-Template enthält `@@TerminText@@`, welches intern `schedule.Caption` liest. Dadurch wurde `ExternalNote` doppelt in den Body eingebettet: einmal über `@@TerminText@@` und einmal direkt durch das Template `@@WorkOrTime@@`.
|
||
|
||
### Implementierter Fix
|
||
|
||
```csharp
|
||
// Subject-Auflösung: Caption temporär setzen damit @@TerminText@@ im Subject funktioniert
|
||
schedule.Caption = timer.ExternalNote;
|
||
schedule.Subject = this.ReplaceVariables(subject, schedule);
|
||
|
||
// Body-Auflösung: Caption leeren damit @@TerminText@@ im Body leer ist
|
||
schedule.Caption = string.Empty;
|
||
schedule.Caption = this.ReplaceVariables(body, schedule);
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. Helpdeskzeit mit externem Hinweis anlegen (z.B. "Vor-Ort-Einsatz Drucker") | Zeit wird gespeichert |
|
||
| 2. Sync abwarten | Outlook-Termin wird erstellt |
|
||
| 3. Outlook-Termin öffnen, Body prüfen | Body enthält "Vor-Ort-Einsatz Drucker" **exakt einmal** |
|
||
| 4. Nochmals Zeit speichern (Update) | Termin wird aktualisiert, Body enthält Text weiterhin nur einmal |
|
||
|
||
---
|
||
|
||
## Ticket 164121
|
||
|
||
**Betreff:** Gelöschte Serientermin-Instanzen bleiben in Nexus sichtbar
|
||
|
||
### Root Cause
|
||
|
||
Microsoft Graph sendet bei gelöschten Serientermin-Instanzen kein `@removed`-Flag, sondern setzt `isCancelled = true`. Der bisherige Code prüfte nur `@removed` → gelöschte Instanzen wurden nie in Nexus als inaktiv markiert.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`StoreEvent()` – `isCancelled` gleichwertig zu `@removed` behandeln:**
|
||
```csharp
|
||
bool isRemoved = graphEvent.AdditionalData.ContainsKey("@removed")
|
||
|| graphEvent.IsCancelled is true;
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. In Outlook einen Serientermin anlegen (z.B. täglich, 5 Instanzen) | Alle 5 Instanzen erscheinen in Nexus nach Sync |
|
||
| 2. Eine einzelne Instanz in Outlook löschen ("Nur diesen Termin") | — |
|
||
| 3. Sync abwarten | **Die gelöschte Instanz verschwindet aus Nexus** |
|
||
| 4. Verbleibende Instanzen prüfen | Übrige 4 Instanzen bleiben unverändert sichtbar |
|
||
|
||
---
|
||
|
||
## Ticket 160145
|
||
|
||
**Betreff:** Ganztages-Events in Nexus um +1 Tag verschoben
|
||
|
||
### Root Cause
|
||
|
||
Microsoft Graph liefert Ganztages-Events mit `Start.TimeZone = "UTC"` und einem exklusiven Enddatum (`End = nächster Tag 00:00 UTC`). Die bisherige UTC→CET-Konvertierung verschob das Datum: `2025-08-16T00:00 UTC` → `2025-08-16T02:00 CEST`. Nexus interpretierte den 16.08 als Enddatum → Termin erschien auf dem falschen Tag.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`InsertScheduleByGraph()` und `UpdateScheduleByGraph()` – neue `ParseAllDayEventDates()`-Hilfsmethode:**
|
||
```csharp
|
||
if (graphEvent.IsAllDay == true)
|
||
(schedule.DateStart, schedule.DateEnd) = ParseAllDayEventDates(graphEvent);
|
||
```
|
||
|
||
```csharp
|
||
private static (DateTime Start, DateTime End) ParseAllDayEventDates(Event graphEvent)
|
||
{
|
||
var start = DateTime.Parse(graphEvent.Start.DateTime).Date;
|
||
var end = DateTime.Parse(graphEvent.End.DateTime).Date.AddDays(-1); // exklusiv → inklusiv
|
||
return (start, end);
|
||
}
|
||
```
|
||
|
||
**Zusätzlich: IANA → Windows Timezone-Mapping in `GetDateTimeFromGraphDateTime()`:**
|
||
```csharp
|
||
// "Europe/Berlin" wird korrekt auf "W. Europe Standard Time" gemappt
|
||
if (TimeZoneInfo.TryConvertIanaIdToWindowsId(graphDateTime.TimeZone, out var windowsId))
|
||
graphTimeZone = TimeZoneInfo.FindSystemTimeZoneById(windowsId);
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. In Outlook einen ganztägigen Termin am **15.08.2026** anlegen | Termin in Outlook korrekt am 15.08 |
|
||
| 2. Sync abwarten | **Termin in Nexus erscheint am 15.08.2026** (nicht 16.08) |
|
||
| 3. Termin in Outlook auf **10.–12.08.2026** (3 Tage) ändern | — |
|
||
| 4. Sync abwarten | Nexus zeigt Termin vom **10.08. bis 12.08.** |
|
||
|
||
---
|
||
|
||
## Ticket 158813
|
||
|
||
**Betreff:** Geplante Zeiten erhalten nach Sync falsches Datum
|
||
|
||
### Root Cause
|
||
|
||
`UpdateScheduleByGraph()` überschrieb `DateStart`/`DateEnd` von Helpdesk-Schedules mit den aus Exchange zurückgegeben Werten (Round-Trip-Problem). Zusätzlich: IANA-Zeitzonennamen (`"Europe/Berlin"`) wurden nicht als Windows-Zeitzone erkannt → unnötige Konvertierung, mögliche 1h-Abweichung je Sommerzeit.
|
||
|
||
### Implementierter Fix
|
||
|
||
Beide Ursachen behoben:
|
||
|
||
1. **Helpdesk-Datum-Schutz** (identisch zu Ticket 164020) – `UpdateScheduleByGraph()` überschreibt Datum bei Helpdesk-Schedules nicht mehr.
|
||
|
||
2. **Timezone-Mapping** (identisch zu Ticket 160145) – IANA → Windows-ID, `HasSameRules()`-Prüfung verhindert unnötige Konvertierungen bei gleicher Zeitzone.
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. In Nexus eine Planung anlegen: **02.03.2026, 08:00–09:00** | Nexus zeigt 02.03.2026 |
|
||
| 2. Sync abwarten (Nexus → Outlook) | Outlook zeigt Termin am 02.03.2026 korrekt |
|
||
| 3. Erneuten Sync abwarten (Outlook → Nexus, Delta-Rückweg) | **Nexus zeigt weiterhin 02.03.2026** (kein Überschreiben) |
|
||
| 4. Prüfen ob Uhrzeit korrekt bleibt | 08:00–09:00 unverändert |
|
||
|
||
---
|
||
|
||
## Ticket 157036
|
||
|
||
**Betreff:** Fehlerhafte Wochenanzeige in Nexus (Arbeitswoche zeigt nur Freitag)
|
||
|
||
### Root Cause
|
||
|
||
Der `DxSchedulerWorkWeekView` in `SchedulerPage.razor` hatte kein `WorkDays`-Attribut. Ohne diese Angabe verwendet DevExpress einen internen Default, der in dieser Version nur den letzten Werktag (Freitag) anzeigte.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`SchedulerPage.razor` – `WorkDays`-Attribut ergänzt:**
|
||
```razor
|
||
<DxSchedulerWorkWeekView VisibleTime="VisibleTime"
|
||
WorkTime="_workTime"
|
||
TimeScale="WeekViewTimeScale"
|
||
WorkDays="DayOfWeek.Monday | DayOfWeek.Tuesday | DayOfWeek.Wednesday | DayOfWeek.Thursday | DayOfWeek.Friday">
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. Nexus öffnen → Zeitplanung → Ansicht "Arbeitswoche" wählen | **Alle 5 Werktage (Mo–Fr) werden angezeigt** |
|
||
| 2. Termin am Mittwoch prüfen | Termin erscheint korrekt auf Mittwoch |
|
||
| 3. Ansicht wechseln (Woche, Monat) und zurück zu Arbeitswoche | Arbeitswoche zeigt weiterhin Mo–Fr |
|
||
|
||
---
|
||
|
||
## Ticket 162826
|
||
|
||
**Betreff:** Outlook-Erinnerungsflut bei synchronisierten Helpdeskzeiten
|
||
|
||
### Root Cause
|
||
|
||
`UpdateTerminplanungPersonTable()` → `AddScheduleToExchange()` setzte `isReminderOn` nicht explizit auf `false` für Helpdesk-Zeiten. Outlook aktiviert standardmäßig eine 15-Minuten-Erinnerung für alle neuen Termine.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`UpdateTerminplanungPersonTable()` – Reminder für Helpdesk-Schedules deaktivieren:**
|
||
```csharp
|
||
bool? reminderOn = this.IsHelpdeskSchedule(schedule) ? false : (bool?)null;
|
||
var result = await this.AddScheduleToExchange(schedule, schedulePerson, isReminderOn: reminderOn);
|
||
```
|
||
|
||
`null` bedeutet: Reminder-Einstellung nicht ändern (Standard-Outlook-Verhalten für normale Termine).
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. Helpdeskzeit speichern → Sync abwarten | Termin in Outlook erscheint |
|
||
| 2. Outlook-Termin öffnen → Eigenschaften prüfen | **Feld "Erinnerung" ist deaktiviert** (kein Häkchen) |
|
||
| 3. Normalen Nexus-Kalendertermin anlegen → Sync | Erinnerung bleibt auf Standard (15 Min.) |
|
||
|
||
---
|
||
|
||
## Ticket 156991
|
||
|
||
**Betreff:** Doppelte Feiertage / falsches Datum in Nexus
|
||
|
||
### Root Cause
|
||
|
||
**A) Datumsversatz:** Identisch zu Ticket 160145 (UTC-Konvertierungsfehler bei `IsAllDay = true`).
|
||
|
||
**B) Duplikate:** Wenn derselbe Feiertag in mehreren Exchange-Kalendern (z.B. persönlicher Kalender + Firmenkalender) enthalten ist, liefert Graph Delta denselben Feiertag mit unterschiedlichen `MailEntryID`s. Der bisherige Code prüfte nur auf `MailEntryID`-Duplikate → zwei identische Schedules wurden angelegt.
|
||
|
||
### Implementierter Fix
|
||
|
||
**A)** UTC-Fix identisch zu Ticket 160145 über `ParseAllDayEventDates()`.
|
||
|
||
**B) Duplikat-Prüfung in `StoreEvent()` für Ganztages-Events:**
|
||
```csharp
|
||
if (graphEvent.IsAllDay == true && graphEvent.Subject is not null)
|
||
{
|
||
var (allDayStart, _) = ParseAllDayEventDates(graphEvent);
|
||
var existingAllDay = GetSchedulesByExpression(x =>
|
||
x.Subject == graphEvent.Subject &&
|
||
x.DateStart == allDayStart &&
|
||
x.FullDay == true &&
|
||
x.IsActive);
|
||
|
||
if (existingAllDay.Any(s => GetSchedulePersonsByFilter(
|
||
new SchedulePersonsFilter { ScheduleI3D = s.I3D })
|
||
.Any(sp => sp.PersonalI3D == employee.I3D)))
|
||
return; // Bereits vorhanden → kein Duplikat anlegen
|
||
}
|
||
```
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. In Outlook einen Feiertag prüfen (z.B. Ostersonntag 05.04.2026) | — |
|
||
| 2. Sync abwarten | **Ostersonntag erscheint in Nexus genau einmal am 05.04.2026** |
|
||
| 3. Weiteren ganztägigen Termin am selben Tag mit gleichem Betreff anlegen (zweiter Kalender) | — |
|
||
| 4. Sync abwarten | **Kein zweiter Eintrag** – Duplikat wird erkannt und übersprungen |
|
||
|
||
---
|
||
|
||
## Zusatz: Serientermin-Kaskade
|
||
|
||
**Problem:** Wenn ein kompletter Serienmaster in Exchange gelöscht wird, wurden die zugehörigen Instanz-Schedules in Nexus nicht mitgelöscht. Graph sendet beim Master-Delete nicht immer einzelne `@removed`-Events für alle Instanzen.
|
||
|
||
### Implementierter Fix
|
||
|
||
**`StoreEvent()` – Kaskade bei Master-Delete:**
|
||
```csharp
|
||
if (isRemoved && graphEvent.SeriesMasterId is null) // SeriesMasterId null = dieser Event IST der Master
|
||
await this.DeleteSeriesInstancesByMasterId(graphEvent.Id, employee, cenSU);
|
||
```
|
||
|
||
**Neue Methode `DeleteSeriesInstancesByMasterId()`:**
|
||
- Sucht alle `SchedulePerson`-Einträge mit `SeriesMasterId = masterEventId` für den Mitarbeiter
|
||
- Lädt nur noch aktive Schedules (`IsActive = true`)
|
||
- Setzt diese auf `IsActive = false` via `SaveSchedule()` (korrekte BL-Kette inkl. Exchange-Löschung)
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. In Outlook Serientermin anlegen (z.B. täglich, 7 Instanzen) | Alle 7 Instanzen erscheinen in Nexus |
|
||
| 2. Gesamte Serie in Outlook löschen ("Alle Termine der Serie") | — |
|
||
| 3. Sync abwarten | **Alle 7 Nexus-Instanzen werden als inaktiv markiert und verschwinden** |
|
||
|
||
---
|
||
|
||
## Zusatz: Inspector für verwaiste Serientermine
|
||
|
||
**Problem:** Historisch bereits entstandene verwaiste Serientermine (vor Einführung des Kaskaden-Fix) müssen manuell bereinigt werden können.
|
||
|
||
### Implementierter Fix
|
||
|
||
Neuer Inspector **"Verwaiste Serientermine (Exchange Sync)"** im c-entron WPF-Client unter `MyCentron → c-entron Inspektor`:
|
||
|
||
- **Check:** Findet alle aktiven Schedules mit `SeriesMasterId`, für die kein aktiver Serienmaster-Schedule mehr in der DB existiert
|
||
- **Repair:** Deaktiviert alle gefundenen verwaisten Instanzen per Knopfdruck
|
||
- **Kategorie:** Zeiten (alphabetisch sortiert)
|
||
|
||
### QS-Testfall
|
||
|
||
| Schritt | Erwartetes Ergebnis |
|
||
|---------|-------------------|
|
||
| 1. c-entron WPF öffnen → MyCentron → Inspektor | Inspector-Liste erscheint |
|
||
| 2. "Verwaiste Serientermine (Exchange Sync)" auswählen und Inspektor ausführen | Check läuft durch |
|
||
| 3a. Keine verwaisten Daten vorhanden | Grünes Häkchen: "Es wurden keine verwaisten Serientermine gefunden." |
|
||
| 3b. Verwaiste Daten vorhanden | Rotes X mit Anzahl + **Reparieren**-Button |
|
||
| 4. Reparieren klicken | Instanzen werden deaktiviert, Check läuft erneut → grünes Häkchen |
|
||
|
||
---
|
||
|
||
## Technische Grundlagen
|
||
|
||
### Sync-Architektur
|
||
|
||
```
|
||
ExchangeSyncService (HostedService, Intervall konfigurierbar, default 60s)
|
||
├── ScheduleBL.SyncByGraphV2() ← Outlook → Nexus (Graph API Delta)
|
||
└── ScheduleBL.SyncOldSchedule() ← Nexus → Outlook (Schedules ohne MailEntryID)
|
||
|
||
HelpdeskTimerBL / ScheduleBL
|
||
├── CreateOrUpdateTimeSchedule() ← Zeiterfassung → Outlook-Termin
|
||
└── UpdateTerminplanungPersonTable() ← Termin gespeichert → Exchange aktualisieren
|
||
```
|
||
|
||
### Schlüssel-Entitäten
|
||
|
||
| Entity | Beschreibung |
|
||
|--------|-------------|
|
||
| `Schedule` | Termin in c-entron (DateStart, DateEnd, FullDay, Subject, IsActive, ObjectType) |
|
||
| `SchedulePerson` | Verknüpfung Schedule ↔ Mitarbeiter ↔ Exchange (MailEntryID, SeriesMasterId) |
|
||
| `CreatedByApp` | Herkunft: `GraphSync = 6`, `HelpdeskTime`, `GraphSyncTimeDelete` |
|
||
| `CenSU` | CentronSystemUser – Systemkonto für alle Hintergrundprozesse |
|
||
| `IsHelpdeskSchedule()` | Neue Hilfsmethode: prüft ob Schedule an Helpdesk/Zeit gebunden ist |
|
||
|
||
### Source of Truth
|
||
|
||
| Schedule-Typ | Datum/Zeit Source of Truth |
|
||
|---|---|
|
||
| `HelpdeskTimerClass` / `HelpdeskClass` | **Nexus** – Exchange darf nie überschreiben |
|
||
| Reine Outlook-Termine (`GraphSync`) | **Exchange/Outlook** |
|
||
| Ganztages-Events (`FullDay = true`) | **Datumswert aus Graph, ohne Zeitzonenkonvertierung** |
|
||
|
||
---
|
||
|
||
## Bekannte Einschränkungen / Nicht umgesetzt
|
||
|
||
| Thema | Begründung |
|
||
|-------|-----------|
|
||
| Kalenderfilter (welche Exchange-Kalender synchronisiert werden) | Konfigurationsthema, kein Code-Fix – muss über Exchange-Einstellungen gesteuert werden |
|
||
| Blocker-Termine (`ShowAs = Free`) in Nexus anzeigen | Kein Kundenauftrag für diese Version |
|
||
| ApplicationSetting für Erinnerungen (An/Aus im UI) | Für Helpdesk-Zeiten ist `isReminderOn: false` als Standard ausreichend |
|
||
|
||
---
|
||
|
||
## Offene Fragen (für Rollout-Planung)
|
||
|
||
- [x] **Gibt es Kunden mit Exchange On-Premise (kein Graph API)?** → **Ja.** Diese Kunden nutzen den alten EWS-basierten Agent. Für sie greifen die Graph-spezifischen Fixes **nicht** (Delta-Token, `isCancelled`, `ParseAllDayEventDates`, Kaskaden-Delete, Duplikat-Erkennung). Separate Analyse für On-Premise-Kunden erforderlich – ggf. eigener Bugfix-Zyklus.
|
||
- [x] **Welche Exchange-Kalender werden synchronisiert – nur Primärkalender?** → **Alle Kalender.** Der Endpunkt `_graphClient.Users[id].CalendarView.Delta` liefert Events aus *allen* Kalendern des Nutzers aggregiert (Primär + abonnierte Kalender wie „Feiertage in Deutschland", Teamkalender etc.). Das erklärt u.a. den Doppel-Feiertage-Bug (Ticket 156991). So gewollt – bleibt so.
|
||
- [x] **Sollen Kunden nach dem Update den Inspector manuell ausführen (Kommunikation nötig)?** → **Ja.** In den Release Notes darauf hinweisen, dass Kunden mit Exchange Sync nach dem Update den Inspector „Verwaiste Serientermine (Exchange Sync)" einmalig ausführen sollen, um historisch entstandene verwaiste Serientermine zu bereinigen.
|
||
- [x] **Ist der Inspector nur für Admins sichtbar (Rechteprüfung)?** → Zugriff wird über das Modul „c-entron Inspektor" gesteuert – keine separate Rechteprüfung im Inspector selbst erforderlich.
|
||
|
||
## Geltungsbereich der Fixes
|
||
|
||
| Fix | Exchange Online (Graph API) | Exchange On-Premise (alter EWS-Agent) |
|
||
|-----|-----------------------------|---------------------------------------|
|
||
| Ticket 164020 – CenSU überschreibt Helpdesk-Zeiten | ✅ | ⚠️ Teilweise: `SyncOldSchedule()`-Filter greift, EWS-Rückweg unklar |
|
||
| Ticket 163184 – Doppelter Body | ✅ | ✅ (`CreateOrUpdateTimeSchedule()` ist gemeinsamer Pfad) |
|
||
| Ticket 164121 – Instanz-Delete via `isCancelled` | ✅ | ❌ EWS-Agent nutzt anderen Delete-Mechanismus |
|
||
| Ticket 160145 – Ganztages +1 Tag | ✅ | ❌ Graph-spezifisch |
|
||
| Ticket 158813 – Falsches Datum nach Sync | ✅ | ⚠️ Helpdesk-Schutz greift, Timezone-Fix ist Graph-spezifisch |
|
||
| Ticket 157036 – Wochenanzeige Nexus | ✅ | ✅ (Frontend-Fix, kein Exchange-Bezug) |
|
||
| Ticket 162826 – Erinnerungsflut | ✅ | ✅ (`AddScheduleToExchange()` gemeinsamer Pfad) |
|
||
| Ticket 156991 – Doppelte Feiertage | ✅ | ❌ Graph Delta-spezifisch |
|
||
| Zusatz – Serienkaskade | ✅ | ❌ Graph Delta-spezifisch |
|
||
| Inspector – Cleanup verwaiste Serientermine | ✅ | ✅ (DB-Level, unabhängig vom Sync-Pfad) |
|