From: Kai-Martin K. <km...@fa...> - 2006-10-27 03:03:11
|
Hallo. Nachdem ich hier bisher nur haufenweise Verbesserungsvorschläge gemacht habe, kommt hier ein eigener Beitrag: Eine Rechnungsvorlage, bei der eine lange Tabelle von Latex umgebrochen wird. Bisher hat das Script von lx-Office die Tabelle aufgeteilt. Da aber lx nicht wirklich weiß, wieviel Platz Latex zur Verfügung hat, kam es regelmäßig zu Unfällen. Spätestens, wenn man eine längere Bemerkung dazu geschrieben hat, kam der Satz ins schleudern. Nun ist der Umbruch von Tabellen an sich für Latex ein Fingerschnippen. Die Schwierigkeit liegt in der Darstellung der Zwischensumme und Übertrag auf der nächsten Seite. Dafür sind bei Latex alle normalen Zugänge verbaut. Aber mit einem Code-Beispiel, das ein TeX-Guru vor ein paar Jahren im Usenet gepostet hat, können die Hindernisse umgangen werden. Als zweite Voraussetzung wird eine Variable <%cumtotal%> gebraucht, die für jede Zeile der Tabelle die Zwischensumme bis dahin angibt. Dafür muss im Perl-Script Form.pm nach "$sum += $self->parse..." die folgende Zeile eingefügt werden. $self->{"cumtotal"}[$i] = $self->format_amount($myconfig, $sum, 2); (Dank an Udo, von dem dieser Patsch stammt) Die Rechnungsvorlage selbst geht ziemlich tief in die Maschinenräume, überschreibt mit plainTeX Funktionen von Standard-Paketen und hantiert mit Postscript-Code. Auch auf der Latex-Seite werden einige Joker gezogen. Bestellnummern und Preise können sich beliebig dehnen. Die Spalte mit der Artikel-Beschreibung passtdann sich so an, dass die Gesamtbreite der Tabelle genau auf die Seite passt. Damit das auch für umgebrochene Tabellen klappt, verwendet die Vorlage das Paket ltxtable. Das kombiniert tabularx und longtable mit etwas unorthodoxen Mitteln. Aber es funktioniert :-) Ein paar Unschönheiten gibt es natürlich noch: * Die Zwischensummen sind nicht ordentlich gesetzt. Sie stehen immer am selben Punkt und wachsen wenn die Zahlen größer werden, nach rechts. Das ist zum Teil ein prinzipielles Problem. Da die Zahlen erst vom Nachbrenner in Postscript eingeschoben werden, kann Latex nicht wissen, wie groß der benötigte Platz ist. * Wenn die Variable cumtotal nicht belegt ist, stehen "Zwischensumme" und "Übertrag" einsam in der Gegend. Das müsste man eigentlich mit einem latex-if abfangen können. Mit <%if cumtotal%> habe ich mir nur Fehler eingehandelt ---> Was genau macht der Perl-Parser eigentlich aus so einer if-Konstruktion? * Meiner Meinung nach, sollte die Rabatt-Spalte nur erscheinen, wenn tatsächlich Rabatt gewährt wird. Am einfachsten wäre hier vermutlich auch wieder der Weg über eine lx-Variable. In Latex würde dann abhängig von dem Wert die Spalte weggelassen. * Sinnvollerweise sollte die Vorlage in drei Teile aufgeteilt werden, die sich in getrennten Dateien befinden. 1) Die Header für Briefkopf und Seitenstil 2) Die Spezial-Funktionen für die Zwischensummen 3) Das Haupt-Dokument mit der Tabellen-Definition Damit würde es leichter fallen, einen Satz von Templates zu pflegen. Die ersten beiden Dateien bleiben schätzungsweise bei allen gleich. Für eine andere Gestaltung des Briefkopfs müsste man nur in der ersten Datei rumpfuschen. ---> Die Template-Baustelle ist so bald nicht abgefrühstückt... Aber immerhin kann ich jetzt schonmal schönere Rechnungen stellen :^) ---<(kaimartin)>--- Nach so viel Ankündigung hier noch die Vorlage selbst: ----------------------------8<-------------------------------------------- % invoice.tex für lx office % Version: 0.2 % erstellt durch -<(kmk)>- km...@li... % verwendet leicht modifizierten plain TeX Code von Heiko Oberdiek % % Achtung: Die Zwischensummen funktionieren nur, wenn die Variable % <%cumtotal%> definiert ist. Sie gibt für jede Zeile der Tabelle die % Zwischensumme bis dahin an. % %--------------------------------------------------- % % Eigenschaften: % * Briefkopf mit Logo und Texten (Platzierung mit Picture-Umgebung) % * Positionsnummern in der Tabelle % * Tabelle mit dynamischer Breite ---> Spalte Beschreibung passt sich an. % * Fancy Headings --> Dreiteiliger Seitenfuß % * Faxnummer und Telefonnummer des Kunden unter dem Adressfeld. % * Angabe der Gesamt-Seitenzahl im Seitenkopf. % * Im Seitenfuß werden lx-Variablen verwendet, soweit sie definiert sind. % * Im Adressfeld Zeilenumbruch und ein Space zwischen Variablen ergänzt % * Umbruch langer Tabellen durch Latex statt durch lx-office. % * Zwischensummen werden nachträglich in Postscript eingefügt. % % Bekannte Schwächen: % * Da die Zwischensummen erst in Postscript eingefügt werden, eignet % sich diese Vorlage nicht für pdftex. Für die Erstellung von PDF % sollte der Konverter ps2pdf verwendet werden. % * Vielen Größen und Längen im Briefkopf sind absolut angegeben. % * Die Steuernummer und die Bankverbindung sind keine Variablen. % * Die Auswahl der Schriften ist ad hoc improvisiert. % * In der Tabelle sollte zeilenweise der Steuersatz angegeben werden. % * Die Spalte "Rabatt" sollte nur dann erscheinen, wenn es Rabatt gibt % % Info über die Syntax der von lx eingefügten Variablen findet sich im Wiki: % http://wiki.lx-system.de/index.php/Lx-Office_ERP_Reportvariablen % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Diese Vorlage steht unter der GPL-Lizenz, Version2 % siehe http://www.gnu.de/gpl-ger.html %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass[twoside]{scrartcl} \usepackage{fancyhdr} % Für den Seitenkopf und -Fuß %\usepackage[pdftex]{graphicx} % Falls pdf-Ausgabe gewünscht \usepackage{graphicx} % Falls ps-Ausgabe gewünscht \usepackage{german} % Deutsche Trenn-Tabelle \usepackage{tabularx} % Tabelle mit variabler Spaltenbreite (obsolet?) \usepackage[latin1]{inputenc} % Umlaute direkt eingeben \usepackage{textcomp} % Sonderzeichen (wird das gebraucht?) \usepackage{lastpage} % Fuer "Seite 2 von 5" \usepackage{filecontents} % Um von latex aus eine Datei schreiben zu koennen \usepackage{etex} % Damit Marken verwendet werden koennen \usepackage{ltxtable} % Mehrseiten-Tabellen mit variabler Spaltenbreite \setlength{\voffset}{-2.0cm} \setlength{\hoffset}{-2.0cm} \setlength{\topmargin}{0cm} \setlength{\headheight}{0.5cm} \setlength{\headsep}{1cm} \setlength{\topskip}{0pt} \setlength{\oddsidemargin}{1.0cm} \setlength{\evensidemargin}{1.0cm} \setlength{\textwidth}{17cm} \setlength{\textheight}{24.0cm} \setlength{\footskip}{80pt} \setlength{\parindent}{0pt} \renewcommand{\baselinestretch}{1} \fontfamily{cmss}\fontshape{n}\selectfont %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%Plain-TeX-Hack fuer Zwischensummen%%%%%%%%%%%%%%%%%%%%%%% % Das at-Zeichen in Variablen zulassen \makeatletter % Mark-Register bereitstellen \newmarks\ltm@marks \def\ltm@setmarks#1{% \marks\ltm@marks{#1}% } \def\ltm@getmarks{% \botmarks\ltm@marks } % Den aktuellen Wert der Zwischensumme merken \newcommand*{\Wert}[1]{% % #1% auskommentiert Kontrollausgabe an Ort und Stelle % \expandafter\ltm@setmarks\expandafter{#1}% \ltm@setmarks{#1}% } % Merken der aktuellen Position in PostScript-Variablen \newcommand*{\Mark}{% \leavevmode \special{ps:currentpoint /ltm@y exch def /ltm@x exch def}% } % Ausgabe des Wertes #1 an der in den PostScript-Variablen % gemerkten Position \def\ltm@insert#1{% \vbox to\z@{% \vss \hb@xt@\z@{% \color@begingroup \special{ps:gsave % currentpoint neg exch neg exch translate % ltm@x ltm@y translate % }% #1% \special{ps:grestore}% \hss \color@endgroup }% }% } \def\ltm@head{\ltm@insert{\ltm@getmarks}} \def\ltm@lastfoot{\ltm@insert\ltm@getmarks} \def\ltm@foot{\ltm@insert{\ltm@getmarks}} % Ueberschreiben der Output-Routine von longtable \def\LT@output{% \ifnum\outputpenalty <-\@Mi \ifnum\outputpenalty > -\LT@end@pen \LT@err{floats and marginpars % not allowed in a longtable}\@ehc \else \setbox\z@\vbox{\unvbox\@cclv}% \ifdim \ht\LT@lastfoot>\ht\LT@foot \dimen@\pagegoal \advance\dimen@-\ht\LT@lastfoot \ifdim\dimen@<\ht\z@ \setbox\@cclv\vbox{% \unvbox\z@\copy\LT@foot\ltm@foot\vss }% \@makecol \@outputpage \setbox\z@\vbox{\box\LT@head}% \fi \fi \global\@colroom\@colht \global\vsize\@colht \vbox{% \unvbox\z@ \box\ifvoid\LT@lastfoot \LT@foot\ltm@foot \else \LT@lastfoot\ltm@lastfoot \fi }% \fi \else \setbox\@cclv\vbox{% \unvbox\@cclv\copy\LT@foot\ltm@foot\vss }% \@makecol \@outputpage \global\vsize\@colroom \copy\LT@head\ltm@foot \fi } \makeatother % Das at-Zeichen in Variablen wieder verbieten %%%%%%%%%%%%%%%%%%%%Ende PlainTeX-Hack%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Seitenköpfe und -Füße %%%%%%%%%% \newsavebox{\fusslinks} \sbox{\fusslinks}{ \sffamily\small \parbox[b]{5cm}{ \begin{flushleft} <%company%>\\ <%address%> \end{flushleft} }%Ende parbox }%Ende sbox \newsavebox{\fussmitte} \sbox{\fussmitte}{ \sffamily\small \parbox[b]{5cm}{ \begin{flushleft} Tel: <%tel%> \\ % UstID: <%ustid%> UstID: DE245596968 \\ Steuer-Nr.: 2327 12201064 \end{flushleft} }%Ende parbox }%Ende sbox \newsavebox{\fussrechts} \sbox{\fussrechts}{ \sffamily\small \parbox[b]{5cm}{ \begin{flushright} Sparda Hannover\\ BLZ: 250 905 00\\ Kto.Nr: 2228700 \end{flushright} }%Ende parbox }%Ende sbox % pagestyle "plain" umdefinieren: \fancypagestyle{plain}{% \fancyhf{} % Erstmal alles löschen \fancyfoot[L]{\usebox{\fusslinks}} \fancyfoot[C]{\usebox{\fussmitte}} \fancyfoot[R]{\usebox{\fussrechts}} \fancyhead[L]{\sffamily <%company%>} \fancyhead[C]{\sffamily Rechnungsnummer <%invnumber%>} \fancyhead[R]{\sffamily Seite \thepage/\pageref{LastPage}} \renewcommand{\headrulewidth}{0.5pt} \renewcommand{\footrulewidth}{0.5pt} \fancyfootoffset{10mm} } % pagestyle "briefkopf" definieren: \fancypagestyle{briefkopf}{% \fancyhf{} % Erstmal alles löschen \fancyhead[L]{\usebox{\kopf}} \fancyfoot[L]{\usebox{\fusslinks}} \fancyfoot[C]{\usebox{\fussmitte}} \fancyfoot[R]{\usebox{\fussrechts}} \renewcommand{\headrulewidth}{0pt} \renewcommand{\footrulewidth}{0.5pt} \fancyfootoffset{10mm} } \pagestyle{plain} % Alle Seiten bekommen plain als Default-Stil %======================== %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% Briefkopf %%%%%%%%%%%%%%%%%%%% \newsavebox{\kopf} \sbox{\kopf}{ % Die Schriftart und Schriftröße im Briefkopf, \fontfamily{cmss}\fontsize{12pt}{14pt}\selectfont % In der picture-Umgebung sollen alle Zahlen die Einheit 1mm haben. \setlength{\unitlength}{1mm} \begin{picture}(0,0) %============ Ein Logo als PDF-Grafik ============ % Das Logo muss sich im lx-erp-Pfad im Ordner users/ befinden und kann das % Format PDF, JPG, PNG oder EPS haben. Mit einer EPS-Grafik kann lx nur einen % Ausdruck nach Postscript machen. Die anderen Grafik-Formate erlauben nur % einen PDF-Ausdruck. % % Die Seitendefinition der Grafik sollte so bemessen sein, dass das Logo % gerade hinein passt. Ich habe das dadurch erreicht, dass ich in inkscape die % Seitengröße in Höhe und Breite so angepasst habe, dass die an das % bestehende Logo gerade so nicht heran reichen. Wenn die Grafik nicht % sinnvoll nach inkscape importierbar ist, dann hilft wahrscheinlich der Weg % über eps und epstopdf. % % Eine Variation über dem gleichen Problem passiert, wenn das PDF/Postscript % gar keine Seiten-Definition enthält, weil der Ausgangs-Filter des % Erstellungsprogramm schlampig programmiert ist. Dann steht Latex im % Regen und hat ebenfalls Probleme mit der Bilddarstellung. % % Tipp für schönere Schriften: Als *.eps mit eingebetteten Schriften aus dem % Malprogramm (z.B. inkscape) exportieren und dann mit epstopdf umwandeln. % \put(50,-55){ % Mit dem put-Befehl wird die Position des Logos bestimmt. \includegraphics[width=130\unitlength]{Logo.eps} % width gibt die Breite des Logos in mm an. }% Ende put % ================== Ein Strich ============= % \put(50,-35){\rule[-5mm]{40mm}{0.5pt}} %============== Ein Kasten mit Schlagworten ======= % \put(0,0){ %Position % \fbox{ %Der Kasten % \parbox[t]{20mm}{ % \centering % \textbf{Ich \& Du}\newline % \textbf{Gnu} % }%Ende parbox % }%Ende fbox % }%Ende put %==================== Firmenanschrift ========= % \put(100,-20){ % Position % \begin{minipage}[t]{70mm} % \textbf{<%company%>}\newline % <%address%>\par % \vspace{2mm} % \begin{tabular}[b]{@{}rr@{}} % Telefon & <%tel%>\\ % Telefax & <%fax%> % \end{tabular} % \end{minipage} % }%Ende put %==================== Lieferantenanschrift ========= \put(10,-70){ % Position \parbox{7cm}{ %Der Kasten für die Empfänger-Adresse \Large \textbf{<%name%> }\\ <%cp_greeting%> <%cp_name%>\\ <%street%>\\ <%country%> <%zipcode%> <%city%>\par \vspace{3mm} \small Fax: <%customerfax%> \newline Tel: <%customerphone%> }%Ende parbox }%Ende put \end{picture} }%Ende sbox %%%%%%%%%%%%% Ende des Briefkopfes %%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{document} % Die Schrift für den Hauptteil des Dokuments (Computermodern-sanserif) \fontfamily{cmss}\fontsize{12}{15pt plus 0.15pt minus 0.1pt}\selectfont \thispagestyle{briefkopf} % Nur die erste Seite bekommt das große Firmenlogo %========Datum und Nummern==================================================== \vspace*{63mm} % deutlich unterhalb des Briefkopf \textbf{\LARGE Rechnung} \hfill \begin{tabular}[b]{r@{\hspace{3mm}}l} \textbf{Seite} & {\thepage} von \pageref{LastPage}\\ \textbf{Datum} & <%invdate%> \\ \textbf{Lieferdatum} & <%deliverydate%>\\ \textbf{Kundennummer} & <%customernumber%>\\ \textbf{Rechnungsnummer} & <%invnumber%>\\ \end{tabular} %=======Bemerkungen (eingefuegt aus dem lx-Formular)========================== <%if notes%> \vspace{ 5mm} <%notes%> <%end if%> \vspace{5mm} %======Die eigentliche Bestell-Tabelle======================================== \renewcommand{\arraystretch}{1.3} % Etwas mehr Abstand zwischen Tabellenzeilen % Die Tabelle in eine Datei schreiben, damit ltx-table gluecklich wird \begin{filecontents}{tabelle.tex} % Definition der Spalten wie in der Umgebung tabularx \begin{longtable}{@{}rlX@{ }rlrrr@{}} % Kopfzeile der Tabelle \textbf{Pos} & \textbf{Nummer} & \textbf{Artikel} & \textbf{Anz} & \textbf{Einh} & \textbf{Preis} & \textbf{Rabatt} &\textbf{Total} \\ \endfirsthead % Tabellenkopf nach dem Umbruch \textbf{Pos} & \textbf{Nummer} & \textbf{Artikel} & \textbf{Anz} & \textbf{Einh} & \textbf{Preis} & \textbf{Rabatt} &\textbf{Total} \\ & & \multicolumn{4}{r}{ Übertrag: } & & \Mark\phantom{000,00}\\ \endhead % Fuss der Teiltabellen & & \multicolumn{5}{r}{ Zwischensumme:} & \Mark\phantom{000,00}\\ \endfoot % Das Ende der Tabelle & & \multicolumn{5}{r}{ Zwischensumme:} & \Mark\phantom{000,00}\\ \endlastfoot <%foreach number%> <%runningnumber%> & <%number%> & <%description%> & <%qty%> & <%unit%> & <%sellprice%> & <%discount%> & <%linetotal%> \Wert{<%cumtotal%> } \\ % <%end number%> \end{longtable} \end{filecontents} \LTXtable{\textwidth}{tabelle.tex} % ltx-table aufrufen \parbox{\textwidth}{ % Die Summen in einen Kasten \rule{\textwidth}{2pt} % Ein dicker Strich \vspace{0.5cm} \hfill \begin{tabular}{rr@{ }l@{}} Summe vor Steuern:& <%subtotal%> & \\ % Die unterschiedlichen Steueranteile getrennt ausweisen <%foreach tax%> <%taxdescription%> von <%taxbase%> & <%tax%> & \\ <%end tax%> \hline % Ein dünner Strich \textbf{Gesamt:} & \textbf{<%invtotal%>} &\textbf{<%currency%>}\\ <%if paid%> \textbf{Bereits gezahlt:} & \textbf{<%paid%>} &\textbf{<%currency%>}\\ \textbf{Noch zu zahlen:} & \textbf{<%total%>} &\textbf{<%currency%>}\\ <%end paid%> \end{tabular} \vspace{0.3cm} }%Ende parbox %%%%%%%%% Das Kleingedruckte %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \vfill {\small Diese Rechnung ist bezahlbar innerhalb von <%terms%> Tagen. Nach dem <%duedate%> werden Zinsen zu einem monatlichen Satz von 1.5\% verrechnet. Bereits gelieferte Waren bleiben im Besitz von <%company%> bis die Rechnung vollsändig beglichen ist. } \end{document} |