Wenn man an Abonnements denkt, fallen einem zunächst Zeitungen oder Magazine ein. Es gibt aber natürlich auch Abonnements für alle anderen Bereiche des Lebens – vom Sockenabo, das einem regelmäßig neue Socken garantiert, bis hin zum Abonnement über die regelmäßige Lieferung von Adressetiketten. Allen Abonnements ist gemeinsam, dass diese verwaltet werden müssen, damit jeder Kunde das richtige Produkt zum passenden Zeitpunkt erhält. Dieser Beitrag zeigt Möglichkeiten zum Verwalten von Abonnements auf.
Wenn Sie sich ein einfaches Zeitschriften-Abonnement vorstellen, bei dem der Leser monatlich eine Ausgabe der Zeitschrift erhält, gibt es grundsätzlich mindestens zwei Möglichkeiten, um dies abzuhandeln:
- Sie legen den Zeitraum fest, innerhalb dessen der Kunde das Produkt erhält – beispielsweise vom 1.1.2012 bis zum 31.12.2012. Befindet sich der Lieferzeitpunkt der aktuellen Ausgabe innerhalb dieses Zeitraumes, erhält der Kunde ein Exemplar, sonst nicht.
- Sie verwalten die einzelnen Ausgaben in einer Tabelle und weisen dem Abonnement die entsprechenden Datensätze dieser Tabelle zu, also beispielsweise 1/2012 bis 12/2012.
Die Variante mit dem Zeitraum hat einen Nachteil: Es kann ja einmal vorkommen, dass die erste Ausgabe des Jahres schon im Vormonat erscheint, also beispielsweise am 31.12.2011, oder dass die letzte Ausgabe des Zeitraums etwas später veröffentlicht wird – etwa am 2.1.2013. In diesem Fall müssen Sie zu jeder Ausgabe noch weitere Angaben festlegen, zum Beispiel einen generischen Erscheinungstermin wie etwa immer den ersten Tag des Monats.
Bei dieser vom Datenmodell her einfacheren Lösung müssten Sie aber dennoch eine Möglichkeit schaffen, die pro Kunde versendeten Ausgaben zu speichern – und dann können Sie auch gleich zu der Variante mit der Verwaltung der einzelnen Ausgaben übergehen.
Produkt oder Abonnement
Ein interessantes Thema ist die Vermengung von Begriffen wie Abonnement, Bestellung, Produkt et cetera. Wenn ein Kunde ein Abonnement bestellt, wie wird dies dann in der Datenbankanwendung behandelt Eines steht fest: Der Kunde wird als Datensatz in einer Kundentabelle gespeichert (egal, ob dieser bereits vorhanden ist oder neu angelegt werden muss). Eine Bestellung hat mit dem Abonnement eigentlich nicht viel gemein, denn eine Bestellung entspricht ja der Zuordnung eines Produktes zu einem Kunden (mit entsprechendem Zeitpunkt, Konditionen et cetera). Das Abonnement lässt sich eher mit einem einzelnen Produkt gleichsetzen beziehungsweise mit einer Bestellposition. Dabei unterschlagen wir zunächst, dass ein Abonnement ja aus mehreren Lieferungen besteht. Während das Abonnement beziehungsweise das Produkt als Bestellposition Teil einer kompletten Bestellung sein kann, soll es doch während der Verwaltung des Abonnements als dem jeweiligen Kunden zugeordnetes Element und somit losgelöst von der Bestellung oder einer Bestellposition betrachtet werden.
Also enthält das Abonnement zumindest ein Fremdschlüsselfeld zum Herstellen einer Beziehung zum betroffenen Kunden.
Der Einfachheit halber lassen wir aus dieser Anwendung die Bestellungen komplett aus und schauen nur auf die reine Abonnementverwaltung.
Datenmodell
Ohne Kunden gibt es kein Abonnement, deshalb beginnen wir hier mit der Tabelle tblKunden. Diese sieht im Entwurf wie in Bild 1 aus. Zu dieser Tabelle gehört noch eine Lookup-Tabelle, welche die Anreden beisteuert. Die Tabelle heißt tblAnreden, enthält die Felder AnredeID und Anrede und wird über das Fremdschlüsselfeld AnredeID der Tabelle tblKunden referenziert. Dieses Fremdschlüsselfeld wurde als Nachschlagefeld ausgelegt.
Bild 1: Die Tabelle tblKunden in der Entwurfsansicht
Die Produkte, also beispielsweise Access im Unternehmen, Access [basics] oder Access-Newsletter speichern wir in einer Tabelle namens tblProdukte. Diese Tabelle enthält neben dem Primärschlüsselfeld ProduktID noch ein Feld mit der Produktbezeichnung sowie ein Feld namens AnzahlAusgaben, das die Anzahl der Ausgaben pro Jahr kennzeichnet (s. Bild 2). Außerdem finden Sie im Datenmodell dieser Tabelle noch ein Feld namens Laufzeit, das die Laufzeit eines Abonnements zu diesem Produkt in Monaten aufnimmt.
Bild 2: Die Tabelle tblProdukte mit einigen Daten
Jedes Abonnement wird selbstverständlich genau einem Kunden zugewiesen. Deshalb steht schon fest: Die Tabelle tblAbonnements muss neben dem Primärschlüsselfeld AbonnementID noch ein Fremdschlüsselfeld zur Tabelle tblKunden enthalten (s. Bild 3). Welche Informationen sollen noch zu einem Abonnement gespeichert werden Ein Abonnement führt auf jeden Fall einen Kunden und ein Produkt zusammen. Daher erhält die Tabelle zwei Fremdschlüsselfelder namens ProduktID und KundeID, die beide als Nachschlagefeld ausgeführt werden. Das Nachschlagefeld ProduktID soll dabei das Feld Produkt der Tabelle tblProdukte zur Auswahl anbieten, das Nachschlagefeld KundeID das Feld Kundenbezeichnung der Tabelle tblKunden – beides in alphabetischer Reihenfolge. Dies erreichen Sie am schnellsten durch den Einsatz des Nachschlage-Assistenten.
Bild 3: Entwurfsansicht der Tabelle tblAbonnements
Normalerweise sollte man annehmen, dass Sie hier einen zusammengesetzten, eindeutigen Index über die beiden Felder ProduktID und KundeID anlegen sollten. Allerdings kann es ja vorkommen, dass ein Kunde ein Abonnement zum gleichen Produkt verlängert oder dieses später nochmals ordert – Eindeutigkeit bezüglich der Kombination aus Produkten und Kunden ist also nicht gefragt.
Welche Informationen soll das Abonnement noch enthalten Interessant ist sicher das Startdatum, das wir im gleichnamigen Feld mit dem Datentyp Datum/Uhrzeit speichern. Außerdem soll noch vermerkt werden, ob ein Abonnement gekündigt oder storniert wurde. Eine Kündigung führt dazu, dass das Abonnement – so dies vorgesehen ist – nicht automatisch verlängert wird.
Eine Stornierung kann beispielsweise innerhalb eines festgelegten Zeitraums (der Rücktrittsfrist) erfolgen und führt dazu, dass das Abonnement nicht weiter ausgeführt wird. Die Felder GekuendigtAm und StorniertAm nehmen die entsprechenden Datumsangaben auf.
Und da wir bereits die Verlängerung eines Abonnements angesprochen haben: Die geschieht in der Regel, wenn der Abonnent das Abonnement nicht bis zu einem bestimmten Termin kündigt. Es gibt sicher auch Abonnements, die automatisch auslaufen, gängig ist aber das Modell der automatischen Verlängerung. Was geschieht bei einer Verlängerung eines Abonnements
Es gibt grundsätzlich zwei Möglichkeiten:
- Sie ändern die Daten des zu verlängernden Abonnements so, dass der Kunde weiterhin die gewohnten Lieferungen erhält.
- Sie legen ein neues Abonnement an, dass sich auf den gleichen Kunden und das gleiche Produkt bezieht. Außerdem sollte hier festgehalten werden, welches Abonnement hier verlängert wurde.
Wir wählen die zweite Variante, da sie besser nachvollziehbar scheint. Damit Sie erkennen können, ob es sich bei dem Abonnement um ein Folge-Abonnement handelt, legen wir in der Tabelle tblAbonnements auch noch ein Feld namens FolgeaboVon an. Dieses wird, wie in Bild 4 zu erkennen, mit dem Primärschlüsselfeld der Tabelle selbst verknüpft. Dies erreichen Sie, indem Sie die Tabelle zwei Mal in das Beziehungsfenster ziehen und dann das Feld FolgeaboVon einer der beiden Repräsentationen der Tabelle auf das Feld AbonnementID der anderen ziehen. Legen Sie für diese Beziehung referentielle Integrität fest.
Bild 4: Zuordnung von Folge-Abonnements
Ausgaben und Versendungen
Es fehlen noch die Ausgaben, die mit jedem Abonnement an die Kunden versendet werden sollen. Das ist komplizierter, als es zunächst den Anschein hat. Immerhin gibt es ja für jedes Produkt verschiedene Ausgaben: Für Access im Unternehmen die Ausgaben 1 bis 6 für jedes Jahr, für Access [basics] etwa 1 bis 12. Diese sollen nun je nach Startzeitpunkt einem Abonnements zugeordnet werden. Wenn der Kunde beispielsweise am 1.7. eines Jahres einsteigt, soll er bei einem Magazin mit zwölf Ausgaben mit der Ausgabe 7 beginnen. Das spezielle Abonnement des Kunden soll also etwa die Ausgaben 7/2012 bis 6/2013 enthalten. Ein anderer Kunde steigt vielleicht einen Monat später ein, sein Abonnement sieht also bezüglich der Versendungen gleich ganz anders aus. Um diese Ausgaben zum jeweiligen Abonnement zuweisen zu können, wollen wir zunächst einmal die Ausgaben je Abonnement definieren. Dies erledigen wir in einer Tabelle namens tblAusgaben, welche die vier Felder AusgabeID, AusgabeBezeichnung, AusgabeNummer und AusgabeJahr enthält. Diese Tabelle könnte beispielsweise Daten wie in Bild 5 enthalten. Wo aber befindet sich hier der Verweis auf das Produkt – und zwar in Form eines Fremdschlüsselfeldes namens ProduktID mit einer Referenz auf das Primärschlüsselfeld der Tabelle tblProdukte Dieses gehört eigentlich in diese Tabelle. Wir möchten aber noch einen Spezialfall abfangen, der durch verschiedene Abonnement-Formen mit den gleichen Ausgaben resultiert: Es ist ja möglich, dass Sie zusätzlich zu einem zwölfmonatigen Abonnement noch ein dreimonatiges Testabo anbieten. Der Kunde mit dem Testabo soll aber etwa die gleiche Ausgabe 5/2012 erhalten wie der Kunde mit dem regulären Abonnement. Deshalb kann ein Eintrag der Tabelle tblAusgaben mehreren Datensätzen der Tabelle tblProdukte zugeteilt werden.
Bild 6: Das komplette Datenmodell der Beispieldatenbank
Damit hätten wir festgelegt, welche Ausgaben es zu einem Produkt gibt. Nun müssen wir nur noch die im Rahmen eines Abonnements zu verschickenden Ausgaben festlegen. Dies wiederum geschieht in der Tabelle tblVersendungen. Diese verknüpft die Tabelle tblAbonnements mit der Tabelle tblAusgaben. Das Feld VersendungID dient als Primärschlüsselfeld, das Feld AusgabeID ist ein Fremdschlüsselfeld zum Herstellen der Beziehung zur Tabelle tblAusgaben und das Feld AbonnementID weist die Ausgabe schließlich einem der Datensätze der Tabelle tblAbonnements zu. Schließlich müssen wir noch irgendwo vermerken, wann die Ausgabe an den über die Tabelle tblAbonnements referenzierten Kunden verschickt wurde. Dies erledigen wir mit einem Datumsfeld namens Versanddatum.
Bild 6 zeigt die Zusammenhänge zwischen den einzelnen Tabellen im Überblick. Auch der noch zu programmierende Ablauf beim Anlegen eines Abonnements lässt sich hier ablesen: Wenn ein neuer Datensatz zur Tabelle tblAbonnements hinzugefügt wird, legt man dort auch das Produkt fest, auf dem das Abonnement basiert. Im nächsten Schritt prüft man, welche Ausgaben angelegt werden sollen. Dazu wird im Feld AktuelleAusgabeID der Tabelle tblProdukte, das ebenfalls mit der Tabelle tblAusgaben verknüpft ist, die aktuelle Ausgabe referenziert. Im Folgenden sollen in der Tabelle tblVersendungen die entsprechenden Einträge vorgenommen werden: Wenn das Feld AnzahlAusgaben der Tabelle tblProdukte also etwa den Wert 12 enthält, sollen in der Tabelle tblVersendungen zwölf Datensätze erstellt werden. Diese enthalten jeweils einen Verweis auf den neu angelegten Datensatz der Tabelle tblAbonnements und auf die zwölf Einträge der Tabelle tblAusgaben, die zur aktuellen Ausgabe und zum gewählten Produkt passen. Wie dies technisch abläuft, schauen wir uns weiter unten an.
Bild 5: Tabelle der Ausgaben
Damit die Tabelle tblVersendungen jede Ausgabe nur einmal je Abonnement aufnimmt, legen wir einen zusammengesetzten eindeutigen Schlüssel für die beiden Felder AbonnementID und AusgabeID fest.
Formulare zur Datenverwaltung
Nachdem die Tabellenstruktur steht, benötigen wir eine Reihe Formulare zum Verwalten der Abonnements und der dazu benötigten Daten.
Die Kunden sollen in einem Formular frmKunden verwaltet werden. Über dieses Formular werden schließlich auch die Abonnements eines jeden Kunden angezeigt und die zu versendenden Ausgaben. Das Formular frmKunden verwendet die Tabelle tblKunden als Datenherkunft und zeigt die Details zu einem Kunden in der Formularansicht an. An dieser Stelle gibt es noch keine Besonderheiten – ziehen Sie einfach alle benötigten Felder aus der Datenherkunft in den Detailbereich der Entwurfsansicht und ordnen Sie diese etwa wie in Bild 7 an.
Bild 7: Das Formular zur Verwaltung der Kunden in der Entwurfsansicht
Produkte verwalten
Bevor wir die Unterformulare zur Darstellung der Abonnements eines Kunden sowie der Versendungen zu dem jeweils aktivierten Abonnement einfügen, sollen zunächst die Möglichkeiten der dazu benötigten Daten geschaffen werden.
Da wären zunächst die Produkte, ohne die nichts geht: Sie können kein Abonnement anlegen ohne ein Produkt und auch keine Ausgaben.
Das Formular frmProdukte verwendet die Tabelle tblProdukte als Datenherkunft und zeigt auch alle Felder dieser Tabelle an (s. Bild 8). Sie können damit zwar nun Daten für die Felder Produkt und AnzahlAusgaben hinzufügen, aber noch nicht für das Fremdschlüsselfeld AktuelleAusgabeID – dafür liegen ebenfalls noch keine Daten vor.
Bild 8: Das Formular zum Anlegen der Produkte
Um eine oder mehrere Ausgaben anzulegen, fügen Sie dem Formular gleich neben dem Feld AnzahlAusgaben eine Schaltfläche namens cmdNeueAusgaben hinzu. Dieses ruft ein Formular auf, mit dem Sie neue Ausgaben zum aktuellen Produkt hinzufügen können – dazu später mehr.
Das Unterformular sfmAusgaben soll alle Ausgaben zum aktuell im Hauptformular frmProdukte angezeigten Produkt anzeigen. Dazu verwendet das Formular eine Abfrage als Datenherkunft, die auf den beiden Tabellen tblProdukteAusgaben und tblAusgaben basiert und die unter dem Namen qryFrmProdukteSfmAusgaben gespeichert wird (s. Bild 10).
Bild 9: Datenherkunft des Unterformulars sfmAusgaben
Bild 10: Produkte und Ausgaben im Entwurf
Das Unterformular zeigt somit automatisch nur noch Ausgaben an, die zum aktuellen Produkt im Hauptformular gehören.
Eine Besonderheit ergibt sich noch bezüglich des Kombinationsfeldes zur Auswahl der aktuellen Ausgabe, das wir in cboAktuelleAusgabeID umbenennen. Es soll genau wie das Unterformular sfmAusgaben nur die zum aktuellen Produkt gehörenden Ausgaben zur Auswahl anbieten.Dazu weisen Sie diesem Kombinationsfeld als Datensatzherkunft eine Abfrage zu, die genauso aussieht wie die Datenherkunft des Unterformulars sfmAusgaben, aber unter dem Namen qryFrmProdukteCboAktuelleAusgabeID als eigene Abfrage gespeichert wird. Man weiß nie, ob nicht eine der Abfragen einmal angepasst werden muss, daher werden diese gleich als eigene Objekte gespeichert.
Wenn nun bereits einige Datensätze in den Tabellen tblAusgaben und tblProdukteAusgaben vorhanden wären, würde das Formular frmProdukte diese wie in Bild 11 anzeigen.
Bild 11: Formularansicht der Produkte und Ausgaben
Also kümmern wir uns doch darum, dass einige Ausgaben angelegt werden. Dies erledigen Sie, in dem Sie die weiter oben angelegte Schaltfläche cmdNeueAusgaben mit Leben füllen.
Legen Sie dazu die folgende Ereignisprozedur an, die durch das Ereignis Beim Klicken der Schaltfläche ausgelöst wird:
Private Sub cmdNeueAusgaben_Click() DoCmd.OpenForm "frmNeueAusgaben", WindowMode:=acDialog, OpenArgs:=Me!ProduktID Me!cboAktuelleAusgabeID.Requery Me!sfmAusgaben.Form.Requery End Sub
Die Prozedur öffnet ein weiteres Formular namens frmNeueAusgaben und übergibt den Wert des Feldes ProduktID des aktuellen Datensatzes per Öffnungsargument an das Formular.
Dieses wird außerdem als modaler Dialog geöffnet und sieht wie in Bild 12 aus. Das Formular zeigt alle kommenden und noch nicht erfassten Ausgaben in einem Listenfeld an.
Bild 13: frmNeueAusgaben in der Entwurfsansicht
Der Benutzer kann die gewünschten Einträge unter Verwendung der Strg– und der Umschalt-Taste markieren und die Ausgaben mit der Schaltfläche OK hinzufügen oder das Formular ohne Ónderung der Ausgaben mit der Schaltfläche Abbrechen schließen. Die Prozedur cmdNeueAusgaben_Click aktualisiert jeweils mit der Requery-Methode noch die Datenherkunft des Unterformulars sfmAusgaben sowie die Datensatzherkunft des Kombinationsfeldes cboAktuelleAusgabe.
Erstellen des Formulars frmNeueAusgaben
Unabhängig von Abonnements, also der Zuordnung von zu versendenden Ausgaben zu einem Kunden, müssen die Ausgaben für das betroffene Produkt zunächst einmal angelegt werden.
Bei Access im Unternehmen heißen solche Ausgaben 1/2012, 2/2012 und so weiter. Die können Sie entweder von Hand anlegen oder Sie ziehen ein Formular hinzu, das Ihnen diese Arbeit abnimmt.
Dieses sieht im Entwurf wie in Bild 13 aus und enthält ein Listenfeld namens lstAusgaben, das erst beim Öffnen des Formulars mit Daten gefüllt wird. Da die Daten als Zeichenkette übergeben werden, stellen Sie die Eigenschaft Herkunftsart auf Wertliste ein. Damit der Benutzer wie im Windows Explorer mehrere Einträge mit der Strg– und der Umschalt-Fläche auswählen kann, stellen Sie die Eigenschaft Mehrfachauswahl auf Erweitert ein.
Bild 12: Formular zum Anlegen neuer Ausgaben
Damit die noch nicht angelegten Ausgaben im Listenfeld lstAusgaben angezeigt werden, legen Sie die Ereignisprozedur aus Listing 1 an, die durch das Ereignis Beim Öffnen des Formulars frmNeueAusgaben ausgelöst wird.
Listing 1: Neu hinzuzufügende Ausgaben in einer Liste anzeigen
Private Sub Form_Open(Cancel As Integer) Dim i As Integer Dim strAusgaben As String Dim intAnzahlAusgaben As Integer Dim intJahrAusgabe As Integer Dim intNummerAusgabe As Integer lngProduktID = Nz(Me.OpenArgs) If lngProduktID = 0 Then Cancel = True Else intAnzahlAusgaben = DLookup("AnzahlAusgaben", "tblProdukte", "ProduktID = " & lngProduktID) intJahrAusgabe = Nz(DMax("AusgabeJahr", "qryProdukteAusgaben", _ "ProduktID = " & lngProduktID), Year(Date)) intNummerAusgabe = Nz(DMax("AusgabeNummer", "qryProdukteAusgaben", _ "ProduktID = " & lngProduktID & " AND AusgabeJahr = " & intJahrAusgabe), 1) For i = 1 To 100 If Not intNummerAusgabe = intAnzahlAusgaben Then intNummerAusgabe = intNummerAusgabe + 1 Else intNummerAusgabe = 1 intJahrAusgabe = intJahrAusgabe + 1 End If strAusgaben = strAusgaben & intNummerAusgabe & ";" & intJahrAusgabe & ";" _ & intNummerAusgabe & "/" & intJahrAusgabe & ";" Next i Me!lstAusgaben.RowSource = strAusgaben Me!lstAusgaben = Me!lstAusgaben.ItemData(0) End If End Sub
Diese Prozedur füllt und verwendet eine Variable namens lngProduktID, deren Wert auch noch in einer weiteren Prozedur verwendet werden soll. Daher deklarieren Sie die entsprechende Variable wie folgt im Kopf des Klassenmoduls Form_frmNeueAusgaben:
Dim lngProduktID As Long
Die Prozedur Form_Open prüft zunächst, ob die Eigenschaft OpenArgs des Formulars einen Wert enthält. Dieser wird beim Aufruf vom Formular frmProdukte aus als Wert des Feldes ProduktID des aktuellen Datensatzes übergeben.
Hat diese den Wert NULL, wird lngProduktID durch die Funktion Nz mit dem Wert 0 gefüllt. In diesem Fall wird das Öffnen des Formulars durch Einstellen des Rückgabeparameters Cancel auf den Wert False abgebrochen.
Anderenfalls ermittelt die Prozedur zunächst die Anzahl der Ausgaben pro Kalenderjahr für dieses Produkt. Dies erledigt ein entsprechender Aufruf der DLookup-Funktion, das Ergebnis landet in der Variablen intAnzahlAusgaben. Warum benötigen wir diesen Wert Weil wir damit festlegen, welche Ausgaben überhaupt zur Liste der Ausgaben hinzugefügt werden. Wenn ein Magazin nur sechs Ausgaben pro Jahr liefert, sollen dementsprechend nur die Nummern 1/2012 bis 6/2012 erscheinen und nicht etwa die 7/2012.
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