Job-Kalender Termine mit externen Kalendern synchronisieren: Unterschied zwischen den Versionen

Aus GEVITAS
Wechseln zu: Navigation, Suche
(Voraussetzungen)
 
(21 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 3: Zeile 3:
 
Hier wird beschrieben, wie man Termine aus dem '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' mit einem externen System synchronisieren kann.
 
Hier wird beschrieben, wie man Termine aus dem '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' mit einem externen System synchronisieren kann.
  
Dabei wird davon ausgegangen, dass der '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' das führende System ist. Das bedeutet:
+
=== Richtung der Synchronisation ===
 +
 
 +
Es wird davon ausgegangen, dass der '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' das '''führende System''' ist.
 +
 
 +
;Das bedeutet:
  
 
* Für einen Termin gibt es drei Synchronisations-Momente:
 
* Für einen Termin gibt es drei Synchronisations-Momente:
Zeile 13: Zeile 17:
 
Basis der Beschreibung ist das Zusatz-Modul '''GooCal''', das Termine aus dem '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' mit Google©-Kalendern synchronisiert.
 
Basis der Beschreibung ist das Zusatz-Modul '''GooCal''', das Termine aus dem '''[[REFLEX Bedienungsanleitung JobKalender|JobKalender]]''' mit Google©-Kalendern synchronisiert.
  
 +
 +
=== Datenbank-Zugriff ===
 +
 +
Es wird für die Synchronisation ein Zugriff auf die REFLEX-Datenbank benötigt. Idealerweise richtet der Admin einen Datenbank-User dafür ein, der über entsprechend eingeschränkte Rechte verfügt.
 +
 +
Im Prinzip ist der Zugriff auf die Daten nur lesend, mit '''einer Ausnahme''':
 +
 +
* Die eindeutige ID eines Termins im externen Kalender muss in die Datenbank geschrieben werden, wenn ein neuer Kalender-Eintrag erfolgte. Das wird unten beschrieben.
 +
* Wenn das Synchronisations-Tool eine '''eigene Tabelle/Datenbank''' für die Kalender-Termin-ID's und die zugehörigen REFLEX-Termin-ID's verwendet, ist das natürlich auch möglich und sogar die bessere Lösung (aus REFLEX-Sicht)!
  
 
== Ablauf einer Synchronisation ==
 
== Ablauf einer Synchronisation ==
 +
 +
Zur Erinnerung: Daten werden hier nur in '''eine''' Richtung synchronisiert, nämlich '''vom''' JobKalender '''zum''' externen Kalender!
  
 
=== Termin-Daten aus dem JobKalender (MAufJob) einlesen ===
 
=== Termin-Daten aus dem JobKalender (MAufJob) einlesen ===
  
 
Dazu kann dieser SQL-Script verwendet werden:
 
Dazu kann dieser SQL-Script verwendet werden:
 
 
   
 
   
  select MAufJob.LFDNR, MAufJob.AUFTRNR, MAufJob.DATUM, MAufJob.DATUMBIS, MAufJob.UHRZEITVON, MAufJob.UHRZEITBIS, MAufJob.PERSONEN,
+
  select
  MAufJob.text, MAufJob.ORT, MAufJob.ERFDAT, MAufJob.ERFUSER,
+
  MAufJob.BETREFF,MAufJob.ADRKURZNAME, MAufJob.AENDDAT, MAufJob.AENDUSER,
+
-- Primary Key, Unique index:
  MAufJob.TERMINART, MAufJob.ID_STRING, MAufJob.TERMSTATUS, MAufJob.TERMIN_GANZTAEGIG, MAufJob.Changed, MAufJob.TerminTyp,
+
MAufJob.LFDNR, MAufJob.ID_STRING,
  MAufJob.Anzahl, MAufJob.OBERGRUPPE, MAufJob.GRUPPE, MAufJob.UNTGRUPPE, MAufJob.ARTNR, MAufJob.MENGENTERMIN,
+
  MAufJob.ZU_MAUFJOB_LFDNR, MAufJob.BeauftragungsArt, MAufJob.JobCalChanged, MAufJob.BemerkungIntern, MAufJob.BemerkungExtern,
+
-- Datum/Uhrzeit von-bis:
  MAufJob.VeranstOrt, MAufJob.VeranstRaum, MAufJob.CLOUD_EVENT_ID,
+
MAufJob.DATUM, MAufJob.DATUMBIS, MAufJob.UHRZEITVON, MAufJob.UHRZEITBIS,
  MAUFKO.VERANSTNAME, MAUFKO.BEARBEITER,
+
  Pers.PERSNR, Pers.KURZZEICHEN, Pers.VORNAME, Pers.NACHNAME, Pers.SPRACHE, Pers.SyncStatus, Pers.SyncAddress
+
-- Person-Kurzname lt. Pers.Stammdaten:
 +
MAufJob.PERSONEN,
 +
 +
-- Gehört zu AuftrNr (kann auch 0 sein!):
 +
  MAufJob.AUFTRNR,
 +
 +
-- Betreff, Text, Ort:
 +
  MAufJob.BETREFF, MAufJob.text, MAufJob.ORT,
 +
 +
-- Wenn Termin für Kunde: Kurzname lt. Stammdaten "Adr"
 +
MAufJob.ADRKURZNAME,
 +
 +
  MAufJob.TerminArt, MAufJob.TermStatus, MAufJob.TerminTyp,
 +
 +
-- Ganztägiger Termin 0/1
 +
MAufJob.TERMIN_GANZTAEGIG,
 +
 +
-- Geändert 0/1:
 +
MAufJob.Changed,
 +
 +
-- Anzahl bei Mengenterminen:
 +
MAufJob.Anzahl, MAufJob.MENGENTERMIN,
 +
   
 +
-- Artikel-Daten, wenn Termin einen Artikelbezug hat:
 +
MAufJob.OBERGRUPPE, MAufJob.GRUPPE, MAufJob.UNTGRUPPE, MAufJob.ARTNR,  
 +
 +
  MAufJob.ZU_MAUFJOB_LFDNR,
 +
MAufJob.BeauftragungsArt, MAufJob.JobCalChanged, MAufJob.BemerkungIntern, MAufJob.BemerkungExtern,
 +
  MAufJob.VeranstOrt, MAufJob.VeranstRaum,  
 +
 +
-- Verweis auf Externes Kalender-Item varchar(512):
 +
MAufJob.CLOUD_EVENT_ID,
 +
   
 +
MAufJob.ERFDAT, MAufJob.ERFUSER,
 +
MAufJob.AENDDAT, MAufJob.AENDUSER,
 +
 +
MAufKo.VERANSTNAME, MAufKo.BEARBEITER,
 +
 +
-- Personen/Ressourcen-Daten aus den Stammdaten:
 +
  Pers.PERSNR, Pers.KURZZEICHEN, Pers.VORNAME, Pers.NACHNAME, Pers.SPRACHE, Pers.SyncStatus, Pers.SyncAddress,
 +
 +
-- Eigentliche Termin-Tabelle:
 
  from MAufJob MAufJob
 
  from MAufJob MAufJob
  left outer join pers Pers on Pers.KURZZEICHEN=MAufJob.PERSONEN
+
  left outer join maufko MAUFKO on MAUFKO.AUFTRNR=MAufJob.AUFTRNR
+
-- Personen-/Ressourcen-Stammdaten:
 
+
  left outer join Pers Pers on Pers.KURZZEICHEN=MAufJob.PERSONEN
 +
 +
-- Auftragskopf (wenn AuftrNr im Termin steht):
 +
  left outer join MAufKo MAufKo on MAufKo.AuftrNr=MAufJob.AuftrNr
 +
 
  -- Nur TerminTyp 3=Arbeitszeit-Termine:
 
  -- Nur TerminTyp 3=Arbeitszeit-Termine:
 
  where MAufJob.TerminTyp=3
 
  where MAufJob.TerminTyp=3
 
+
 
  -- Nur Personen/Ressourcen mit Kennzeichen > 0: 1=Google 2=Outlook/Exchange
 
  -- Nur Personen/Ressourcen mit Kennzeichen > 0: 1=Google 2=Outlook/Exchange
 
  -- Das wird in den Personen-Stammdaten in REFLEX so festgelegt.
 
  -- Das wird in den Personen-Stammdaten in REFLEX so festgelegt.
 
  and ( Pers.SyncStatus = 1 or Pers.KURZZEICHEN is null )
 
  and ( Pers.SyncStatus = 1 or Pers.KURZZEICHEN is null )
 
+
 
  -- Nur wenn in den Personen-Stammdaten eine Synch-Adresse steht:
 
  -- Nur wenn in den Personen-Stammdaten eine Synch-Adresse steht:
 
  and Pers.SyncAddress > ''
 
  and Pers.SyncAddress > ''
 
+
 
 
  -- Auch überlappende Termine (wie in der Dispo):
 
  -- Auch überlappende Termine (wie in der Dispo):
 
  and ( (MAufJob.DATUM <= 'DatumVon'
 
  and ( (MAufJob.DATUM <= 'DatumVon'
Zeile 51: Zeile 110:
 
   
 
   
 
  -- Nur Termine mit diesem Status:
 
  -- Nur Termine mit diesem Status:
-- 1=Zugesagt, Bestätigt 4=Angefragt 6=Bestellung (Überhang)
+
-- 1=Zugesagt, Bestätigt 4=Angefragt 6=Bestellung (Überhang)
 
  and ( MAufJob.TERMSTATUS=1 or MAufJob.TERMSTATUS=4 or MAufJob.TERMSTATUS=6)
 
  and ( MAufJob.TERMSTATUS=1 or MAufJob.TERMSTATUS=4 or MAufJob.TERMSTATUS=6)
 
  order by MAufJob.DATUM
 
  order by MAufJob.DATUM
 
  
 +
=== Externe Kalender-Einträge einlesen ===
  
=== Google-Kalender-Einträge einlesen ===
+
Daten aus dem Google-Kalender, Exchange o.ä im Zeitraum von DatumVon bis DatumBis (DatumVon + Anzahl Tage) idealerweise in den Speicher puffern/einlesen und dort untersuchen, damit die Verarbeitung/Suche usw. schneller geht und weniger Traffic verursacht.
  
// Daten aus dem Google-Kalender:
+
=== Daten verarbeiten ===
DM.GetCalendarItemsClientDateSet( Date {dDatumVon}, speditAnzahlTage.Value {iAnzahlTage} , iErrorCode , sErrorMessage );
 
if iErrorCode > 0 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + '* Error on DM.GetCalendarItemsClientDateSet: ' + sErrorMessage );
 
  
 +
==== Kalender-Einträge durchlaufen und ggf. löschen: ====
 +
* Den Key (z.B. "EventID") des Termins in der Tabelle MAufJob.CLOUD_EVENT_ID suchen.
 +
select LFDNR from MAufJob where CLOUD_EVENT_ID='xxx'
  
// ---------------------------------------------------------------
+
* Wenn der dort nicht (mehr) gespeichert ist: Kalender-Item löschen!
// 2. Google-Einträge durchlaufen, (in DM.cdsGoogleKalenderItems)
 
// EventID in MAufJob.CLOUD_EVENT_ID suchen.
 
// Wenn dort nicht gespeichert: Google-Kalender-Item löschen!!!
 
// iErrorCode = -1: Erfolgreich, sErrorMessage
 
// iErrorCode > 0 :  sErrorMessage beinhaltet Fehlertext
 
// ---------------------------------------------------------------
 
DM.FindCalendarItemsToDelete( chkZuLoeschendeTermineSucheInSyncTabelle.Checked, iErrorCode , sErrorMessage ); //JC//240307// chkZuLoeschendeTermineSucheInSyncTabelle.Checked
 
if iErrorCode = -1 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + 'Delete unused items: ' + sErrorMessage );
 
if iErrorCode > 0 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + '* Error on Delete unused items: ' + sErrorMessage );
 
  
 +
==== Tabelle MAufJob durchlaufen und ggf. neu einfügen ====
  
// ---------------------------------------------------------------
+
* Mit dem o.a. '''select''' die Termindaten einlesen.
// 3. MAufJob durchlaufen, (in DM.qryMAufJob)
+
* Wichtig sind - neben den Inhaltsfelder des Termins - diese Felder:
// Wenn kein MAufJob.CLOUD_EVENT_ID : Google-Kalender-Item einfügen!!!
 
// iErrorCode = -1: Erfolgreich, sErrorMessage
 
// iErrorCode > 0 :  sErrorMessage beinhaltet Fehlertext
 
// ---------------------------------------------------------------
 
DM.FindCalendarItemsToInsert( chkSyncAdresseFest.Checked,editFesteMailAdresse.Text,
 
iErrorCode , sErrorMessage );
 
if iErrorCode = -1 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + 'Inserted items: ' + sErrorMessage );
 
if iErrorCode > 0 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + '* Error on Insert new items: ' + sErrorMessage );
 
  
 +
