Home > Artikel > Ausgabe 10/2016 > Lokaler Webshop, Teil II

Lokaler Webshop, Teil II

Achtung: Sie sind nicht angemeldet. Wenn Sie Abonnent sind und sich anmelden, lesen Sie den kompletten Artikel, laden das PDF herunter oder probieren die Beispieldatenbank aus (sofern vorhanden).

Nach dem Anzeigen der Artikel unseres in Access nachgebildeten Webshops der vorletzten Ausgabe geht es nun an das Bestellen der Produkte bei der ABasics Computer GmbH. Für den hier benötigten Warenkorb, die Kasse und die Bestellvorgänge kommen ganz neue Formulare ins Spiel. Deren Design, Aufbau und Programmierung widmet sich dieser Beitrag, wobei allerlei Tricks und Kniffe nicht zu kurz kommen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1610_Web­shopII.zip.

Bestellen im Webshop

Sie möchten im Hauptformular frmShop der Anwendung einen Drucker bestellen. Nach Auswahl der Produktkategorie Drucker im Kombinationsfeld links oben, und nach Markierung der Unterkategorie Tintenstrahler ab A3 im Listenfeld links, füllt sich die Produktliste lstArtikel in der Mitte mit den verfügbaren Produkten. Da Ihnen der Hersteller Hewlett-Packard am vertrauenswürdigsten erscheint, filtern Sie diese Liste über das Kombinationsfeld rechts oben (siehe Bild 1). In dieser Combobox finden sich ausschließlich jene Hersteller, die auch in der Produktliste aufgeführt sind. Ihre Datenherkunft verhält sich also dynamisch in Abhängigkeit von der gewählten Unterkategorie.

So präsentiert sich der Webshop nach dem Start des Formulars frmShop mit eingestellten Kategorien und einem Produkt

Bild 1: So präsentiert sich der Webshop nach dem Start des Formulars frmShop mit eingestellten Kategorien und einem Produkt

Listing 1 zeigt, wie sie bei Auslösen des Ereignisses Nach Aktualisierung des Listenfelds lstKategorien über einen neuen SQL-Select-Ausdruck festgelegt wird. Die RowSource des Steuerelements ist im Prinzip an die Tabelle tblHersteller gebunden. Damit aus dieser aber nur jene Datensätze ausgewählt werden, die in ihrem Feld ID den gleichen Wert aufweisen, der auch in der Kategorienliste markiert ist (lstKategorien.Value), benötigen wir eine Zwischenabfrage, denn die Herstellertabelle kennt selbst keine Kategorien. Das ist ein schönes Beispiel für den Einsatz der IN-Klausel. Aus tblArtikel ermittelt die Select-Abfrage innerhalb der Klammer alle Datensätze, deren Kategorien (IDKategorie) mit dem markierten Eintrag des Listenfelds übereinstimmen und gibt aus ihren nur jeweils die IDHersteller zurück. Befindet sich wiederum eine ID der Herstellertabelle IN diesem Pool, so ist die Bedingung positiv und der Datensatz mitsamt der Herstellerbezeichnung wird ausgegeben, so dass er im Kombinationsfeld erscheinen kann.

Private Sub lstKategorien_AfterUpdate()

     ...

     Me!cbHerstellerFilter.RowSource = "SELECT * FROM tblHersteller " & _

         "WHERE ID IN(SELECT IDHersteller FROM tblArtikel WHERE IDKategorie=" & _

         Me!lstKategorien.Value & ")"

     ...

End Sub

Listing 1: Setzen der Datenherkunft der Hersteller-Combobox cbHersteller

Sie interessieren sich für den Deskjet T730 und haben ihn in der Produktliste markiert. Seine Details gibt nun das Unterformular rechts im Bild wieder. Den Preis finden Sie OK und Sie klicken auf den Button In den Warenkorb. Damit erscheint sogleich in einer Msgbox die Info aus Bild 2. Klicken Sie erneut auf den Button, so zeigt sich eine abweichende Meldung (Bild 3).

