Ticketsystem, Teil 2

Im ersten Teil der Beitragsreihe haben wir uns darum gekümmert, die E-Mails mit Kundenanfragen zu erfassen und diese in einem Formular in neue Tickets umzuwandeln. Außerdem haben wir in diesem Zuge auch gleich die Kundendaten erfasst. Nun schauen wir uns an, wie wir die Tickets bearbeiten, Antworten an die Kunden senden und deren Antworten automatisch zum Ticket hinzufügen. Außerdem stehen noch einige Feinheiten an, die uns die Arbeit mit den Tickets erleichtern sollen. Schließlich sollen die Tickets auf einfachste Weise von Outlook in unsere Anwendung gelangen.

Bin ich im richtigen Frontend

Bevor wir zu den eigentlichen Funktionen der Ticketverwaltung übergehen, wollen wir noch eine Sache sicherstellen, die mir beim Vorbereiten der Anwendung für den zweiten Teil dieser Beitragsreihe kurz Kopfzerbrechen bereitet hat. In der vorherigen Ausgabe von Access im Unternehmen haben wir im Beitrag Benutzerdefinierte Outlook-Eigenschaften (www.access-im-unternehmen.de/1030) erfahren, wie wir in Outlook benutzerdefinierte Eigenschaften speichern können.

Genau genommen war das eine Vorarbeit für die Ticketverwaltung, denn wir wollen dort in Outlook speichern, in welche Datenbank die Kundenanfragen, die dort als E-Mail eingehen, gespeichert werden sollen. Dabei handelt es sich natürlich um unser Ticketsystem.

Wenn wir dort den Pfad zur entsprechenden Datenbankdatei speichern, soll Outlook eingehende Kundenanfragen als E-Mails in dieser Datenbank speichern. Das gelingt genau so lange, bis die Datenbank nicht mehr am angegebenen Speicherort vorgebunden werden kann.

Es kann aber auch der Fall eintreten, dass Sie – aus welchen Gründen auch immer – die Datenbank mit dem Ticketsystem nicht von einem Verzeichnis in ein anders verschieben, sondern es lediglich kopieren. Bei mir war dies der Fall, weil ich die Datenbank von dem Ordner, in dem die Daten für den ersten Teil der Beitragsreihe lagen, in den Ordner mit den Daten für den zweiten Teil kopiert habe. Dadurch lag die Zieldatenbank für die neu eingehenden Kundenanfragen zwar nun in einem neuen Verzeichnis, gleichzeitig aber auch noch im alten Verzeichnis!

Hätte ich die Datenbank einfach verschoben und somit aus dem alten Ordner entfernt, wäre dies beim öffnen von Outlook durch den dann ausgelösten VBA-Code aufgefallen und ich hätte einfach den Pfad zum neuen Speicherort eingeben müssen.

Nun habe ich aber fleißig neue Kundenanfragen in den dafür angelegten Outlook-Ordner kopiert, in der Hoffnung, diese würden so auch automatisch in der Access-Datenbank im Ordner für den zweiten der Beitragsreihe gespeichert. Dort geschah aber gar nichts – keine neuen Einträge!

Damit Sie eine Möglichkeit haben, zu erkennen, dass etwas falsch läuft, fügen wir also nun zunächst etwas Code zum Access-Ticketsystem hinzu, der prüft, ob die aktuell geöffnete Access-Datenbankdatei auch diejenige ist, die in Outlook für die entsprechende Eigenschaft hinterlegt ist.

Dazu können wir auch von Access aus die im Beitrag Benutzerdefinierte Outlook-Eigenschaften formulierten VBA-Routinen nutzen – mit Ausnahme einer einzigen Routine, die wir noch anpassen müssen.

Die Routinen EigenschaftEinlesen, EigenschaftSetzen und OpenFilename können Sie aus der Beispieldatenbank zu Benutzerdefinierte Outlook-Eigenschaften entnehmen und in das Ticketsystem einpflegen.

Die weitere Routine DatenbankpfadHolen überführen wir in eine parameterlose Routine namens Datenbankpfad-InOutlookAktualisieren (s. Listing 1). Diese deklariert zunächst einige Variablen und legt deren Werte fest, zum Beispiel für die Variablen strElement (Wert: Ticketsystem) und strName (Datenbankpfad).