;MAufJob.LFDNR
 +
:Eindeutige ID (Integer not null) eines Termins in der REFLEX-Datenbank.
  
// ---------------------------------------------------------------
+
;MAufJob.CLOUD_EVENT_ID
// 4. MAufJob durchlaufen, (in DM.qryMAufJob)
+
:Eindeutige ID (String null) eines Termins im externen Kalender.
// Anhand MAufJob.CLOUD_EVENT_ID das Google-Kalender-Item einlesen, EInträge vergleichen,
 
// wenn Unterschied (irgendwo): Item updaten!
 
// iErrorCode = -1: Erfolgreich, sErrorMessage
 
// iErrorCode > 0 : sErrorMessage beinhaltet Fehlertext
 
// ---------------------------------------------------------------
 
DM.FindCalendarItemsToUpdate( chkSyncAdresseFest.Checked,editFesteMailAdresse.Text,iErrorCode , sErrorMessage );
 
if iErrorCode = -1 then
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + 'Updated items: ' + sErrorMessage );
 
if iErrorCode > 0 then
 
begin
 
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + '* Error on Update new items: ' + sErrorMessage );
 
  
// Kalender initialisieren:
+
* Wenn in MAufJob.CLOUD_EVENT_ID kein Eintrag steht: Kalender-Item einfügen!
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': ' + '* Try to reconnect...' );
+
* Kalender-Item (Eintrag) Felder:
DM.InitCalendar( strIniFile ); //JC//220901//
+
** Summary: Betreff
 +