Info zum Warenkorb

Bild 2: Info zum Warenkorb

Info zum Warenkorb bei wiederholtem Hinzufügen

Bild 3: Info zum Warenkorb bei wiederholtem Hinzufügen

Die beim Klicken auf den Button cmdAdd ausgelöste Prozedur finden Sie in Listing 2.

Private Sub cmdAdd_Click()

     Dim n As Long

     Dim ID As Long

     

     n = Nz(DLookup("Anzahl", "tblBestellDetails", "ArtikelID=" & Me!ID.Value & _

            " AND BestellID=" & BestellID))

     If n = 0 Then

         CurDB.Execute "INSERT INTO tblBestellDetails" & _

                       " (BestellID,ArtikelID,Anzahl,Netto,Ust)" & _

                       " VALUES (" & BestellID & "," & Me!ID.Value & ",1," & _

                       Str(Me!Netto.Value) & "," & Str(Me!USt.Value) & ")"

         MsgBox "Sie haben '" & Me!Produkt & "' in den Warenkorb gelegt.", vbInformation

     Else

         ID = DLookup("ID", "tblBestellDetails", "ArtikelID=" & Me!ID.Value & _

              " AND BestellID=" & BestellID)

         CurDB.Execute "UPDATE tblBestellDetails SET Anzahl=" & (n + 1) & _

                       " WHERE ID=" & ID

         MsgBox "Sie haben die Anzahl von '" & Me!Produkt & "' im Warenkorb erhöht."

     End If

     Me!cbWarenkorb.Requery

End Sub

Listing 2: Das Legen eines Produkts in den Warenkorb über den Button cmdAdd

In der ersten Zeile ermittelt eine DLookup-Funktion zunächst die Anzahl jener Artikel, die sich bereits in der Bestellung befinden, wobei nach dem aktuell ausgewählten Produkt (ArtikelID = Me!ID) und nach der momentanen Bestellung (BestellID) gefiltert wird. Im ersten Betrag wurde erläutert, dass schon beim Öffnen des Shop-Formulars ein temporärer Bestelldatensatz in der Tabelle tblBestellDetails angelegt und ein Verweis auf dessen ID in der globalen Variablen BestellID abgespeichert wird. Hier nun kommt diese ID zum Tragen. Ist der Artikel bereits in der Bestellung vorhanden, so gibt DLookup einen von 0 abweichenden Wert zurück und weist diesen der Variablen n zu. Die Prozedur verzweigt jetzt in Abhängigkeit von diesem Wert.

Ist der Artikel neu, was den einfacheren Fall darstellt so findet das Einfügen eines Datensatzes über die INSERT INTO-Anweisung statt. Ihr werden als Feldparameter die aktuelle BestellID, der Preis (Me!Netto) und der Umsatzsteuersatz (Me!Ust) verabreicht. Diese letzten beiden Werte können nicht aus der Artikeltabelle entnommen werden! Denn bald könnte sich der Preis ja ändern, was bei Bearbeitung der Bestellung zu einer erhöhten Summe führen würde.

Hier muss der aktuelle Preis gelten, weshalb er fest in den Bestelldatensatz gespeichert werden muss. Die anschließende MsgBox zeigt nun lediglich das Produkt (Me!Produkt) in einem entsprechenden Text an.

Ist der Artikel in der Bestellung bereits vorhanden, also der Wert von n ungleich 0, so führt sich der nächste Zweig der Prozedur aus. Hier muss erst ermittelt werden, welche ID der betreffende Bestelldatensatz hat, wobei abermals eine DLookup-Funktion verwendet wird. Anschließend erhöht eine SQL-UPDATE-Anweisung den Wert des Felds Anzahl im gefundenen Datensatz. Die Meldung sieht nun geringfügig anders aus.