Public Function DatenbankpfadInOutlookAktualisieren()
     Dim strDatenbankpfad As String
     Dim bolVorhanden As Boolean
     Dim strElement As String
     Dim strName As String
     strElement = "Ticketsystem"
     strName = "Datenbankpfad"
     strDatenbankpfad = EigenschaftEinlesen(strElement, strName)
     If Not Len(strDatenbankpfad) = 0 Then
         If Not Len(Dir(strDatenbankpfad)) = 0 Then
             bolVorhanden = True
         End If
     End If
     If Not bolVorhanden Then
         strDatenbankpfad = CurrentDb.Name
         MsgBox "Der Datenbankpfad für die Ticketverwaltung in Outlook ist noch nicht gesetzt. Dies wird nun erledigt.", _
             vbOKOnly + vbExclamation
         EigenschaftSetzen strElement, strName, strDatenbankpfad
     Else
         If Not (strDatenbankpfad = CurrentDb.Name) Then
             MsgBox "Der Datenbankpfad in Outlook ist nicht auf die aktuelle Datenbankdatei gesetzt, sondern auf:" _
                 & vbCrLf & vbCrLf & strDatenbankpfad & vbCrLf & vbCrLf _
                 & "Er wird nun auf die aktuelle Datenbank eingestellt."
             strDatenbankpfad = CurrentDb.Name
             EigenschaftSetzen strElement, strName, strDatenbankpfad
         End If
     End If
End Function

Listing 1: Aktualisieren des Datenbankpfades in Outlook

Außerdem haben wir den Code dieser Methode etwas angepasst. Sie soll nun nicht mehr prüfen, ob die benutzerdefinierte Eigenschaft Datenbankpfad einen Wert enthält und ob dieser dem Pfad einer vorhandenen Datenbankdatei entspricht, um gegebenenfalls den Benutzer aufzufordern, den passenden Pfad per Datei öffnen-Dialog anzugeben.

Das ist nicht nötig, denn wir befinden uns ja gerade in der gewünschten Datenbank-Datei. Also müssen wir nur noch prüfen: Ist in der Eigenschaft Datenbankpfad überhaupt ein Pfad gespeichert und entspricht dieser dem Datenbankpfad der aktuell geöffneten Datei Falls nicht, soll die Eigenschaft einfach auf die aktuelle Datenbank eingestellt werden.

Die Routine liest über die Funktion EigenschaftEinlesen den aktuell in der Eigenschaft Datenbankpfad des StorageItem-Elements namens Ticketsystem gespeicherten Wert in die Variable strDatenbankpfad ein. Sie prüft, ob strDatenbankpfad überhaupt einen Wert enthält. Falls ja, prüft sie, ob der Wert einem gültigen Dateipfad entspricht. Falls ja, wird die Variable bolVorhanden auf True eingestellt. Ist bolVorhanden danach False, stellt die Prozedur den Wert der Variablen strDatenbankpfad auf den Pfad zur aktuellen Datenbankdatei ein und ruft dann die Funktion EigenschaftSetzen auf, um den Wert der Eigenschaft Datenbankpfad auf den Namen der aktuellen Datenbankdatei einzustellen – nicht ohne den Benutzer zuvor über diese Aktion zu informieren.

War bereits ein Datenbankpfad gespeichert, vergleicht die Prozedur diesen mit dem Pfad zur aktuellen Datenbankdatei. Sind die beiden Zeichenketten nicht gleich, erscheint ebenfalls eine Meldung und der Pfad der aktuellen Datenbankdatei landet in der benutzerdefinierten Eigenschaft Datenbankpfad von Outlook.

Mails von Outlook halbautomatisch in die Datenbank einlesen

Bevor wir uns nun weiter um die Verarbeitung der Tickets in Access kümmern, wollen wir noch eine Möglichkeit schaffen, neue Kundenanfragen, die im Ticketsystem verarbeitet werden sollen, dort hinein zu kopieren.

Dies soll auf der Outlook-Seite ganz einfach geschehen: Der Benutzer soll die infrage kommenden Mails von Kunden einfach per Drag and Drop in einen Ordner namens Ticketsystem ziehen (s. Bild 1).

Verschieben einer Anfrage in den Ordner Ticketsystem

Bild 1: Verschieben einer Anfrage in den Ordner Ticketsystem