** Description: Beschreibung, üblicherweise aus Terminart, VA-Name, VA-Ort, VA-Raum usw.
 +
** Location: Ort
 +
** Email
 +
** StartTime (Datum + Uhrzeit)
 +
** EndTime (Datum + Uhrzeit)
 +
* Die API des Kalenders muss dabei die ID des Kalender-Eintrags zurückliefern.
 +
* Diese ID muss das Synch-Programm in MAufJob.CLOUD_EVENT_ID speichern:
 +
  update MAUFJOB set CLOUD_EVENT_ID=@GlobalAppointmentID where LFDNR=N
  
FrmMainGooCalTest.memoProt.Lines.Add( FormatDateTime('dd/mm/yyyy', Date) + ' ' + FormatDateTime('hh:mm:ss:zzz', Time) + ': '  + '* 2nc try FindCalendarItemsToUpdate...' );
+
* Der Ordner-Name in Outlook/Exchange muss in MAufJob.OrdnerName varchar(255) gespeichert werden.
DM.FindCalendarItemsToUpdate( chkSyncAdresseFest.Checked,editFesteMailAdresse.Text,iErrorCode , sErrorMessage );
 
  
end;
 
  
 +
==== Tabelle MAufJob durchlaufen, bei Änderungen/Unterschieden Kalender-Item updaten ====
  