Auf die Bedeutung der letzten Zeile, die ein Requery auf das Kombinationsfeld cbWarenkorb auslöst, kommen wir gleich zu sprechen.

Die SQL-Anweisungen geschehen übrigens über die Objektvariable CurDB. Das ist tatsächlich eine Funktion, die nur die Access-Eigenschaft CurrentDb ersetzt. Sie befindet sich im Modul mdlShop der Datenbank (Listing 3). Die im Kopf des Moduls deklarierte Objektvariable thisdb bekommt beim ersten Aufruf der Funktion den Inhalt von CurrentDb verpasst. Forthin gibt sie deren Wert zurück. Der Vorteil der Funktion ist, neben kürzerer Schreibweise, eine bessere Performance.

Private thisdb As Database

Function CurDB() As Database

     If thisdb Is Nothing Then Set thisdb = CurrentDb

     Set CurDB = thisdb

End Function

Listing 3: CurDB gibt die aus CurrentDb erhaltene Objektvariable thisdb zurück

Artikel im Warenkorb

Für den Warenkorb ist dieselbe Tabelle verantwortlich, wie für die Bestellungen: tblBestelldetails. Ohne das Formular für den Warenkorb zu öffnen können bereits im Shop-Formular die hinzugefügten Artikel inspiziert werden. Dazu klicken Sie mit der Maus auf den Button Warenkorb rechts. Sogleich klappt eine Combobox mit der Liste der bestellten Produkte auf (Bild 4). Der Vorgang mag ungewöhnlich sein. In Webshops passiert dies häufig beim Überfahren der Maus auf ein Warenkorbsymbol. Dies nachzustellen wäre hier des Aufwands etwas zu viel gewesen. Die Combobox soll genügen. Ihre Datenherkunft ist an eine Abfrage gebunden, die folgenden Text hat:

Der Inhalt des Warenkorbs kann über eine Combobox eingesehen werden, die sich hinter dem Button befindet

Bild 4: Der Inhalt des Warenkorbs kann über eine Combobox eingesehen werden, die sich hinter dem Button befindet

SELECT

   "- " & [tblArtikel].[Produkt] AS P,

   tblArtikel.ID,

   tblArtikel.IDKategorie,

   tblBestellDetails.BestellID

FROM tblArtikel

INNER JOIN

tblBestellDetails ON tblArtikel.ID =

    tblBestellDetails.ArtikelID

WHERE tblBestellDetails.BestellID =

[Forms]![frmShop]![txtBestellID]

Die Datensätze bilden sich über den JOIN aus der Verknüpfung zwischen tblBestellDetails mit tblArtikel. Da nur die aktuelle Bestellung berücksichtigt werden soll, filtert die WHERE-Bedingung nach der BestellID. Und die kommt direkt aus dem Formular frmShop, in dem das unsichtbare Textfeld txtBestellID vorhanden ist. Superelegant ist derlei nicht, aber als Datenherkunft für ein Kombinationsfeld innerhalb des Formulars ist so ein fester Bezug durchaus akzeptabel. Die Requery-Anweisung auf cbWarenkorb führt zur Neuauswertung der SQL-Abfrage, so dass ihr Datenbestand immer aktuell ist.

Das Ausklappen der Combobox erledigt die Routine in Listing 4. Bei Rechtsklick auf die Schaltfläche Warenkorb (cmdWarenkorb) ereignet sich das MouseDown-Event. Steht der Parameter Button auf 2, so handelt es sich um die rechte Maustaste. Dann stellt die Prozedur den Fokus auf die Combobox cbWarenkorb und bewirkt über die Methode Dropdown deren Ausklappen. Sie gerät dadurch in den Vordergrund, obwohl sie im Formularentwurf in den Hintergrund verfrachtet wurde. Bei Verlassen des Steuerelements wird automatisch wieder der Ausgangszustand hergestellt.

Private Sub cmdWarenkorb_MouseDown(Button As Integer, Shift As Integer, _

                                    X As Single, Y As Single)

     If Button = 2 Then

         Me!cbWarenkorb.SetFocus

         Me!cbWarenkorb.Dropdown

     End If

End SUb

Listing 4: Ausklappen der Combobox beim Maustaste Ab-Ereignis des Buttons

Beim normalen Linksklick auf den Button Warenkorb ruft dieser die Click-Prozedur auf und öffnet das Formular frmWarenkorb (Bild 5).

Allerdings auch nur dann, wenn sich im Warenkorb Artikel befinden. Ansonsten erfolgt eine Meldung, dass der Warenkorb leer sei. Die erste Zeile in Listing 5 wertet über COUNT(*) aus, wie viele Datensätze die Tabelle tblBestellDetails zur aktuellen BestellID aufweist. Ist die Anzahl 0, so kommt es zur Meldung und andernfalls zum Öffnen des Formulars.

Private Sub cmdWarenkorb_Click()

     Dim n As Long

     

     n = CurDB.OpenRecordset("SELECT COUNT(*) FROM tblBestellDetails" & _

                             " WHERE BestellID=" & BestellID)(0)

     If n = 0 Then

         MsgBox "Ihr Warenkorb ist leer!", vbExclamation, sLogo

     Else

         DoCmd.OpenForm "frmWarenkorb", , , , , , BestellID

     End If

End SUb

Listing 5: Das Starten des Warenkorbformulars hängt von der Anzahl der aktuell bestellten Artikel in tblBestellDetails ab

Warenkorbformular

Das gestartete Warenkorbformular frmWarenkorb (Bild 5) weist die folgenden Grundeinstellungen auf. Die Eigenschaft Popup steht auf Ja und die Eigenschaft Gebunden ebenfalls. Dadurch steht es im Vordergrund vor dem Shop-Formular, auf welches nun kein Zugriff mehr möglich ist. Als Ansichtsmodus ist Endlosformular vorgesehen. Im Detailbereich finden sich die Felder für die Bestelldetails, also die Produkte mit deren Artikelnummer, der Bezeichnung, der gewünschten Anzahl, dem Preis und der Verfügbarkeit. Bis auf das Steuerelement für die Anzahl sind alle weiteren gesperrt (Eigenschaftenblatt für das Steuerelement | Daten | Gesperrt = Ja). Die Anzahl kann der Kunde im Warenkorb noch ändern.

So etwa zeigt sich das Formular frmWarenkorb mit zwei bestellten Produkten

Bild 5: So etwa zeigt sich das Formular frmWarenkorb mit zwei bestellten Produkten

Die Felder für Rechnungssumme und enthaltene Mehrwertsteuer sind im Fußbereich des Formulars platziert. Ganz oben gibt es noch ein Label, das Sie, Herrn Fuchs, persönlich anspricht, falls Sie sich bereits registriert und eingeloggt hatten. Und zuunterst geht es über den Button Weiter > zum nächsten Bestellschritt, oder über Zurück zum Shop schließt sich das Formular wieder.

Im Entwurf des Formulars (Bild 6) gibt es nicht viele Besonderheiten zu entdecken. Seine Datenherkunft ist über einen SQL-Ausdruck an die Tabelle tblBestelldetails geknüpft, wobei hier noch die Artikeltabelle für die Produktbezeichnung angeflanscht ist.

Entwurfsansicht des Warenkorbformulars mit Bestellungen im Detailsbereich

Bild 6: Entwurfsansicht des Warenkorbformulars mit Bestellungen im Detailsbereich

Interessant allerdings ist das Textfeld txtLieferzeit für die Verfügbarkeit. Es ist an kein Datenfeld gebunden, zeigt also auch keinen Text an. Seine Hintergrundfarbe aber ist über Bedingte Formatierung mit dem Wert von IDVerfuegbarkeit des Datensatzes verknüpft. Der Dialog mit den Ausdrücken zur Bedingten Formatierung des Felds wird aus Bild 7 ersichtlich. Sie öffnen ihn über das Kontextmenü des Steuerelements und dessen Eintrag Bedingte Formatierung.... Hier wurden drei Regeln festgelegt, die jeweils die Hintergrundfarbe des Textfelds steuern. Je nach dem Wert von IDVerfuegbarkeit kommt eine andere Farbe zum Zug. Diese Werte finden Sie wiederum in der Tabelle tblVerfuegbarkeit.

Die drei möglichen Hintergrundfarben des Felds txtLieferzeit hängen über Bedingte Formatierung vom Wert des Datenfelds IDVerfuegbarkeit ab

Bild 7: Die drei möglichen Hintergrundfarben des Felds txtLieferzeit hängen über Bedingte Formatierung vom Wert des Datenfelds IDVerfuegbarkeit ab

Die Programmierung des Formulars verlangt einige Erläuterungen. Da wäre zunächst das Ereignis Beim Öffnen (Form_Open), welches die Routine in Listing 6 anwirft.

Private Sub Form_Open(Cancel As Integer)

     BestID = Nz(Me.OpenArgs, 0)

     If BestID = 0 Then

         Cancel = True

     Else

         Me.Filter = "BestellID=" & BestID

         Me.FilterOn = True

     End If

End SUb

Listing 6: Die Ereignisprozedur Open des Formulars wertet die OpenArgs aus

Sie weist der globalen Variablen BestID, deren Wert im weiteren Verlauf auch noch für das Kassenformular benötigt wird, den dem Formular beim Starten (Listing 5) verabreichten Parameter OpenArgs zu. Ist dieser Wert nicht übergeben worden, so wandelt die Nz-Funktion die Null des Variants in die Zahl 0 um. In diesem Fall wird der Cancel-Parameter der Ereignisprozedur auf True gesetzt, was zum sofortigen Beenden des Formulars führt. Versuchen Sie einmal, das Formular aus dem Navigationsbereich heraus zu starten. Es wird nicht gelingen.

Sind die OpenArgs hingegen übergeben worden, so filtert das Formular seine Endlosdaten auf die Bestelldetails mit der BestellID aus BestID – also die aktuelle Bestellung.

Damit sind die gewünschten Daten im Formular und die Gesamtsummen für Rechnungsbetrag und Mehrwertsteuer können berechnet werden, was im Ereignis Beim Laden (Form_Load) geschieht. Die Prozedur ruft die zwei Hilfsfunktionen CalcSum und ShowKunde des Formulars auf (siehe Listing 7).

Private Sub Form_Load()

     CalcSum

     ShowKunde

End Sub

Private Sub CalcSum()

     Dim rs As DAO.Recordset

     Dim sum1 As Currency, sum2 As Currency

     

     Set rs = Me.RecordsetClone

     rs.MoveFirst

     Do While Not rs.EOF

         sum1 = sum1 + rs!Brutto.Value

         sum2 = sum2 + rs!Netto.Value * rs!Anzahl.Value

         rs.MoveNext

     Loop

     Me!txtSumme = sum1: Me!txtMwst = sum1 - sum2

End Sub

Sub ShowKunde()

     Dim sKunde As String

     

     If TempVars("KundeID") = 0 Then Exit Sub

     sKunde = DLookup("[Vorname] & ' ' & [Nachname]", "tblKunden", _

                      "ID=" & TempVars("KundeID"))

     Me!LblKunde.Caption = "Hallo, " & sKunde

Sie haben das Ende des frei verfügbaren Teil dieses Artikels erreicht!

Wenn Sie mehr lesen und auf viele weitere Artikel zugreifen möchten, melden Sie sich als Abonnent unter Login an. Falls nicht, bestellen Sie doch einfach ein Jahresabonnement!