Rechnungsverwaltung: Bestellformular

Nachdem wir das Datenmodell für unsere Rechnungsverwaltung angelegt sowie die Tabellen mit Beispieldaten gefüllt haben, kommt als Nächstes die Benutzeroberfläche zum Verwalten der Kunden-, Produkt- und Bestelldaten an die Reihe. Die dazu notwendigen Formulare stellen wir in mehreren Teilen dieser Beitragsreihe vor. Die Basis ist das Formular zum Anzeigen der Bestellungen, mit dem wir den Kunden auswählen, die Bestelldaten eingeben und die Bestellpositionen hinzufügen können. Die Programmierung dieses Formulars zeigen wir im vorliegenden Beitrag – inklusive Validierung und mehr.

Leichter programmieren mit Testdaten

Das Schöne ist, dass wir im Beitrag Rechnungsverwaltung: Beispieldaten (www.access-im-unternehmen.de/1381) bereits einige Beispieldatensätze angelegt haben, sodass wir beim Programmieren der Formulare nicht immer noch mühselig von Hand Testdaten eingeben müssen. Dabei sollten wir aber nicht vergessen, dass der Kunde die Anwendung gegebenenfalls ohne Daten erhält. In diesem Fall müssen wir die Formulare auch noch mit leeren Tabellen testen, um zu prüfen, ob das initiale Anlegen von Daten ebenfalls funktioniert und ob die Formulare komplett ohne Daten genauso gut funktionieren.

Reihenfolge beim Anlegen der Formulare

Wenn Sie keine Testdaten zur Verfügung hätten, würden Sie die Formulare logischerweise in einer Reihenfolge erstellen, in der auch die Daten eingegeben werden. Wir würden also zuerst ein Formular zur Eingabe von Anreden, Einheiten und Mehrwertsteuersätzen benötigen, dann für Kunden und Produkte und schließlich für Bestellungen und Bestelldetails (wobei letztere in einem Formular plus Unterformular untergebracht werden).

Mit Testdaten können wir das Erstellen der Formulare allerdings in beliebiger Reihenfolge gestalten. Also beginnen wir doch gleich mal mit dem aufwendigsten Formular – dem zur Eingabe der Bestellungen.

Formulare zur Eingabe von Bestellungen

Zur Eingabe von Bestellungen benötigen wir eigentlich nur ein einfaches Formular, aber zu Bestellungen gehören ja auch noch Bestellpositionen. Und da diese über eine 1:n-Beziehung mit den Bestellungen verknüpft sind, bietet sich die Verwendung eines Unterformulars an.

Wir erstellen zuerst das Hauptformular und öffnen es in der Entwurfsansicht. Dieses nennen wir frmBestellungDetails und weisen ihm für die Eigenschaft Datensatzquelle die Tabelle tblBestellungen zu. Danach wechseln wir zur Feldliste und ziehen alle Felder außer ID in den Detailbereich des Formularentwurfs (siehe Bild 1). Warum nicht das Feld ID? Weil dieses ein rein für die Herstellung von Beziehungen verwendetes Feld ist und der Benutzer dieses ohnehin nicht ändern kann und soll. Die Beschriftungen müssen wir noch ein wenig anpassen, sodass beispielsweise aus KundeID die Beschriftung Kunde wird oder aus BestelltAm die Beschriftung Bestellt am. Außerdem fehlen überall noch Doppelpunkte.

Das Formular frmBestellungDetails in der Entwurfsansicht

Bild 1: Das Formular frmBestellungDetails in der Entwurfsansicht

Diese Änderungen hätten wir auch schon zu einem früheren Zeitpunkt vorbereiten können, nämlich im Tabellenentwurf. Dort hätten wir diese Bezeichnungen für die Eigenschaft Beschriftung der jeweiligen Felder eintragen können. Da wir nicht wissen, ob wir noch weitere Formulare oder Berichte auf Basis dieser Felder erstellen, nehmen wir diese Änderungen noch schnell vor. Dazu öffnen wir die Tabelle tblBestellungen nochmals in der Entwurfsansicht und stellen dort für die verschiedenen Felder die gewünschten Beschriftungen in der gleichnamigen Eigenschaft ein (siehe Bild 2).

Voreinstellung für die Beschriftung von Feldern, auch in Formularen oder Berichten

Bild 2: Voreinstellung für die Beschriftung von Feldern, auch in Formularen oder Berichten

Doppelpunkte zu Beschriftungen hinzufügen

Und auch den Doppelpunkt hinter der Beschriftung müssen wir nicht von Hand anlegen. Wir können dies für das aktuelle Formular auch so einstellen, dass jedes Feld automatisch einen Doppelpunkt erhält. Dazu müssen Sie jedoch bereits vor dem Hinzufügen der Felder der Datensatzquelle eine bestimmte Eigenschaft einstellen.

