 | Unser Angebot für Sie! Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'! |
| | | | |
Artikelverwaltung mit Inventurfunktion, Teil 2
André Minhorst, Duisburg
Im ersten Teil dieser Beitragsreihe haben Sie das Datenmodell und die wesentlichen Formulare der Artikelverwaltung kennen gelernt. Nun beschäftigen Sie sich mit den Inventuren: Welche Faktoren sind dafür wichtig? Wie werden bereits in einer Inventur berücksichtigte Daten bei folgenden Inventuren ausgeschlossen? Außerdem erfahren Sie in dieser Folge, wie Sie Berichte für die Ausgabe von Artikel- und Inventurdaten erstellen.
HinweisDie Musterlösung zu diesem Beitrag heißt Artikelverwaltung00.mdb und funktioniert mit Access 2000 und höher. Sie finden die Datenbank auf der Heft-CD oder im Internet unter http:// www.access-im-unternehmen.de (Shortlink 280). Der erste Teil dieses Beitrags steht an gleicher Stelle unter dem Shortlink 273 bereit. (
Inventur
Eine Inventur ist die Aufnahme des Bestands aller Vermögenswerte eines Unternehmens, also auch der vorhandenen Artikel. Die in die Artikelverwaltung integrierte Inventurfunktion ermöglicht dies ausschließlich für die Artikel; die Aufnahme aller für eine Inventur notwendigen Informationen würde den Umfang des Beitrags leider sprengen. Diese Inventurfunktion berücksichtigt lediglich die in der Datenbank gespeicherten Artikel. Die Menge der vorhandenen Artikel sowie deren Maßeinheiten sollten regelmäßig zum Bilanzstichtag bekannt sein, um eine ordnungsgemäße Buchhaltung zu ermöglichen. Der Artikelbestand muss allerdings nicht genau an diesem Tag gezählt werden.
Inventurarten

Abb. 1: Aufruf der Inventur
Es gibt mehrere Varianten von Inventuren, unter anderem die folgenden:
Stichtagsinventur: Findet innerhalb von zehn Tagen vor und zehn Tagen nach dem Abschluss-Stichtag statt. Das Ergebnis wird auf den Abschluss-Stichtag hochgerechnet.
Verlegte Inventur: Erfolgt zwischen drei Monaten vor und zwei Monaten nach dem Abschluss-Stichtag. Die Zählung der unterschiedlichen Artikel muss nicht an einem Tag erfolgen. Die Werte für jeden einzelnen Artikel werden auf den Abschluss-Stichtag hochgerechnet.
Permanente Inventur: Die Inventur erfolgt fast ausschließlich über die Bewertung der Ein- und Ausgänge eines Artikels. Der Soll- und der Ist-Zustand müssen einmal im Jahr abgeglichen werden.
Die Anwendung unterstützt alle genannten Inventurarten. Der einzige Unterschied zwischen den Arten ist der Zeitpunkt, an dem Soll- und Istwert des Bestands eines Artikels ermittelt werden müssen. Mit der Artikelverwaltung können Sie den Zeitpunkt der Zählung für jeden einzelnen Artikel festlegen.
Wie arbeitet die Inventurfunktion?
Die Inventurfunktion kann für jeden Artikel einzeln aufgerufen werden. Der Aufruf erfolgt über die Schaltfläche Inventur starten der Detailansicht eines Artikels im Formular frmArtikel (s. Abb. 1).
Die dadurch ausgelöste Ereignisprozedur öffnet das Formular frmInventur für den aktuell im Artikelformular angezeigten Artikel (s. Abb. 2).