// Daten aktualisieren:
+
* MAufJob.CLOUD_EVENT_ID das Kalender-Item einlesen, die Einträge miteinander vergleichen
// --------------------
+
* Üblicherweise hat ein Kalender-Eintrag diese Felder:
 +
** Summary: Betreff
 +
** Description: Beschreibung, üblicherweise aus Terminart, VA-Name, VA-Ort, VA-Raum usw.
 +
** Location: Ort
 +
** Email
 +
** StartTime (Datum + Uhrzeit)
 +
** EndTime  (Datum + Uhrzeit)
 +
* Wenn es irgendwo einen Unterschied gibt: Kalender-Item entsprechend updaten!
  
// Daten Aus MAufJob:
+
== Termin-Item in Outlook/Exchange ==
DM.MakeSQL_MAufJob_Pers( DM.qryMAufJob, Date {dDatumVon}, speditAnzahlTage.Value {iAnzahlTage},
 
chkNurPersMitSyncStatus.Checked
 
);
 
  
// Daten aus dem Google-Kalender:
+
Wenn der externe Kalender Outlook/Exchange ist, werden dort diese Item-Properties verwendet:
DM.GetCalendarItemsClientDateSet( Date {dDatumVon}, speditAnzahlTage.Value {iAnzahlTage}, iErrorCode, sErrorMessage );
+
 +