Wenn die E-Mails dann im Zielordner gelandet sind, sollen sie vor allem mit dem Zusatz [Ticket xxx] ausgestattet sein. Damit können wir zwei Dinge erkennen: Erstens, dass der Mechanismus, der durch das Hineinziehen einer Mail in diesen Ordner ausgelöst werden soll, funktioniert hat.

Zweitens soll der Platzhalter xxx den Wert des Primärschlüsselfeldes enthalten, unter dem die Mail als neuer Datensatz in der Tabelle tblMailItems angelegt wurde (s. Bild 2).

In den Ordner Ticketsystem verschobene Anfragen

Bild 2: In den Ordner Ticketsystem verschobene Anfragen

Mechanismus zum übertragen der Kundenanfragen in Outlook

Damit kommen wir zu dem Mechanismus selbst. Dieser soll ausgelöst werden, wenn wir eine E-Mail in den Ordner Ticketsystem von Outlook ziehen.

Diesen initialisieren wir in einer Prozedur namens Application_Startup_Ticketverwaltung (s. Listing 2). Diese wird nicht automatisch mit Outlook gestartet. Allerdings löst Outlook beim Start automatisch eine Prozedur aus, die wir Application_Startup nennen und die sich in der Klasse ThisOutlookSession des VBA-Projekts von Outlook befindet.

Public Sub Application_Startup_Ticketverwaltung()
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim objFolder As Outlook.Folder
     Dim objFolderArchiv As clsFolderArchiv
     Dim strTicketsystemDatenbank As String
     On Error GoTo Application_Startup_Err
     strTicketsystemDatenbank = DatenbankpfadHolen("Ticketsystem", "Datenbankpfad")
     Set db = DBEngine.OpenDatabase(strTicketsystemDatenbank, , True)
     Set rst = db.OpenRecordset("SELECT * FROM tblOptionen", dbOpenDynaset)
     Set colFolders = New Collection
     Do While Not rst.EOF
         Set objFolderArchiv = New clsFolderArchiv
         With objFolderArchiv
             Set objFolder = GetFolderByPath(rst!Verzeichnis)
             If objFolder Is Nothing Then
                 MsgBox "Der in der Export-Datenbank ''" & strTicketsystemDatenbank & "'' angegebene Outlook-Ordner ''" _
                     & rst!Verzeichnis & "'' ist nicht in Outlook vorhanden. Wählen Sie diesen nun erneut aus."
                 Set objFolder = Outlook.GetNamespace("MAPI").PickFolder
                 db.Execute "UPDATE tblOptionen SET Verzeichnis = ''" & objFolder.FolderPath & "''", dbFailOnError
             End If
             Set .Folder = objFolder
             .AnlagenSpeichern = rst!AnlagenSpeichern
             Set .Database = db
             .NeuEinlesen = rst!NeuEinlesen
             .Groesse = Nz(rst!Groesse)
         End With
         colFolders.Add objFolderArchiv
         If rst!Rekursiv Then
             UnterordnerInstanzieren objFolder, db, Nz(rst!Groesse), rst!NeuEinlesen, rst!AnlagenSpeichern, colFolders
         End If
         rst.MoveNext
     Loop
End Sub

Listing 2: Starten des Mechanismus zum automatischen übertragen von E-Mails, die in einen bestimmten Ordner verschoben werden

Sollten Sie diese noch nicht verwenden, tragen Sie nur den Aufruf der Prozedur Application_Startup_Ticketverwaltung dort ein. Anderenfalls fügen Sie diesen Aufruf einfach an die bereits vorhandenen Anweisungen an:

Public Sub Application_Startup()
     ...
     Application_Startup_Ticketverwaltung
End Sub

Die Prozedur Application_Startup_Ticketverwaltung deklariert Variablen zum Speichern von Verweisen auf die Ticketsystem-Datenbank und ein Recordset für den Zugriff auf die Optionentabelle, ein Folder-Objekt für den Zugriff auf den Ziel-Ordner in Outlook sowie eine Variable zum Speichern von Objekten des Typs clsFolderArchiv.

Da wir hier innerhalb des Outlook-VBA-Projekts auf Elemente der DAO-Bibliothek zugreifen, fügen wir dem VBA-Projekt noch einen Verweis auf diese Objektbibliothek hinzu – plus einen Verweis auf die Access-Bibliothek, falls wir mal eine Funktion wie Nz() oder DLookup() benötigen (s. Bild 3).

