{"id":55000872,"date":"2013-02-01T00:00:00","date_gmt":"2020-05-22T21:34:22","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=872"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Abonnements_verwalten_Teil_2","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/","title":{"rendered":"Abonnements verwalten, Teil 2"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Im ersten Teil dieser Beitragsreihe haben Sie das Datenmodell der Abonnementverwaltung und einige Formulare zur Eingabe von Kunden, Produkten und Abonnements erstellt. Im zweiten Teil k&uuml;mmern wir uns um weitere Funktionen &#8211; zum Beispiel das Ermitteln abgelaufener Abonnements und die Verl&auml;ngerung, K&uuml;ndigung und Stornierung von Abonnements.<\/b><\/p>\n<p><b>Abonnements k&uuml;ndigen und stornieren<\/b><\/p>\n<p>Kunden k&ouml;nnen Abonnements k&uuml;ndigen und stornieren. Dies sind zwei unterschiedliche Vorg&auml;nge. Bei einer K&uuml;ndigung wird das laufende Abonnement noch durchgef&uuml;hrt, die automatische Verl&auml;ngerung bleibt jedoch aus.<\/p>\n<p>Bei einer Stornierung wird das aktuelle Abonnement storniert. Dies ist in der Regel nur innerhalb der ersten zwei bis vier Wochen nach der Bestellung m&ouml;glich &#8211; je nach Kulanz des Anbieters.<\/p>\n<p>F&uuml;r beide F&auml;lle enth&auml;lt die Tabelle <b>tblAbonnements <\/b>je ein Datumsfeld. Das erste hei&szlig;t <b>GekuendigtAm<\/b>, das zweite <b>StorniertAm<\/b>. F&uuml;r den Bearbeiter des Abonnements ist es wohl am einfachsten, wenn er das Datum der K&uuml;ndigung beziehungsweise der Stornierung manuell eingibt. Dies soll &#8211; neben dem Speichern des jeweiligen Datums &#8211; gleichzeitig noch eine weitere Aktion ausl&ouml;sen, n&auml;mlich den Versand einer E-Mail mit der Best&auml;tigung der K&uuml;ndigung oder Stornierung.<\/p>\n<p>Wenn Sie nicht komplett digital arbeiten, k&ouml;nnen Sie nat&uuml;rlich auch einen Bericht mit den Kundendaten und den Daten der K&uuml;ndigung oder Stornierung erzeugen, diesen ausdrucken und per Briefpost versenden.<\/p>\n<p><b>K&uuml;ndigung eintragen<\/b><\/p>\n<p>Die K&uuml;ndigung tragen Sie beispielsweise in das Textfeld <b>txtGekuendigtAm <\/b>des Unterformulars <b>sfmAbonnements <\/b>des Formulars <b>frmKunden <\/b>ein (s. Bild 1). Dies sorgt nach dem Best&auml;tigen des eingetragenen Wertes f&uuml;r die Anzeige eines Meldungsfensters, mit dem der Benutzer festlegt, ob der Kunde eine E-Mail mit einer K&uuml;ndigungsbest&auml;tigung erhalten soll.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic003.png\" alt=\"pic003.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 1: Eintragen des K&uuml;ndigungsdatums<\/span><\/b><\/p>\n<p>Klickt der Benutzer anschlie&szlig;end auf <b>Ja<\/b>, erstellt die Anwendung eine E-Mail auf Basis der Benutzerdaten und &ouml;ffnet diese (s. Bild 2).<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic002.png\" alt=\"pic002.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 3: K&uuml;ndigungsbest&auml;tigung per Ereignisprozedur ausl&ouml;sen<\/span><\/b><\/p>\n<p>Das Erstellen dieser E-Mail erfolgt mit Outlook. Der dazu ben&ouml;tigte Code ist &uuml;berschaubar, da wir eine Klasse namens <b>clsMail <\/b>verwenden, die das Erstellen von E-Mails mit Outlook erheblich vereinfacht.<\/p>\n<p>Diese Klasse und ihre Beschreibung finden Sie im Beitrag <b>Outlook-Mails mit Klasse <\/b>(<b>www.access-im-unternehmen.de\/859<\/b>).<\/p>\n<p>Um die Klasse <b>clsMail <\/b>in der Beispieldatenbank <b>Abonnementverwaltung.mdb <\/b>verf&uuml;gbar zu machen, importieren Sie diese aus der Beispieldatenbank des Artikels <b>Outlook-Mails mit Klasse<\/b>. Dies gelingt am einfachsten, indem Sie die VBA-Projekte beider Datenbankanwendungen &ouml;ffnen und die Klasse <b>clsMails <\/b>per Drag and Drop von einem Projekt-Explorer in den anderen ziehen.<\/p>\n<p>Damit die Klasse funktioniert, f&uuml;gen Sie au&szlig;erdem noch einen Verweis auf die Bibliothek <b>Microsoft Outlook x.0 Object Library <\/b>zum VBA-Projekt hinzu (Men&uuml;eintrag <b>Extras|Verweise <\/b>im VBA-Editor).<\/p>\n<p>Danach legen Sie die entsprechende Ereignisprozedur an. &Ouml;ffnen Sie das Formular <b>sfmAbonnements <\/b>in der Entwurfsansicht und markieren Sie das Textfeld <b>txtGekuendigtAm <\/b>(die gebundenen Steuerelemente wurden mit entsprechenden Pr&auml;fixen wie <b>txt<\/b>, <b>cbo <\/b>et cetera versehen).<\/p>\n<p>W&auml;hlen Sie f&uuml;r die Eigenschaft <b>Nach Aktualisierung <\/b>den Eintrag <b>[Ereignisprozedur]<\/b> aus und klicken Sie auf die Schaltfl&auml;che mit den drei Punkten (s. Bild 3<\/a><\/span>). Danach erg&auml;nzen Sie die im VBA-Editor erscheinende Ereignisprozedur wie in <span class=\"verweis-ohneumbruch\"><a href=\"#anker-59-anchor\">Listing 1.<\/p>\n<p class=\"listingueberschrift\">Listing 1: Erstellen einer K&uuml;ndigungsbest&auml;tigung per E-Mail<\/p>\n<pre>Private Sub txtGekuendigtAm_AfterUpdate()\r\n    Dim objMail As clsMail\r\n    If MsgBox(&quot;Best&auml;tigung per E-Mail versenden&quot;, vbYesNo) = vbYes Then\r\n        Set objMail = New clsMail\r\n        With objMail\r\n            .AnHinzufuegen Me.Parent!EMail\r\n            .Betreff = &quot;K&uuml;ndigung ''&quot; &amp; Me!cboProduktID.Column(1) &amp; &quot;''&quot;\r\n            .Inhalt = &quot;Hallo &quot; &amp; Me.Parent!cboAnredeID.Column(1) &amp; &quot; &quot; &amp; Me.Parent!txtNachname _\r\n                &amp; &quot;, &quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;hiermit best&auml;tigen wir Ihnen die K&uuml;ndigung Ihres Abonnements von ''&quot; _\r\n                &amp; Me!cboProduktID.Column(1) &amp; &quot;''&quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;Mit freundlichen Gr&uuml;&szlig;en&quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;Ihr Abo-Service&quot;\r\n            .Anzeigen\r\n        End With\r\n    End If\r\nEnd Sub<\/pre>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic004.png\" alt=\"pic004.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 2: K&uuml;ndigungsbest&auml;tigung per E-Mail<\/span><\/b><\/p>\n<p>Die Prozedur deklariert ein Objekt des Typs <b>clsMail <\/b>mit dem Namen <b>objMail<\/b>. Danach fragt sie mit einer entsprechenden <b>MsgBox<\/b>-Anweisung, ob die K&uuml;ndigung per E-Mail versendet werden soll. Falls ja, wird <b>objMail <\/b>mit einem neuen Objekt der Klasse <b>clsMail <\/b>instanziert. Mit der Funktion <b>AnHinzufuegen <\/b>wird der Empf&auml;nger hinzugef&uuml;gt, der aus dem Feld <b>EMail <\/b>des &uuml;bergeordneten Formulars (also <b>frmKunden<\/b>) gewonnen wird.<\/p>\n<p>Den Betreff stellt die Prozedur aus dem Ausdruck <b>K&uuml;ndigung <\/b>und dem in Hochkommata eingefassten Namen des Produkts zusammen, der aus der zweiten Spalte des Kombinationfeldes <b>cboProduktID <\/b>des aktuellen Datensatzes im Unterformular <b>sfmAbonnements <\/b>gewonnen wird.<\/p>\n<p>Den Inhalt der E-Mail stellt die Prozedur ebenfalls aus einer Mischung aus Literalen und dynamischen Elementen zusammen. Dazu geh&ouml;ren die Anrede und der Nachname aus dem &uuml;bergeordneten Formular und nochmals die Produktbezeichnung.<\/p>\n<p><b>Weitere Kunden- und Abonnementdaten verwenden<\/b><\/p>\n<p>Sie erkennen bereits, dass es relativ m&uuml;&szlig;ig ist, sich die Daten f&uuml;r die K&uuml;ndigungsbest&auml;tigung aus dem Haupt- und dem Unterformular zusammenzuklauben. Noch aufwendiger und wartungsunfreundlicher wird es, wenn Sie noch andere M&ouml;glichkeiten zum Eintragen von K&uuml;ndigungs- und Stornierungsdaten vorsehen m&ouml;chten.<\/p>\n<p>Sie werden gleich beispielsweise noch ein Formular kennenlernen, das einen &Uuml;berblick &uuml;ber alle Abonnements gew&auml;hrleistet und verschiedene Aktionen erlauben soll &#8211; auch hier soll das Eintragen von K&uuml;ndigungs- und Stornierungsdaten m&ouml;glich sein.<\/p>\n<p>Normalerweise w&uuml;rde man dort zum Versenden der K&uuml;ndigungs- und Stornierungsbest&auml;tigung gern den gleichen Code verwenden, den Sie soeben kennengelernt haben &#8211; entweder per Copy and Paste des Codes und kleinere Anpassungen oder durch Refaktorieren. Das bedeutet, dass Sie die Hauptelemente des Codes in eine eigene Funktion f&uuml;llen und diese von den verschiedenen Stellen aus aufrufen.<\/p>\n<p>Das Problem ist das Zusammenstellen der in der Best&auml;tigung verwendeten Daten: Diese stammen teils aus dem Unterformular, in das Sie das K&uuml;ndigungs- oder Stornierungsdatum eintragen, teils aus dem &uuml;bergeordneten Hauptformular. Dieses ist aber vielleicht an anderer Stelle gar nicht vorhanden. Also m&uuml;ssen wir einen flexibleren Weg f&uuml;r den Zugriff auf die Daten des zu bearbeitenden Abonnements und des entsprechenden Kunden finden.<\/p>\n<p>Dies w&auml;re beispielsweise &uuml;ber eine Klasse m&ouml;glich, die alle relevanten Daten des Kunden und des Abonnements enth&auml;lt.<\/p>\n<p>Diese wird vor einer Operation, welche auf die Daten des Abonnements zugreifen muss, erzeugt und mit den entsprechenden Stammdaten gef&uuml;llt und stellt diese dann &uuml;ber eine einfache Schnittstelle bereit.<\/p>\n<p><b>Klasse f&uuml;r den einfachen Zugriff auf Abonnementdaten<\/b><\/p>\n<p>Die Klasse soll einige der Daten der Tabellen des Datenmodells enthalten. Diese finden Sie in Bild 4 in einer Abfrage namens <b>qryAbonnementsKlasse<\/b>, die wir speziell f&uuml;r diesen Fall erstellt haben. Um diese Klasse zu erstellen &#8211; und zus&auml;tzlich eine kleine Prozedur, welche ein Objekt auf Basis dieser Klasse erstellt und diese mit den Daten eines angegebenen Datensatzes f&uuml;llt -, verwenden wir das Add-In <b>aiuKlassengenerator<\/b>, das im Beitrag <b>Klassengenerator <\/b>(<b>www.access-im-unternehmen.de\/871<\/b>) vorgestellt wird.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic005.png\" alt=\"pic005.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 4: Felder, die in der Abonnement-Klasse abgebildet werden sollen<\/span><\/b><\/p>\n<p>Starten Sie das Add-In mit dem Eintrag <b>aiuKlassengenerator <\/b>des Add-In-Men&uuml;s (s. Bild 5). W&auml;hlen Sie im Kombinationsfeld <b>Tabelle oder Abfrage <\/b>den Eintrag <b>qryAbonnementsKlasse <\/b>aus. Stellen Sie die Eigenschaften <b>Klassenname <\/b>auf <b>clsAbonnement <\/b>und <b>Prim&auml;rschl&uuml;sselfeld <\/b>auf <b>AbonnementID <\/b>ein. Aktivieren Sie die Option <b>Ladeprozedur hinzuf&uuml;gen <\/b>und klicken Sie auf <b>Erzeugen<\/b>, um den Code zu erstellen.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic006.png\" alt=\"pic006.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 5: Erstellen der Klasse f&uuml;r den einfachen Zugriff auf die Daten eines Abonnements<\/span><\/b><\/p>\n<p>Kopieren Sie den oberen Teil des erstellten Codes (bis zur Prozedur <b>ErzeugeAbonnement<\/b>) in ein neues Klassenmodul namens <b>clsAbonnement<\/b>. Die Prozedur <b>ErzeugeAbonnement<\/b> kopieren Sie in das vorhandene Modul <b>mdlAbonnements<\/b>.<\/p>\n<p>Das Klassenmodul enth&auml;lt f&uuml;r jedes Feld der zugrunde liegenden Abfrage drei Elemente &#8211; eine Membervariable, eine <b>Set\/Let<\/b>-Prozedur und eine <b>Get<\/b>-Prozedur:<\/p>\n<pre>Dim m_AbonnementID As Long\r\nPublic Property Get AbonnementID() As Long\r\n    AbonnementID = m_AbonnementID\r\nEnd Property\r\nPublic Property Let AbonnementID(lngAbonnementID As Long)\r\n    m_AbonnementID = lngAbonnementID\r\nEnd Property<\/pre>\n<p>Die Prozedur zum Erzeugen der Klasse auf Basis eines Datensatzes der Abfrage <b>qryAbonnementsKlasse <\/b>sieht wie in <span class=\"verweis-ohneumbruch\"><a href=\"#anker-63-anchor\">Listing 2<\/a><\/span> aus. Vereinzelt sind dort noch Anpassungen n&ouml;tig &#8211; so kann es beispielsweise vorkommen, dass die Felder <b>GekuendigtAm<\/b> oder <b>StorniertAm <\/b>leer sind. F&uuml;r diesen Fall f&uuml;gen Sie dort noch die <b>Nz<\/b>-Funktion ein, die Nullwerte in diesem Fall durch <b>0 <\/b>ersetzt:<\/p>\n<pre>objAbonnementsKlasse.StorniertAm = Nz(rst!StorniertAm, 0)<\/pre>\n<p class=\"listingueberschrift\">Listing 2: Erstellen eines Objekts auf Basis der Klasse clsAbonnements und F&uuml;llen der Eigenschaften der Klasse<\/p>\n<pre>Public Function ErzeugeAbonnementsKlasse(lngID As Long) As clsAbonnement\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim objAbonnementsKlasse As clsAbonnement\r\n    Set db = CurrentDb\r\n    Set rst = db.OpenRecordset(&quot;SELECT * FROM qryAbonnementsKlasse WHERE AbonnementID = &quot; _\r\n        &amp; lngID, dbOpenDynaset)\r\n    Set objAbonnementsKlasse = New clsAbonnementsKlasse\r\n    objAbonnementsKlasse.AbonnementID = rst!AbonnementID\r\n    objAbonnementsKlasse.ProduktID = rst!ProduktID\r\n    objAbonnementsKlasse.Produkt = rst!Produkt\r\n    ''... weitere Zuweisungen\r\n    Set ErzeugeAbonnementsKlasse = objAbonnementsKlasse\r\n    Set db = Nothing\r\nEnd Function<\/pre>\n<p>Alternativ k&ouml;nnen Sie auch den Datentyp f&uuml;r eine Eigenschaft in der Klasse <b>clsAbonnement <\/b>auf <b>Variant <\/b>einstellen &#8211; dieser nimmt auch Nullwerte entgegen. In diesem Fall ist dies die bessere Variante &#8211; hier umgesetzt f&uuml;r das Feld <b>GekuendigtAm<\/b>:<\/p>\n<pre>Dim m_GekuendigtAm As <span class=\"quellcode\">Variant<\/span>\r\nPublic Property Get GekuendigtAm() As Variant\r\n    GekuendigtAm = m_GekuendigtAm\r\nEnd Property\r\nPublic Property Let GekuendigtAm(datGekuendigtAm As Variant)\r\n    m_GekuendigtAm = datGekuendigtAm\r\nEnd Property<\/pre>\n<p>Mit dieser Klasse k&ouml;nnen Sie etwa das Zusammenstellen der K&uuml;ndigungsbest&auml;tigung viel einfacher gestalten &#8211; siehe <span class=\"verweis-ohneumbruch\"><a href=\"#anker-64-anchor\">Listing 3<\/a><\/span>. Diese Prozedur k&ouml;nnen Sie flexibel aufrufen &#8211; also beispielsweise von der Ereignisprozedur <b>txtGekuendigtAm_AfterUpdate <\/b>aus, in der sich die Funktionalit&auml;t zuvor befunden hat. Sie m&uuml;ssen nur die Nummer des Abonnements &uuml;bergeben:<\/p>\n<pre>Private Sub txtGekuendigtAm_AfterUpdate()\r\n    KuendigungSenden Me!AbonnementID\r\n    End Sub<\/pre>\n<p class=\"listingueberschrift\">Listing 3: Erstellen der K&uuml;ndigungsbest&auml;tigung, verbesserte Variante<\/p>\n<pre>Public Sub KuendigungSenden(lngAbonnementID As Long)\r\n    Dim objMail As clsMail\r\n    Dim objAbonnement As clsAbonnement\r\n    If MsgBox(&quot;Best&auml;tigung der K&uuml;ndigung per E-Mail versenden&quot;, vbYesNo + vbExclamation, _\r\n            &quot;K&uuml;ndigungsbest&auml;tigung&quot;) = vbYes Then\r\n        Set objMail = New clsMail\r\n        Set objAbonnement = ErzeugeAbonnementsKlasse(lngAbonnementID)\r\n        With objMail\r\n            .AnHinzufuegen objAbonnement.EMail\r\n            .Betreff = &quot;K&uuml;ndigung ''&quot; &amp; objAbonnement.Produkt &amp; &quot;''&quot;\r\n            .Inhalt = &quot;Hallo &quot; &amp; objAbonnement.Anrede &amp; &quot; &quot; &amp; objAbonnement.Nachname &amp; &quot;, &quot; _\r\n                &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;hiermit best&auml;tigen wir Ihnen die K&uuml;ndigung Ihres Abonnements von ''&quot; _\r\n                &amp; objAbonnement.Produkt &amp; &quot;''.&quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;Das Abonnement endet am &quot; &amp; Format(DateAdd(&quot;m&quot;, objAbonnement.Laufzeit, _\r\n                objAbonnement.Startdatum), &quot;dd.mm.yyyy&quot;) &amp; &quot;.&quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;Mit freundlichen Gr&uuml;&szlig;en&quot; &amp; vbCrLf &amp; vbCrLf _\r\n                &amp; &quot;Ihr Abo-Service&quot;\r\n            .Anzeigen\r\n        End With\r\n    End If\r\nEnd Sub<\/pre>\n<p>Auf die gleiche Weise k&ouml;nnten Sie diese Prozedur nun von anderen Formularen aus aufrufen &#8211; dazu sp&auml;ter mehr.<\/p>\n<p>Die Prozedur <b>KuendigungSenden <\/b>zeigt auch, wie einfach Sie mit den Eigenschaften der Klasse etwa das K&uuml;ndigungsdatum ermitteln k&ouml;nnen. Dazu greifen Sie direkt auf die Laufzeit und das Startdatum zu:<\/p>\n<pre>Format(DateAdd(&quot;m&quot;, objAbonnement.Laufzeit, _\r\nobjAbonnement.Startdatum), &quot;dd.mm.yyyy&quot;)<\/pre>\n<p>Nat&uuml;rlich k&ouml;nnten Sie dies auch erledigen, wenn Sie statt des Objekts <b>objAbonnement <\/b>einfach ein Recordset auf Basis des betroffenen Datensatzes verwenden. Allerdings l&auml;sst es sich mit der Klasse viel einfacher programmieren, weil diese alle Felder beziehungsweise Eigenschaften per IntelliSense zur Verf&uuml;gung stellt.<\/p>\n<p><!--30percent--><\/p>\n<p><b>Abgelaufene Abonnements ermitteln und verl&auml;ngern<\/b><\/p>\n<p>Wenn alle Ausgaben eines Abonnements versendet wurden, soll das Abonnement &#8211; so der Kunde dieses nicht gek&uuml;ndigt hat &#8211; in der Regel verl&auml;ngert werden.<\/p>\n<p>Mit steigender Kunden- und Abonnement-Zahl wird der Aufwand, alle zu verl&auml;ngernden Abonnements von Hand zu ermitteln und zu verl&auml;ngern, immer gr&ouml;&szlig;er.<\/p>\n<p>Also ben&ouml;tigen wir eine Prozedur, die alle Abonnements ermittelt, f&uuml;r die bereits alle Ausgaben versendet wurden. Diese muss nat&uuml;rlich die stornierten und gek&uuml;ndigten Abonnements ausklammern und &#8211; ganz wichtig &#8211; auch die bereits zuvor verl&auml;ngerten Abonnements.<\/p>\n<p>Solch eine Funktion sollte man nicht blind aufrufen, sondern sich zuvor optisch einen &Uuml;berblick &uuml;ber die zu verl&auml;ngernden Abonnements verschaffen. Dazu erstellen Sie ein &Uuml;bersichtsformular, das die Abonnements diesmal nicht aus Kundensicht, sondern allgemein erfasst. Das hei&szlig;t, dass einfach alle Abonnements untereinander angezeigt werden. Das Formular sieht wie in Bild 6 aus und bietet hier bereits einige Filterm&ouml;glichkeiten an:<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic001.png\" alt=\"pic001.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 6: Formular zum Verwalten der einzelnen Abonnements<\/span><\/b><\/p>\n<ul>\n<li class=\"aufz-hlung\">Stornierte Abonnements<\/li>\n<li class=\"aufz-hlung\">Gek&uuml;ndigte Abonnements<\/li>\n<li class=\"aufz-hlung\">Abonnements, deren Ausgaben komplett versendet wurden<\/li>\n<li class=\"aufz-hlung\">Abonnements, die verl&auml;ngert wurden<\/li>\n<li class=\"aufz-hlung\">Abonnements, deren Laufzeit erf&uuml;llt ist<\/li>\n<\/ul>\n<p>F&uuml;r alle Filterm&ouml;glichkeiten stehen die Einstellungen <b>Alle anzeigen<\/b>, <b>Ja <\/b>und <b>Nein <\/b>bereit.<\/p>\n<p>Ein Doppelklick auf die ID des Abonnements soll dieses in Zusammenhang mit dem jeweiligen Kunden im Formular <b>frmKunden<\/b> anzeigen. Die beiden Schaltfl&auml;chen rechts oben sollen alle Abonnements verl&auml;ngern oder das aktuelle Abonnement verl&auml;ngern, soweit m&ouml;glich.<\/p>\n<p>Wozu gibt es eine Filterm&ouml;glichkeit nach Abonnements, deren Ausgaben komplett versendet wurden, und eine f&uuml;r Abonnements, deren Laufzeit erf&uuml;llt ist<\/p>\n<p>Dies ist f&uuml;r den Fall vorgesehen, dass ein neuer Abonnent etwa am 15. Januar 2012 in das Abonnement einsteigt und gleich die aktuelle Ausgabe erh&auml;lt, die m&ouml;glicherweise bereits am 1. Januar 2012 erschienen ist.<\/p>\n<p>Das hei&szlig;t, dass der Kunde &#8211; sofern alle Ausgaben am ersten Tag eines Monats erscheinen &#8211; am 1. Dezember 2012 alle zw&ouml;lf Ausgaben erhalten hat. Eine Verl&auml;ngerung sollte jedoch erst erfolgen, wenn auch der Zeitraum des Abonnements (hier ein Jahr) abgelaufen ist.<\/p>\n<p>Der Grund ist, dass sich K&uuml;ndigungsfristen in der Regel auf das Bestelldatum beziehen und nicht auf den Zeitpunkt, zu dem das Abonnement erf&uuml;llt ist.<\/p>\n<p>Erh&auml;lt der Kunde also bei Beginn des Abonnements gleich die aktuelle Ausgabe, sollte vor der Verl&auml;ngerung dennoch der Abozeitraum abgelaufen sein.<\/p>\n<p><b>Datenherkunft des Formulars<\/b><\/p>\n<p>Als Datenherkunft des Unterformulars der Abonnement&uuml;bersicht, <b>sfmAbonnementsVerwalten<\/b>, dient die Abfrage aus Bild 7. Die Abfrage f&uuml;hrt die Daten aus den Tabellen <b>tblAbonnements<\/b>, <b>tblVersendungen <\/b>und <b>tblProdukte <\/b>zusammen.<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic007.png\" alt=\"pic007.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 7: Datenherkunft des Unterformulars sfmAbonnementsVerwalten<\/span><\/b><\/p>\n<p>Die Verkn&uuml;pfung zur Tabelle <b>tblProdukte <\/b>erlaubt das Ber&uuml;cksichtigen der Produktdaten zum jeweiligen Abonnement. Die Tabelle <b>tblAbonnements <\/b>taucht gleich zweimal auf. Dies hat den Grund, dass die Abfrage auch die M&ouml;glichkeit bieten soll, verl&auml;ngerte Abonnements zu identifizieren.<\/p>\n<p>Zu diesem Zweck ist die Tabelle <b>tblAbonnements_1 <\/b>&uuml;ber das Feld <b>FolgeAboVon <\/b>mit der Tabelle <b>tblAbonnements <\/b>verkn&uuml;pft. Die Beziehung ist so ausgelegt, dass alle Datens&auml;tze der Tabelle <b>tblAbonnements <\/b>angezeigt werden, aber nur solche der Tabelle <b>tblAbonnements_1<\/b>, die mit <b>tbl-Abonnements <\/b>verkn&uuml;pft sind.<\/p>\n<p>Sprich: Wenn das Feld <b>AbonnementID <\/b>der Tabelle <b>tblAbonnements_1 <\/b>der Abfrage den Wert <b>Null <\/b>hat, gibt es f&uuml;r das Abonnement des entsprechenden Datensatzes der Tabelle <b>tblAbonnemtens <\/b>kein Folgeabonnement &#8211; das Abonnement wurde also noch nicht verl&auml;ngert.<\/p>\n<p>Auch zur Tabelle <b>tblVersendungen <\/b>gibt es eine solche Verkn&uuml;pfung. Dies stellt sicher, dass auch solche Abonnements angezeigt werden, f&uuml;r die noch keine Versendungen in der Tabelle <b>tblVersendungen <\/b>hinterlegt wurden.<\/p>\n<p>Da die Tabelle <b>tblVersendungen <\/b>in der Regel mehrere Datens&auml;tze enth&auml;lt, die mit einem Datensatz der Tabelle <b>tblAbonnements <\/b>verkn&uuml;pft sind, zeigt die Abfrage standardm&auml;&szlig;ig jeden Datensatz der Tabelle <b>tblAbonnements <\/b>entsprechend der Anzahl der verkn&uuml;pften Datens&auml;tze in der Tabelle <b>tblVersendungen <\/b>an. Es soll jedoch jedes Abonnement nur einmal erscheinen.<\/p>\n<p>Deshalb aktivieren Sie die Gruppierungsebene f&uuml;r Abfragen. F&uuml;r die meisten Felder behalten Sie die Einstellung <b>Gruppierung <\/b>in der Zeile <b>Funktion <\/b>bei &#8211; jedoch nicht f&uuml;r die beiden Felder <b>VersendungID <\/b>und <b>Versanddatum<\/b> der Tabelle <b>tblVersendungen<\/b>.<\/p>\n<p>F&uuml;r diese beiden Felder soll die Abfrage die Anzahl der Werte f&uuml;r jede Gruppierung (also f&uuml;r jedes Abonnement) liefern. Praktischerweise ermittelt die <b>Anzahl<\/b>-Funktion in Gruppierungen nur die Anzahl f&uuml;r Datens&auml;tze, die im betroffenen Feld einen Wert ungleich <b>Null <\/b>enthalten.<\/p>\n<p>Das bedeutet, dass die <b>Anzahl<\/b>-Funktion f&uuml;r das Feld <b>VersendungID <\/b>die Gesamtzahl der zu versendenden Ausgaben f&uuml;r ein Abonnement liefert (das Prim&auml;rschl&uuml;sselfeld ist immer gef&uuml;llt).<\/p>\n<p>Das Feld <b>Versanddatum <\/b>hingegen enth&auml;lt nur einen Wert, wenn die Versendung bereits erfolgt ist.<\/p>\n<p>Sind also etwa zw&ouml;lf Datens&auml;tze in der Tabelle <b>tblVersendungen <\/b>f&uuml;r ein Abonnement enthalten, von denen drei bereits versendet wurden, liefern die beiden mit der <b>Anzahl<\/b>-Funktion versehenen Felder genau diese Werte zur&uuml;ck.<\/p>\n<p>Wozu das Ganze Nun: Das ist ein einfacher Weg, um zu ermitteln, ob bereits alle Versendungen zu einem Abonnement durchgef&uuml;hrt wurden &#8211; in diesem Fall muss die Anzahl der Werte im Feld <b>VersendetAm <\/b>gleich der Anzahl der Werte im Feld <b>VersendungID <\/b>sein.<\/p>\n<p><b>Wurde das Abonnement bereits verl&auml;ngert<\/b><\/p>\n<p>Ob ein Abonnement bereits verl&auml;ngert wurde, ermittelt die Abfrage mit dem folgenden Ausdruck:<\/p>\n<pre>Verlaengert:\r\nNicht [tblAbonnements_1].[AbonnementID] Ist Null<\/pre>\n<p><b>Ist die Laufzeit abgelaufen<\/b><\/p>\n<p>Die Abfrage enth&auml;lt auch ein Feld namens <b>LaufzeitErfuellt <\/b>mit einem Ausdruck zur Pr&uuml;fung der Laufzeit. Dazu pr&uuml;ft der Ausdruck, ob das aktuelle Datum gr&ouml;&szlig;er ist als das Startdatum plus der Anzahl der im Feld <b>Laufzeit <\/b>angegebenen Anzahl der Monate:<\/p>\n<pre>LaufzeitErfuellt: Datum()&gt;DatAdd(&quot;m&quot;;[Laufzeit];\r\n[tblAbonnements].[Startdatum])<\/pre>\n<p><b>Filtern der Datens&auml;tze im Unterformular<\/b><\/p>\n<p>Die f&uuml;nf Optionsgruppen <b>ogrGekuendigt<\/b>, <b>ogrLaufzeit<\/b>, <b>ogrStorniert<\/b>, <b>ogrVerlaengert <\/b>und <b>ogrVersendet <\/b>im Hauptformular l&ouml;sen jeweils das Ereignis <b>Nach Aktualisierung <\/b>aus.<\/p>\n<p>Alle Ereignisprozeduren rufen die Routine <b>AbonnementsAktualisieren <\/b>auf &#8211; hier etwa f&uuml;r die Optionsgruppe <b>ogrGekuendigt<\/b>:<\/p>\n<pre>Private Sub ogrGekuendigt_AfterUpdate()\r\n    AbonnementsAktualisieren\r\n    End Sub<\/pre>\n<p>Die Prozedur <b>AbonnementsAktualisieren <\/b>finden Sie in <span class=\"verweis-ohneumbruch\"><a href=\"#anker-67-anchor\">Listing 4<\/a><\/span>. Diese Prozedur pr&uuml;ft nacheinander den Wert der Optionsgruppen. Dazu sehen wir uns zun&auml;chst an, welche Werte &uuml;berhaupt f&uuml;r die einzelnen Optionen festgelegt sind:<\/p>\n<ul>\n<li class=\"aufz-hlung\"><b>Alle anzeigen<\/b>: <b>1<\/b><\/li>\n<li class=\"aufz-hlung\"><b>Ja<\/b>: <b>-1<\/b><\/li>\n<li class=\"aufz-hlung\"><b>Nein<\/b>: <b>0<\/b><\/li>\n<\/ul>\n<p class=\"listingueberschrift\">Listing 4: Aktualisieren der Anzeige der Abonnements im Unterformular sfmAbonnementsVerwalten<\/p>\n<pre>Private Sub AbonnementsAktualisieren()\r\n    Dim strSQL As String\r\n    Select Case Me!ogrGekuendigt\r\n        Case -1\r\n            strSQL = strSQL &amp; &quot; AND NOT GekuendigtAm IS NULL&quot;\r\n        Case 0\r\n            strSQL = strSQL &amp; &quot; AND GekuendigtAm IS NULL&quot;\r\n    End Select\r\n    Select Case Me!ogrStorniert\r\n        Case -1\r\n            strSQL = strSQL &amp; &quot; AND NOT StorniertAm IS NULL&quot;\r\n        Case 0\r\n            strSQL = strSQL &amp; &quot; AND StorniertAm IS NULL&quot;\r\n    End Select\r\n    Select Case Me!ogrVersendet\r\n        Case -1\r\n            strSQL = strSQL &amp; &quot; AND AnzahlVonVersendungID = AnzahlVonVersanddatum&quot;\r\n        Case 0\r\n            strSQL = strSQL &amp; &quot; AND NOT AnzahlVonVersendungID = AnzahlVonVersanddatum&quot;\r\n    End Select\r\n    Select Case Me!ogrVerlaengert\r\n        Case -1\r\n            strSQL = strSQL &amp; &quot; AND Verlaengert = TRUE&quot;\r\n        Case 0\r\n            strSQL = strSQL &amp; &quot; AND Verlaengert = FALSE&quot;\r\n    End Select\r\n    Select Case Me!ogrLaufzeit\r\n        Case -1\r\n            strSQL = strSQL &amp; &quot; AND LaufzeitErfuellt = TRUE&quot;\r\n        Case 0\r\n            strSQL = strSQL &amp; &quot; AND LaufzeitErfuellt = FALSE&quot;\r\n    End Select\r\n    If Len(strSQL) &gt; 0 Then\r\n        strSQL = Mid(strSQL, 5)\r\n        strSQL = &quot; WHERE &quot; &amp; strSQL\r\n    End If\r\n    Me!sfmAbonnementsVerwalten.Form.RecordSource = &quot;SELECT * FROM qrySfmAbonnementsVerwalten&quot; &amp; strSQL\r\nEnd Sub<\/pre>\n<p>Die Prozedur pr&uuml;ft diese Werte nacheinander mithilfe entsprechender <b>Select Case<\/b>-Bedingungen und stellt eine von den Einstellungen abh&auml;ngige SQL-Where-Bedingung zusammen. Im Falle der Optionsgruppe <b>ogrGekuendigt <\/b>lautet der SQL-Teilausdruck beispielsweise wie folgt, wenn nicht gek&uuml;ndigte Abonnements angezeigt werden sollen:<\/p>\n<pre>AND NOT GekuendigtAm IS NULL<\/pre>\n<p>Sollen nur gek&uuml;ndigte Abonnements erscheinen, verwendet die Prozedur diesen Ausdruck:<\/p>\n<pre>AND GekuendigtAm IS NULL<\/pre>\n<p>F&uuml;r die Option <b>Alle anzeigen <\/b>ist keine Bedingung n&ouml;tig. Genauso verf&auml;hrt die Prozedur beim Filtern stornierter oder nicht stornierter Bestellungen.<\/p>\n<p>Wenn der Benutzer alle Abonnements sehen will, die komplett versendet wurden, h&auml;ngt die Prozedur diesen Ausdruck an die SQL-Bedingung an:<\/p>\n<pre>AND AnzahlVonVersendungID = AnzahlVonVersanddatum<\/pre>\n<p>Diese Bedingung ist also erf&uuml;llt, wenn die Anzahl der Werte im Feld <b>VersendungID <\/b>gleich der Anzahl der Werte im Feld <b>Versanddatum <\/b>ist.<\/p>\n<p>Die beiden zur Laufzeit ermittelten Felder <b>Verlaengert <\/b>und <b>LaufzeitErfuellt <\/b>werden auf &auml;hnliche Weise zusammengesetzt.<\/p>\n<p>Sollen noch nicht verl&auml;ngerte Abonnements erscheinen, ist etwa dieser Ausdruck in die Bedingung zu integrieren:<\/p>\n<pre>AND Verlaengert = FALSE<\/pre>\n<p>Nach dem Zusammenstellen pr&uuml;ft die Prozedur, ob <b>strSQL <\/b>&uuml;berhaupt eine Bedingung enth&auml;lt, und schneidet in diesem Fall das erste <b>AND <\/b>des Ausdrucks ab und f&uuml;gt vorn ein <b>WHERE <\/b>an. Das Ergebnis wird schlie&szlig;lich an die folgende Abfrage angeh&auml;ngt und als Datenherkunft des Unterformulars angegeben:<\/p>\n<pre>SELECT * FROM qrySfmAbonnementsVerwalten<\/pre>\n<p>Dies filtert das Unterformular entsprechend den Einstellungen der einzelnen Optionsgruppen.<\/p>\n<p><b>Aufruf der Verl&auml;ngerung eines Abonnements<\/b><\/p>\n<p>Ein Mausklick auf die Schaltfl&auml;che mit der Beschriftung <b>Aktuelles Abonnement verl&auml;ngern <\/b>l&ouml;st die Prozedur aus <span class=\"verweis-ohneumbruch\"><a href=\"#anker-68-anchor\">Listing 5<\/a><\/span> aus.<\/p>\n<p class=\"listingueberschrift\">Listing 5: Verl&auml;ngern des aktuell markierten Abonnements<\/p>\n<pre>Private Sub cmdAktuellesAbonnementVerlaengern_Click()\r\n    Dim lngNeuesAbonnementID As Long\r\n    If Not IsNull(Me!sfmAbonnementsVerwalten.Form.AbonnementID) Then\r\n        lngNeuesAbonnementID = AbonnementVerlaengern(Me!sfmAbonnementsVerwalten.Form!AbonnementID)\r\n        If lngNeuesAbonnementID &gt; 0 Then\r\n            Me!sfmAbonnementsVerwalten.Form.Requery\r\n            Me!sfmAbonnementsVerwalten.Form.Recordset.FindFirst &quot;AbonnementID = &quot; &amp; lngNeuesAbonnementID\r\n        Else\r\n            MsgBox &quot;Das Abonnement kann nicht verl&auml;ngert werden.&quot;\r\n        End If\r\n    End If\r\nEnd Sub<\/pre>\n<p>Diese Prozedur pr&uuml;ft zun&auml;chst, ob &uuml;berhaupt ein Datensatz im Unterformular <b>sfmAbonnementsVerwalten <\/b>markiert ist &#8211; und zwar, indem sie den Wert des Feldes <b>AbonnementID <\/b>auf den Wert <b>Null <\/b>pr&uuml;ft.<\/p>\n<p>Ist diese Bedingung nicht wahr, versucht die Prozedur, ein neues Abonnement als Verl&auml;ngerung des markierten Abonnements anzulegen. Dazu ruft sie die Funktion <b>AbonnementVerlaengern <\/b>auf und &uuml;bergibt die <b>AbonnementID <\/b>des zu verl&auml;ngernden Abonnements. Das Ergebnis &#8211; die <b>AbonnementID <\/b>des neuen Abonnements &#8211; speichert die Prozedur in der Variablen <b>lngNeuesAbonnementID<\/b>.<\/p>\n<p>Hat diese Variable nach dem Aufruf der Prozedur einen Wert gr&ouml;&szlig;er als <b>0<\/b>, war der Vorgang erfolgreich.<\/p>\n<p>Die Prozedur aktualisiert dann den Inhalt des Unterformulars und markiert automatisch den neu hinzugef&uuml;gten Abonnement-Datensatz. War die Verl&auml;ngerung nicht erfolgreich, erscheint eine entsprechende Meldung.<\/p>\n<p><b>Ein Abonnement verl&auml;ngern<\/b><\/p>\n<p>Die Funktion <b>AbonnementVerlaengern<\/b> finden Sie in <span class=\"verweis-ohneumbruch\"><a href=\"#anker-70-anchor\">Listing 6<\/a><\/span>. Sie erwartet die <b>AbonnementID <\/b>des zu verl&auml;ngernden Abonnements. Die Verl&auml;ngerung erfolgt in zwei voneinander abh&auml;ngigen Schritten:<\/p>\n<p class=\"listingueberschrift\">Listing 6: Verl&auml;ngern eines Abonnements mit einer bestimmten AbonnementID<\/p>\n<pre>Public Function AbonnementVerlaengern(lngAbonnementID As Long) As Long\r\n    Dim wrk As DAO.Workspace\r\n    Set wrk = DBEngine(0)\r\n    Dim db As DAO.Database\r\n    Dim lngProduktID As Long\r\n    Dim lngFolgeaboID As Long\r\n    Dim intAnzahlAusgaben As Integer\r\n    Set db = wrk(0)\r\n    lngProduktID = DLookup(&quot;ProduktID&quot;, &quot;tblAbonnements&quot;, &quot;AbonnementID = &quot; &amp; lngAbonnementID)\r\n    wrk.BeginTrans\r\n    db.Execute &quot;INSERT INTO tblAbonnements(ProduktID, KundeID, Startdatum, FolgeaboVon) &quot; _\r\n        &amp; &quot;SELECT ProduktID, KundeID, &quot; &amp; ISODatum(Now) &amp; &quot; AS Startdatum, &quot; &amp; lngAbonnementID _\r\n        &amp; &quot; AS FolgeaboVon FROM tblAbonnements WHERE AbonnementID = &quot; &amp; lngAbonnementID, dbFailOnError\r\n    lngFolgeaboID = db.OpenRecordset(&quot;SELECT @@IDENTITY&quot;).Fields(0)\r\n    intAnzahlAusgaben = DLookup(&quot;AnzahlAusgaben&quot;, &quot;tblProdukte&quot;, &quot;ProduktID = &quot; &amp; lngProduktID)\r\n    If VersendungenAnlegenFolgeabo(db, lngAbonnementID, lngFolgeaboID, lngProduktID, _\r\n            intAnzahlAusgaben) = True Then\r\n        wrk.CommitTrans\r\n        AbonnementVerlaengern = lngFolgeaboID\r\n    Else\r\n         wrk.Rollback\r\n    End If\r\n    Set db = Nothing\r\n    Set wrk = Nothing\r\nEnd Function<\/pre>\n<ul>\n<li class=\"aufz-hlung\">Anlegen des neuen Datensatzes in der Tabelle <b>tblAbonnements<\/b><\/li>\n<li class=\"aufz-hlung\">Anlegen der neuen Versendungen in der Tabelle <b>tblVersendungen<\/b><\/li>\n<\/ul>\n<p>Den zweiten Schritt f&uuml;hrt eine eigene Funktion namens <b>VersendungenAnlegenFolgeabo <\/b>durch. Diese liefert im Erfolgsfall den Wert <b>True <\/b>zur&uuml;ck.<\/p>\n<p>Wenn das Anlegen der Versendungs-Datens&auml;tze nicht erfolgreich war, soll auch der Datensatz in der Tabelle <b>tblAbonnements <\/b>wieder gel&ouml;scht werden.<\/p>\n<p>Dies erledigt die Prozedur, indem sie eine Transaktion startet, die als erste Aktion den neuen Datensatz in der Tabelle <b>tblAbonnements <\/b>anlegt. Wenn das Anlegen der Versendungen f&uuml;r das neue Abonnement erfolgreich ist, wird die Transaktion abgeschlossen. Konnten die Versendungen nicht vollst&auml;ndig angelegt werden, findet ein Rollback der Transaktion statt.<\/p>\n<p>Warum aber gehen wir diesen vermeintlich aufwendigen Weg der Transaktion und legen nicht einfach zuerst die Versendungen an und dann im Erfolgsfall den Abonnement-Datensatz Weil wir zum Anlegen der Versendungen bereits die <b>AbonnementID <\/b>des neuen Abonnements ben&ouml;tigen.<\/p>\n<p>Im Detail ermittelt die Prozedur zun&auml;chst die <b>ProduktID <\/b>des zu verl&auml;ngernden Abonnements. Eine <b>INSERT INTO<\/b>-Aktionsabfrage f&uuml;gt dann einen Datensatz in die Tabelle <b>tblAbonnements <\/b>ein, der die Werte der Felder <b>ProduktID <\/b>und <b>KundeID <\/b>aus dem Datensatz f&uuml;r das zu verl&auml;ngernde Abonnement ausliest.<\/p>\n<p>Als Startdatum verwendet die Abfrage das aktuelle Datum, ermittelt mit der VBA-Funktion <b>Now<\/b> und mit der Hilfsfunktion <b>ISODatum <\/b>(siehe Modul <b>mdlTools<\/b> der Beispieldatenbank) in das entsprechende Format gebracht.<\/p>\n<p>Das Feld <b>FolgeaboVon <\/b>wird schlie&szlig;lich mit dem Wert des Funktionsparameters <b>lngAbonnementID <\/b>gef&uuml;llt &#8211; dieser Wert gilt gleichzeitig als Kriterium f&uuml;r den Datensatz der Tabelle <b>tblAbonnements<\/b>, der die zu &uuml;bernehmenden Werte liefert.<\/p>\n<p>Die Variable <b>lngFolgeAboID <\/b>nimmt den mit der Abfrage <b>SELECT @@IDENTITY <\/b>ermittelten Prim&auml;rschl&uuml;sselwert des neuen Datensatzes der Tabelle <b>tblAbonnements <\/b>und somit die neue <b>AbonnementID <\/b>auf. <b>intAnzahlAusgaben <\/b>wird mit der Anzahl der Ausgaben f&uuml;r ein Abonnement mit der angegebenen <b>ProduktID <\/b>gef&uuml;llt.<\/p>\n<p>Schlie&szlig;lich folgt der Aufruf der Funktion <b>VersendungenAnlegen <\/b>mit einigen Parametern &#8211; einem Verweis auf die aktuelle Datenbank, der neuen und der zu verl&auml;ngernden <b>AbonnementID<\/b>, der <b>ProduktID <\/b>und der Anzahl der ben&ouml;tigten Ausgaben.<\/p>\n<p>Liefert die Funktion den Wert <b>True <\/b>zur&uuml;ck, f&uuml;hrt <b>CommitTrans <\/b>die Transaktion durch und der Funktionswert <b>AbonnementVerlaengern <\/b>wird auf <b>True <\/b>eingestellt, sonst setzt die <b>Rollback<\/b>-Methode die &Atilde;&#8220;nderungen zur&uuml;ck.<\/p>\n<p><b>Versendungen f&uuml;r ein verl&auml;ngertes Abonnement anlegen<\/b><\/p>\n<p>Die Funktion <b>VersendungenAnlegen <\/b>legt die f&uuml;r ein neues Abonnement ben&ouml;tigten Datens&auml;tze in der Tabelle <b>tblVersendungen <\/b>an (s. <span class=\"verweis-ohneumbruch\"><a href=\"#anker-71-anchor\">Listing 7<\/a><\/span>).<\/p>\n<p class=\"listingueberschrift\">Listing 7: Anlegen der Versendungen f&uuml;r ein verl&auml;ngertes Abonnement<\/p>\n<pre>Public Function VersendungenAnlegenFolgeabo(db As DAO.Database, lngAbonnementID As Long, _\r\n        lngFolgeaboID As Long, lngProduktID As Long, intAnzahlAusgaben As Integer) As Boolean\r\n    Dim qdf As DAO.QueryDef\r\n    Dim prm As DAO.Parameter\r\n    Dim rst As DAO.Recordset\r\n    Dim strSQL As String\r\n    Dim i As Integer\r\n    Dim lngLetzteAusgabeID As Long\r\n    lngLetzteAusgabeID = LetzteAusgabe(db, lngAbonnementID)\r\n    Set qdf = db.QueryDefs(&quot;qryProdukteAusgabenNachProduktID&quot;)\r\n    Set prm = qdf.Parameters(&quot;GesuchteProduktID&quot;)\r\n    prm.Value = lngProduktID\r\n    Set rst = qdf.OpenRecordset\r\n    rst.FindFirst &quot;AusgabeID = &quot; &amp; lngLetzteAusgabeID\r\n    rst.MoveNext\r\n    Do While Not rst.EOF And i &lt; intAnzahlAusgaben\r\n        strSQL = &quot;INSERT INTO tblVersendungen(AbonnementID, AusgabeID) VALUES(&quot; _\r\n            &amp; lngFolgeaboID &amp; &quot;, &quot; &amp; rst!AusgabeID &amp; &quot;)&quot;\r\n        db.Execute strSQL, dbFailOnError\r\n        i = i + 1\r\n        rst.MoveNext\r\n    Loop\r\n    If i = intAnzahlAusgaben Then\r\n        VersendungenAnlegenFolgeabo = True\r\n    End If\r\nEnd Function<\/pre>\n<p>Die Funktion bekommt die neue <b>AbonnementID<\/b>, die <b>AbonnementID <\/b>des zu verl&auml;ngernden Abonnements, die <b>ProduktID <\/b>und die Anzahl der Ausgaben als Parameter &uuml;bergeben.<\/p>\n<p>Die Funktion ermittelt zun&auml;chst mithilfe einer weiteren Funktion namens <b>LetzteAusgabe <\/b>die <b>AusgabeID <\/b>der zuletzt f&uuml;r das zu verl&auml;ngernde Abonnement verschickten Versendung (siehe unten) und speichert diese in der Variablen <b>lngLetzteAusgabeID<\/b>.<\/p>\n<p>Die grundlegenden Daten zum Erstellen der Versendungen stammen aus der Abfrage <b>qryProdukteAusgabenNachProduktID<\/b> (s. Bild 8).<\/p>\n<p class=\"listingueberschrift\">Listing 8: Ermittlung der letzten Ausgabe eines zu verl&auml;ngernden Abonnements<\/p>\n<pre>Public Function LetzteAusgabe(db As DAO.Database, lngAbonnementID As Long) As Long\r\n    Dim rstLetzteAusgabe As DAO.Recordset\r\n    Dim prm As DAO.Parameter\r\n    Dim qdf As DAO.QueryDef\r\n    Set qdf = db.QueryDefs(&quot;qryLetzteVersendeteAusgabe&quot;)\r\n    Set prm = qdf.Parameters(&quot;ZuVerlaengerndesAbonnementID&quot;)\r\n    prm.Value = lngAbonnementID\r\n    Set rstLetzteAusgabe = qdf.OpenRecordset\r\n    LetzteAusgabe = rstLetzteAusgabe!AusgabeID\r\nEnd Function<\/pre>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic008.png\" alt=\"pic008.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 8: Diese Abfrage liefert die zu versendenden Ausgaben.<\/span><\/b><\/p>\n<p>Diese wird zun&auml;chst als <b>QueryDef<\/b>-Objekt referenziert, da die Prozedur noch den Wert des Parameters <b>GesuchteProduktID <\/b>zuweisen muss.<\/p>\n<p>Dieser erh&auml;lt den Wert der von der aufrufenden Funktion &uuml;bergebenen Variablen <b>lngProduktID<\/b>.<\/p>\n<p>Erst nach der Zuweisung des Parameterwertes erzeugt die Prozedur ein <b>Recordset<\/b>-Objekt auf Basis des <b>QueryDef<\/b>-Objekts. Im Recordset springt die Prozedur zun&auml;chst mit der <b>FindFirst<\/b>-Methode zu dem Datensatz, dessen Wert im Feld <b>AusgabeID <\/b>dem der letzten Ausgabe des zu verl&auml;ngernden Abonnements entspricht.<\/p>\n<p>Ein Aufruf der <b>MoveNext<\/b>-Methode platziert den Datensatzzeiger schlie&szlig;lich auf dem Datensatz mit der ersten Ausgabe des neuen Abonnements.<\/p>\n<p>Von hier aus durchl&auml;uft die Prozedur die folgenden Datens&auml;tze des Recordsets &#8211; und zwar so lange, wie die folgenden beiden Bedingungen erf&uuml;llt sind:<\/p>\n<ul>\n<li class=\"aufz-hlung\">der Datensatzzeiger befindet sich nicht hinter dem letzten Datensatz und<\/li>\n<li class=\"aufz-hlung\">es wurden noch nicht gen&uuml;gend Versendungen angelegt. Dies ist der Fall, solange die Variable <b>i<\/b>, die bei jedem Schleifendurchlauf um eins erh&ouml;ht wird, kleiner als die in <b>intAnzahlAusgaben <\/b>gespeicherte Anzahl der Ausgaben ist.<\/li>\n<\/ul>\n<p>Innerhalb der Schleife legt die Prozedur jeweils einen Datensatz in der Tabelle <b>tblVersendungen <\/b>an, wobei die Werte f&uuml;r die Felder <b>AbonnementID <\/b>und <b>AusgabeID <\/b>der Variablen <b>lngFolgeaboID <\/b>und dem Wert des Feldes <b>AusgabeID <\/b>des Recordsets entnommen werden.<\/p>\n<p>Nach dem Durchlaufen der Schleife kommen wir zur Pr&uuml;fung, ob das Anlegen der Versendungsdatens&auml;tze erfolgreich war. Dies ist n&auml;mlich nur der Fall, wenn ausreichend Versendungen angelegt wurden &#8211; anderenfalls beh&auml;lt der Funktionswert seinen Standardwert <b>False<\/b>.<\/p>\n<p><b>Letzte Ausgabe eines zu verl&auml;ngernden Abonnements ermitteln<\/b><\/p>\n<p>Die Prozedur <b>LetzteAusgabe <\/b>aus <span class=\"verweis-ohneumbruch\"><a href=\"#anker-74-anchor\">Listing 8<\/a><\/span> ermittelt die <b>AusgabeID <\/b>der letzten Ausgabe eines Abonnements.<\/p>\n<p>Sie erwartet einen Verweis auf die aktuelle Datenbank sowie die <b>AbonnementID <\/b>des zu untersuchenden Datensatzes der Tabelle <b>tblAbonnements<\/b>.<\/p>\n<p>Die Prozedur verwendet die Abfrage <b>qryLetzteVersendeteAusgabe <\/b>als Basis f&uuml;r die Ermittlung der gesuchten <b>AusgabeID<\/b>. Die Abfrage verwendet die beiden Tabellen <b>tblAusgaben <\/b>und <b>tblVersendungen <\/b>als Datenherkunft (s. Bild 9).<\/p>\n<p><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2013_01\/AbonnementsVerwalten_2-web-images\/pic009.png\" alt=\"pic009.png\" \/><\/p>\n<p><b><span style=\"color:darkgrey\">Bild 9: Abfrage zum Ermitteln der letzten Augabe eines Abonnements<\/span><\/b><\/p>\n<p>Sie enth&auml;lt einen Parameter namens <b>ZuVerlaengerndesAbonnementID<\/b>, der von der Funktion <b>LetzteAusgabe <\/b>gef&uuml;llt werden muss.<\/p>\n<p>Da die Abfrage das Ergebnis nach den Feldern <b>AusgabeJahr <\/b>und <b>AusgabeNummer <\/b>absteigend sortiert und die Eigenschaft <b>Spitzenwerte <\/b>den Wert <b>1 <\/b>enth&auml;lt, liefert diese direkt den gew&uuml;nschten Datensatz.<\/p>\n<p>Der Wert des Feldes <b>AusgabeID <\/b>dieses Datensatzes ist gleichzeitig der R&uuml;ckgabewert der Funktion.<\/p>\n<p><b>Alle aktuell ausgew&auml;hlten Abonnements verl&auml;ngern<\/b><\/p>\n<p>Mit einem Klick auf die Schaltfl&auml;che <b>Aktuelle Auswahl verl&auml;ngern <\/b>kann der Benutzer alle aktuell in der Liste angezeigten Abonnements verl&auml;ngern. Diese sollte zuvor &uuml;ber die f&uuml;nf Optionsgruppen entsprechend eingeschr&auml;nkt werden.<\/p>\n<p>Auch wenn dies nicht geschieht, verl&auml;ngert die Prozedur, die durch die Schaltfl&auml;che <b>cmdAktuelleAuswahlVerlaengern <\/b>ausgel&ouml;st wird, nur solche Abonnnements, die sich dazu eignen.<\/p>\n<p>Davon schlie&szlig;t die Prozedur jene aus, die bereits gek&uuml;ndigt oder storniert wurden, bei denen noch nicht alle Ausgaben versendet wurden oder bei denen die Laufzeit noch nicht erreicht wurde. Die Prozedur finden Sie in <span class=\"verweis-ohneumbruch\"><a href=\"#anker-75-anchor\">Listing 9<\/a><\/span>.<\/p>\n<p class=\"listingueberschrift\">Listing 9: Verl&auml;ngern aller aktuell angezeigten Abonnements<\/p>\n<pre>Private Sub cmdAktuelleAuswahlVerlaengern_Click()\r\n    Dim db As DAO.Database\r\n    Dim rst As DAO.Recordset\r\n    Dim strSQL As String\r\n    Dim bolGekuendigt As Boolean\r\n    Dim bolStorniert As Boolean\r\n    Dim bolNichtAlleVersendet As Boolean\r\n    Dim bolLaufzeitNichtErfuellt As Boolean\r\n    Dim bolVerlaengert As Boolean\r\n    Set db = CurrentDb\r\n    strSQL = Me!sfmAbonnementsVerwalten.Form.RecordSource\r\n    Set rst = db.OpenRecordset(strSQL, dbOpenDynaset)\r\n    Do While Not rst.EOF\r\n        bolGekuendigt = Not IsNull(rst!GekuendigtAm)\r\n        bolStorniert = Not IsNull(rst!StorniertAm)\r\n        bolNichtAlleVersendet = rst!AnzahlvonVersendungID &gt; rst!AnzahlvonVersanddatum\r\n        bolLaufzeitNichtErfuellt = Not rst!LaufzeitErfuellt\r\n        bolVerlaengert = rst!Verlaengert\r\n        If bolGekuendigt Or bolStorniert Or bolNichtAlleVersendet Or bolLaufzeitNichtErfuellt Or bolVerlaengert Then\r\n            MsgBox &quot;Abo &quot; &amp; rst!AbonnementID &amp; &quot; konnte nicht verl&auml;ngert werden.&quot;\r\n        Else\r\n            AbonnementVerlaengern rst!AbonnementID\r\n        End If\r\n        rst.MoveNext\r\n    Loop\r\n    Me!sfmAbonnementsVerwalten.Form.Requery\r\n    rst.Close\r\n    Set rst = Nothing\r\n    Set db = Nothing\r\nEnd Sub<\/pre>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Zusammenfassung und Ausblick<\/p>\n<p>Damit ist die Eingabe der grundlegenden Daten abgeschlossen. Sie k&ouml;nnen Kunden anlegen, Produkte definieren, Ausgaben hinzuf&uuml;gen und Abonnements mit den entsprechenden Ausgaben als Versendungen anlegen. Au&szlig;erdem lassen sich Abonnements verl&auml;ngern (sofern diese daf&uuml;r geeignet sind).<\/p>\n<p>Damit fehlt noch der Komplex rund um das Versenden der Ausgaben. Dies kann verschiedene Formen annehmen: Der einfachste Weg w&auml;re der Versand per E-Mail.<\/p>\n<p>Das Versenden und das Eintragen des Versanddatums k&ouml;nnten in einem Zuge geschehen. Der zweite Weg ist das Erstellen einer Liste etwa im Textformat &#8211; zur Weiterverarbeitung beispielsweise als Adressenliste f&uuml;r den Etikettendruck.<\/p>\n<p>Hier w&uuml;rde das Versanddatum in die Tabelle <b>tblVersendungen <\/b>in dem Moment eingetragen, in dem die Adresse in der Liste landet.<\/p>\n<p>F&uuml;r beide Wege m&uuml;ssen wiederum die Batch-Bearbeitung (etwa beim Erscheinen der Ausgabe) f&uuml;r alle aktuellen Abonnenten und auch die Durchf&uuml;hrung einzelner Versendung f&uuml;r neu hinzugekommene Abonnenten vorgesehen werden.<\/p>\n<p>Diesen Part sehen wir uns im dritten Teil der Beitragsreihe an.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>Abonnementverwaltung.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/{A5C2121D-BE32-46D2-B62D-FD8CE4016BFA}\/aiu_872.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im ersten Teil dieser Beitragsreihe haben Sie das Datenmodell der Abonnementverwaltung und einige Formulare zur Eingabe von Kunden, Produkten und Abonnements erstellt. Im zweiten Teil k&uuml;mmern wir uns um weitere Funktionen &#8211; zum Beispiel das Ermitteln abgelaufener Abonnements und die Verl&auml;ngerung, K&uuml;ndigung und Stornierung von Abonnements.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66012013,662013,44000027],"tags":[],"class_list":["post-55000872","post","type-post","status-publish","format-standard","hentry","category-66012013","category-662013","category-Loesungen"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Abonnements verwalten, Teil 2 - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Abonnements verwalten, Teil 2\" \/>\n<meta property=\"og:description\" content=\"Im ersten Teil dieser Beitragsreihe haben Sie das Datenmodell der Abonnementverwaltung und einige Formulare zur Eingabe von Kunden, Produkten und Abonnements erstellt. Im zweiten Teil k&uuml;mmern wir uns um weitere Funktionen - zum Beispiel das Ermitteln abgelaufener Abonnements und die Verl&auml;ngerung, K&uuml;ndigung und Stornierung von Abonnements.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-22T21:34:22+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"27\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Abonnements verwalten, Teil 2\",\"datePublished\":\"2020-05-22T21:34:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/\"},\"wordCount\":4290,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/c08e0d7d03324a37a7118d4732634dc4\",\"articleSection\":[\"1\\\/2013\",\"2013\",\"L\u00f6sungen\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/\",\"name\":\"Abonnements verwalten, Teil 2 - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/c08e0d7d03324a37a7118d4732634dc4\",\"datePublished\":\"2020-05-22T21:34:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/c08e0d7d03324a37a7118d4732634dc4\",\"contentUrl\":\"http:\\\/\\\/vg05.met.vgwort.de\\\/na\\\/c08e0d7d03324a37a7118d4732634dc4\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Abonnements_verwalten_Teil_2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Abonnements verwalten, Teil 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Abonnements verwalten, Teil 2 - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/","og_locale":"de_DE","og_type":"article","og_title":"Abonnements verwalten, Teil 2","og_description":"Im ersten Teil dieser Beitragsreihe haben Sie das Datenmodell der Abonnementverwaltung und einige Formulare zur Eingabe von Kunden, Produkten und Abonnements erstellt. Im zweiten Teil k&uuml;mmern wir uns um weitere Funktionen - zum Beispiel das Ermitteln abgelaufener Abonnements und die Verl&auml;ngerung, K&uuml;ndigung und Stornierung von Abonnements.","og_url":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/","og_site_name":"Access im Unternehmen","article_published_time":"2020-05-22T21:34:22+00:00","og_image":[{"url":"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"27\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Abonnements verwalten, Teil 2","datePublished":"2020-05-22T21:34:22+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/"},"wordCount":4290,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4","articleSection":["1\/2013","2013","L\u00f6sungen"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/","url":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/","name":"Abonnements verwalten, Teil 2 - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#primaryimage"},"thumbnailUrl":"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4","datePublished":"2020-05-22T21:34:22+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#primaryimage","url":"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4","contentUrl":"http:\/\/vg05.met.vgwort.de\/na\/c08e0d7d03324a37a7118d4732634dc4"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Abonnements_verwalten_Teil_2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Abonnements verwalten, Teil 2"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000872","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55000872"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000872\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000872"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000872"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000872"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}