Subject        -> Betreff          (String)
 +
Body            -> Termin Text      (String)
 +
Start          -> Termin Anfang    (Datum/Uhrzeit als DateTime)
 +
Duration        -> Termin Dauer in Minuten  (Integer)
 +
End            -> Termin Ende      (Datum/Uhrzeit als DateTime)
 +
Location        -> Ort              (String)
 +
CreationTime    -> Termin wurde an diesem Datum und Uhrzeit angelegt (Datum/Uhrzeit als String!)
 +
 +
BusyStatus      -> Termin Option [Anzeige als]: (Mögliche Werte: 0, 1, 2, 3 => (Numerische Werte als String!!!)
 +
                      0 => Frei
 +
                      1 => Mit Vorbehalt / Unter Vorbehalt
 +
                      2 => Beschäftig / Gebucht
 +
                      3 => Abwesend
 +
 +
ReminderPlaySound          -> Auskunft (true, false), ob ein Termin mit einem Sound versehen ist (Boolean)
 +
ReminderSet                -> Gibt Auskunft (true, false), ob ein Termin mit einer Erinnerung versehen ist (Boolean)
 +
ReminderMinutesBeforeStart -> Zeigt in Minuten (Numerische Werte als String!) die Erinnerung des Termins
 +
                              (also z.B. 30 bedeutet, dass die Erinnerung 30 Minuten von dem Anfangs des Termin aktiv wird)
 +
AllDayEvent                -> True, wenn auch Enddatum angegeben, dann über mehrere Tage!
 +
 +
GlobalAppointmentID        -> Eindeutige ID des Termins.
  
 +
== Voraussetzungen ==
  
 +
;ID des Kalender-Termins
 +
* Die ID des Kalender-Termins darf max. 512 Zeichen groß sein.
 +
* Sie wird zum Abgleich in der Tabelle MAufJob im Feld CLOUD_EVENT_ID varchar(512) gespeichert.
 +
* In '''[[Kalenderelement (Calendar Item/Appointment) in Outlook und Exchange|Outlook/Exchance]]''' ist dies das Feld Item.GlobalAppointmentID.
 +
: Anders als reguläre Entry IDs ändert sich die GlobalAppointmentID nicht beim Verschieben in andere Ordner/Speicher oder beim Export/Import!
  
 +
;Outlook:
 +
* Der Ordner-Name in Outlook/Exchange (=E-Mail des Users) darf max. 255 Zeichen groß sein.
 +
* Er wird in MAufJob.OrdnerName varchar(255) gespeichert.
  
 
== Links ==
 
== Links ==
Zeile 136: Zeile 207:
  
 
'''[[Datenbank-Beschreibung zum Job-Kalender]]'''
 
'''[[Datenbank-Beschreibung zum Job-Kalender]]'''
 +
 +
'''[[Kalenderelement (Calendar Item/Appointment) in Outlook und Exchange]]'''

Aktuelle Version vom 17. Februar 2026, 09:15 Uhr

1 Allgemeines

Hier wird beschrieben, wie man Termine aus dem JobKalender mit einem externen System synchronisieren kann.

1.1 Richtung der Synchronisation

Es wird davon ausgegangen, dass der JobKalender das führende System ist.

Das bedeutet
  • Für einen Termin gibt es drei Synchronisations-Momente:
    • Termin wurde angelegt
    • Termin wurde geändert. Das kann der Zeitraum, die Ressource/Person, der Text usw. sein.
    • Termin wurde gelöscht
  • Auftrags-Termine werden ausschließlich im JobKalender angelegt, geändert oder gelöscht.

Basis der Beschreibung ist das Zusatz-Modul GooCal, das Termine aus dem JobKalender mit Google©-Kalendern synchronisiert.


1.2 Datenbank-Zugriff

Es wird für die Synchronisation ein Zugriff auf die REFLEX-Datenbank benötigt. Idealerweise richtet der Admin einen Datenbank-User dafür ein, der über entsprechend eingeschränkte Rechte verfügt.

Im Prinzip ist der Zugriff auf die Daten nur lesend, mit einer Ausnahme:

  • Die eindeutige ID eines Termins im externen Kalender muss in die Datenbank geschrieben werden, wenn ein neuer Kalender-Eintrag erfolgte. Das wird unten beschrieben.
  • Wenn das Synchronisations-Tool eine eigene Tabelle/Datenbank für die Kalender-Termin-ID's und die zugehörigen REFLEX-Termin-ID's verwendet, ist das natürlich auch möglich und sogar die bessere Lösung (aus REFLEX-Sicht)!

2 Ablauf einer Synchronisation

Zur Erinnerung: Daten werden hier nur in eine Richtung synchronisiert, nämlich vom JobKalender zum externen Kalender!

2.1 Termin-Daten aus dem JobKalender (MAufJob) einlesen

Dazu kann dieser SQL-Script verwendet werden:

select

-- Primary Key, Unique index:
MAufJob.LFDNR, MAufJob.ID_STRING,

-- Datum/Uhrzeit von-bis:
MAufJob.DATUM, MAufJob.DATUMBIS, MAufJob.UHRZEITVON, MAufJob.UHRZEITBIS,

-- Person-Kurzname lt. Pers.Stammdaten:
MAufJob.PERSONEN,

-- Gehört zu AuftrNr (kann auch 0 sein!):
MAufJob.AUFTRNR,

-- Betreff, Text, Ort:
MAufJob.BETREFF, MAufJob.text, MAufJob.ORT,

-- Wenn Termin für Kunde: Kurzname lt. Stammdaten "Adr"
MAufJob.ADRKURZNAME,

MAufJob.TerminArt, MAufJob.TermStatus, MAufJob.TerminTyp,

-- Ganztägiger Termin 0/1
MAufJob.TERMIN_GANZTAEGIG,

-- Geändert 0/1:
MAufJob.Changed,

-- Anzahl bei Mengenterminen:
MAufJob.Anzahl, MAufJob.MENGENTERMIN,

-- Artikel-Daten, wenn Termin einen Artikelbezug hat:
MAufJob.OBERGRUPPE, MAufJob.GRUPPE, MAufJob.UNTGRUPPE, MAufJob.ARTNR, 

MAufJob.ZU_MAUFJOB_LFDNR,
MAufJob.BeauftragungsArt, MAufJob.JobCalChanged, MAufJob.BemerkungIntern, MAufJob.BemerkungExtern,
MAufJob.VeranstOrt, MAufJob.VeranstRaum, 

-- Verweis auf Externes Kalender-Item varchar(512):
MAufJob.CLOUD_EVENT_ID,

MAufJob.ERFDAT, MAufJob.ERFUSER,
MAufJob.AENDDAT, MAufJob.AENDUSER,

MAufKo.VERANSTNAME, MAufKo.BEARBEITER,

-- Personen/Ressourcen-Daten aus den Stammdaten:
Pers.PERSNR, Pers.KURZZEICHEN, Pers.VORNAME, Pers.NACHNAME, Pers.SPRACHE, Pers.SyncStatus, Pers.SyncAddress,

-- Eigentliche Termin-Tabelle:
from MAufJob MAufJob

-- Personen-/Ressourcen-Stammdaten:
left outer join Pers Pers on Pers.KURZZEICHEN=MAufJob.PERSONEN

-- Auftragskopf (wenn AuftrNr im Termin steht):
left outer join MAufKo MAufKo on MAufKo.AuftrNr=MAufJob.AuftrNr

-- Nur TerminTyp 3=Arbeitszeit-Termine:
where MAufJob.TerminTyp=3

-- Nur Personen/Ressourcen mit Kennzeichen > 0: 1=Google 2=Outlook/Exchange
-- Das wird in den Personen-Stammdaten in REFLEX so festgelegt.
and ( Pers.SyncStatus = 1 or Pers.KURZZEICHEN is null )

-- Nur wenn in den Personen-Stammdaten eine Synch-Adresse steht:
and Pers.SyncAddress > 
  
-- Auch überlappende Termine (wie in der Dispo):
and ( (MAufJob.DATUM <= 'DatumVon'
       AND
	(MAufJob.DATUMBIS >= 'DatumVon'
    )

-- Nur Termine mit diesem Status:
-- 1=Zugesagt, Bestätigt 4=Angefragt 6=Bestellung (Überhang)
and ( MAufJob.TERMSTATUS=1 or MAufJob.TERMSTATUS=4 or MAufJob.TERMSTATUS=6)
order by MAufJob.DATUM

2.2 Externe Kalender-Einträge einlesen

Daten aus dem Google-Kalender, Exchange o.ä im Zeitraum von DatumVon bis DatumBis (DatumVon + Anzahl Tage) idealerweise in den Speicher puffern/einlesen und dort untersuchen, damit die Verarbeitung/Suche usw. schneller geht und weniger Traffic verursacht.

2.3 Daten verarbeiten

2.3.1 Kalender-Einträge durchlaufen und ggf. löschen:

  • Den Key (z.B. "EventID") des Termins in der Tabelle MAufJob.CLOUD_EVENT_ID suchen.
select LFDNR from MAufJob where CLOUD_EVENT_ID='xxx'
  • Wenn der dort nicht (mehr) gespeichert ist: Kalender-Item löschen!

2.3.2 Tabelle MAufJob durchlaufen und ggf. neu einfügen

  • Mit dem o.a. select die Termindaten einlesen.
  • Wichtig sind - neben den Inhaltsfelder des Termins - diese Felder:
MAufJob.LFDNR
Eindeutige ID (Integer not null) eines Termins in der REFLEX-Datenbank.
MAufJob.CLOUD_EVENT_ID
Eindeutige ID (String null) eines Termins im externen Kalender.
  • Wenn in MAufJob.CLOUD_EVENT_ID kein Eintrag steht: Kalender-Item einfügen!
  • Kalender-Item (Eintrag) Felder:
    • Summary: Betreff
    • Description: Beschreibung, üblicherweise aus Terminart, VA-Name, VA-Ort, VA-Raum usw.
    • Location: Ort
    • Email
    • StartTime (Datum + Uhrzeit)
    • EndTime (Datum + Uhrzeit)
  • Die API des Kalenders muss dabei die ID des Kalender-Eintrags zurückliefern.
  • Diese ID muss das Synch-Programm in MAufJob.CLOUD_EVENT_ID speichern:
 update MAUFJOB set CLOUD_EVENT_ID=@GlobalAppointmentID where LFDNR=N
  • Der Ordner-Name in Outlook/Exchange muss in MAufJob.OrdnerName varchar(255) gespeichert werden.


2.3.3 Tabelle MAufJob durchlaufen, bei Änderungen/Unterschieden Kalender-Item updaten

  • MAufJob.CLOUD_EVENT_ID das Kalender-Item einlesen, die Einträge miteinander vergleichen
  • Üblicherweise hat ein Kalender-Eintrag diese Felder:
    • Summary: Betreff
    • Description: Beschreibung, üblicherweise aus Terminart, VA-Name, VA-Ort, VA-Raum usw.
    • Location: Ort
    • Email
    • StartTime (Datum + Uhrzeit)
    • EndTime (Datum + Uhrzeit)
  • Wenn es irgendwo einen Unterschied gibt: Kalender-Item entsprechend updaten!

3 Termin-Item in Outlook/Exchange

Wenn der externe Kalender Outlook/Exchange ist, werden dort diese Item-Properties verwendet:

Subject         -> Betreff           (String)
Body            -> Termin Text       (String)
Start           -> Termin Anfang     (Datum/Uhrzeit als DateTime)
Duration        -> Termin Dauer in Minuten  (Integer)
End             -> Termin Ende       (Datum/Uhrzeit als DateTime)
Location        -> Ort               (String)
CreationTime    -> Termin wurde an diesem Datum und Uhrzeit angelegt (Datum/Uhrzeit als String!)

BusyStatus      -> Termin Option [Anzeige als]: (Mögliche Werte: 0, 1, 2, 3 => (Numerische Werte als String!!!)
                      0 => Frei
                      1 => Mit Vorbehalt / Unter Vorbehalt
                      2 => Beschäftig / Gebucht
                      3 => Abwesend

ReminderPlaySound          -> Auskunft (true, false), ob ein Termin mit einem Sound versehen ist (Boolean)
ReminderSet                -> Gibt Auskunft (true, false), ob ein Termin mit einer Erinnerung versehen ist (Boolean)
ReminderMinutesBeforeStart -> Zeigt in Minuten (Numerische Werte als String!) die Erinnerung des Termins
                              (also z.B. 30 bedeutet, dass die Erinnerung 30 Minuten von dem Anfangs des Termin aktiv wird)
AllDayEvent                -> True, wenn auch Enddatum angegeben, dann über mehrere Tage!

GlobalAppointmentID         -> Eindeutige ID des Termins.

4 Voraussetzungen

ID des Kalender-Termins
  • Die ID des Kalender-Termins darf max. 512 Zeichen groß sein.
  • Sie wird zum Abgleich in der Tabelle MAufJob im Feld CLOUD_EVENT_ID varchar(512) gespeichert.
  • In Outlook/Exchance ist dies das Feld Item.GlobalAppointmentID.
Anders als reguläre Entry IDs ändert sich die GlobalAppointmentID nicht beim Verschieben in andere Ordner/Speicher oder beim Export/Import!
Outlook
  • Der Ordner-Name in Outlook/Exchange (=E-Mail des Users) darf max. 255 Zeichen groß sein.
  • Er wird in MAufJob.OrdnerName varchar(255) gespeichert.

5 Links

JobKalender

Datenbank-Beschreibung zum Job-Kalender

Kalenderelement (Calendar Item/Appointment) in Outlook und Exchange