Verweis auf die DAO-Bibliothek, zusätzlich auch noch auf die Access-Bibliothek

Bild 3: Verweis auf die DAO-Bibliothek, zusätzlich auch noch auf die Access-Bibliothek

Die Prozedur verwendet dann zunächst die Funktion DatenbankpfadHolen, die wir bereits im Beitrag Benutzerdefinierte Outlook-Eigenschaften beschrieben haben. Diese Funktion liefert den Pfad zur Datenbank Ticketsyste.accdb zurück, der in der dafür angelegten benutzerdefinierten Eigenschaft gespeichert ist. Diese Vorgehensweise verwenden wir, damit wir diesen Pfad nicht fest im Code verdrahten müssen – etwa in Form einer Konstanten -, sondern ihn auch einmal dynamisch ändern können, wenn sich der Speicherort des Ticketsystems ändert. Dann öffnen wir mit der OpenDatabase-Methode die soeben ermittelte Datenbankdatei und speichern einen Verweis darauf in der Database-Variablen db.

Als Nächstes greifen wir über die OpenRecordset-Methode auf die Tabelle tblOptionen dieser Datenbank zu und speichern den Verweis darauf in der Recordset-Variablen rst.

Die Prozedur könnte auch mehrere Ordner als Zielordner definieren, daher verwenden wir eine Collection, um für jeden Zielordner ein Objekt der Klasse clsFolderArchiv zu speichern. Diese deklarieren wir wir folgt im Kopf des Moduls ThisOutlookSession:

Dim colFolders As Collection

Da wir wie gesagt auch mehrere Ordner nutzen könnten, durchlaufen wir die Datensätze der Tabelle tblOptionen in einer Do While-Schleife. Im ersten Schritt erstellen wir eine neue Instanz der Klasse clsFolderArchiv.

Dieser weisen wir einige Informationen zu, die wir dem aktuellen Datensatz der Tabelle tblOptionen entnehmen. Der erste ist der Zielordner. Den Pfad zu diesem Ordner, in unserer Beispielkonstellation \\Outlook\Posteingang\Ticketsystem, lesen wir aus dem Feld Verzeichnis ein.

Dann verwenden wir die Hilfsfunktion GetFolderByPath, um ein Folder-Objekt zu diesem Verzeichnis zu ermitteln und das Ergebnis in der Variablen objFolder zu speichern.

Die Funktion GetFolderByPath erwartet den Pfad als String. Sie durchläuft alle Ordner, auch rekursiv, bis sie den entsprechenden Outlook-Folder gefunden hat. Dieser landet dann in der Variablen objFolder.

Diese Funktion haben wir, neben einigen anderen Funktionen für den Zugriff auf die Outlook-Objekte, schon in mehreren Ausgaben genutzt. In der nächsten Ausgabe finden Sie im Beitrag Outlook-Ordner im Griff (www.access-im-unternehmen.de/1043) eine Erläuterung dieser Funktion sowie einige weiterführende Informationen zu Outlook-Ordnern.

Es kann sein, dass die Funktion GetFolderByPath kein Ergebnis zurückliefert. Das ist genau dann der Fall, wenn es kein Folder-Objekt mit dem angegebenen Pfad gibt.

Deshalb prüfen wir mit der folgenden If…Then-Bedingung, ob objFolder den Wert Nothing liefert, also dass es leer ist. Ist das der Fall, folgt eine entsprechende Meldung und die PickFolder-Methode zeigt einen Dialog an, mit dem der Benutzer den gewünschten Zielordner auswählen kann (s. Bild 4).

Auswählen eines neuen Zielordners für die Kundenanfragen

Bild 4: Auswählen eines neuen Zielordners für die Kundenanfragen

Der auf diese Weise der Variablen objFolder zugewiesene Outlook-Folder liefert mit der Eigenschaft FolderPath seine Pfadangabe, die wir mit der nächsten Anweisung per UPDATE-Abfrage in der Tabelle tblOptionen speichern.