Klicken Sie dazu in der Entwurfsansicht des Formulars im Ribbon auf Formularentwurf|Steuerelemente|Textfeld, aber fügen Sie kein Textfeld zum Formular hinzu. Das Eigenschaftsfeld zeigt nun einige Eigenschaften an, die nach dem Hinzufügen von Textfeldern nicht mehr erscheinen – zum Beispiel Mit Doppelpunkt. Diese Eigenschaft stellen Sie, falls dies noch nicht der Fall ist, auf Ja ein (siehe Bild 3).

Aktivieren des Doppelpunkts für Beschriftungsfelder

Bild 3: Aktivieren des Doppelpunkts für Beschriftungsfelder

Anschließend betätigen Sie die Esc-Taste, um die Auswahl des Textfeldes abzubrechen. Das Feld KundeID haben wir in der Tabelle als Nachschlagefeld ausgelegt. Das heißt, dieses Feld wird beim Ziehen aus der Feldliste in den Formularentwurf als Kombinationsfeld erstellt. Da wir die Änderung zum Anzeigen des Doppelpunkts soeben nur für Textfelder eingestellt haben, müssen wir dies auch noch für Kombinationsfelder erledigen.

Anschließend ziehen wir erneut die gewünschten Felder aus der Feldliste in den Detailbereich des Formularentwurfs. Das Ergebnis sieht schon viel besser aus – die Beschriftungen aus dem Tabellenentwurf wurden übernommen und auch die Doppelpunkte wurden hinzugefügt (siehe Bild 4).

Bezeichnungsfelder mit Doppelpunkten

Bild 4: Bezeichnungsfelder mit Doppelpunkten

Damit sind die Arbeiten am Hauptformular vorerst erledigt. Sie können nun bereits in die Datenblattansicht wechseln und sehen dort die Testdaten zum Durchblättern. Hier erkennen Sie auch, dass wir die Breite des Kombinationsfeldes KundeID noch vergrößert haben (siehe Bild 5).

Steuerelemente mit angepasster Breite

Bild 5: Steuerelemente mit angepasster Breite

Unterformular für Bestellpositionen

Damit kommen wir zum Unterformular, das die Bestellpositionen zur jeweiligen Bestellung anzeigen soll. Dieses legen wir unter dem Namen sfmBestellungDetails an. Als Datensatzquelle fügen wir die Tabelle tblBestellpositionen zu. Diese passen wir zuvor ähnlich wie die Tabelle tblBestellungen noch an, indem wir passende Beschriftungen für die Felder ProduktID (Produkt), Mehrwertsteuersatz (MwSt.-Satz) und EinheitID (Einheit) einstellen.

Danach ziehen wir alle Felder außer ID und BestellungID in den Detailbereich des Formulars. ID benötigen wir nicht, weil es das Primärschlüsselfeld der Tabelle ist und BestellungID dient nur dem Herstellen einer Beziehung zum jeweils im Hauptformular angezeigten Datensatz.

Außerdem stellen wir die Eigenschaft Standardansicht des Formulars auf Datenblatt ein. Die Bestellpositionen sollen im Unterformular tabellarisch dargestellt werden. Danach schließen wir das als Unterformular zu verwendende Formular.

Unterformular zum Hauptformular hinzufügen

Nun öffnen wir wieder das Formular frmBestellungDetails in der Entwurfsansicht und fügen das Unterformular sfmBestellungDetails zum Hauptformular hinzu, indem wir es aus dem Navigationsbereich in den Detailbereich des Hauptformulars ziehen – und es unter den dort bereits befindlichen Steuerelementen fallenlassen.

Dort platzieren wir es wie in Bild 6 und ändern seine Beschriftung auf Bestellpositionen. Für die bessere Bedienbarkeit nehmen wir noch weitere Änderungen vor. So stellen wir die Eigenschaften Horizontaler Anker und Vertikaler Anker des Unterformular-Steuerelements jeweils auf Beide ein.

Haupt- und Unterformular

Bild 6: Haupt- und Unterformular

Dies ändert automatisch die entsprechenden Eigenschaften des Bezeichnungsfeldes des Unterformular-Steuerelemente, die wir wieder auf Oben beziehungsweise Links zurückstellen.

Wie soll das Unterformular nun wissen, dass es nicht alle Bestellpositionen anzeigen soll, sondern nur die Bestellpositionen, die zum aktuell im Hauptformular angezeigten Datensatz gehören? Die notwendige Einstellung haben wir indirekt bereits getroffen, indem wir eine Beziehung zwischen den Feldern BestellungID der Tabelle tblBestellpositionen und ID der Tabelle tblBestellungen definiert haben. Access erkennt diese Beziehung beim Hinzufügen eines Unterformulars und trägt die relevanten Daten direkt in die beiden Eigenschaften Verknüpfen von und Verknüpfen nach des Unterformular-Steuerelements ein. Das Ergebnis sieht wie in Bild 7 aus.

Herstellen der Beziehung der Daten aus Haupt- und Unterformular

Bild 7: Herstellen der Beziehung der Daten aus Haupt- und Unterformular

Ausprobieren des Bestellungen-Formulars

Nun wechseln wir in die Formularansicht des Formulars frmBestellungDetails und finden das Ergebnis aus Bild 8 vor.

Haupt- und Unterformular in der Formularansicht

Bild 8: Haupt- und Unterformular in der Formularansicht

Wenn wir durch die Datensätze des Hauptformulars navigieren, zeigt das Unterformular auch jeweils die passenden Datensätze an.

Damit können wir nun auch einen der weiter oben erwähnten Tests durchführen, der das Anlegen einer neuen Bestellung betrifft. Dazu wechseln wir im Hauptformular zu einem neuen, leeren Datensatz. Dieser zeigt sowohl im Hauptformular als auch im Unterformular noch keine Daten an. Wenn wir nun den herkömmlichen Weg gehen und zuerst die Daten der Bestellung im Hauptformular anlegen und dann über das Unterformular Bestellpositionen hinzufügen, läuft alles wie erwartet.

Aber es kann ja auch sein, dass der Kunde anruft und direkt sagt, er möchte Produkt X und Produkt Y erhalten und der neue Mitarbeiter trägt erst einmal die entsprechenden Bestellpositionen ein, ohne die übrigen Bestelldaten hinzuzufügen.

Dann geschieht Folgendes: Da im Hauptformular noch kein Datensatz angelegt wurde, trägt Access in die neuen Datensätze im Unterformular mit den Bestellpositionen den Wert in das Fremdschlüsselfeld BestellungID ein, der auch gerade im damit verknüpften Feld ID im Hauptformular enthalten ist – und dieser lautet Null (siehe Bild 9).

Anlegen von Daten im Unterformular ohne Datensatz im Hauptformular

Bild 9: Anlegen von Daten im Unterformular ohne Datensatz im Hauptformular

Wir haben also ein paar Bestellpositionen ohne Zuweisung zu einer Bestellung (siehe Bild 10).

Bestellpositionen ohne BestellungID

Bild 10: Bestellpositionen ohne BestellungID

Noch schlimmer wird es, wenn der Mitarbeiter nun nach der Eingabe der Bestellpositionen die übrigen Daten der Bestellung nachträgt. Das sieht zu Beginn noch so aus, als würde es funktionieren (siehe Bild 11).

Eingabe der Bestelldaten nach den Bestellpositionen ...

Bild 11: Eingabe der Bestelldaten nach den Bestellpositionen …

Wenn der Benutzer dann allerdings zu einem anderen Datensatz wechselt und dann zum vorherigen Datensatz zurückkehrt, ist das Unterformular mit den Bestellpositionen plötzlich leer (siehe Bild 12).

... führt nach dem Datensatzwechsel zum Verschwinden der vermeintlich verknüpften Daten im Unterformular

Bild 12: … führt nach dem Datensatzwechsel zum Verschwinden der vermeintlich verknüpften Daten im Unterformular

Was tun? Wir müssen irgendwie dafür sorgen, dass der Benutzer nur Daten in das Unterformular eingeben kann, wenn der Datensatz im Hauptformular bereits angelegt wurde.

Daten erst ins Hauptformular eingeben

Um sicherzustellen, dass zuerst Daten ins Hauptformular und dann erst ins Unterformular eingegeben werden, wollen wir den Benutzer auf irgendeine Weise davon abhalten, die Daten umgekehrt einzugeben.

Die erste Idee wäre, das Unterformular einfach zu sperren, wenn der Benutzer gerade einen neuen Datensatz anlegt und versucht, in das Unterformular zu springen, bevor es einen Primärschüsselwert im Hauptformular gibt.

Das können wir mit einer Ereignisprozedur erledigen, die durch das Ereignis Beim Anzeigen des Hauptformulars ausgelöst wird. Diese prüft mit der Eigenschaft NewRecord, ob das Formular gerade einen neuen, leeren Datensatz enthält und stellt in diesem Fall die Eigenschaft Enabled des Unterformular-Steuerelements auf den Wert False ein, anderenfalls auf den Wert True:

Private Sub Form_Current()
     If Me.NewRecord Then
         Me!sfmBestellungDetails.Enabled = False
     Else
         Me!sfmBestellungDetails.Enabled = True
     End If
End Sub

Dies können wir auch wesentlich kürzer schreiben:

Private Sub Form_Current()
     Me!sfmBestellungDetails.Enabled = Not Me.NewRecord
