Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Erstellung eines Intranets in ASP (Teil 5) - Application Day

Geschrieben von: Alexander Zeitler
Kategorie: ASPIntranet.de

This printed page brought to you by AlphaSierraPapa

Zum letzten Teil der Artikelserie zur Erstellung eines Intranets mit ASP heiße ich Sie herzlich willkommen. Nachdem wir in den bisherigen Artikel unser Hauptaugenmerk auf theoretische Aspekte und grundlegende Programmierung des Intranets gelegt hatten, werden wir heute fast ausschließlich die Programmierung von Anwendungen für das Intranet fokusieren.

Beachten Sie bitte, daß alle in diesem Teil gezeigten Datenbanktabellen in die bereits bestehende Datenbank aspintranet.mdb aus der im Download verfügbaren update.mdb importiert werden müssen, um zu vermeiden, daß bereits erfasste Daten und getätigte Einstellungen überschrieben werden.

Weiterhin erhalten Sie heute den kompletten Code aller Artikel zum Download (außer der Datenbank), so daß Sie sofort auf dem aktuellsten Stand von ASPIntranet sind.

Telefonliste

Die erste Anwendung, die wir heute programmieren werden, ist eine Telefonliste mit Sortierfunktion:

Die Besonderheit an der Sortierfunktion (die im Screenshot auf "Nachname absteigend" steht): sie ist clientseitig. Dies bringt den Vorteil, daß zum einen die Sortierung schneller durchgeführt wird und zum anderen wird der Server bzw. die Datenbank nicht mit einer erneuten SQL-Abfrage belastet.

Die Telefonliste ist ein "Abfallprodukt", welches aus den bisher erfassten Daten entsteht. Es ist also keine neue Tabelle in der Datenbank notwendig. Allerdings benötigen wir in unserem Intranet-Verzeichnisbaum drei neue Verzeichnisse:

Die Telefonliste soll in unserer Outlookleiste im Bereich "Verwaltung" erscheinen. Hierzu müssen wir sie über unsere Menüverwaltung anlegen:

Das Icon für die Telefonliste befindet sich in dem Unterverzeichnis "images" im Verzeichnis "~olmenu".

Bitte weisen Sie den Mitarbeitern, die Sie inzwischen angelegt haben, die Berechtigung für den Zugriff auf die Telefonliste zu. Danach sollte die Anwendung "Telefonliste" in der Outlookleiste sichtbar sein:

Somit sind alle Vorarbeiten erledigt und wir können mit der Programmierung der Telefonliste beginnen, welche in der Datei default.asp im Verzeichnis "telefonliste" erstellt wird.

Wie bereits erwähnt, besteht die Telefonliste aus Informationen, die wir bereits zu einem früheren Zeitpunkt erfasst haben. Hierbei handelt es sich um folgende mitarbeiterbezogene Daten innerhalb des Unternehmens:

Die benötigten Informationen stehen in mehreren Tabellen unserer Datenbank. Wir benötigen in jeder Zeile die Informationen, die zum in dieser Zeile aufgelisteten Mitarbeiter gehören. Wenn wir die Abfragen so gestalten würden, wie wir das bisher getan haben, also immer dann eine SQL-Abfrage zu starten, wenn wir die entsprechende Information aus der Tabelle benötigen ("...WHERE ID = " & Kriterium & "...."), bekämen wir eine Unzahl von SQL-Abfragen, die mit jedem neu angelegten Mitarbeiter steigen würden. Abhilfe schafft hier SQL selbst durch das JOIN-Statement. Dieses ermöglicht es uns, mehrere Tabellen miteinander zu "verbinden" und somit die Daten aus mehreren Tabellen so auszulesen, als würde es sich um eine einzige Tabelle handeln.

Als Beispiel zeige ich Ihnen zunächst die JOIN-Abfrage, um die Nachnamen und Telefonnummern aller Mitarbeiter zu erhalten:

SELECT * Mitarbeiter.Nachname, Telefonnummern.Telefonnummer FROM Telefonnummern RIGHT JOIN 
    Telefonnummern ON (Telefonnummern.ID = Mitarbeiter.Telefonintern)

Da wir allerdings insgesamt sechs Tabellen abfragen müssen, benötigten wir entsprechend auch sechs JOIN-Abfrage, die miteinander verknüpft werden. Die JOIN-Abfrage für unsere Telefonliste sieht dann wie folgt aus:

SELECT Mitarbeiter.Vorname, Mitarbeiter.Nachname, Telefonnummern.Telefonnummer, Emailadressen.Emailadresse, 
    Abteilungen.Abteilung, Faxnummern.Faxnummer, Mobilnummern.Mobilnummer FROM Emailadressen 
    RIGHT JOIN (Telefonnummern RIGHT JOIN (Abteilungen RIGHT JOIN (Faxnummern RIGHT JOIN 
    (Mobilnummern RIGHT JOIN Mitarbeiter ON Mobilnummern.ID = Mitarbeiter.Mobilnummer) ON 
    Faxnummern.ID = Mitarbeiter.Telefax) ON Abteilungen.ID = Mitarbeiter.Abteilung) ON 
    Telefonnummern.ID = Mitarbeiter.Telefonintern) ON Emailadressen.ID = Mitarbeiter.Email

Die Ausgabe der Datensätze erfolgt wie bei einer normalen SELECT-Abfrage auch. Allerdings möchten wir die einzelnen Tabellenzeilen zur besseren Lesbarkeit mit abwechselnd mit zwei verschiedenen Farben hinterlegen.

Hierzu definieren wir via CSS zwei verschieden Klassen "listrow1" und "listrow2" ("/~include/aspintranet.css") für die Tabellenzellen. Während der Ausgabe der Datensätze aus der vorangegangenen JOIN-Abfrage wechseln wir bei jedem Durchlauf die Klasse:

Do While Not rs.Eof
    Response.Write "<tr>"
    If RowStyle = 0 Then
        RowStyle = 1
        RowClass = "listrow1"
    Else
        RowStyle = 0
        RowClass = "listrow2"
    End if

"RowStyle" wurde vor der Abfrage mit

Rowstyle = 0

initialisiert. Um die wechselnden Klassen der Tabellenzellen zuzuweisen ist nun noch folgende Anweisung in jeder Zelle, die Datensätze enthält, notwendig:

Response.Write "<td class=""" & RowClass & """>" & rs("vorname") & "</td>"

Um die clientseitige Sortierung der Telefonliste zu erreichen, bedienen wir uns eines Features des Internet Explorer: Behaviors, welche es ermöglichen HTML-Elementen "Verhaltenweisen" (Behaviors) zuzuordnen. Das in der Telefonliste verwendete Behavior ist das "Sort-Behavior", welches es ermöglicht, ohne großen Aufwand Daten innerhalb einer HTML-Tabelle nach allen vorhandenen Spalten auf- und absteigend sortieren zu lassen. Voraussetzung hierfür ist, daß die Tabelle mit den HTML-Tags " <THEAD>" und "<TBODY>" in Tabellenkopf und Tabellenkörper unterteilt wird. Die Sortierung wird aktiviert, indem man auf den jeweiligen Titel der Spalte, nach der sortiert werden soll, klickt. Ein weiterer Klick kehrt die Sortierreihenfolge um.

Am Ende der Seite wird schließlich die Funktion "doNav()" aufgerufen, sodaß die Navigationsleiste mit dem Titel "Telefonliste intern" eingeblendet wird. Damit ist die Telefonliste voll funktionstüchtig und wir können uns der nächsten Anwendung zuwenden.

Urlaubs- und Krankheitstageverwaltung

Ein Teil der Verwaltungsarbeit von Mitarbeitern besteht aus der Verwaltung der Urlaubs- und Krankheitstage der Mitarbeiter. Um Ihnen diese Arbeit in Zukunft zu erleichtern, werden wir eine Anwendung zur Verwaltung der Urlaubs- und Krankheitstage in unserem Intranet implementieren:

Wie für die Telefonliste benötigen wir auch für die Urlaubsverwaltung ein neues Unterverzeichnis. Dieses liegt im Verzeichnis "mitarbeiter" und trägt den Namen "urlaub".

Weiterhin benötigen wir in unserer Datenbank drei neue Tabellen:

In dieser Tabelle werden die Urlaubs- und Krankheitstage zum jeweiligen Mitarbeiter zugeordnet gespeichert.

In dieser Tabelle wird der Urlaubsanspruch gespeichert, der vom vertraglich vereinbarten Urlaub abweicht. Dieser gilt für das jeweils definierte Jahr und den zugeordneten Mitarbeiter.

Diese Tabelle schließlich beinhaltet Urlaub, der ausgetragen wurde, z.B. bei der Ausbezahlung von Urlaub oder wenn Urlaub verfällt.

Der Aufbau der Seite, wie Sie sie im Screenshot gesehen haben, findet in der Datei default.asp im neu angelegten Verzeichnis "urlaub" statt.

Zunächst müssen wir eine im Screenshot nicht sichtbare Einstellung tätigen:

Session.LCID = 1031

Hiermit stellen wir die Ländereinstellungen unseres Servers auf "de" für deutsch, wodurch auch auf englischsprachigen Servern die deutschen Datums- und Zahlenformate verwendet werden. Weitere Informationen zu diesem Thema finden Sie hier.

Ausgehend vom Screenshot arbeiten wir uns nun von oben nach unten durch die einzelnen Funktionen vor.

Die erste Anzeige, die wir also realisieren, ist die Anzeige des Jahres, für welches der Urlaub des Mitarbeiters angezeigt wird. Um zwischen den Jahren umschalten zu können, implementieren wir die Möglichkeit zum Blättern zwischen den Jahren, beginnend ab dem Jahr, seit dem der Mitarbeiter beim Unternehmen beschäftigt ist. Standardmäßig wird das aktuelle Jahr angezeigt.

Zunächst müssen wir also feststellen, welches Jahr vorgegeben wurde, und falls kein Jahr vorgegeben wurde, soll das aktuelle Jahr angezeigt werden:

If IntAktuellesJahr = "" Then IntAktuellesJahr = Year(Now())
DatStartDatum = ("01/" & "01" & "/" & IntAktuellesJahr)

Die zweite Anweisung setzt das Startdatum für unseren Kalender, der der Urlaubsplanung zugrunde liegt auf den 1.1. des eingestellten Jahres, da wir uns von dort ausgehend durch alle Tage des Jahres arbeiten.

Jetzt müssen wir feststellen, wann der Mitarbeiter die Arbeit im Unternehmen begonnen hat. Außerdem benötigen wir den vertraglich vereinbarten Urlaubsanspruch:

StrSQL1 = "SELECT Urlaubsanspruch, Eintrittsdatum FROM Mitarbeiter WHERE 
    ID = " & ID & "" ' ID prüfen
Set rs = Conn.Execute(StrSQL1)
IntAnspruchvertraglich = rs("urlaubsanspruch")
IntJahrEintritt = Year(rs("eintrittsdatum"))

Um die Funktion zum Blättern zu integrieren müssen wir feststellen, ob das aktuell vorgegebene Jahr nach dem Eintrittsjahr liegt oder ob es gleich dem Eintrittsjahr ist. Ist es gleich dem Eintrittsjahr, darf keine Möglichkeit zum Zurückblättern bestehen, da wir auf der dann angezeigten Seite Fehlermeldungen erhalten würden. Sonst können wir ein Icon zum Zurückblättern anzeigen. Das Icon zum Vorwärtsblättern wird immer angezeigt, da der Urlaub für die Zukunft, sofern planbar, jederzeit eingetragen werden kann.

Hier der Code, der die Jahresanzeige mit Blätterfunktion erzeugt:

Response.Write "	<td>"
If Int(IntAktuellesJahr) > Int(IntJahrEintritt) Then
    Response.Write "<a href=""default.asp?jahr="
    Response.Write (IntAktuellesJahr - 1)
    Response.Write "&name="
    Response.Write Server.UrlEncode(StrMAName)
    Response.Write "&id="
    Response.Write ID
    Response.Write """ onfocus=""blur()""><img src=""images/button_zurueck.gif"" 
        width=""5"" height=""10"" border=""0"" alt=""""></a>"
End If
Response.Write "</td>"
Response.Write "    <td><div class=""eintrag"" style=""width: 40px;"">"
Response.Write IntAktuellesJahr
Response.Write "</div></td>"
Response.Write "    <td><a href=""default.asp?jahr="
Response.Write (IntAktuellesJahr + 1)
Response.Write "&name="
Response.Write Server.UrlEncode(StrMAName)
Response.Write "&id="
Response.Write ID
Response.Write """ onfocus=""blur()""><img src=""images/button_vor.gif"" width=""5"" 
    height=""10"" border=""0"" alt=""""></a></td>"

Die nächste angezeigte Information im Screenshot ist der Resturlaub der Vorjahre. Um diesen zu erhalten, müssen sämtliche Urlaubstage der Vorjahre (bis zum Eintrittsjahr) addiert und mit den Urlaubsansprüchen der Vorjahre verrechnet werden.

For IntJahrCount = IntJahrEintritt To IntAktuellesJahr
    StrSQL = "SELECT Anspruch FROM Urlaubsanspruch WHERE (MAID = " & ID & ") AND (Jahr = " & IntJahrCount & ")"
    Set rs = Conn.Execute(StrSQL)
    If Not rs.Eof Then
        IntAnspruchIst = rs("anspruch")
    Else
        IntAnspruchIst = IntAnspruchvertraglich
    End if
    IntAnzahlRest = IntAnzahlRest + IntAnspruchIst
Next
StrSQL = "SELECT Anzahl FROM Urlaub WHERE (MAID = " & id & ") AND (Datum < #" & DatStartDatum & "#)"
Set rs = Conn.Execute(StrSQL)
Do While Not rs.Eof
    IntAnzahlRest = IntAnzahlRest - rs("anzahl")
    rs.Movenext
Loop

Hierzu wird aus der Tabelle "Urlaubsanspruch" in einer Schleifen, die vom Eintrittsjahr bis zum aktuellen Jahr läuft, der Urlaubsanspruch gelesen, der nicht dem vertraglich festgelegten Urlaubanspruch entspricht. Diesen benötigen Sie, falls der Mitarbeiter in einem Jahr mehr oder weniger Urlaub erhalten soll, als vertraglich festgelegt wurde. Sollte kein Eintrag für das Jahr vorliegen, so wird der vertragliche Urlaubsanspruch verwendet, den wir bereits zu Beginn in die Variable "IntAnspruchvertraglich" übernommen hatten. Diesen Anspruch summieren wir in der Variable "IntAnzahlrest"

Danach lesen wir aus der Tabelle "Urlaub" alle Urlaubstage des Mitarbeiters, die vor dem Startdatum liegen, also vor dem 1.1. des aktuellen Jahres.

Response.Write "    <td width=""30"">&nbsp;</td>"
Response.Write "    <td nowrap><b>Rest "
Response.Write (IntAktuellesJahr - 1)
Response.Write ":<b></td>"
StrSQL = "SELECT Austrag FROM Urlaubaustrag WHERE (MAID = " & ID & ") AND 
    (Jahr < " & IntAktuellesJahr & ")"
Set rs = Conn.Execute(StrSQL)
Do While Not rs.Eof
    IntAustrag = IntAustrag + rs("Austrag")
    rs.Movenext
Loop
Response.Write "    <td><div class=""eintrag"">"
Response.Write (IntAnzahlrest - IntAustrag - IntAnspruchIst)
Response.Write "</div></td>"

Nun geben wir die Vorjahreszahl aus, welche dadurch erzeugt wird, daß wir von der aktuellen Jahreszahl "1" subtrahieren. Im Anschluß daran lesen wir noch aus der Tabelle "Urlaubaustrag" alle ausgetragenen Urlaubstage der Vorjahre. Diesen summieren wir in der Variable "IntAustrag". Wir haben nun drei Variablen mit verschiedenen Werten erzeugt:

Diese drei Werte verrechnen wir nun in der gezeigten Art und Weise, sodaß wir den aktuellen Restanspruch aus dem Vorjahr erhalten. Diesen lassen wir schließlich ausgeben.

Nun möchten wir den Urlaubsanspruch des aktuellen Jahres anzeigen:

Response.Write "    <td nowrap><b>Vertragl. Anspruch "
Response.Write IntAktuellesJahr & ":</b></td>"
Response.Write "    <td><div class=""eintrag"">"
Response.Write FormatNumber(IntAnspruchIst,1)
Response.Write "</div></td>"

Hierzu müssen wir nur die bereits ermittelten Daten des aktuellen Jahres ausgeben lassen - die Jahreszahl sowie den Urlaubsanspruch.

Die letzte Information, die sich in der ersten Zeile des Urlaubsplaners befindet, ist der Gesamtanspruch des aktuellen Jahres, also die Differenz aus "IntAnzahlRest" und "IntAustrag":

IntGesamtJahrverbleibend = IntAnzahlRest - IntAustrag
Response.Write "    <td width=30>&nbsp;</td>"
Response.Write "    <td nowrap><b>Gesamtanspruch "
Response.Write IntAktuellesJahr & ":</b></td>"
Response.Write "    <td><div class=""eintrag"">"
Response.Write FormatNumber(IntGesamtJahrverbleibend,1)
Response.Write "</div></td>"

Diesen lassen wir zusammen mit der aktuellen Jahreszahl ausgeben.

Danach speichern wir die Zahl der verbliebenen Urlaubstage für das aktuelle Jahr in einer neuen Variable "IntGesamtJahrFix", da wir von diesem Wert nun unsere über das Jahr hinweg genommenen Urlaubstage abziehen werden. Allerdings benötigen wir für manche Berechnungen auch den ursprünglichen Wert:

IntGesamtJahrFix = IntGesamtJahrverbleibend

Die beiden Felder der zweiten Zeile "beantragter Urlaub 2001" und "Krankheitstage 2001" erzeugen wir zunächst leer. Diese werden wir am Ende mittels JavaScript mit Werten füllen:

Response.Write "<tr>"
Response.Write "    <td nowrap><b>beantragter Urlaub "
Response.Write IntAktuellesJahr & ":</b></td>"
Response.Write "    <td nowrap width=""6""></td>"
Response.Write "    <td><div class=""eintrag"" id=""genommen""></div></td>"
Response.Write "    <td width=""35"" nowrap></td>"
Response.Write "    <td nowrap><b>Krankheitstage "
Response.Write IntAktuellesJahr & ":</b></td>"
Response.Write "    <td nowrap width=""1""></td>"
Response.Write "    <td><div class=""eintrag"" id=""krankgesamt""></div></td>"
Response.Write "</tr>"

Nun kommen wir zur Generierung des Kalenders mit der Anzeige der Urlaubs- und Krankheitstage. Hierzu lassen wir zuerst eine Zeile mit den Namen der Monate von Januar - Dezember erzeugen:

Response.Write "<tr>"
Response.Write "    <td class=""headline"">&nbsp;</td>"
For IntCounterI = 1 To 12
    Response.Write "<td class=""headline"">"
    Response.Write MonthName(IntCounterI,True)
    Response.Write ".</td>"
Next
Response.Write "</tr>"

Bevor wir jetzt fortfahren ist etwas Theorie notwendig. Wir müssen nämlich in jeder Zelle des Kalenders einige Prüfungen durchführen:

Die Prüfungen auf Feiertag und ob bereits ein Urlaubs-/Krankheitstag existiert würde jeweils eine Datenbankabfrage je Tag erfordern. Bei 365 Tagen ergäbe dies 730 Datenbankabfragen. Außerdem wären noch einige andere Abfragen notwendig. Auf alle Fälle aber wäre die Zahl der Datenbank-Abfragen derart hoch, daß die Geschwindigkeit der Anwendung einen sinnvollen Einsatz unmöglich machen würde. Um dieser Problematik aus dem Weg zu gehen, lesen wir die Tabellen "Feiertage" und "Urlaub" für das jeweils aktuelle Jahr in Arrays ein. Zwar müssen wir später auch entsprechend viele Abfragen mit den Arrays durchführen. Diese sind allerdings wesentlich schneller als die Datenbankzugriffe. Grobe Tests haben bei dieser Anwendung ergeben, daß die Version mit Datenbankzugriffen etwa 400 - 500 % langsamer ist, als die aktuelle Version mit Arrays. Hier das Einlesen der Datenbanktabellen in die entsprechenden Arrays:

Dim Arrkranktagemonat(11)
Dim ArrUrlaubsTageMonat(11)
For IntCounterI = 0 To 11
    Arrkranktagemonat(IntCounterI) = 0
    ArrUrlaubsTageMonat(IntCounterI) = 0
Next
'Feiertage in Array übertragen
StrSQL1 = "SELECT Datum,Bezeichnung FROM Feiertage"
Conn.CursorLocation = 3 ' adUseClient
Set rs1 = Conn.Execute(StrSQL1)
IntAnzahlFeiertage = rs1.RecordCount
IntArrayCount = 0
ReDim ArrFeierTage(IntAnzahlFeiertage,1)
Do While Not rs1.Eof
    ArrFeierTage(IntArrayCount,0) = rs1("Datum")
    ArrFeierTage(IntArrayCount,1) = rs1("Bezeichnung")
    IntArrayCount = IntArrayCount + 1
    rs1.Movenext
Loop
'Urlaubstage in Array übertragen
StrSQL1 = "SELECT Datum, Anzahl FROM Urlaub WHERE (maid = " 
    & ID & ") AND (Year(datum) = " & IntAktuellesJahr & ")"
Conn.CursorLocation = 3 ' adUseClient
Set rs1 = Conn.Execute(StrSQL1)
IntUrlaubstageZahl = rs1.RecordCount
IntArrayCount = 0
ReDim ArrUrlaubsTage(IntUrlaubsTageZahl,1)
Do While Not rs1.Eof
    ArrUrlaubstage(IntArrayCount,0) = rs1("Datum")
    Arrurlaubstage(IntArrayCount,1) = rs1("Anzahl")
    IntArrayCount = IntArrayCount + 1
    rs1.Movenext
Loop

Zunächst initialisieren wir die zwei Arrays "ArrKranktagemonat" und "ArrUrlaubsTageMonat" mit jeweils 11 Elementen. Danach lassen wir die Zahl der Urlaub- und Krankheitstage für alle Monate auf den Wert "0" initialisieren. Die weitere Verwendung der beiden Arrays wird später erklärt.

Nun lesen wir die Feiertage aus der Tabelle "Feiertage". Hierzu verwenden wir ein client-side Recordset, sodaß sofort alle Datensätze geholt werden. Hierdurch haben wir die Möglichkeit sofort mittels "rs1.RecordCount" die Anzahl der Datensätze zu lesen (wir sparen uns hierdurch eine COUNT-Abfrage). Mit diesem Wert dimensionieren wir das zweidimensionale Array "ArrFeiertage". Danach lesen wir die Feiertage selbst in einer Schleife aus der Tabelle. Bei jedem Schleifendurchlauf lassen wir jeweils das Datum und die Bezeichnung des Feiertags in das Array schreiben. Außerdem lassen wir einen Zähler "IntArrayCount" erhöhen. Auch zu dessen Verwendung später mehr. Somit haben wir die Feiertage von der Datenbank in ein Array übertragen. Exakt den gleichen Vorgang wiederholen wir nun mit der Tabelle "Urlaub", welche wir in das Array "ArrUrlaubstage" übertragen.

Jetzt können wir mit der bereits theoretisch abgehandelten Generierung der einzelnen Kalendertage beginnen. Hierzu benötigen wir ein Feld, das 12 x 31 Felder groß ist (12 Monate, max. 31 Tage je Monat). Bedingt durch die Art wie HTML-Tabellen aufgebaut werden, müssen wir hierbei die Tabelle zeilenweise von links oben nach rechts unten generieren. Somit benötigen wir zwei verschachtelte For-Next-Schleifen, die von "1" bis "31" (für die Tage) und von "1" bis "12" (für die Monate) zählen, wobei die erste Schleife die äußere Schleifen darstellt. In der ersten Spalte stehen die Tageszahlen, welche durch den Zählerwert der äußeren Schleife dargestellt werden:

For IntCounterI = 1 To 31
    Response.Write "<tr>"
    Response.Write "    <td class=""headline"">"
    Response.Write IntCounterI
    Response.Write "</td>"

Mit der ersten Anweisung der inneren Schleife definieren wir, daß die aktuelle Zelle nicht belegt ist. Außerdem genieren wir das Datum dieser Zelle:

    For IntMonat = 1 To 12
        Response.Write "<td class=""tage"">"
        bIstBelegt = False
        DatDatum = (IntMonat & "/" & IntCounterI & "/" & IntAktuellesJahr)

Dieses besteht aus dem Zähler der inneren Schleife, dem Zähler der äußeren Schleife und dem aktuellen Datum. Dieses wird im amerikanischen Datumsformat erzeugt, um Probleme beim Schreiben, Lesen und Vergleichen mit dem Datum in den Datenbanktabellen zu vermeiden. Um einen Vergleich des Datums mit dem Datum im Array vornehmen zu können, muß es hingegen im deutschen Format vorliegen.:

If IntCounterI < 10 Then
    If IntMonat < 10 Then
        ArrDatum = ("0" & IntCounterI & "." & "0" & IntMonat & "." & IntAktuellesJahr)
    Else
        ArrDatum = ("0" & IntCounterI & "." & IntMonat & "." & IntAktuellesJahr)
    End If
Else
    If IntMonat < 10 Then
        ArrDatum = (IntCounterI & "." & "0" & IntMonat & "." & IntAktuellesJahr)
	Else
        ArrDatum = (IntCounterI & "." & IntMonat & "." & IntAktuellesJahr)
    End If
End If

Für den Vergleich müssen die beiden zu vergleichenden Daten nicht auf "Datums-Ebene" identisch sein, sondern auf "String-Ebene". Deshalb werden führende Nullen in den Datumsangaben "Tag" und "Monat" benötigt. Dies erledigt der oben gezeigte Code, in Abhängigkeit davon, ob die Zahlen größer oder kleiner "10" sind. Zahlen kleiner "10" müssen mit einer führenden Null versehen werden. Zahlen größer "10" nicht.

Nun folgt der eigentliche Vergleich zwischen dem Datum der Zelle und dem Datum des Feiertags im Array:

If IsDate(DatDatum) = True Then
    For IntCountFeiertage = 0 To UBound(ArrFeierTage)
        If CStr(ArrFeierTage(IntCountFeiertage,0)) = CStr(ArrDatum) Then
            Response.Write("<div class=""nodate"" title=""" & 
            ArrFeierTage(IntCountFeiertage,1) & """>Feiertag</div>")
            bIstBelegt = True
        End If
    Next

Zuvor wird noch überprüft, ob das Datum, das im amerikanischen Format vorliegt, gültig ist. Hierzu verwenden wir die "IsDate()"-Funktion. Ist das Datum gültig, so wird in einer Schleife, die das komplette "Feiertagsarray" durchläuft geprüft, ob in dem Array ein Datum existiert, welches dem aktuellen Datum auf Stringebene entspricht. Ist dies der Fall, so wird in dieser Zelle der CSS-Style entsprechend gesetzt. Außerdem wird das Wort "Feiertag" in die Zelle geschrieben. Als Titeltext des Wortes "Feiertag" wird der Name des Feiertags ausgegeben. Dieser wird angezeigt, wenn man mit dem Mauszeiger über dem Wort "Feiertag" verweilt. Außerdem setzen wir eine Markierung, daß das Feld jetzt belegt ist.

Die nächste Prüfung, die wir durchführen ist, ob es sich um einen Samstag oder Sonntag handelt:

If bIstBelegt <> True Then
    Select Case WeekDay(IntCounterI & "." & IntMonat & "." & IntAktuellesJahr)
    Case 1
        Response.Write("<div class=""nodate"" title=""Sonntag"">So.</div>")
        bIstBelegt = True
    Case 7
        Response.Write("<div class=""nodate"" title=""Samstag"">Sa.</div>")
        bIstBelegt = True
    End Select
End If

Nachdem geprüft wurde, ob das Feld evtl. bereits belegt ist (durch einen Feiertag), wird falls das Feld noch nicht belegt ist, mittels der WeekDay-Funktion überprüft um welchen Wochentag es sich handelt. Sollte es sich um einen Samstag oder Sonntag handeln, so werden die entsprechenden Kürzel und Titeltexte in die Zelle ausgegeben. Auch hier wird, falls etwas in die Zelle geschrieben wurde, die Markierung "bIstBelegt" gesetzt.

Kommen wir nun zur Prüfung, ob bereits ein Urlaubs- oder Krankheitstag im entsprechenden Array existiert, so daß dieser im Bedarfsfall angezeigt wird.

For IntCounterX = 0 To UBound(ArrUrlaubsTage)
    If CStr(ArrUrlaubsTage(IntCounterX,0)) = CStr(ArrDatum) Then IntAnzahlTemp = 
        arrurlaubstage(IntCounterX,1)
Next
If IntAnzahlTemp <> "" Then
Response.Write("<div title=""Klicken, um den Urlaub-/Krankheitstag 
    zu löschen"" class=button onclick=")
    Response.Write(chr(34))
    Response.Write("doModalDel('" & IntCounterI &"." & IntMonat & "."& 
        IntAktuellesJahr & "'" & "," & "'" & ID & "'" & "," 
            & "'" & StrMAName & "'" & ")" & chr(34))
    If IntAnzahlTemp = 0 Then 
        VarAnzahl = "Krank"
        Arrkranktagemonat(IntMonat - 1) = Arrkranktagemonat(IntMonat - 1) + 1
    Else
        VarAnzahl = FormatNumber(IntAnzahlTemp,1)
    End If
    Response.Write(">" & VarAnzahl &"</div>")		
    ArrUrlaubsTageMonat(IntMonat - 1) = ArrUrlaubsTageMonat(IntMonat - 1) + IntAnzahlTemp
Else
    If bIstBelegt = False Then
        Response.Write("<div title=""Klicken, um Urlaub-/Krankheitstag 
            einzutragen"" class=button onclick=")
        Response.Write(chr(34))
        Response.Write("doModalAdd('" & IntCounterI &"." & IntMonat & "." 
            & IntAktuellesJahr & "'" & "," & "'" & ID & "'" & "," 
            & "'" & StrMAName & "'" & ")" & chr(34))
        Response.Write(">&nbsp;</div>")
    End If
End If
IntAnzahlTemp=""

Zunächst prüfen wir wie bereits bei den Feiertagen auf "String-Ebene", ob im Array "ArrUrlaubsTage" ein Datumseintrag existiert, der mit dem Datum der aktuellen Zelle übereinstimmt. Ist dies der Fall, so wird in die Variable "IntAnzahlTemp" die Zahl der Urlaubstage (1 oder 0,5), welche zu diesem Datum im Array gehört, gespeichert.

Darauf folgt die Abfrage der Variable "IntAnzahlTemp", ob diese leer ist. Ist das nicht der Fall, existiert ein Urlaubstag, der in die Zelle eingetragen werden muß. Da hierfür auch die Möglichkeit geschaffen werden muß, den Urlaubstag zu löschen, benötigen wir den Aufruf einer entsprechenden Funktion im OnClick-Event der Zelle. Hierfür verwenden wir wieder einmal Modal Dialogs, doch dazu später mehr - akzeptieren Sie den Aufruf so, wie er im Moment ist. Sollte die Zahl in "IntAnzahlTemp" gleich "0" sein, so handelt es sich um einen Krankheitstag. Entsprechend wird der Zähler im Array "Arrkranktagemonat" (das wir bereits dimensioniert haben), für den momentan bearbeiteten Monat um den Wert "1" erhöht. Ist "IntAnzahlTemp" gleich "0,5" oder "1", so handelt es sich um einen Urlaubstag. Entsprechend einem halben (0,5) oder ganzen (1) Urlaubstag wird auch hier der Zähler im Array "ArrUrlaubsTageMonat" um den Faktor "0,5" oder "1" erhöht. Sollte kein Urlaubs- oder Krankheitstag im Array auffindbar gewesen sein, der dem Datum der Zelle entspricht, muß in die Zelle die Möglichkeit integriert werden, einen Urlaubs- oder Krankheitstag zu erzeugen. Auch dies geschieht über einen Modal Dialog, dessen Aufruf analog zum vorherigen geschieht.

Am Ende dieser Überprüfung wird die Variable "IntAnzahlTemp" wieder geleert, damit sie für den nächsten Durchlauf der Schleife "initialisiert" ist.

Schließlich fehlt noch der Fall, daß die IsDate-Prüfung, von der wir die letzten drei Prüfungen abhängig machten, fehlschlug:

Else
Response.Write("<div class=""nodate2"" title=""Tag nicht 
    vorhanden in diesem Monat"">&nbsp;</div>")
End If

Sollte das der Fall sein, so lassen wir eine Zelle generieren, welche im Titeltext angibt, daß der entsprechende Tag kein Tag des Monats ist.

Nachdem unsere beiden Schleifen von "1" bis "31" und von "1" bis "12" komplett durchlaufen wurden, haben wir die Hauptarbeit an unserem Urlaubsplaner bereits geschafft. Was jetzt noch fehlt, ist die Summierung der Urlaubs- und Krankheitstage der jeweiligen Monate und die schlußendliche Anzeige.

Beginnen wir mit der Anzeige der Urlaubstage je Monat. Diese folgt direkt im Anschluß unter dem Kalender:

Response.Write "<tr>"
Response.Write "	<td class=""headline"">Urlaub</td>"
For IntCounterI = 0 To 11
    Response.Write "<td class=""tage"">" & FormatNumber(ArrUrlaubsTageMonat
        (IntCounterI),1) & "</td>"
Next
Response.Write "</tr>"

Hierzu lassen wir einfach in einer Schleife von "0" bis "11" (bei 0 beginnend, da Arrays 0-basierend sind, wir erhalten also 12 Durchläufe) den Inhalt des Arrays "ArrUrlaubsTageMonat" ausgeben.

In der nächsten Zeile des Urlaubsplaners befindet sich die Ausgabe des Resturlaubs nach Abzug des Urlaubs des Monats. Außerdem wird eventuell ausbezahlter Urlaub (welcher bisher als ausgetragener Urlaub bezeichnet wurde) abgezogen:

Response.Write " <td class=""headline"">Rest</td>"
For IntCounterI = 0 To 11
    Response.Write "    <td class=""tage"">"
    IntAustrag = 0
    StrSQLAustrag = "SELECT * FROM urlaubaustrag WHERE (MAID = " & ID & ") 
        AND (Monat= " & (IntCounterI + 1) & ") AND (Jahr = " & IntAktuellesJahr & ")"
    Set rsAustrag = Conn.Execute(StrSQLAustrag)
    If Not rsAustrag.Eof Then IntAustrag = rsAustrag("Austrag")
    IntGesamtJahrverbleibend = IntGesamtJahrverbleibend - 
        (ArrUrlaubsTageMonat(IntCounterI) + IntAustrag)
    rsAustrag.Close
    Response.Write(FormatNumber(IntGesamtJahrverbleibend,1))
    Response.Write "</td>"
Next

Hierzu erzeugen wir wieder eine Schleife, die von "0" bis "11" zählt. Innerhalb dieser Schleife lassen wir aus der Datenbanktabelle "Urlaubaustrag" den für den jeweiligen Monat ausgetragenen Urlaub lesen. Danach lassen wir von dem ursprünglich verfügbaren Jahresurlaub "IntGesamtJahrverbleibend" den genommenen Urlaub des jeweiligen Monats und den Urlaubaustrag aus der Datenbank abziehen. Den erhaltenen Wert lassen wir dann als Resturlaub für diesen Monat ausgeben.

In der dritten Zeile befindet sich die Möglichkeit, Urlaub auszutragen, falls der Mitarbeiter z.B. Urlaub ausbezahlt bekommt, oder Urlaub verfallen sollte. Der entsprechende Code sieht wie folgt aus:

Response.Write "<tr>"
Response.Write " <td class=""headline"">Austrag</td>"
For IntCounterI = 0 To 11
    Response.Write "    <td class=""tage"">"
    IntAustrag = 0
    StrSQLAustrag = "SELECT * FROM urlaubaustrag WHERE (MAID = " & ID & ") 
        AND (Monat= " & (IntCounterI + 1) & ") AND (Jahr = " & IntAktuellesJahr & ")"
    Set rsAustrag = Conn.Execute(StrSQLAustrag)
    If Not rsAustrag.Eof Then
        IntAustrag = rsAustrag("Austrag")
        Response.Write "<div title=""Klicken, um den Austrag zu löschen"" 
            class=button onclick="
        Response.Write(chr(34))
        Response.Write("doModalAustragDel('" & rsAustrag("ID") 
            &"'" & ")" & chr(34))
        Response.Write(">" & FormatNumber(IntAustrag,1) &"</div>")
    Else
        Response.Write "<div title=""Klicken, um Urlaub auszutragen 
            / auszubezahlen"" class=button onclick="
        Response.Write(chr(34))
        Response.Write("doModalAustrag('" & (IntCounterI+1) &"'" & "," & "'" 
            & ID & "'" & "," & "'" & IntAktuellesJahr & "'" & "," & "'" & 
            IntGesamtJahrverbleibend & "'" & ")" & chr(34))
        Response.Write(">" & FormatNumber(IntAustrag,1) &"</div>")
    End If
    rsAustrag.Close	
    Response.Write "</td>"
Next
Response.Write "</tr>"

Zunächst wird der Urlaubaustrag aus der Datenbanktabelle "Urlaubaustrag" gelesen. Ist für den aktuellen Monat kein Austrag vorhanden, so wird unter Angabe des für dieses Jahr noch verfügbarer Urlaubs sowie der Mitarbeiter-ID, dem Monat und dem Jahr die Funktion "doModalAustrag()" aufgerufen. Falls bereits Urlaub ausgetragen wurde, besteht mittels "doModalAustragDel()" unter Angabe der ID des Austrags die Möglichkeit, den Austrag zu löschen. Das Anlegen und Löschen des Austrags ist bis auf marginale Unterschiede identisch mit dem unter "Speichern und Löschen von Urlaubs- bzw Krankheitstagen" weiter unten im Artikel beschriebenen Vorgang identisch.

Schließlich fehlen uns nur noch die Krankheitstage je Monat, welche wir auf dem gleichen Weg erhalten, wie die Urlaubstage je Monat:

Response.Write "    <td class=""headline"">Krank</td>"
IntKrankGesamt = 0
For IntCounterI = 0 To 11
    Response.Write "<td class=""tage"">" & FormatNumber
        (Arrkranktagemonat(IntCounterI),1) & "</td>"
    IntKrankGesamt = IntKrankGesamt + Arrkranktagemonat(IntCounterI)
Next

Einzig das verwendete Array unterscheidet sich hier.

Nun müssen wir nur noch, wie am Anfang der Erklärungen definiert, die Summen der Urlaub- und Krankheitstage via JavaScript in die beiden noch leeren Felder "beantragter Urlaub 2001" und "Krankheitstage 2001" einfügen. Dies erledigt der folgende Code:

Response.Write "<scr" & "ipt type=""text/javascript"">" & VbCrLf
Response.Write "{" & VbCrLf
Response.Write "genommen.innerHTML="""
Response.Write FormatNumber(IntGesamtJahrFix - IntGesamtJahrverbleibend,1)
Response.Write """" & VbCrLf
Response.Write "krankgesamt.innerHTML="""
Response.Write FormatNumber(IntKrankGesamt,1)
Response.Write """" & VbCrLf
Response.Write "}" & VbCrLf
Response.Write "</scr" & "ipt>" & VbCrLf

Hiermit wird einfach mittels innerHTML auf die definierten Objekte zugegriffen und deren Wert mit den Werten für die Urlaubs- und Krankheitstage überschrieben.

Speichern und Löschen von Urlaubs- bzw Krankheitstagen

Wie bereits erwähnt verwenden wir für das Löschen und Speichern von Urlaubs- bzw. Krankheitstagen Modal Dialogs. Deren Aufruf erfolgt durch folgende Scripts:

<SCRIPT>
<!--
function doModalDel(datum,maid,maname){
    var cSearchValue=showModalDialog("delete.asp?datum=" + datum + "&id=" + maid + 
        "&name=" + maname,0,"dialogHeight: 120px; dialogWidth: 450px; dialogTop: ; 
        dialogLeft: px; center: Yes; help: No; resizable: No; scroll: No; status: No;");
	
    if (cSearchValue == 1)
    {
       self.location.reload();
    }
}
-->
</SCRIPT>
<SCRIPT>
<!--
function doModalAdd(datum,maid,maname){
    var cSearchValue=showModalDialog("change.asp?datum=" + datum + "&id=" + 
        maid + "&name=" + maname,0,"dialogHeight: 120px; dialogWidth: 400px; dialogTop: ; 
        dialogLeft: px; center: Yes; help: No; resizable: No; scroll: No; status: No;");
	
    if (cSearchValue == 1)
    {
       self.location.reload();
    }
}
-->
</SCRIPT>

Diese erwarten als Parameter jeweils das Datum, für den der Tag eingetragen oder gelöscht werden soll sowie die ID und den Namen des Mitarbeiters.

Beginnen wir mit dem Hinzufügen von Urlaubs- und Krankheitstagen. Dies findet, wie in der Funktion "doModalAdd()" zu sehen ist, in der Datei change.asp statt.

Diese beherbergt zunächst ein Frameset, aus dem aus dem zweiten Artikel bekannten Problem (Modaldialogs und Formulare). Das Frameset beinhaltet nur einen Frame, welcher auf die Datei "change1.asp" verweist. Diese beinhaltet vier Buttons, wie im Screenshot zu sehen ist:

Jedem Button ist eine JavaScript- Funktion zugeordnet, welche beim Klick auf den Button ausgeführt wird. Die ersten drei Buttons führen dazu, daß die Seite "makeentry.asp" mit der entsprechend Anzahl der einzutragenden Tage (0, 0,5 oder 1) aufgerufen wird. Der Button "Abbrechen" macht seinem Namen alle Ehre und schließt das Popup ohne weitere Aktionen umgehend.

Da wir natürlich wissen möchten, was sich in der Datei makeentry.asp abspielt, haben wir auf einen der drei ersten Buttons geklickt. Der Inhalt der Datei ist allerdings schnell erklärt:

Nachdem wir die aus der vorangegangenen Seite übergebenen Variablen eingelesen haben, speichern wir diese in die Datenbanktabelle "Urlaub":

anzahl=cdbl(tag_zahl)
strDate = DatePart("d",datum) & "." & DatePart("m",datum) 
    & "." & DatePart("yyyy",datum)
SQLQuery = "INSERT INTO urlaub (MaID,Datum,Anzahl) VALUES 
    ('" & int(id) & "','" & strDate & "','" & anzahl1 & "')"
Set RS = Conn.Execute(SQLQuery)
Conn.close

Das folgende, mittels OnLoad-Event-Handler aufgerufene Skript, schließt schließlich das PopUp und übergibt an die das PopUp öffnende "doModalAdd()"-Funktion den Returnvalue "1", wodurch die Anweisung die Seite default.asp neu lädt:

<script>
function doOK() {
    top.returnValue="1";
    top.close();
}
</script>

Der Aufruf der Funktion "doModalDel()" zum Löschen von Urlaubs- und Krankheitstagen erfolgt analog und unterscheidet sich im wesentlichen nur durch die DELETE-Anweisung in der Seite delentry.asp:

strDate = DatePart("m",datum) & "/" & DatePart("d",datum) 
    & "/" & DatePart("yyyy",datum)
SQLQuery = "DELETE FROM urlaub WHERE (MaID=" & id & ") 
    AND (datum= #" & strDate & "#)"
Set RS = Conn.Execute(SQLQuery)
Conn.close

Anlegen von Feiertagen

Um die in der Tabelle "Feiertage" gespeicherten Feiertag zu administrieren, wurde ein weiterer Assistent integriert:

Der Assistent stellt Ihnen, wie im Screenshot zu sehen ist, die Möglichkeit zur Verfügung Feiertage zu definieren bzw. zu löschen. Diese stehen dann unter anderem im Urlaubsplaner zur Verfügung.

Um den Feiertagsassistenten in Betrieb nehmen zu können, müssen Sie in der Administrationsseite für die Mitarbeiteroptionen eine allgemeine Mitarbeiteroption anlegen, die folgenden Link aufruft:

javascript:doModalFeiertage()

Die Datei tree.asp wurde um die benötigte Funktion erweitert.

Schlußbemerkung

Hiermit sind wir am Ende der fünfteiligen Artikelserie zur Erstellung eines Intranet mit Active Server Pages angelangt. Ich hoffe, daß Sie aus dem vorgestellten Code sowohl einen theoretischen als auch praktischen Nutzen ziehen konnten. Sobald neue Module zu ASPIntranet fertiggestellt werden, wird es hier weitere Artikel zu diesem Thema geben.

Redaktioneller Hinweis

Aufgrund des Umfanges des Intranets - und der damit möglichen Fehlerquellen bei der Umsetzung auf den unterschiedlichen Serverkonfigurationen der Leser - bittet der Autor, die Anfragen über öffentliche Foren (so zum Beispiel aspGerman) oder die ASPIntranet Mailingliste abzuwickeln. Fehler in ASPIntranet können Sie auf der Bugreport-Seite von ASPIntranet melden.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20010921.zip

Verwandte Artikel

Das Array, unendliche Weiten?
http:/www.aspheute.com/artikel/20000927.htm
Das SQL Join Statement
http:/www.aspheute.com/artikel/20001023.htm
Erstellung eines Intranets in ASP - Grundlagen
http:/www.aspheute.com/artikel/20010917.htm
Erstellung eines Intranets in ASP (Teil 2) - Setup
http:/www.aspheute.com/artikel/20010918.htm
Erstellung eines Intranets in ASP (Teil 3) - Navigation
http:/www.aspheute.com/artikel/20010919.htm
Erstellung eines Intranets in ASP (Teil 4) - Mitarbeiter
http:/www.aspheute.com/artikel/20010920.htm

Links zu anderen Sites

ASPIntranet
http://www.aspintranet.de/
ASPIntranet Download
http://www.aspintranet.de/aspintranet/download/default.asp?a=72
HOWTO: Set the ASP Locale ID Per the Browser's Language Settings
http://support.microsoft.com/support/kb/articles/q229/6/90.asp
MSDN: Sort Behavior
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndude/html/dude07232001.asp
selfhtml: innerHTML
http://www.teamone.de/selfhtml/tfba.htm#a4
selfhtml: THEAD
http://www.teamone.de/selfhtml/tcea.htm#a4

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.