Nun können wir die Eigenschaft Folder der Klasse clsFolderArchiv auf dieses Objekt einstellen. Auf wesentlich einfachere Weise füllen wir nun die Eigenschaften AnlagenSpeichern, NeuEinlesen und Groesse, denen wir einfach die Werte der entsprechenden Felder des Recordsets rst zuweisen – und die Eigenschaft Database erhält einen Verweis auf das Objekt db, also die aktuelle referenzierte Datenbank.

Das so gefüllte Objekt objFolderArchiv fügen wir dann per Add-Methode zum Collection-Objekt colFolders hinzu, damit es nach Beenden der Prozedur und somit dem Verfallen der Gültigkeit der Objektvariablen nicht in den ewigen Jagdgründen landet. Auf die gleiche Weise werden, sollten auch Unterordner dieses Ordners als Ziel für das Verschieben von Kundenanfragen infrage kommen, auch noch alle Ordner unterhalb des angegebenen Ordners referenziert und in der Collection colFolders gespeichert – und natürlich deren Unterordner und so weiter. Dafür muss jedoch das Feld Rekursiv der Tabelle tblOptionen den Wert True aufweisen.

Die Klasse clsFolderArchiv haben wir weitgehend aus der gleichnamigen Klasse des Beitrags Outlook-Mails nach Empfang archivieren (www.access-im-unternehmen.de/1007) übernommen. Es gibt ein paar kleinere Unterschiede.

So verfügt unsere Tabelle tblMailItems in der Ticketsystem-Datenbank über ein eindeutiges Feld namens EntryID, sodass eine E-Mail nur einmalig in der Tabelle gespeichert werden kann und eine Fehlermeldung angezeigt wird, wenn der Benutzer die gleiche Mail zwei Mal in einen der Outlook-Ordner der Ticketverwaltung zieht.

Die übrigen Vorgänge sind mit den in diesem Beitrag beschriebenen Abläufen identisch: Beim Hineinziehen einer E-Mail in einen Outlook-Ordner, den wir mit einem Objekt der Klasse clsFolderArchiv referenziert haben, löst dies ein Ereignis aus, das den Inhalt der E-Mail in die Tabelle tblMailItems der Zieldatenbank schreibt.

Funktionsweise des Formulars frmOffeneMails

Das Formular frmOffeneMails soll alle Mails anzeigen, die in den entsprechenden Outlook-Ordner gezogen und so automatisch in der Tabelle tblMailItems gespeichert wurden (s. Bild 5). Dazu nutzt das Formular das Listenfeld lstOffeneMails links oben.

Das Formular frmOffeneMails in der Entwurfsansicht

Bild 5: Das Formular frmOffeneMails in der Entwurfsansicht

Die Datensatzherkunft des Listenfeldes sieht wie in Bild 6 aus. Sie verwendet die Tabelle tblMailItems und die Tabelle tblTickets als Datenquelle. Die Tabelle tblTickets ist über das Fremdschlüsselfeld MailItemID mit der Tabelle tblTickets verknüpft.

Datensatzherkunft für das Listenfeld lstOffeneMails

Bild 6: Datensatzherkunft für das Listenfeld lstOffeneMails

Ohne weitere Maßnahmen würde die Abfrage also alle Datensätze der beiden Tabellen liefern, für die eine Verknüpfung vorliegt. Wir wollen aber ja gerade die Datensätze der Tabelle tblMailItems anzeigen, die noch nicht in Form eines Tickets in die Datenbank aufgenommen wurden. Also führen wir zwei Schritte durch: Als Erstes klicken Sie doppelt auf den Beziehungspfeil, sodass sich die Verknüpfungseigenschaften öffnen. Hier wählen Sie die Option Beinhaltet ALLE Datensätze aus “tblMailItems“ und nur die Datensätze aus “tblTickets“, bei denen die Inhalte der verknüpften Felder beider Tabellen gleich sind. aus.

Damit zeigt die Abfrage nun auch solche Datensätze der Tabelle tblMail-Items an, die noch nicht mit einem Datensatz der Tabelle tblTickets verknüpft sind. Wir wollen aber ausschließlich die Datensätze, die noch nicht mit dieser Tabelle verknüpft sind, liefern.

Das ist ohne viel Aufwand umzusetzen: Wir müssen einfach nur ein Kriterium festlegen, das nur diejenigen Datensätze zulässt, für die das Feld TicketID den Wert Null enthält.

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar