% header % Dies ist version-0.9.8 der IHK Doku [FINAL] % let's go... %\documentclass[10pt,a4paper,ngerman]{article} \documentclass[11pt,a4paper]{article} \usepackage[latin1]{inputenc} % Ascii-Format dieses Dokuments %\usepackage{babel} % neue deutsche Rechtschreibung % Teutsch \usepackage{german} % lange Tabellen \usepackage{longtable} % pagestyle.. \usepackage{fancyhdr} \pagestyle{fancy} %\fancyhf{} % bisherige Kopf- und Fusszeilen loeschen \fancyhead[R]{Nico Schottelius} % rechter Kopfzeileneintrag %\fancyhead[L]{Projekdokumentation} % linker Kopfzeileneintrag %\fancyhead[L]{\thepage} % linker Kopfzeileneintrag %\fancyfoot[C]{\thepage} % Fusszeileneintrag (Seitenzahl zentriert) \renewcommand{\headrulewidth}{0.4pt} % Strichstaerke unter der Kopfzeile % let's start \begin{document} \title{Projektdokumentation: Webzugriff auf eine Datenbank via PHP} \date{01.03.2004 - 30.04.2004} \author{ Nico Schottelius\\ Rodenstra\ss{}e 12\\ 30826 Garbsen\\ nico-ihk@schottelius.org\\ \\ Ausbildungsbetrieb:\\ Wirtschaftsgenossenschaft deutscher Tier\"arzte eG\\ Siemensstra\ss{}e 14\\ 30827 Garbsen\\ http://www.wdt.de\\ } % Title \maketitle \newpage % Inhaltsverzeichnis \tableofcontents \newpage \section{Pers\"onliche Erkl\"arung} Ich versichere durch meine Unterschrift, dass ich die betriebliche Projektarbeit und die dazugeh\"orige Dokumentation selbst\"andig in der vorgegebenen Zeit erarbeitet habe. Ich habe keine anderen als die von mir angegebenen Quellen und Hilfsmittel verwendet.\\ \begin{verbatim} \end{verbatim} \begin{tabbing} xxxxxxxxxxxxxxxxx\=xxxxxxxxx\kill $\overline{\mbox{\large{Ort, Datum}}}$ \>$\overline{\mbox{\large{Unterschrift des Pr\"uflings}}}$\\ \end{tabbing} \begin{verbatim} \end{verbatim} Zur Kenntnis genommen: \begin{verbatim} \end{verbatim} $\overline{\mbox{\large{Ausbilder/-in}}}$ \newpage \section{Einleitung} \subsection{Projektumfeld} Das Projekt fand in der Wirtschaftsgenossenschaft deutscher Tier\"arzte eG ("`\textit{WDT}"') am Standort in Garbsen statt. Die WDT liefert Arzneimittel und medizinisches Zubeh\"or f\"ur tier\"arztliche Praxen in ganz Deutschland. Zur Zeit arbeiten circa 170 Mitarbeiter an drei Firmenstandorten, davon sind 14 Aussendienstmitarbeiter ("`\textit{ADM}"') in der Kundenbetreuung t\"atig. Ein Gro\ss{}teil der Arbeitsabl\"aufe wird durch das \textit{ERP-System}\footnote{ERP-System: Enterprise Resource Planing System} unterst\"utzt. So erfolgt die gesamte Auftragsbearbeitung, die Kommissionierung, der Versand, der Einkauf sowie die Buchhaltung mit Hilfe dieses Systems. Die Aussendiensmitarbeiter erhalten monatlich einen f\"ur sie relevanten, individuellen Auszug aus der Datenbank. \subsection{Ist-Zustand} Zur Zeit werden Informationen zur Planung der Kundenberatung an die Aussendienstmitarbeiter als \textit{Excel-Tabelle}\footnote{Excel ist ein Produkt von Microsoft. Excel-Tabellen stellen ein properit\"ares Format zur Speicherung von Tabellen dar} via E-Mail versandt. Aufgrund der Verbindungsart (\textit{HSCSD/GSM}\footnote{GSM ist die Technik die zum Verbinden der Mobiltelefone verwendet wird, HSCSD ist die Daten\"ubertragung \"uber GSM}, ISDN oder Modem) und der Gr\"o\ss{}e (circa 16 \textit{MiB}\footnote{KiB, MiB, GiB sind Alternativbezeichnungen zu KB, MB und GB. Bei der Verwendung von den letztern ist nicht klar, ob der Faktor 1000 oder 1024 gemeint ist, 1024Byte entsprechen \textbf{immer} 1KiB. So sind z.B. manche 80GB Platten nur 80000000000 Byte gross, was 74,51GiB entspricht.}) der Datei kommt es zu Problemen bei der \"Ubertragung. Diese Probleme \"au\ss{}ern sich wie folgt: \begin{itemize} \item der Transfer der Datei dauert sehr lange (\"uber 30 Minuten) \item durch eine Zwangstrennung im Funknetz nach 30 Minuten kann die Datei nicht vollst\"andig \"ubertragen werden \end{itemize} Da E-Mail ein \textit{Push-System}\footnote{Als "`Push-Systeme"' werden Systeme bezeichnet, die die Informationen ohne Aufforderung des Benutzers zu selbigen senden (neben E-Mail auch z.B. Plakate). Bei "`Pull-Sytemen"' hingegen muss der Benutzer interaktiv die Informationen anfordern (z.B. Webseite).} ist, ergibt sich zus\"atzlich das Problem, dass grunds\"atzlich in dreifacher Hinsicht unn\"otige Daten \"ubertragen werden k\"onnen: \begin{enumerate} \item der ADM ben\"otigt die Informationen z.Z. nicht: komplett unn\"otiger Versand.\\ Eine Filterung nach Anforderung ist mit dem momentanen System schwer zu realisieren und k\"onnte bestenfalls manuell mit R\"ucksprache geschehen. \item selbst wenn sich die Daten nicht ge\"andert haben, wird ein Update versandt\\ Dies hat zwei Gr\"unde: \begin{enumerate} \item die regelm\"assig empfangene E-Mail ist ein Indiz f\"ur den ADM, dass das System ordnungsgem\"ass{} funktioniert \item zum Zweiten wird keine Versionsverwaltung betrieben, sondern die Excel-Tabellen jedes Mal komplett neu generiert \end{enumerate} \item Der ADM ben\"otigt nur einen Teil der Daten, bekommt jedoch immer eine vorgefertigte Version mit gro\ss{}em Umfang (Daten\"uberfluss) \item Bei jeder Aktualisierung werden auch die unver\"anderten Stammdaten transferiert \end{enumerate} \subsection{Soll-Zustand} Auf den Transfer via E-Mail soll grunds\"atzlich verzichtet und der Zugriff in ein Pull-System umgewandelt werden. Der Zugriff soll \"uber das Internet auf ein Webinterface geschehen. Die Implementation muss auf dem \textit{LAMP-System}\footnote{LAMP ist die Abk\"urzung f\"ur ein \textbf{L}inux, \textbf{A}pache, \textbf{M}ySQL, \textbf{P}HP Kombination, wobei Linux das Betriebssystem, Apache der Webserver, MySQL die Datenbank und PHP die Programmiersprache ist.} des Providers lauff\"ahig sein. Dazu soll ein m\"oglichst identisches Testsystem intern verf\"ugbar sein. Die Daten m\"ussen aus dem ERP-System in einem f\"ur MySQL importf\"ahigen Format bereitgestellt werden. Die entsprechenden Aufgaben sollen gem\"a\ss{} der Qualifizierung der Mitarbeiter delegiert werden: \begin{itemize} \item das LAMP-System soll durch die Systemintegration bereitgestellt werden \item der Export der Daten aus dem ERP-System soll durch die hauseigenen Programmierer bereitgestellt werden \item die Erstellung des Webzugriffs und der Dokumentation (inklusive Handbuch f\"ur Benutzer und Entwickler, Schnittstellendefinitionen und Pflichtenheft) soll vom Pr\"ufling erledigt werden \end{itemize} Ziel ist es, ein verwendbares System zu entwickeln und die Machbarkeit zu beweisen. \section{Planung} \subsection{Ablauf des Projektes} Zu Beginn des Projektes wurden die Projektgrundsteine "`gelegt"': \begin{enumerate} \item es wurde ein Zeitplan f\"ur die einzelnen Phasen festgelegt, inklusive der Festlegung von Besprechungsterminen \item es wurden f\"ur die einzelnen Teams Schnittstellen definiert, damit diese unabh\"angig voneinander arbeiten konnten und um eine klare Trennung zwischen den einzelnen Modulen zu schaffen \end{enumerate} \subsection{Schnittstellendefinition} Durch den sauberen Einsatz von Schnittstellen kann zum Beispiel ein Mitarbeiter die Daten aus dem ERP-System auslesen und sie in einem definierten Format speichern, w\"ahrend ein anderer schon die Umwandlung in die neue Datenbank mit Hilfe von Testdaten programmiert. \\ Es sollen folgende Schnittstellendefinitionen erzeugt werden: \begin{enumerate} \item Benutzer/Weboberfl\"ache: Layoutdefiniton \item Weboberfl\"ache/Datenbank: Zugriffsdefiniton \item Datenbank/Quelldaten: Importformatdefiniton \end{enumerate} \subsection{Kompatibilit\"at} Die Skripte m\"ussen ein identisches Verhalten auf dem Testsystem und auf dem Echtsystem vorweisen. Um dies zu gew\"ahrleisten sind Modifizierungen, die im Rahmen der Installation von Programmen als normal zu betrachten sind (z.B. Pfad-, Limitierungs- oder Namensmodifikationen), n\"otig. Das Erscheinungsbild dem Benutzer gegen\"uber soll soweit wie m\"oglich identisch bleiben. Eine Absprache mit dem \textit{ISP}\footnote{Internet Service Provider bieten Internetanbindungen und Service an} soll vor Beginn der Entwicklung stattfinden, damit eventuelle Inkompatibilit\"aten vermieden werden k\"onnen.\\ Ein System, das nur In-House funktioniert, w\"are f\"ur die bestehende Situation keine ad\"aquate L\"osung. Da die Kompatibilit\"at ein wichtiger Punkt im Rahmen des Projektes ist, wurde diese in einer eigenen Schnittstelle definiert ("`Test /Echtsystem"'). \subsection{Layout (Benutzerschnittstelle)} Das Layout muss vor Beginn der Entwicklung der Weboberfl\"ache definiert sein und den spezifischen Anforderungen entsprechen. An dieses Layout sollen s\"amtliche Skripte angepasst sein. \section{Durchf\"uhrung} \subsection{Erzeugen des PHP Such-Skripts} Das PHP Skript wurde nach der Vorlage des Excel Dokumentes erzeugt. Zuerst wurde das Skript monolithisch erzeugt, d.h. es enthielt keinerlei Module und s\"amtliche Einstellungen waren in dem Skript selbst zu finden. Da sich schnell herausstellte, dass eine solche Struktur schlecht zu erweitern ist, wurde das Skript in mehrere Teile aufgeteilt, welche wiederum modularisiert sind. Die Module wurden in ein Unterverzeichnis "`modules"' verschoben. Konfigurationen k\"onnen nun zentral in den dazu passenden Dateien im "`includes"' Verzeichnis vorgenommen werden. Es soll die M\"oglichkeit geben, dass die Skripte sp\"ater extern gepflegt oder erweitert werden. Da dies auch durch Dienstleistungen ausl\"andischer Firmen geschehen k\"onnte, sind die Kommentare in Englisch. Da ein Gro\ss{}teil der technischen Literatur in Englisch geschrieben ist, werden deutsche Informatiker mit den englischen Kommentaren keine Probleme haben. Mit einigen Problemen behaftet war der Wechsel von Apache 1.3 zu Apache 2.0. Der Letztere setzt keine globalen Variablen, wenn selbige durch eine Form an ein PHP Skript gesendet werden. Dadurch musste nach dem Wechsel der Inhalt der Formvariablen aus einem speziellen Parameterarray namens "`\_REQUEST"' ausgelesen werden. \subsection{Erzeugen des Importfilters} Der Importfilter ist dem Layout des Hauptskriptes, und damit der allgemeinen Definition, nachempfunden. Er wurde ebenfalls in PHP geschrieben und importiert die hochgeladenen ASCII Tabellen in die MySQL Datenbank. Das Format der ASCII-Tabellen wurde in der entsprechenden Schnittstellendefinition hinterlegt. \\ Das Standardverhalten bei identischen Primary Key Feldern ist, den alten Wert mit dem neuen zu \"uberschreiben. Die Alternative, Werte zu addieren ist nur in wenigen F\"allen sinnvoll und w\"urde in der praktischen Anwendung zu komplizierten Abh\"angigkeiten und einigen zus\"atzlichen Definitionen und Tests f\"uhren. So m\"usste man z.B. Felder wie die Postleitzahl oder die Telefonnummer gesondert behandeln. Ebenfalls problematisch w\"are das Ergebnis, wenn jemand aus Versehen zweimal den Upload Knopf bet\"atigt und somit zum Beispiel den Umsatz f\"ur einen bestimmten Zeitraum verdoppelt. \\ Problematisch ist auch das Standardverhalten von PHP, welches nur eine maximale Dateigr\"o\ss{}e von 2MiB vorsieht und einem Skript nur 8MiB Arbeitsspeicher erlaubt. Wenn die Dateien dieses Limit \"uberschreiten, erzeugt PHP keine Fehlermeldung, sondern ignoriert die hochzuladende Datei (verifiziert bei PHP 4.3.4 und 4.3.5RC3). \subsection{Erzeugen des Initialdatenbankgenerators} Zur Vereinfachung der Arbeit wurde zus\"atzlich ein Skript eingerichtet, welches eine initiale Datenbank mit den entsprechenden Tabellen anlegt. Die entsprechenden Tabellen wurden in den Quelltext integriert, da eine Auslagerung der Tabellenspezifikation einen zus\"atzlichen Parser erfordert h\"atte. \subsection{Transfer in das Echtsystem} Der Transfer in das Echtsystem verlief problemlos. Der Einsatz im Echtsystem wurde mit dem ISP der WDT abgesprochen und getestet. Zuerst wurden die Sicherheitseinstellungen transferiert. Danach wurden die PHP-Skripte hochgeladen, welche dann gleich zur Initialisierung der Datenbank und dem Transfer der Echtdaten benutzt wurden. Da die Verbindung \"uber das \textit{HTTPS}\footnote{Sicheres, verschl\"usselt \"ubertragen von Dateien; Alternative zu HTTP} Protokoll erfolgt, sind die Daten w\"ahrend des Transfers verschl\"usselt und f\"alschungssicher. Die maximale Dateigr\"o\ss{}e f\"ur einen Upload durch PHP ist beim ISP auf 8MB beschr\"ankt. Deswegen mussten die Umsatz Tabellen, die pro Jahr eine Gr\"o\ss{}e von 22MiB haben, aufgeteilt werden. \section{Dokumentationsphase} \subsection{Erstellen der Programmdokumentation} Die Programmdokumentation ist f\"ur die Verwendung durch Entwickler und Administratoren konzipiert und enth\"alt technische Fachbegriffe. Sie wurde in Stichw\"ortern w\"ahrend der Durchf\"uhrungsphase niedergeschrieben und danach zusammengefasst in ein Latex-Dokument. \textit{Latex}\footnote{ Siehe auch die deutsche TeX Webseite http://www.dante.de} ist ein Textsatzsystem, welches insbesondere f\"ur wissenschaftliche und technische Dokumentation verwendet wird. Die Programmdokumentation ist entsprechend der Bed\"urfnisse von Administratoren und Entwicklern gegliedert und aufgebaut. Dies bedeutet z.B., dass der Administrator schnell die entsprechenden Sektionen zur Installation und der Entwickler ebenso einfach den Aufbau der Programme finden kann. Als Ausgabeformat wurde PDF gew\"ahlt, da dieses Format auf jeder Plattform lesbar ist. \subsection{Erstellen des Benutzerhandbuches} Das Benutzerhandbuch wurde nach der Durchf\"uhrung erstellt. Dadurch wurde garantiert, dass die Screenshots der Oberfl\"ache sich nicht mehr ver\"andern k\"onnen. Vom Aufraggeber wurde eine Dokumentation gefordert, die den Ablauf m\"oglichst pr\"azise und zugleich simpel beschreibt. Dies wurde durch den Einsatz von Screenshots bewerkstelligt. Als Ausgabeformat wurde eine Pr\"asentation (entweder im \textit{PowerPoint oder OpenOffice}\footnote{ PowerPoint ist ein Programm aus der Programmgruppe "`Microsoft Office"' von Microsoft, OpenOffice ist ein OpenSource Konkurrenzprodukt zu Microsoft Office} Format) gew\"ahlt, da in einer Pr\"asentation wie in einem Buch am Bildschirm gebl\"attert werden kann. \subsection{Erstellen der IHK-Projektdokumentation} Bei der Erstellung dieser Dokumentation wurde \"ahnlich verfahren wie bei der Erstellung der Programmdokumentation. Jedoch wurde diese Dokumentation zuerst in OpenOffice geschrieben und nachher in Latex konvertiert, da Latex wesentlich effektiver f\"ur die Erstellung von gro\ss{}en Dokumenten ist. \section{Testphase} \subsection{Testabschnitte w\"ahrend der Erstellung} W\"ahrend der Entwicklungsphase wurde die Funktionalit\"at der Skripte mit Hilfe einer Datenbank mit Pseudodaten getestet. Da die Anzahl der S\"atze in der Datenbank sehr gering war, waren die Ergebnisse schnell verf\"ugbar und es musste in Betracht gezogen werden, dass die Arbeit mit Echtdaten wesentlich langsamer laufen k\"onnte. Dies best\"atigte sich jedoch nicht, die Auslieferung der Daten durch die MySQL Datenbank verlief weiterhin gewohnt schnell. Die Ausgabe erfolgte teilweise mit Debug Informationen. Diese beinhalteten z.B. Variableninhalte oder R\"uckgabewerte von Funktionen. \subsection{Eigentest aller Programme} Nach der Entwicklungsphase wurden die Programme auf Funktionalit\"at und saubere Ausgaben \"uberpr\"uft. So durften keine Debug-Nachrichten mehr erscheinen und es sollten keine Fehler beim "`normalen Benutzen"' auftreten. Es wurde kritisch darauf geachtet, dass keine Situation auftritt, die ein normaler Benutzer nicht verstehen kann (z.B. Bildschirm ohne Inhalt). Des weiteren wurden generelle Sicherheitsl\"ucken \"uberpr\"uft, wie \textit{Buffer Overflows, SQL-Injektion und Cross-Site-Scripting}\footnote{Buffer Overflows, SQL-Injektion und Cross-Site-Scripting sind bekannte Fehler die bei der Programmierung auftreten. Die letzteren beiden besonders bei der Programmierung von Skripten mit Webzugriff}. \subsection{Fremdtest aller Programme} Zum Abschlu\ss{} wurden die Programme von Mitarbeitern aus der EDV Abteilung getestet und zus\"atzlich auf Sicherheitsl\"ucken gepr\"uft. Durch diese Hilfe konnten Fehler entdeckt werden, die ein Entwickler selbst nicht bemerkt. So wurde z.B. bemerkt, dass auf einer Seite ein Teil des Firmenlogos anders angezeigt wurde ("`stehende Karawane"', sollte "`bewegte Karawane"' sein). \section{Fazit} \subsection{R\"uckblick} Die Programmierung des Skriptes und die Einrichtung des Apache 2.0 mit PHP 4.3.4 aus dem Quelltext verliefen ohne Probleme. Dies wurde durch den Einsatz des LAMP-Systems gef\"ordert, denn die einzelnen Komponenten, Linux, Apache, MySQL und PHP, sind sehr gut aufeinander abgestimmt. Einen Gro\ss{}teil der Zeit musste in die Entwicklung der Schnittstellen und das Testen investiert werden. \\ Das Teamwork erleichterte die Entwicklungsarbeit, da durch vorhergehende Schulungen und Tipps w\"ahrend der Entwicklung Probleme im Design fr\"uhzeitig vermieden werden konnten. So empfahlen Kollegen das Datenbankdesign in die dritte Form der Normalisierung zu \"ubertragen um eine Umsetzung in eine relationale Datenbank zu erleichtern und zeigten an Beispielen, wie dies zu geschehen hat. Die strikte Trennung der einzelnen Aufgaben erleichterte die Arbeit. Dadurch konnte problemlos an einem anderen Teil weitergearbeitet werden, w\"ahrend noch die Echtdaten f\"ur die Datenbank fehlten. \newpage \subsection{Zeitplan mit Abweichungen} \setlongtables \begin{longtable}{||l|c|c||} \caption{Zeitplan} \endfirsthead \hline \hline \textbf{Aufgabe} & \textbf{ben\"otigte Zeit} & \textbf{geplante Zeit} \\ & (in h) & (in h) \\ \hline \hline \textbf{1 Vorarbeiten [Differenz: -1h]} & \textbf{10} & \textbf{11} \\ \hline \hline 1.1 Vorbesprechung mit unserem Au\ss{}endienst & 1 & 3 \\ \hline 1.2 Eruieren und dokumentieren des genauen Problems & 4 & 3 \\ und Definition des IST-Status & & \\ \hline 1.3 R\"ucksprache mit dem Au\ss{}endienst und Abgleich & 1 & 1 \\ des dokumentierten IST-Status mit dem Au\ss{}endienst & & \\ \hline 1.4 Definieren des SOLL-Status & 3 & 3 \\ \hline 1.5 R\"ucksprache mit dem Au\ss{}endienst und Abgleich & 1 & 1 \\ des dokumentierten SOLL-Status mit dem Au\ss{}endienst & & \\ \hline \hline \textbf{2 Planungsphase [-2h (gesamt: -3h)]} & \textbf{10} & \textbf{12}\\ \hline \hline 2.1 Grob-Modell entwickeln, das die Anforderungen des & 2 & 2 \\ Soll-Status widerspiegelt & & \\ \hline 2.2 Schnittstellen definieren & & \\ \hline 2.2.1 Benutzer/Webinterface ("`Layoutdefinition"') & 1 & 2 \\ \hline 2.2.2 Webinterface/Datenbank & 2 & 2 \\ \hline 2.2.3 Datenbank/Quelldaten & 1 & 2 \\ \hline 2.2.4 Echtsystem/Testsystem & 2 & 2 \\ \hline 2.3 Pflichtenheft erstellen & 2 & 2 \\ \hline \hline \textbf{3 Realisierungs- und Testphase [+2h (gesamt: -1h)]} & \textbf{28} & \textbf{25} \\ \hline \hline \multicolumn{3}{||l||}{3.1 Erstellen des Webinterfaces} \\ \hline \hline 3.1.1 Erstellen der Zugriffseinstellungen & 1 & 1 \\ \hline 3.1.2 Erstellen des Hauptprogrammes inklusive & 3 & 2 \\ Datenbankverbindung & & \\ \hline 3.1.3 Erstellen der Loginprozedur & 2 & 2 \\ \hline 3.1.4 Erstellen der Suchprozedur & 3 & 4 \\ \hline 3.1.5 Erstellen der Anzeigeprozedur & 5 & 2 \\ \hline 3.2 Testen des Webinterface in der Testumgebung & 5 & 5 \\ \hline 3.3 Erstellen des Konverters & 3 & 4 \\ (CSV Datenbank in MySQL-Format) & & \\ \hline 3.4 Testen des Konverters in der Testumgebung & 2 & 2 \\ \hline 3.5 Fremdtest des Webinterface in der Testumgebung & 1 & 1 \\ \hline 3.6 Fremdtest des Konverters in der Testumgebung & 1 & 1 \\ \hline 3.7 Transfer des Testsystems in das Echtsystem des Providers & 2 & 2 \\ \hline \hline \textbf{4 Dokumentationsphase [-5h (gesamt: -6h)]} & \textbf{12} & \textbf{17} \\ \hline \hline 4.1 Erstellen der Programmdokumentation & 7 & 7 \\ \hline 4.2 Erstellen des Benutzerhandbuches & 5 & 10 \\ \hline \hline \textbf{5 Abschlussphase [(gesamt: -6h)]} & \textbf{1} & \textbf{1} \\ \hline \hline 5.1 Vorstellung des Systems in der Au\ss{}endienstleitung & 1 & 1 \\ \hline \hline \textbf{6. Pufferzeit f\"ur nicht vorhersehbare Ereignisse} & \textbf{9} & \textbf{3} \\ \hline \hline \hline \textbf{Gesamtzeit} & \textbf{70} & \textbf{70} \\ \hline \hline \end{longtable} Einige Differenzen erl\"autere ich detaillierter, da sie wichtige Aspekte der Projektentwicklung widerspiegeln.\\ Vorarbeiten: \begin{itemize} \item Die Besprechung mit dem Au\ss{}endienst verk\"urzte sich, da diesem die Problematik des alten Systems schon bekannt war \end{itemize} Realisierungs- und Testphase: \begin{itemize} \item Die Anbindung an die Datenbank verlief nicht so problemlos wie erwartet, da w\"ahrend der Entwicklung der Datenbankserver zwei kurze Ausf\"alle hatte. Dies f\"uhrte zu unvermuteten Ergebnissen im Hauptprogramm. \item Die Gestaltung der Anzeigeprozedur erwies sich als wesentlich komplexer als anfangs angenommen, da das Aussehen so wenig wie m\"oglich vom alten System abweichen sollte. \end{itemize} Dokumentationsphase: \begin{itemize} \item Die Benutzerhandb\"ucher wurden als einfache Pr\"asentation realisiert und bedurften kein spezielles Layout oder des Drucks. \end{itemize} Pufferzeit f\"ur nicht vorhersehbare Ereignisse: \begin{itemize} \item Im Zeitplan wurde der Punkt "`Erstellung der Projektdokumentation"' vergessen und musste deswegen hier einsortiert werden. \end{itemize} \subsection{Ausblick} \subsubsection{Erweiterung der bestehenden Programme} Zus\"atzlich zu den bestehenden Anzeigen k\"onnten noch Auswertungen programmiert werden, die sowohl schriftlich als auch graphisch pr\"asentiert werden. Des weiteren k\"onnte die Tabellenspezifikation aus init-database.php ausgelagert und selbiges Skript um einen Parser erweitert werden. \subsubsection{Hinzuf\"ugen weiterer Programme} Die Verwaltung der Benutzer k\"onnte durch ein Skript vereinfacht werden, welches das Hinzuf\"ugen, L\"oschen oder Modifizieren der MySQL Datenbank und der htpasswd Datei \"ubernimmt. Die Aktualisierung k\"onnte automatisiert werden, wenn von ProAlpha aus regelm\"assig (z.B. tagesweise) die aktuellen Ums\"atze an ein Programm \"ubermittelt werden, welches die Daten via HTTPS an das Upload Skript \"ubertr\"agt. \subsection{Kostenrechnung} \subsubsection{altes System} Die Kosten vorher setzten sich wie folgt zusammen: 14 Au\ss{}endienstler holen 15 MiB gro\ss{}e Dateien zweimal im Monat ab. Die Internetverbindung erlaubt eine Transferrate von ca. 4 KiB pro Sekunde.\\ \textbf{Rechnung:} $$\mbox{Transferrate} = \frac{4\mbox{KiB}}{\mbox{s}}$$\\ $$15 \mbox{MiB} * 14 \mbox{ADM} = 210 \mbox{MiB} = 210 * \mbox{1024 KiB} = 215040 \mbox{KiB}$$\\ $$\frac{215040 \mbox{ KiB}}{\frac{4\mbox{ KiB}}{\mbox{s}}} = 53760\mbox{ Sekunden} = 896\mbox{ Minuten}$$\\ $$1 \mbox{ Minute } \hat{=} 0,39 \mbox{Euro}$$\\ $$896\mbox{ Minuten } * \frac{0,39 \mbox{ Euro}}{\mbox{Minute}} = 349,44 \mbox{ Euro pro Update}$$\\ 2 $*$ Monatlich wird das Update gesendet.\\ $$349,44\mbox{ Euro } * 2 = 698,88\mbox{ Euro pro Monat}$$ Dies ergibt eine Online-Nutzdauer von 53.760 Sekunden, was 896 Minuten entspricht. Bei einem Minutenpreis von 39 Cent pro Minute enstehen \textbf{698,88 Euro} monatliche Kosten. \subsubsection{neues System} Die momentane Kosten setzen sich zusammen aus den Kosten f\"ur das Hosting des Webinterfaces und den Onlinekosten. Der Provider bietet einen Webauftritt, der \"uber eine HTTPS Verbindung abgesichert ist und eine Unterst\"utzung f\"ur PHP und MySQL enth\"alt, f\"ur 50 Euro pro Monat an. \\ \textbf{Rechnung:}\\ $$\mbox{MySQL Datenbank + PHP + Webspace } = \mbox{"`Webauftritt"' }= 50\mbox{ Euro pro Monat}$$ $$\mbox{Nutzh\"aufigkeit} = \frac{\mbox{2mal}}{\mbox{Tag}}\mbox{ (benutzt der ADM das Webinterface)}$$\\ Dies macht er an 20 Tagen (Arbeitstage) im Monat. $$\frac{\mbox{zweimal}}{\mbox{Tag}} * \frac{\mbox{Arbeitstage}}{\mbox{Monat}} = 40 \mbox{mal Nachsehen pro Monat (pro ADM)}$$\\ $$14 \mbox{ ADM} * 40\mbox{mal} \mbox{ Nachsehen} = 560\mbox{mal} \frac{\mbox{Nachsehen}}{\mbox{Monat}}$$\\ Einmal Nachsehen entspricht statistisch nach einer Umfrage einer Minute (da nicht jeder das Angebot nutzen w\"urde und manche l\"anger brauchen).\\ $$560 \mbox{Minuten} * \frac{0,39\mbox{ Euro}}{\mbox{Minute}} = 218.40 \mbox{Euro}$$\\ $$218,40 + 50,00 = 268,40 \frac{\mbox{Euro}}{\mbox{Monat}}$$ Damit ergeben sich kumuliert Kosten von \textbf{268,40 Euro} pro Monat.\\ In dieser Rechnung sind jedoch nicht die Entwicklungskosten f\"ur das neue System enthalten. Diese setzen sich zusammen aus dem Stundenlohn und der Anzahl der ben\"otigten Stunden.\\ \textbf{Rechnung:} $$70\mbox{ Stunden} * 5\mbox{ Euro} = 350\mbox{ Euro}$$ Dieser Betrag ist nur einmal f\"allig und amortisiert sich im Normalfall \"uber die Jahre. In unserem speziellen Fall sind die Entwicklungskosten sogar schon nach einem Monat kompensiert.\\ \subsubsection{Kostenvergleich} Das neue System erbringt eine Kostenvorteil von \textbf{430,48 Euro} beziehungsweise verbraucht nur \textbf{38\%} der alten Kosten pro Monat. \section{Anhang} \subsection{Literatur} \subsubsection{PHP} \begin{itemize} \item "`PHP kurz \& gut"', O'Reilly, ISBN 3-89721-225-0 \item "`Programmieren lernen in PHP4"', Hanser, ISBN 3-446-21754-1 \item http://www.php.net \end{itemize} \subsubsection{Apache} \begin{itemize} \item http://httpd.apache.org/ \end{itemize} \subsubsection{HTTP} \begin{itemize} \item HTTP Pocket Reference, O'Reilly, ISBN 1-56592-862-8 \end{itemize} \subsubsection{MySQL} \begin{itemize} \item Managing and Using MySQL, 2nd Edition, O'Reilly, ISBN 0-596-00211-4 \end{itemize} \subsection{Schnittstellen} \subsubsection{Benutzer/Weboberfl\"ache: Layoutdefiniton} \begin{verbatim} -------------------------------------------------------------------------------- Nico Schottelius, v0.6 Schnittstelle: Webinterface/Layout -------------------------------------------------------------------------------- 1. Generelle Layoutdefiniton 2. Benutzerinteraktion 3. Authentifizierung 4. Sicherheit 5. Einstiegsbildschirm 6. Suchergebnisse 7. Suchdetails 1. Generelle Layoutdefiniton Die Weboberfläche sollte von der Farbwahl der Firmenhomepage [WL1] ähneln. Zudem sollte ein firmentypisches Objekt (Logo, Motto, etc.) präsent sein. 2. Benutzerinteraktion Der Benutzer soll die Oberfläche intuitiv bedienen können. Fachwörter und technische Details sind zu verbergen, Fehlermeldungen wenn möglich durch einfache Hinweise zu ersetzen. 3. Authentifizierung Der Webserver (hier: Apache) sendet nach dem GET Aufruf des Clients einen 401 (Unauthorized) Code und den WWW-Authenticate Header an den Browser. [WL2] Dieser Header kann zusätzlich noch einen Text enthalten, wie z.B. "Nur für Außendienstler erlaubt". Die Authentifizierung wird dann in einem vom Browser selbstdefinierten Authentifizierungsfenster durchgeführt, das den zusätzlichen Text des Serverheaders beinhalten kann, jedoch nicht muss. Das bedeutet das man Serverseitig nur definieren kann, das sich der Client authentifizieren muss, jedoch nicht wie dieses Fenster aussieht (im Gegensatz zu Javascript basierten Authentifizierungen, die jedoch keine echte Sicherheit bieten). Die Authentifizierung wird Serverseitig durch die htaccess Methodik [WL3] definiert. Diese benötigt die htaccess Einstellungen selbst [WL4] und zur Benutzerverwaltung die htpasswd [WL5]. Des weiteren müssen die in der htpasswd vorhandenen Benutzer noch in der MySOL Benutzerverwaltung angelegt werden. Diese erhalten dort ein leeres Passwort vergeben, da die htaccess Authentifizierung ausreichend ist. 4. Sicherheit Folgende Sicherheitsanforderungen sind vorhanden: - Datenintegrität: die Daten müssen gewährleistet unveränderbar sein - Vertraulichkeit: niemand darf unauthorisiertes Daten lesen können - Verfügbarkeit: die Datenverbindung muss ständig verfügbar sein Die ersten beiden Anforderungen werden durch das TLS Protokoll [WL5] erfüllt. Die Verfügbarkeit des Webinterface im Internet ist abhängig von der Verfügbarkeit des Web- und MySQL Servers. 5. Einstiegsbildschirm Die Einstiegsseite zeigt den Namen des aktuell angemeldeten Benutzers an. Des weiteren befindet sich ein Auswahlfeld, das die Suchoptionen enthält, und des Suchbegrifffeld auf dieser Seite. Dem Benutzer muss eine angemessen Suchfunktion zur Verfügung stehen, die das Suchen nach den erforderlichen Parametern erlaubt. Als Suchoptionen müssen die folgenden vorhanden sein: - Kundennummer - Kundenname - Praxennummer - Telefon - PLZ - Ort 6. Suchergebnisse Auf der Seite der Ergebnisse sollen der Suchbegriff und das Suchkriterium als Überschrift dargestellt werden. Die Suchergebnisse werden tabellarisch dargestellt, die Ergebnisse sind geordnet nach Kundennummern, aufsteigend. Des weiteren muss die Möglichkeit bestehen die Ergebnisse nach den anderen angezeigten Feldern zu sortieren. 7. Suchdetails Diese Seite soll sich an dem Beispiel Excel Dokument [WL6] orientieren. Sie muss Details über den Kunden enhalten, u.a. die Anschrift und soweit vorhanden die Telefonnummer. [WL1]: http://www.wdt.de [WL2]: RFC 2616 [WL3]: http://httpd.apache.org/docs-2.0/howto/auth.html [WL4]: Beispiel: siehe Anhang dot-htaccess [WL5]: RFC 2246, RFC 3546, Beispiel: htpasswd [WL6]: Internes Dokument: Beispiel_Excel.xls \end{verbatim} \subsubsection{Weboberfl\"ache/Datenbank: Zugriffsdefiniton} \begin{verbatim} -------------------------------------------------------------------------------- Nico Schottelius, v0.2 Schnittstelle: Webinterface/Datenbank -------------------------------------------------------------------------------- Tabellen und Felder in der Datenbank ------------------------------------ Die Typen der Felder und Tabellen sind in [WED1] dokumentiert. Die Namen der Felder und Tabellen sind in [WED2] definiert. Der Datenbankname und Datenbankserver sind frei wählbar, muessen jedoch in der Konfigurationsdatei [WED3] angegeben werden. Quellen: [WED1]: Siehe Anhang: 2.2.3 Schnittstelle Datenbank/Quelldaten [WED2]: Siehe Anhang: Quellcode zu modules/init-db-create.php, Funktion create_db, array "$creat_query" [WED3]: Siehe Anhang: Quellcode zu includes/db-settings.php \end{verbatim} \subsubsection{Datenbank/Quelldaten: Importformatdefiniton} \begin{verbatim} -------------------------------------------------------------------------------- Nico Schottelius, v0.6 Schnittstelle: Quelldaten/Datenbank -------------------------------------------------------------------------------- 1. Quelldaten 2. Zielform 3. Ziellayout / Typdefinition 4. Verhalten beim Import 1. Quelldaten Die Quelldaten liegen in der Progressdatenbank [QDD1]. Sie müssen mithilfe von Progress oder Alternativ ProAlpha [QDD2] ausgelesen werden. 2. Zielform Es sollen CSV Tabellen entsprechend der Typdefinition erstellt werden, wobei pro Tabelle eine Datei erstellt wird. Der Trenner soll jedoch kein Komma sein, sondern ein Semikolon. Tabellenspalten sind getrennt durch Semikolen, Tabellenzeilen getrennt durch Zeilenumbruch, kein Header (wie z.B. ADM Name; ADM Nr) ist vorhanden, Reihenfolge wie unten in der Typdefinition. 3. Ziellayout / Typdefinition Feldnamen(*) genutzte Feldergroessen * = Bemerkung vorhanden 0. ADM-Tabelle ADM-Nr.* [int] ADM-Name. [text 255 stellig] ADM-Nr. ist >=0 und <=999. Nummern >=1000 und <=9999 werden fuer adminstrative Zwecke genutzt. 1. Kundentabelle KundenNr.* [int] Prax.Nr.* [int] ADM-Nr. [int] [wie oben] Name [text 255 stellig] Strasse [text 255 stellig] Strassennummer [text 255 stellig] PLZ [text 255 stellig] Ort [text 255 stellig] Telefon [text 255 stellig] KundenNr sind 6 stellig PraxNr sind 6 stellig 2. Umsatztabelle KundenNr. [int] [wie oben] ArtNr. [int] [wie unten] Umsatz [float] Menge [int] Tag [int] Monat [int] Jahr* [int] Jahr ist vierstellig 3. Artikeltabelle ArtNr. [int] ArtName [text 255 stellig] GruppenName [text 255 stellig] 4. Verhalten beim Import Wenn ein Datensatz mit identischen Primary Key vorhanden ist, so werden die alten Daten in der Zieldatenbank mit den neuen Werten überschrieben. Quellen: [QDD1]: http://www.progress.de/, http://www.progress.com/ [QDD2]: http://www.proalpha.de/ \end{verbatim} \subsubsection{Test-/Echtsystem} \begin{verbatim} -------------------------------------------------------------------------------- Nico Schottelius, v0.2 Schnittstelle: Test-/Echtsystem -------------------------------------------------------------------------------- 1 generelle Anpassungen 2 Probleme mit anderen PHP oder Apache Versionen oder Konfigurationen 1 generelle Anpassungen Der Datenbankname und Datenbankserver müssen in der Konfiguration "includes/db-settings.php" angepasst werden. 2 Probleme mit anderen PHP oder Apache Versionen oder Konfigurationen Der Apache 2.0 bietet bei PHP keine globalen Variablen. Der Aufruf von "/script.php?option=test" setzt nur im Apache 1.3.x die Variable "option". Im Apache 1.3.x und im Apache 2.0 kann man jedoch über den Array _REQUEST und dem index des Variablennamens (z.B. $_REQUEST['option']) den Inhalt auslesen. Für das Verzeichnis in dem die Skripte liegen muss in der Apache Config "AllowOverride Auth" angeschaltet sein, damit die .htaccess Datei beachtet wird. In der Standard Installation von PHP dürfen Dateien beim Upload nicht größer sein als 2MiB. Somit muss unter Umständen die Tabellengröße auf 2MiB limitiert werden. Dies ist jedoch unproblematisch, da die Aktualisierung der Datenbank inkrementell geschehen kann. Somit ist die Möglichkeit gegeben z.B. monatlich, wöchentlich, oder sogar täglich eine Aktualisierung vorzunehmen. Je kleiner die Abstände sind, desto kleiner sind logischerweise auch die Dateien. Sollte eine Tabelle größer werden als 2MiB, so kann man sie auch teilen und in mehreren Schritten hochladen. Das Limit muss mit dem Provider abgeklärt werden. \end{verbatim} \subsection{Quelltext} \subsubsection{includes/search.php} \begin{verbatim} "kunden", "Kundenname" => "kunden", "Praxennummer" => "kunden", "Telefon" => "kunden", "PLZ" => "kunden", "Ort" => "kunden" ); /* felder in den tabellen */ $suchbegriff_felder = array( "Kundennummer" => "nr", "Praxennummer" => "praxisnr", "Kundenname" => "name", "Telefon" => "telefon", "PLZ" => "plz", "Ort" => "ort", ); /* felder in den tabellen */ $suchbegriff_map_field_eqto_kdnr = array( "Kundennummer" => "nr", "Praxennummer" => "praxisnr", "Kundenname" => "name", "Telefon" => "telefon", "PLZ" => "plz", "Ort" => "ort", ); /* Ausgabe: wie in excel PraxenNummer Kundennummer Kundenname PLZ Telefon Ort WDT-Laufendes Jahr Differenz Vorjahr PRX-Laufendes Jahr Differenz Vorjahr GRO-Laufendes Jahr Differenz Vorjahr */ ?> \end{verbatim} \subsubsection{includes/db-settings.php} \begin{verbatim} \end{verbatim} \subsubsection{init-database.php} \begin{verbatim} Initialisieren der Datenbank

WDT

'; $FOOTER='

'; /* output header */ echo $HEADER; /* select what todo */ switch($_REQUEST["option"]) { case 1: /* create it */ include "modules/init-db-create.php"; create_db($_REQUEST['dbserver'], $_REQUEST['database'], $_REQUEST['dbuser'], $_REQUEST['dbpass']); break; default: /* login */ include "modules/init-db-login.php"; login($dbserver,$database,$_SERVER["PHP_SELF"]); break; } echo $FOOTER; ?> \end{verbatim} \subsubsection{modules/init-db-login.php} \begin{verbatim} Erzeugen der initialen Datenbank\n"; /* the upload form */ echo "
\n"; $dbserver_name="Datenbankserver"; $dbuser_name="Benutzer"; $dbpass_name="Passwort"; $database_name="Name der Datenbank"; /* fields */ echo "\n"; echo "" . '\n"; echo "" . '\n"; echo "" . '\n"; echo "" . '\n"; echo "
$dbserver_name:' . "
$dbuser_name:' . "
$dbpass_name:' . "
$database_name:' . "
\n"; /* hidden */ echo '' . "\n"; echo '' . "\n"; echo '
' . "\n"; } ?> \end{verbatim} \subsubsection{modules/init-db-create.php} \begin{verbatim} Initialisere die Datenbank '$database' auf $server als $user"; /* open connection */ $conn = mysql_connect($server,$user,$pass) or die("

Verbindung zur Datenbank fehlgeschlagen: ". mysql_error() ."

"); /* choose database */ /* initial query */ $query= "CREATE DATABASE IF NOT EXISTS $database;"; $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); echo "

Datenbank ist angelegt.

"; /* select the new db */ $db_check = mysql_select_db($database,$conn) or die("

Selektion der Datenbank fehlgeschlagen: ". mysql_error() ."

"); /* create tables */ $create_query = array( "CREATE TABLE IF NOT EXISTS adm ( nr INT PRIMARY KEY, name TINYTEXT NOT NULL );", "CREATE TABLE IF NOT EXISTS kunden ( nr INT PRIMARY KEY, praxisnr INT NOT NULL, admnr INT NOT NULL, name TINYTEXT NOT NULL, strasse TINYTEXT NOT NULL, strnr TINYTEXT NOT NULL, plz TINYTEXT NOT NULL, ort TINYTEXT NOT NULL, telefon TINYTEXT NOT NULL );", "CREATE TABLE IF NOT EXISTS umsatz ( kundennr INT NOT NULL, artikelnr INT NOT NULL, umsatz FLOAT, menge INT, tag INT, monat INT, jahr INT );", "CREATE TABLE IF NOT EXISTS artikel ( nr INT PRIMARY KEY, name TINYTEXT NOT NULL, gruppe TINYTEXT NOT NULL );" ); foreach($create_query as $query) { echo "

SQL-Kommando: $query ...\n"; $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); echo "done

"; } echo "

Datenbank und die Tabellen sind angelegt.

"; } ?> \end{verbatim} \subsubsection{search.php} \begin{verbatim} WDT Datenbankzugriff

WDT

'; $FOOTER='

'; /* database settings */ $dbuser = $_SERVER["REMOTE_USER"]; $dbpass = ""; include "includes/db-settings.php"; /* output header */ echo $HEADER; /* select option */ switch($_REQUEST['option']) { case 1: /* display list of results */ include "modules/search_results.php"; search_results($dbserver,$database,$dbuser,$dbpass,$_REQUEST['finde'],$_REQUEST['suchoption'],$_REQUEST['sortby']); break; case 2: /* display details */ include "modules/search_details.php"; search_details($dbserver,$database,$dbuser,$dbpass,$_REQUEST['kdnr']); break; default: /* login */ include "modules/search_login.php"; search_login($dbserver,$database,$dbuser,$dbpass,$PHP_SELF); break; } echo $FOOTER; ?> \end{verbatim} \subsubsection{modules/search\_results.php} \begin{verbatim} Verbindung zur Datenbank fehlgeschlagen: ". mysql_error() .""); /* choose database */ $db_check = mysql_select_db($database,$conn) or die("

Selektion der Datenbank fehlgeschlagen: ". mysql_error() ."

"); /* display name of ADM */ echo "

Suchergebnisse: (gesucht in \"$kriterium\", nach \"$begriff\")

\n"; /* do the query */ $table = $suchbegriff_tabelle[$kriterium]; $field = $suchbegriff_felder[$kriterium]; /* really sort by */ $rsortby = $suchbegriff_felder[$sortby]; /*****************************************************/ /* table begin */ /*****************************************************/ echo "\n"; /* table headers */ $query="DESCRIBE $table;"; $resultcode = mysql_query($query,$conn) or die("

Query1 fehlgeschlagen: ". mysql_error() ."

"); /* display header */ echo "\n"; /* create links to sort */ while ($row = mysql_fetch_array($resultcode) ) { echo "\n"; } echo "\n"; /* construct the query */ $query_begin="SELECT * FROM `$table` "; $query_end=" admnr = '$user' ORDER BY "; $query = $query_begin; /* sort */ if($sortby != "") $query_end .= " '$sortby'; "; else $query_end .= " 'nr'; "; /* search string */ if($begriff == "") $query .= " WHERE "; else $query .= " WHERE $field LIKE " . "'$begriff" . "%'" . " AND "; /* complete the search */ $query .= $query_end; $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); /* count if we got results */ $count=0; /* get number of fields */ $size=mysql_num_fields ($resultcode); /* print all entries */ while ($row = mysql_fetch_array($resultcode) ) { $count++; echo "\n"; for($i=0;$i<$size;$i++) { if($i == 0) { $TD="\n"; } else { $TD="\n"; } echo "$TD"; } echo "\n"; } echo "
$row[0]
$row[$i]$row[$i]
\n"; /* sanity check */ if(!$count) { echo "

Keine Ergebnisse gefunden.

\n"; } } ?> \end{verbatim} \subsubsection{modules/seach\_details.php} \begin{verbatim} Verbindung zur Datenbank fehlgeschlagen: ". mysql_error() .""); /* choose database */ $db_check = mysql_select_db($database,$conn) or die("

Selektion der Datenbank fehlgeschlagen: ". mysql_error() ."

"); echo "

Details für Kunde $nr

\n"; /******************************************************/ /* table begin / headers */ /******************************************************/ $TABLE_BEGIN="\n"; $TABLE_END="
\n"; echo $TABLE_BEGIN; $query= "SELECT praxisnr , name , strasse , strnr ," . "plz , ort , telefon FROM kunden WHERE nr = $nr " . "AND admnr = $user;"; $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); $row = mysql_fetch_array($resultcode); $size = mysql_num_fields ($resultcode); echo "".$row['name'].""; echo "".$row['strasse']. " " . $row['strnr'] . ""; echo "".$row['plz']. " " . $row['ort'] . ""; echo "".$row['telefon'].""; echo "".$nr .""; echo $TABLE_END; /****************************************************************/ /* last 3 months FIXME */ /****************************************************************/ echo $TABLE_BEGIN; $last_month = $this_month-1; $before_last_month = $last_month-1; /* get data like in .xls */ $query= "SELECT umsatz FROM umsatz WHERE " . "kundennr = $nr " . /* Kunde */ "AND jahr = $this_year " . /* Jahr */ "AND monat = ($this_month OR $last_month OR $before_last_month);" ; /* Monat */ $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); $row = mysql_fetch_array($resultcode); $size = mysql_num_fields ($resultcode); echo "A: $row[0]"; echo "B: $query"; echo "C: $row[1] :: $row[2]"; echo $TABLE_END; /****************************************************************/ /* kummultative results */ /****************************************************************/ echo $TABLE_BEGIN; /* get data like in .xls */ $query= "SELECT praxisnr , name , strasse , strnr ," . "plz , ort , telefon FROM kunden WHERE nr = $nr " . "AND admnr = $user;"; echo $TABLE_END; /****************************************************************/ /* display all articles (details) */ /****************************************************************/ echo $TABLE_BEGIN; /* get all articles from this customer */ /* $query= "SELECT artikelnr FROM umsatz WHERE kundennr = $nr " . "GROUP BY artikelnr;"; */ $query= "SELECT umsatz.artikelnr FROM umsatz,kunden WHERE " . "umsatz.kundennr = $nr AND umsatz.kundennr = kunden.nr " . "AND kunden.admnr = $user " . "GROUP BY artikelnr;"; $rs_all_artikel = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); /* display table header */ echo " \n"; echo "\n"; echo "\tArtikelnummer\n"; echo "\tUmsatz $oldest_year\n"; echo "\tMenge $oldest_year\n"; echo "\tUmsatz $last_year\n"; echo "\tMenge $last_year\n"; echo "\tUmsatz $this_year\n"; echo "\tMenge $this_year\n"; echo "\n"; /* count columns */ $count=0; /* go through all articles */ while ($row = mysql_fetch_array($rs_all_artikel) ) { $count++; $artikelnr="$row[0]"; echo "\n"; echo "$artikelnr\n"; /* go through all years */ foreach($all_years as $work_year) { $query = "SELECT SUM(umsatz.umsatz) , SUM(umsatz.menge) " . "FROM umsatz WHERE umsatz.kundennr = $nr AND " . "umsatz.artikelnr = $artikelnr AND umsatz.jahr = $work_year;"; $rs_cur_artikel = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); $cur_umsatz_menge = mysql_fetch_array($rs_cur_artikel); $cur_umsatz=$cur_umsatz_menge[0]; $cur_menge=$cur_umsatz_menge[1]; /* select 0 if nothing was found */ if($cur_umsatz == "") { echo "\t0\n"; } else { echo "\t" . $cur_umsatz . "\n"; } /* same here */ if($cur_menge == "") { echo "\t0\n"; } else { echo "\t" . $cur_menge . "\n"; } } echo "\n"; } echo $TABLE_END; /* sanity check if there were no articles bought*/ if(!$count) { echo "

Keine Ergebnisse gefunden.

\n"; } } ?> \end{verbatim} \subsubsection{upload.php} \label{upload.php} \begin{verbatim} Update der Datenbank

WDT

'; $FOOTER='

'; /* DB settings */ include "includes/db-settings.php"; /* database settings */ $dbuser = $_SERVER["REMOTE_USER"]; $dbpass = ""; /* output header */ echo $HEADER; /* select option */ switch($_REQUEST["option"]) { case 1: /* do the update */ $filenames=array( 'adm' => $_FILES['tabelle_adm'], 'kunden' => $_FILES['tabelle_kunden'], 'umsatz' => $_FILES['tabelle_umsatz'], 'artikel' => $_FILES['tabelle_artikel'] ); include "modules/upload-it.php"; update_it($dbserver,$database,$dbuser,$dbpass,$filenames); break; default: /* login */ include "modules/upload-login.php"; login($dbserver,$database,$dbuser,$dbpass,$PHP_SELF); break; } echo $FOOTER; ?> \end{verbatim} \subsubsection{modules/upload-login.php} \begin{verbatim} Update der Datenbank...\n"; /* the upload form */ echo "
\n"; /* fields */ echo "\n"; echo "" . '\n"; echo "" . '\n"; echo "" . '\n"; echo "" . '\n"; echo "
$adm_tab:' . "
$kunden_tab:' . "
$umsatz_tab:' . "
$artikel_tab:' . "
\n"; /* hidden */ echo '\n"; echo '' . "\n"; echo '' . "\n"; echo '' . "\n"; echo '
' . "\n"; } ?> \end{verbatim} \subsubsection{modules/upload-it.php} \begin{verbatim} Verbindung zur Datenbank fehlgeschlagen: ". mysql_error() .""); /* choose database */ $db_check = mysql_select_db($database,$conn) or die("

Selektion der Datenbank fehlgeschlagen: ". mysql_error() ."

"); $i=0; foreach($filenames as $tablename => $tableinfo) { /* debug */ // echo "

table: $tablename\n"; // echo "
tableinfo: $tableinfo

\n"; $wert = $tableinfo["tmp_name"]; $filename = $tableinfo["name"]; /* check if this table should get updated */ if($wert != "none" and $wert != "") { echo "

Update die Tabelle '$tablename' " . "aus Datei $filename ...\n"; /* reset query */ $query = ""; /* basic query array */ $sql = array ('LOAD DATA LOCAL INFILE', " '$wert'", ' REPLACE INTO TABLE ', " `$tablename` ", 'FIELDS TERMINATED BY \';\' ENCLOSED BY \'"\' ESCAPED BY \'\\\\\' LINES TERMINATED BY \'\\n\';'); /* construct query */ foreach($sql as $partofquery) { $query .= $partofquery; } /* query() */ echo "

$query

"; $resultcode = mysql_query($query,$conn) or die("

Query fehlgeschlagen: ". mysql_error() ."

"); echo "done
\n"; $i++; } } /* result display */ echo "

$i Tabelle(n) verarbeitet.

\n"; } ?> \end{verbatim} \subsection{Testtabellen} Diese Tabellen wurden zum Testen benutzt, w\"ahrend noch keine Echtdaten verf\"ugbar waren. Sie werden von upload.php verarbeitet (siehe Seite \pageref{upload.php}). \subsubsection{Aussendienstmitarbeiter ("`adm.test"')} \begin{verbatim} 120; Heinz Martin 140; Mutter Albert 150; Kruenling 1000; Der Admin \end{verbatim} \subsubsection{Artikel ("`artikel.test"')} \begin{verbatim} 12345; Elefantenpritze; wdt 12346; Elefantenbesteck; wdt 12347; Kaenguruhnapf; prx \end{verbatim} \subsubsection{Kunden ("`kunden.test"')} \begin{verbatim} 019066; 201749; 120;prakt. Tierärztin aus Test;Testwegrein;23;22527;Hamburg;01234/4555 010646; 201749; 120;prakt. Tieraerztin2 aus Test3;Testwegrein;23;22527;Hamburg;01234/4555 019067; 019066; 140; Tierliebhaber Burgberg; veilenweg; 42; 30456; Hannover; 00123/123 019068; 019069; 150; Tierer Bargberg;blumenstarre;235;33334; Testhause; 0042455/5533 \end{verbatim} \subsubsection{Umsatz ("`umsatz.test"')} \begin{verbatim} 019066;12345;120,00;12; 01; 07; 2004 019066;12346;120,00;12; 02; 06; 2004 019066;12347;120,00;12; 03; 05; 2004 019066;12347;150,00;12; 19; 04; 2003 019066;12347;140,00;12; 19; 03; 2003 019066;12346;110,00;12; 19; 02; 2002 019066;12345;100,00;12; 10; 01; 2002 019066;12346;150,00;12; 12; 01; 2004 019067;12347;160,00;12; 12; 01; 2004 019068;12346;160,00;12; 22; 01; 2004 \end{verbatim} \subsection{Konfigurationsdateien} \subsubsection{Apache: .htaccess (oder "`dot-htaccess"')} .htaccess ist die Standardmethode f\"ur Authentfizierung beim Apache Webserver. In der gleichnamigen Datei wird definiert, um was f\"ur eine Art Authentifizierung es sich handelt ("`AuthType"'), welcher Text optional angezeigt wird beim Darstellen der Passwortbox ("`AuthName"'), woraus die Authentifizierungsinformationen gelesen werden ("`AuthUserFile"') und welche Bedingung erf\"ullt sein muss ("`Require"'). \begin{verbatim} AuthType Basic AuthName "Willkommen zum WDT Aussendienstler Programm" AuthUserFile /home/user/nico/www/projekt/source/testpasswd Require valid-user \end{verbatim} \subsubsection{Apache: htpasswd} In der htpasswd werden die Passw\"orter f\"ur die Authentifizierung \"uber das Webinterface definiert. \begin{verbatim} 120:8ZnzPTAQ5IJkM 9999:$apr1$/9xFh...$/Qy28iJzLaPWfhFWwsy1C/ 1000:$apr1$shOFt...$xoRiz8F4lClo2hv/MGi7n. \end{verbatim} \subsubsection{PHP: Auszug php.ini} Im Gegensatz zur normalen Konfiguration ist hier die maximale Dateigr\"o\ss{}e 120MB erh\"oht. Es ist der \textit{Manpage} nicht zu entnehmen, ob MB hier MiB entspricht oder der Faktor 1000 als Basis genommen wurde. \begin{verbatim} ;;;;;;;;;;;;;;;;;;; ; Resource Limits ; ;;;;;;;;;;;;;;;;;;; max_execution_time = 30 ; Maximum execution time of each script, in seconds max_input_time = 60 ; Maximum amount of time each script may spend parsing request data memory_limit = 128M ; Maximum amount of memory a script may consume (8MB) ;;memory_limit = 8M ; Maximum amount of memory a script may consume (8MB) ; Maximum size of POST data that PHP will accept. ;;post_max_size = 8M post_max_size = 120M ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; ; Whether to allow HTTP file uploads. file_uploads = On ; Temporary directory for HTTP uploaded files (will use system default if not ; specified). ;upload_tmp_dir = ; Maximum allowed size for uploaded files. ;;upload_max_filesize = 2M upload_max_filesize = 120M \end{verbatim} \end{document}