+ <programlisting><<<<<varname1 varname2 varname3>>>>>>>>>>>>>>>>>>>>>>>>>></programlisting>
+
+ <para>Die Variablen werden interpoliert, und linksbündig mit
+ Leerzeichen auf die gewünschte Länge aufgefüllt. Ist der String zu
+ lang, werden überzählige Zeichen abgeschnitten.</para>
+
+ <para>Es ist ausserdem möglich, Daten rechtsbündig darzustellen, wenn
+ der Block mit einem Leerzeichen anfängt. Beispiel:</para>
+
+ <programlisting><<<<<< varname>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>></programlisting>
+
+ <para>Dies würde rechtsbündig triggern. Wenn bei rechtsbündiger
+ Ausrichtung Text abgeschnitten werden muss, wird er vom linken Ende
+ entfernt.</para>
+ </sect2>
+
+ <sect2 id="excel-templates.limitations">
+ <title>Einschränkungen</title>
+
+ <para>Das Excelformat bis 2002 ist ein binäres Format, und kann nicht
+ mit vertretbarem Aufwand editiert werden. Der Templatemechanismus
+ beschränkt sich daher darauf, Textstellen exakt durch einen anderen
+ Text zu ersetzen.</para>
+
+ <para>Aus dem gleichen Grund sind die Kontrolllstrukturen
+ <command><%if%></command> und
+ <command><%foreach%></command> nicht vorhanden. Der Delimiter
+ <constant><% %></constant> kommt in den Headerinformationen
+ evtl. vor. Deshalb wurde auf den sichereren Delimiter
+ <constant><<</constant> und <constant>>></constant>
+ gewechselt.</para>
+ </sect2>
+ </sect1>
+ </chapter>
+
+ <chapter>
+ <title>Entwicklerdokumentation</title>
+
+ <sect1 id="devel.globals" xreflabel="Globale Variablen">
+ <title>Globale Variablen</title>
+
+ <sect2>
+ <title>Wie sehen globale Variablen in Perl aus?</title>
+
+ <para>Globale Variablen liegen in einem speziellen namespace namens
+ "main", der von überall erreichbar ist. Darüber hinaus sind bareword
+ globs global und die meisten speziellen Variablen sind...
+ speziell.</para>
+
+ <para>Daraus ergeben sich folgende Formen:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>$main::form</literal></term>
+
+ <listitem>
+ <para>expliziter Namespace "main"</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>$::form</literal></term>
+
+ <listitem>
+ <para>impliziter Namespace "main"</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>open FILE, "file.txt"</literal></term>
+
+ <listitem>
+ <para><varname>FILE</varname> ist global</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>$_</literal></term>
+
+ <listitem>
+ <para>speziell</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>Im Gegensatz zu <productname>PHP</productname> gibt es kein
+ Schlüsselwort wie "<function>global</function>", mit dem man
+ importieren kann. <function>my</function>, <function>our</function>
+ und <function>local</function> machen was anderes.</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>my $form</literal></term>
+
+ <listitem>
+ <para>lexikalische Variable, gültig bis zum Ende des
+ Scopes</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>our $form</literal></term>
+
+ <listitem>
+ <para><varname>$form</varname> referenziert ab hier
+ <varname>$PACKAGE::form</varname>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>local $form</literal></term>
+
+ <listitem>
+ <para>Alle Änderungen an <varname>$form</varname> werden am Ende
+ des scopes zurückgesetzt</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2>
+ <title>Warum sind globale Variablen ein Problem?</title>
+
+ <para>Das erste Problem ist <productname>FCGI</productname>.</para>
+
+ <para><productname>SQL-Ledger</productname> hat fast alles im globalen
+ namespace abgelegt, und erwartet, dass es da auch wiederzufinden ist.
+ Unter <productname>FCGI</productname> müssen diese Sachen aber wieder
+ aufgeräumt werden, damit sie nicht in den nächsten Request kommen.
+ Einige Sachen wiederum sollen nicht gelöscht werden, wie zum Beispiel
+ Datenbankverbindungen, weil die sehr lange zum Initialisieren
+ brauchen.</para>
+
+ <para>Das zweite Problem ist <function>strict</function>. Unter
+ <function>strict</function> werden alle Variablen die nicht explizit
+ mit <function>Package</function>, <function>my</function> oder
+ <function>our</function> angegeben werden als Tippfehler angemarkert,
+ dies hat, seit der Einführung, u.a. schon so manche langwierige
+ Bug-Suche verkürzt. Da globale Variablen aber implizit mit Package
+ angegeben werden, werden die nicht geprüft, und somit kann sich
+ schnell ein Tippfehler einschleichen.</para>
+ </sect2>
+
+ <sect2>
+ <title>Kanonische globale Variablen</title>
+
+ <para>Um dieses Problem im Griff zu halten gibt es einige wenige
+ globale Variablen, die kanonisch sind, d.h. sie haben bestimmte
+ vorgegebenen Eigenschaften, und alles andere sollte anderweitig
+ umhergereicht werden.</para>
+
+ <para>Diese Variablen sind im Moment die folgenden neun:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><varname>$::form</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>%::myconfig</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::locale</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::lxdebug</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::auth</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::lx_office_conf</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::instance_conf</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::dispatcher</varname></para>
+ </listitem>
+
+ <listitem>
+ <para><varname>$::request</varname></para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Damit diese nicht erneut als Müllhalde missbraucht werden, im
+ Folgenden eine kurze Erläuterung der bestimmten vorgegebenen
+ Eigenschaften (Konventionen):</para>
+
+ <sect3>
+ <title>$::form</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Ist ein Objekt der Klasse
+ "<classname>Form</classname>"</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird nach jedem Request gelöscht</para>
+ </listitem>
+
+ <listitem>
+ <para>Muss auch in Tests und Konsolenscripts vorhanden
+ sein.</para>
+ </listitem>
+
+ <listitem>
+ <para>Enthält am Anfang eines Requests die Requestparameter vom
+ User</para>
+ </listitem>
+
+ <listitem>
+ <para>Kann zwar intern über Requestgrenzen ein Datenbankhandle
+ cachen, das wird aber momentan absichtlich zerstört</para>
+ </listitem>
+ </itemizedlist>
+
+ <para><varname>$::form</varname> wurde unter <productname>SQL
+ Ledger</productname> als Gottobjekt für alles misbraucht. Sämtliche
+ alten Funktionen unter SL/ mutieren <varname>$::form</varname>, das
+ heißt, alles was einem lieb ist (alle Variablen die einem ans Herz
+ gewachsen sind), sollte man vor einem Aufruf (!) von zum Beispiel
+ <function>IS->retrieve_customer()</function> in Sicherheit
+ bringen.</para>
+
+ <para>Z.B. das vom Benutzer eingestellte Zahlenformat, bevor man
+ Berechnung in einem bestimmten Format durchführt (SL/Form.pm Zeile
+ 3552, Stand version 2.7beta), um dies hinterher wieder auf den
+ richtigen Wert zu setzen:</para>
+
+ <programlisting> my $saved_numberformat = $::myconfig{numberformat};
+ $::myconfig{numberformat} = $numberformat;
+ # (...) div Berechnungen
+ $::myconfig{numberformat} = $saved_numberformat;</programlisting>
+
+ <para>Das Objekt der Klasse Form hat leider im Moment noch viele
+ zentrale Funktionen die vom internen Zustand abhängen, deshalb bitte
+ nie einfach zerstören oder überschreiben (zumindestens nicht kurz
+ vor einem Release oder in Absprache über bspw. die devel-Liste ;-).
+ Es geht ziemlich sicher etwas kaputt.</para>
+
+ <para><varname>$::form</varname> ist gleichzeitig der Standard Scope
+ in den <productname>Template::Toolkit</productname> Templates
+ außerhalb der Controller: der Ausdruck <function>[% var
+ %]</function> greift auf <varname>$::form->{var}</varname> zu.
+ Unter Controllern ist der Standard Scope anders, da lautet der
+ Zugriff <function>[% FORM.var %]</function>. In Druckvorlagen sind
+ normale Variablen ebenfall im <varname>$::form</varname> Scope, d.h.
+ <function><%var%></function> zeigt auf
+ <varname>$::form->{var}</varname>. Nochmal von der anderen Seite
+ erläutert, innerhalb von (Web-)Templates sieht man häufiger solche
+ Konstrukte:</para>
+
+ <programlisting>[%- IF business %]
+# (... Zeig die Auswahlliste Kunden-/Lieferantentyp an)
+[%- END %]</programlisting>
+
+ <para>Entweder wird hier dann $::form->{business} ausgewertet
+ oder aber der Funktion
+ <function>$form->parse_html_template</function> wird explizit
+ noch ein zusätzlicher Hash übergeben, der dann auch in den
+ (Web-)Templates zu Verfügung steht, bspw. so:</para>
+
+ <programlisting>$form->parse_html_template("is/form_header", \%TMPL_VAR);</programlisting>
+
+ <para>Innerhalb von Schleifen wird
+ <varname>$::form->{TEMPLATE_ARRAYS}{var}[$index]</varname>
+ bevorzugt, wenn vorhanden. Ein Beispiel findet sich in SL/DO.pm,
+ welches über alle Positionen eines Lieferscheins in Schleife
+ läuft:</para>
+
+ <programlisting>for $i (1 .. $form->{rowcount}) {
+ # ...
+ push @{ $form->{TEMPLATE_ARRAYS}{runningnumber} }, $position;
+ push @{ $form->{TEMPLATE_ARRAYS}{number} }, $form->{"partnumber_$i"};
+ push @{ $form->{TEMPLATE_ARRAYS}{description} }, $form->{"description_$i"};
+ # ...
+}</programlisting>
+ </sect3>
+
+ <sect3>
+ <title>%::myconfig</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Das einzige Hash unter den globalen Variablen</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird spätestens benötigt wenn auf die Datenbank
+ zugegriffen wird</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird bei jedem Request neu erstellt.</para>
+ </listitem>
+
+ <listitem>
+ <para>Enthält die Userdaten des aktuellen Logins</para>
+ </listitem>
+
+ <listitem>
+ <para>Sollte nicht ohne Filterung irgendwo gedumpt werden oder
+ extern serialisiert werden, weil da auch der Datenbankzugriff
+ für diesen user drinsteht.</para>
+ </listitem>
+
+ <listitem>
+ <para>Enthält unter anderem Listenbegrenzung vclimit,
+ Datumsformat dateformat und Nummernformat numberformat</para>
+ </listitem>
+
+ <listitem>
+ <para>Enthält Datenbankzugriffinformationen</para>
+ </listitem>
+ </itemizedlist>
+
+ <para><varname>%::myconfig</varname> ist im Moment der Ersatz für
+ ein Userobjekt. Die meisten Funktionen, die etwas anhand des
+ aktuellen Users entscheiden müssen, befragen
+ <varname>%::myconfig</varname>. Innerhalb der Anwendungen sind dies
+ überwiegend die Daten, die sich unter <guimenu>Programm</guimenu>
+ -> <guimenuitem>Einstellungen</guimenuitem> befinden, bzw. die
+ Informationen über den Benutzer die über die
+ Administrator-Schnittstelle (admin.pl) eingegeben wurden.</para>
+ </sect3>
+
+ <sect3>
+ <title>$::locale</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse "Locale"</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird pro Request erstellt</para>
+ </listitem>
+
+ <listitem>
+ <para>Muss auch für Tests und Scripte immer verfügbar
+ sein.</para>
+ </listitem>
+
+ <listitem>
+ <para>Cached intern über Requestgrenzen hinweg benutzte
+ Locales</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Lokalisierung für den aktuellen User. Alle Übersetzungen,
+ Zahlen- und Datumsformatierungen laufen über dieses Objekt.</para>
+ </sect3>
+
+ <sect3>
+ <title>$::lxdebug</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse "LXDebug"</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird global gecached</para>
+ </listitem>
+
+ <listitem>
+ <para>Muss immer verfügbar sein, in nahezu allen
+ Funktionen</para>
+ </listitem>
+ </itemizedlist>
+
+ <para><varname>$::lxdebug</varname> stellt Debuggingfunktionen
+ bereit, wie "<function>enter_sub</function>" und
+ "<function>leave_sub</function>", mit denen in den alten Modulen ein
+ brauchbares Tracing gebaut ist, "<function>log_time</function>", mit
+ der man die Wallclockzeit seit Requeststart loggen kann, sowie
+ "<function>message</function>" und "<function>dump</function>" mit
+ denen man flott Informationen ins Log (tmp/kivitendo-debug.log)
+ packen kann.</para>
+
+ <para>Beispielsweise so:</para>
+
+ <programlisting>$main::lxdebug->message(0, 'Meine Konfig:' . Dumper (%::myconfig));
+$main::lxdebug->message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form->{vc});</programlisting>
+ </sect3>
+
+ <sect3>
+ <title>$::auth</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse "SL::Auth"</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird global gecached</para>
+ </listitem>
+
+ <listitem>
+ <para>Hat eine permanente DB Verbindung zur Authdatenbank</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird nach jedem Request resettet.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para><varname>$::auth</varname> stellt Funktionen bereit um die
+ Rechte des aktuellen Users abzufragen. Obwohl diese Informationen
+ vom aktuellen User abhängen wird das Objekt aus
+ Geschwindigkeitsgründen nur einmal angelegt und dann nach jedem
+ Request kurz resettet.</para>
+ </sect3>
+
+ <sect3>
+ <title>$::lx_office_conf</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse
+ "<classname>SL::LxOfficeConf</classname>"</para>
+ </listitem>
+
+ <listitem>
+ <para>Global gecached</para>
+ </listitem>
+
+ <listitem>
+ <para>Repräsentation der
+ <filename>config/kivitendo.conf[.default]</filename>-Dateien</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Globale Konfiguration. Configdateien werden zum Start gelesen
+ und danach nicht mehr angefasst. Es ist derzeit nicht geplant, dass
+ das Programm die Konfiguration ändern kann oder sollte.</para>
+
+ <para>Beispielsweise ist über den Konfigurationseintrag [debug] die
+ Debug- und Trace-Log-Datei wie folgt konfiguriert und
+ verfügbar:</para>
+
+ <programlisting>[debug]
+file = /tmp/kivitendo-debug.log</programlisting>
+
+ <para>ist der Key <varname>file</varname> im Programm als
+ <varname>$::lx_office_conf->{debug}{file}</varname>
+ erreichbar.</para>
+
+ <warning>
+ <para>Zugriff auf die Konfiguration erfolgt im Moment über
+ Hashkeys, sind also nicht gegen Tippfehler abgesichert.</para>
+ </warning>
+ </sect3>
+
+ <sect3>
+ <title>$::instance_conf</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse
+ "<classname>SL::InstanceConfiguration</classname>"</para>
+ </listitem>
+
+ <listitem>
+ <para>wird pro Request neu erstellt</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Funktioniert wie <varname>$::lx_office_conf</varname>,
+ speichert aber Daten die von der Instanz abhängig sind. Eine Instanz
+ ist hier eine Mandantendatenbank. Beispielsweise überprüft
+ <programlisting>$::instance_conf->get_inventory_system eq 'perpetual'</programlisting>
+ ob die berüchtigte Bestandsmethode zur Anwendung kommt.</para>
+ </sect3>
+
+ <sect3>
+ <title>$::dispatcher</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Objekt der Klasse
+ "<varname>SL::Dispatcher</varname>"</para>
+ </listitem>
+
+ <listitem>
+ <para>wird pro Serverprozess erstellt.</para>
+ </listitem>
+
+ <listitem>
+ <para>enthält Informationen über die technische Verbindung zum
+ Server</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Der dritte Punkt ist auch der einzige Grund warum das Objekt
+ global gespeichert wird. Wird vermutlich irgendwann in einem anderen
+ Objekt untergebracht.</para>
+ </sect3>
+
+ <sect3>
+ <title>$::request</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Hashref (evtl später Objekt)</para>
+ </listitem>
+
+ <listitem>
+ <para>Wird pro Request neu initialisiert.</para>
+ </listitem>
+
+ <listitem>
+ <para>Keine Unterstruktur garantiert.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para><varname>$::request</varname> ist ein generischer Platz um
+ Daten "für den aktuellen Request" abzulegen. Sollte nicht für action
+ at a distance benutzt werden, sondern um lokales memoizing zu
+ ermöglichen, das garantiert am Ende des Requests zerstört
+ wird.</para>
+
+ <para>Vieles von dem, was im moment in <varname>$::form</varname>
+ liegt, sollte eigentlich hier liegen. Die groben
+ Differentialkriterien sind:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>Kommt es vom User, und soll unverändert wieder an den
+ User? Dann <varname>$::form</varname>, steht da eh schon</para>
+ </listitem>
+
+ <listitem>
+ <para>Sind es Daten aus der Datenbank, die nur bis zum Ende des
+ Requests gebraucht werden? Dann
+ <varname>$::request</varname></para>
+ </listitem>
+
+ <listitem>
+ <para>Muss ich von anderen Teilen des Programms lesend drauf
+ zugreifen? Dann <varname>$::request</varname>, aber Zugriff über
+ Wrappermethode</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ </sect2>
+
+ <sect2>
+ <title>Ehemalige globale Variablen</title>
+
+ <para>Die folgenden Variablen waren einmal im Programm, und wurden
+ entfernt.</para>
+
+ <sect3>
+ <title>$::cgi</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>war nötig, weil cookie Methoden nicht als
+ Klassenfunktionen funktionieren</para>
+ </listitem>
+
+ <listitem>
+ <para>Aufruf als Klasse erzeugt Dummyobjekt was im
+ Klassennamespace gehalten wird und über Requestgrenzen
+ leaked</para>
+ </listitem>
+
+ <listitem>
+ <para>liegt jetzt unter
+ <varname>$::request->{cgi}</varname></para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+
+ <sect3>
+ <title>$::all_units</title>