Abb. 2: Eingabe des gezählten Bestands
Dieses Formular zeigt folgende Informationen an:
Datum der letzten Inventur
Bestand bei der letzten Inventur
Datum der aktuellen Inventur
Berechneter Bestand (aktuelle Inventur)
Tatsächlicher Bestand (aktuelle Inventur)
Differenz zwischen tatsächlichem und berechnetem Bestand
Die mit einem roten Rahmen versehenen Informationen kann der Benutzer selbst ändern. Das Datum wird automatisch mit dem aktuellen Datum vorgelegt, es kann aber nachträglich geändert werden.
Programmierung der Inventurfunktion
Bei der Programmierung des Formulars frmInventur und der enthaltenen Funktionen sind einige Punkte zu beachten, die im nachfolgenden Abschnitt geklärt werden. Anschließend finden Sie eine Beschreibung der Funktion selbst.
Wichtige Voraussetzungen
Die Inventurfunktion startet mit dem Mausklick auf die Schaltfläche Inventur starten des Artikelformulars. Das Formular frmInventur zur Eingabe der Inventurdaten bezieht seine Daten über diverse Zugriffe auf die in der Datenbank enthaltenen Tabellen.
So durchsucht das Formular die Tabelle tblInventuren, ob es bereits eine Inventur zu dem zu untersuchenden Artikel gibt, und liest gegebenenfalls das Datum der Inventur und den Bestand in die entsprechenden Felder des Formulars ein.
Ist noch kein Inventurdatensatz vorhanden und dementsprechend noch keine Zählung des Artikels erfolgt, verwendet das Formular Standardwerte: als Datum den 1.1.1900 und als Bestand den Wert 0.
Der im Feld Berechneter Bestand angezeigte Wert ist die Anzahl des Artikels, die sich aus dem Stand bei der letzten Inventur und den seitdem erfolgten Zu- und Abgängen berechnet.
Dabei spielt natürlich das Datum der aktuellen Inventur eine wichtige Rolle: Beim Öffnen des Formulars zeigt dieses das aktuelle Datum und den Bestand zu diesem Datum an. Wenn der Benutzer das Datum verändert, wird auch der berechnete Bestand angepasst. Neben dem Inventurdatum muss der Benutzer auch die tatsächliche Anzahl des Artikels angeben. Erst dann wird die Differenz berechnet und die Inventurdaten können gespeichert werden.
Private Sub Form_Current()
Dim datLetzteInventur As Date
Dim lngBestandLetzteInventur As Long
Me!txtArtikel = Artikelbezeichnung(lngArtikelID)
Call LetzteInventur(lngArtikelID, datLetzteInventur, lngBestandLetzteInventur)
Me!txtDatumLetzteInventur = datLetzteInventur
Me!txtBestandLetzteInventur = lngBestandLetzteInventur
Me!txtDatumAktuelleInventur = Date
Me!txtBestandAktuelleInventurBerechnet = lngBestandLetzteInventur + _ BestandsaenderungSumme(lngArtikelID, Date)
End Sub
Quellcode 1
Private Function Artikelbezeichnung(lngArtikelID As Long) As String
Dim db As DAO.Database
Dim rstArtikel As DAO.Recordset
Set db = CurrentDb
Set rstArtikel = db.OpenRecordset("SELECT Bezeichnung FROM tblArtikel " _ & "WHERE ArtikelID = " & lngArtikelID, dbOpenDynaset)
Artikelbezeichnung = rstArtikel!Bezeichnung
Set db = Nothing
End Function
Quellcode 2
Technische Umsetzung
Die erste Prozedur, die bei der Verwendung des Formulars frmInventuren zum Zuge kommt, ist die Ereignisprozedur, die beim Öffnen des Formulars ausgelöst wird. Sie weist der modulweit deklarierten Variablen lngArtikel die per Öffnungsargument von der aufrufenden Instanz übergebene Artikelnummer zu.
Private Sub Form_Open(Cancel As Integer)
lngArtikelID = Me.OpenArgs
End Sub
Die Ermittlung der im Formular angezeigten Werte erfolgt in der Ereignisprozedur, die durch das Ereignis Beim Anzeigen ausgelöst wird (s. Quellcode 1). Die Prozedur ermittelt zunächst anhand des Werts des Feldes ArtikelID die Artikelbezeichnung und trägt diese in das Textfeld txtArtikel ein. Dazu verwendet sie die Prozedur aus Quellcode 2. Diese Prozedur liefert die Artikelbezeichnung für die als Parameter übergebene Artikel-ID. Dabei instanziert sie ein Database-Objekt und ein Recordset-Objekt und ermittelt über eine entsprechende Abfrage eine Datensatzgruppe mit dem gesuchten Artikeldatensatz.
Performance statt Dlookup
Statt dieser Funktion hätte man auch einen entsprechenden Aufruf der Dlookup-Funktion verwenden können (in einer Zeile):
Me!txtArtikel = DLookup("Bezeichnung", "tblArtikel", "ArtikelID = " & lngArtikelID)
Das wäre zwar wesentlich platzsparender und damit auch übersichtlicher gewesen, allerdings ist die Performance der Domänenfunktionen relativ schlecht. Ein konkreter Test mit je 1000 Aufrufen jeder Funktion in einer Schleife brachte ein eindeutiges Ergebnis: Über mehrere Läufe hinweg war die DAO-Variante circa 50-100% schneller als die Dlookup-Funktion.
Private Sub LetzteInventur(lngArtikelID As Long, datLetzteInventur As Date, _ lngBestandLetzteInventur As Long)
Dim db As DAO.Database
Dim rstInventuren As DAO.Recordset
Dim rstBestandsaenderungen As DAO.Recordset
Set db = CurrentDb
Set rstInventuren = db.OpenRecordset("SELECT TOP 1 Inventurdatum, " _ & "Lagerbestand FROM tblInventuren WHERE ArtikelID = " & lngArtikelID _ & " ORDER BY Inventurdatum DESC", dbOpenDynaset)
If Not rstInventuren.EOF Then
datLetzteInventur = rstInventuren!Inventurdatum
lngBestandLetzteInventur = rstInventuren!Lagerbestand
Else
Set rstBestandsaenderungen = db.OpenRecordset("SELECT Datum " _ & "FROM tblBestandsaenderungen WHERE ArtikelID = " & lngArtikelID _ & " ORDER BY Datum DESC", dbOpenDynaset)
If Not rstBestandsaenderungen.EOF Then
datLetzteInventur = rstBestandsaenderungen!Datum
Else
datLetzteInventur = "1.1.1900"
End If
lngBestandLetzteInventur = 0
rstBestandsaenderungen.Close
End If
rstInventuren.Close
Set rstInventuren = Nothing
Set rstBestandsaenderungen = Nothing
End Sub
Quellcode 3
Daten der letzten Inventur ermitteln
In einer weiteren externen Routine ermittelt die Prozedur Form_Current das Datum der letzten Inventur und den Bestand bei der letzten Inventur.
Da die beiden zu ermittelnden Werte aus der gleichen Tabelle stammen und gleichzeitig mit einer Routine ermittelt werden sollen, können nicht beide per Rückgabewert an die aufrufende Prozedur übermittelt werden.
Die betroffene Routine heißt LetzteInventur und hat drei Parameter, von denen einer als Eingangs- und zwei als Ausgangsparameter verwendet werden. Die Ausgangsparameter werden bereits in der aufrufenden Prozedur Form_Current deklariert und als Variable übergeben (in einer Zeile):
Call LetzteInventur(lngArtikelID, datLetzteInventur, lngBestandLetzteInventur)
Die beiden letzten Parameter dieses Aufrufs werden in der Prozedur LetzteInventur mit den gewünschten Daten gefüllt (s. Quellcode 3).
Um diese Daten zu ermitteln, greift die Prozedur auf zwei Tabellen zu. Zunächst sucht sie in der Tabelle tblInventuren nach der aktuellsten Inventur für diesen Artikel. Die dazu verwendete Abfrage ist nach dem Inventurdatum sortiert und gibt durch die Option TOP 1 nur den ersten Eintrag der Datensatzgruppe zurück. Ist dieser Datensatz vorhanden, liest die Prozedur das Inventurdatum und den Lagerbestand aus der Tabelle aus und schreibt sie in die entsprechenden Rückgabeparameter.
Private Function BestandsaenderungSumme(lngArtikelID As Long, _ datAktuelleInventur As Date) As Long
Dim db As DAO.Database
Dim rstBestandsaenderungSumme As DAO.Recordset
Set db = CurrentDb
Set rstBestandsaenderungSumme = _ db.OpenRecordset("SELECT Sum(Anzahl * Vorzeichen) AS LagerbestandGesamt " _ & "FROM tblBestandsaenderungen WHERE ArtikelID = " & lngArtikelID _ & " AND Inventur = False AND Datum <= " _ & ISODatum(Me.txtDatumAktuelleInventur), dbOpenDynaset)
BestandsaenderungSumme = Nz(rstBestandsaenderungSumme!LagerbestandGesamt, 0)
rstBestandsaenderungSumme.Close
Set rstBestandsaenderungSumme = Nothing
Set db = Nothing
End Function
Quellcode 4
Wenn die Datensatzgruppe leer ist, wurde noch keine Inventur für diesen Artikel durchgeführt. In dem Fall sind weitere Untersuchungen erforderlich, um halbwegs sinnvolle Werte zurückzugeben. Wenn noch keine Inventur stattgefunden hat, wäre der Stand vor der ersten Bestandsänderung der letzte bestätigte Stand - und der ist immer 0. Als Datum stellt die Prozedur in dem Fall das Datum der ersten Bestandsänderung ein. Wenn weder eine Bestandsänderung noch eine Inventur für einen Artikel stattgefunden hat, ist Fantasie gefragt: Als Datum wird dann der 1.1.1900 und als Bestand der Wert 0 zurückgegeben.
Einstellen des Datums der aktuellen Inventur
Als Datum für die aktuelle Inventur trägt die Prozedur Form_Current das aktuelle Datum ein.
|