End Sub

Was aber, wenn der Benutzer dann zum Beispiel den Kunden auswählt, was ja faktisch dazu führt, dass das Autowertfeld ID der Tabelle tblBestellungen einen Wert erhält und wir nun Bestellpositionen zum Unterformular hinzufügen könnten? Dann wäre das Unterformular immer noch gesperrt, weil die obigen Prozedur nur feuert, wenn ein Datensatz angezeigt wird. Aber auch das Ändern eines neuen Datensatzes löst ein Ereignis aus, nämlich Bei Geändert. Für dieses Ereignis hinterlegen wir die folgende Prozedur, die das Unterformular aktiviert:

Private Sub Form_Dirty(Cancel As Integer)
     Me!sfmBestellungDetails.Enabled = True
End Sub

Somit kann der Benutzer nun auch Daten in das Unterformular eingeben.

Benutzer sind allerdings einfallsreich und die nächste Schwachstelle, die sie finden könnten, ist Folgende: Wenn man nämlich den Datensatz im Hauptformular „dirty“ macht und dann die Esc-Taste betätigt, werden die Änderungen wieder zurückgekommen und wir haben wieder einen unbefleckten Bestelldatensatz ohne ID. Das Anlegen von Bestellpositionen im Unterformular ist allerdings aktuell möglich und würde zu den oben beschriebenen Problemen führen.

Um dies zu verhindern, greifen wir das nächste spannende Ereignis ab, nämlich Bei Rückgängig. Dieses wird ausgelöst, wenn der Benutzer beispielsweise mit der Esc-Taste die bereits vorgenommen Änderungen verwirft. Hier deaktivieren wir einfach wieder das Unterformular, sofern sich im Hauptformular wieder ein neuer, leerer Datensatz befindet:

Private Sub Form_Undo(Cancel As Integer)
     Me!sfmBestellungDetails.Enabled = Not Me.NewRecord
End Sub

Damit kann der Benutzer nun zumindest keine nicht verknüpften Bestellpositionen mehr anlegen.

Bezeichnungen der Steuerelemente anpassen

Bevor wir gleich mit weiteren VBA-Prozeduren fortfahren, in denen wir unter anderem auf die Inhalte von Steuerelementen zugreifen wollen, legen wir für diese zunächst sinnvolle Namen mit Präfix fest. Das heißt, Textfelder erhalten das Präfix txt, Kombinationsfelder das Präfix cbo und so weiter.

Dies sollten Sie zumindest für solche Steuerelemente durchführen, auf die Sie per VBA zugreifen. Auf diese Weise unterscheiden Sie die Steuerelemente von den gleichnamigen, an das Formular gebundenen Felder der Datensatzquelle.

In Bild 13 sehen Sie, wie wir den Namen für das Kombinationsfeld eingestellt haben, das an das Feld KundeID gebunden ist.

Einstellen von Präfixen für die Steuerelementnamen

Bild 13: Einstellen von Präfixen für die Steuerelementnamen

Validierung

Wir wollen sicherstellen, dass wichtige Felder gefüllt werden. Also fügen wir eine Prozedur hinzu, die vor dem Speichern eines Datensatzes prüft, ob alle notwendigen Daten angegeben wurden. Im Falle des Hauptformulars handelt es sich um die Felder Bestellnummer, KundeID und BestelltAm.

Als Ereignis wählen wir hierfür das Ereignis Vor Aktualisierung des Hauptformulars aus. Dieses wird immer ausgelöst, wenn der Benutzer Änderungen an einem vorhandenen oder neuen Datensatz vorgenommen hat und die Änderungen speichert, indem er den Datensatz wechselt, das Formular schließt oder auch den Datensatz gezielt beispielsweise mit der Tastenkombination Strg + S speichert.

Es gibt noch ein ähnliches Ereignis namens Nach Aktualisierung, das direkt nach Vor Aktualisierung ausgelöst wird – aber in diesem sind die Änderungen bereits gespeichert. Die durch das Ereignis Vor Aktualisierung ausgelöste Ereignisprozedur hat außerdem den praktischen Vorteil, dass es einen Parameter namens Cancel mitbringt, den man auf True einstellen und so mit das Speichern des Datensatzes abbrechen kann.

Also fügen wir für das Ereignis Vor Aktualisierung die Prozedur aus Listing 1 hinzu. Diese prüft zunächst, ob das Steuerelement txtBestellnummer leer ist. Falls ja, gibt es eine Meldung aus, setzt den Fokus auf dieses Steuerelement, stellt den Parameter Cancel auf True ein und bricht die Prozedur ab (siehe Bild 14).

Anzeige einer Validierungsmeldung

Bild 14: Anzeige einer Validierungsmeldung

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

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar