buster (deb 10)
[freeside.git] / conf / invoice_latex
1 %% file: Standard Multipage.tex
2 %% Purpose: Multipage bill template for e-Bills
3 %%
4 %% Created by Mark Asplen-Taylor
5 %% Asplen Management Ltd
6 %% www.asplen.co.uk
7 %%
8 %% Modified for Freeside by Kristian Hoffman
9 %%
10 %% Changes
11 %%      0.1     4/12/00 Created
12 %%      0.2     18/10/01        More fields added
13 %%      1.0     16/11/01        RELEASED
14 %%      1.2     16/10/02        Invoice number added
15 %%      1.3     2/12/02 Logo graphic added
16 %%      1.4     7/2/03  Multipage headers/footers added
17 %%      n/a     forked for Freeside; checked into CVS
18 %%      12/04/18  added support for coupon placement, top or bottom of page
19
20 \documentclass[letterpaper]{article}
21
22 \usepackage{fancyhdr,lastpage,ifthen,array,longtable,afterpage,caption,multirow,bigstrut}
23 \usepackage[breakwords]{truncate} % to avoid overflowing boxes
24 \usepackage{graphicx}                   % required for logo graphic
25 \usepackage[utf8]{inputenc}             % multilanguage support
26 \usepackage[T1]{fontenc}
27 [@-- if ( length($watermark) ) {
28   $OUT .= '
29 \usepackage{background}
30 \backgroundsetup{
31   placement=center,
32   opacity=0.25,
33   color=black,
34   angle=0,
35   contents=' . $watermark . '
36 }';
37 }
38 '';
39 --@]
40
41 \catcode`\{=1
42 \catcode`\}=2
43 \special{ps:%
44   SDict begin [
45     /Title (Invoice \#[@-- $invnum --@])
46     /DOCINFO pdfmark
47   end
48 }
49 \end
50
51 \newcommand{\extracouponspace}{[@-- defined($extracouponspace) ? $extracouponspace : '2.7in' --@]}
52
53 \addtolength{\voffset}{-0.0cm}          % top margin to top of header
54 \addtolength{\hoffset}{-0.6cm}          % left margin on page
55 \addtolength{\topmargin}{[@-- defined($topmargin) ? $topmargin : '-1.00cm' --@]}
56 \setlength{\headheight}{2.0cm}          % height of header
57 [@-- if ($coupon && $couponlocation eq "top") { '\addtolength{\headheight}{\extracouponspace}'; } --@] % add coupon space to header
58 \setlength{\headsep}{[@-- defined($headsep) ? $headsep : '1.0cm' --@]}
59 \setlength{\footskip}{1.0cm}            % bottom of footer from bottom of text
60
61 %\addtolength{\textwidth}{2.1in}        % width of text
62 \setlength{\textwidth}{19.5cm}
63 \setlength{\textheight}{[@-- defined($textheight) ? $textheight : '19.5cm' --@]}
64 \setlength{\oddsidemargin}{-0.9cm}      % odd page left margin
65 \setlength{\evensidemargin}{-0.9cm}     % even page left margin
66
67 \LTchunksize=40
68
69
70 \begin{document}
71
72
73 \renewcommand{\headrulewidth}{0pt}
74 \renewcommand{\footrulewidth}{1pt}
75
76 \renewcommand{\footrule}{
77 [@--
78   $coupon ? '\ifthenelse{\equal{\thepage}{1}}' : '';
79 --@]
80   {
81   }
82   {
83     \vbox to 0pt{\rule{\headwidth}{\footrulewidth}\vss}
84   }
85 }
86
87 % Adjust the inset of the mailing address
88 \newcommand{\addressinset}[1][]{\hspace{1.0cm}}
89
90 % Adjust the inset of the return address and logo
91 \newcommand{\returninset}[1][]{\hspace{-0.25cm}}
92
93 % New command for address lines i.e. skip them if blank
94 \newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\\}}
95
96 % Inserts dollar symbol
97 \newcommand{\dollar}[1][]{\symbol{36}}
98
99 % Remove plain style header/footer
100 \fancypagestyle{plain}{
101   \fancyhead{}
102 }
103 \fancyhf{}
104
105 % Define fancy header/footer for first and subsequent pages
106 \fancyfoot[C]{
107 \ifpageone{\global\setlength\headheight{2cm}}
108   \ifthenelse{\equal{\thepage}{1}}
109   { % First page
110 [@--
111   if ($coupon && $couponlocation eq "bottom") {
112     $OUT .= '\vspace{-\extracouponspace}';
113     $OUT .= '\rule[0.5em]{\textwidth}{\footrulewidth}\\\\';
114     $OUT .= $coupon;
115     $OUT .= '\vspace{'.
116       (defined($couponfootsep) ? $couponfootsep : '0.2in') .
117       '}';
118   }
119   elsif ($coupon && $couponlocation eq "top") {
120     $OUT .= '\vspace{-\extracouponspace}';
121   }
122   '';
123 --@] [@-- $smallerfooter ? '\scriptsize{' : '\small{' --@]
124 [@-- $footer --@]
125     }[@-- ($coupon && $couponlocation eq "bottom") ? '\vspace{\extracouponspace}' : '' --@]
126   }
127   { % ... pages
128     [@-- $smallerfooter ? '\scriptsize{' : '\small{' --@]
129 [@-- $smallfooter --@]
130     }
131   }
132 }
133
134 \fancyfoot[R]{
135   \ifthenelse{\equal{\thepage}{1}}
136   { % First page
137   }
138   { % ... pages
139     \small{\thepage~[@-- emt('of') --@]~\pageref{LastPage}}
140   }
141 }
142
143 \fancyhead[L]{
144   \ifthenelse{\equal{\thepage}{1}}
145   { % First page
146 [@--
147   if ($coupon && $couponlocation eq "top") {
148     $OUT .= '\vspace{-\extracouponspace}';
149     $OUT .= $coupon;
150     $OUT .= '\rule[0.5em]{\textwidth}{\footrulewidth}\\\\';
151     $OUT .= '\vspace{'.
152       (defined($couponfootsep) ? $couponfootsep : '0.2in') .
153       '}';
154   }
155 --@]\returninset
156     \makebox{
157       \begin{tabular}{ll}
158         \begin{minipage}[b]{5.5cm}
159 [@-- $returnaddress --@]
160         \end{minipage} &
161         \includegraphics{[@-- $logo_file --@]}\\
162       \end{tabular}
163     }
164   }
165   { % ... pages
166     %\includegraphics{[@-- $logo_file --@]}     % Uncomment if you want the logo on all pages.
167   }
168 }
169
170 \fancyhead[R]{
171   \ifthenelse{\equal{\thepage}{1}}
172   { % First page
173     \begin{tabular}{ccc}
174     [@-- join(' & ', ( $no_date   ? '' : emt('Invoice date') ),
175                      ( $no_number ? '' : emt('Invoice #')    ),
176                      emt('Customer #')
177              )
178     --@]\\
179     \vspace{0.2cm}
180     \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]} \\\hline
181     \rule{0pt}{5ex} &~~ \huge{\textsc{[@-- emt($notice_name) --@]}} & \\
182     \vspace{-0.2cm}
183      & & \\\hline
184     \end{tabular}
185   }
186   { % ... pages
187     \small{
188       \begin{tabular}{lll}
189       [@-- join(' & ', emt('Invoice date'), emt('Invoice #'), emt('Customer #') ) --@]\\
190       \textbf{[@-- $date --@]} & \textbf{[@-- $invnum --@]} & \textbf{[@-- $custnum --@]}\\
191       \end{tabular}
192     }
193   }
194 }
195
196 \pagestyle{fancy}
197
198
199 %% Font options are:
200 %%      bch     Bitsream Charter
201 %%      put     Utopia
202 %%      phv     Adobe Helvetica
203 %%      pnc     New Century Schoolbook
204 %%      ptm     Times
205 %%      pcr     Courier
206
207 \renewcommand{\familydefault}{phv}
208
209
210 % Commands for freeside table header...
211
212 \newcommand{\FSdescriptionlength} { [@-- $unitprices ? '8.2cm' : '12.8cm' --@] }
213 \newcommand{\FSdescriptioncolumncount} { [@-- $unitprices ? '4' : '6' --@] }
214 \newcommand{\FSunitcolumns}{ [@--
215   $unitprices
216   ? '\makebox[2.5cm][r]{\textbf{~~' . emt('Unit Price') . '}} &' .
217     '\makebox[1.4cm]{\textbf{~' . emt('Quantity') . '}} & '
218   : '' --@] }
219
220 \newcommand{\FShead}{
221   \hline
222   \rule{0pt}{2.5ex}
223   \makebox[1.4cm]{} &
224   \multicolumn{\FSdescriptioncolumncount}{l}{
225     \truncate{\FSdescriptionlength}{\textbf{[@-- emt('Description') --@]}}
226   } &
227   \FSunitcolumns
228   \makebox[1.6cm][r]{\textbf{[@-- emt('Amount') --@]}} \\
229   \hline
230 }
231
232 \newcommand{\FSusagehead}{
233   \hline
234   \rule{0pt}{2.5ex}
235   \makebox[1.4cm]{} &
236   \multicolumn{4}{l}{
237     \truncate{\FSdescriptionlength}{\textbf{[@-- emt('Description') --@]}}
238   } &
239   \textbf{~~[@-- emt('Calls') --@]} &
240   \textbf{~~[@-- emt('Duration') --@]} &
241   \textbf{~~[@-- emt('Amount') --@]} \\
242   \hline
243 }
244
245 % ...description...
246 \newcommand{\FSdesc}[5]{
247   \multicolumn{1}{c}{\rule{0pt}{2.5ex}\textbf{#1}} &
248   \multicolumn{[@-- $unitprices ? '4' : '6' --@]}{l}{
249     \truncate{\FSdescriptionlength}{\textbf{#2}}
250   } &
251 [@-- $unitprices ? '  \multicolumn{1}{r}{\textbf{#3}} &'."\n".
252                    '  \multicolumn{1}{r}{\textbf{#4}} &'."\n"
253                  : ''
254 --@]
255   \multicolumn{1}{r}{\textbf{#5}}\\
256 }
257 % ...extended description...
258 \newcommand{\FSextdesc}[1]{
259   \multicolumn{1}{l}{\rule{0pt}{1.0ex}} &
260   \multicolumn{6}{l}{
261     \truncate{12.8cm}{\small{~~~#1}}
262   } \\
263 }
264 % ...call detail (multiple columns already)...
265 \newcommand{\FScalldetail}[1]{
266   \multicolumn{1}{l}{\rule{0pt}{1.0ex}} &
267   ~~~#1
268   \\
269 }
270 }
271 % ...and total line items (which use the full 12.8cm length, ignoring
272 % unitprice/quantity
273 \newcommand{\FStotaldesc}[2]{
274   & \multicolumn{6}{l}{
275     \truncate{12.8cm}{#1}
276   } & #2\\
277 }
278
279 % ...usage class summary
280 \newcommand{\FSusagedesc}[4]{
281   \multicolumn{1}{c}{\rule{0pt}{2.5ex}} &
282   \multicolumn{4}{l}{\textbf{#1}} &
283   \multicolumn{1}{r}{\textbf{#2}} &
284   \multicolumn{1}{r}{\textbf{#3}} &
285   \multicolumn{1}{r}{\textbf{#4}}
286   \\
287 }
288
289 %       Headers and footers defined for the first page
290 \addressinset \rule{0.5cm}{0cm}
291 \makebox{
292 \begin{minipage}[t]{7.0cm}
293 \vspace{[@-- defined($addresssep) ? $addresssep : '0.25cm' --@]}
294 \textbf{[@-- $payname --@]}\\
295 \addressline{[@-- $company --@]}
296 \addressline{[@-- $address1 --@]}
297 \addressline{[@-- $address2 --@]}
298 \addressline{[@-- $city --@], [@-- $state --@]~~[@-- $zip --@]}
299 \addressline{[@-- $country --@]}
300 \end{minipage}}
301 \hfill
302 \makebox{
303 \begin{minipage}[t]{6.4cm}
304 [@--
305   if ($ship_enable) {
306     $OUT .= '\textbf{' . emt('Service Address') . '}\\\\';
307     $OUT .= "\\addressline{$ship_company}";
308     $OUT .= "\\addressline{$ship_address1}";
309     $OUT .= "\\addressline{$ship_address2}";
310     $OUT .= "\\addressline{$ship_city, $ship_state~~$ship_zip}";
311     $OUT .= "\\addressline{$ship_country}";
312     $OUT .= '~\\\\';
313   }else{
314     $OUT .= '';
315   }
316 --@]
317 \begin{flushright}
318 [@-- $terms ? emt('Terms') . ': ' . emt($terms) : '' --@]\\
319 [@-- $po_line --@]\\
320 \end{flushright}
321 \end{minipage}}
322 \vspace{1.5cm}
323 %
324 [@-- $summary --@]
325 %
326 \section*{}
327 [@--
328   foreach my $section ( grep { !$summary || $_->{description} ne $finance_section } @sections ) {
329     if ($section->{'pretotal'} && !$summary) {
330       $OUT .= '\begin{flushright}';
331       $OUT .= '\large\textsc{'. $section->{'pretotal'}. '}\\\\';
332       $OUT .= '\\end{flushright}';
333     }
334     $OUT .= '\pagebreak' if $section->{'post_total'};
335     unless ($section->{'summarized'} ) {
336       $OUT .= '\captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}';
337       $OUT .= '\ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}'
338         if $coupon;
339       $OUT .= '\begin{longtable}{cllllllr}';
340       $OUT .= '\caption*{ ';
341       if ($section->{'location'}) {
342         $OUT .= $section->{'location'}{'label_prefix'}. ': '
343           if length($section->{'location'}{'label_prefix'});
344         $OUT .= $section->{'location'}{'address1'};
345         $OUT .= ', ' . $section->{'location'}{'address2'}
346           if length($section->{'location'}{'address2'});
347         $OUT .= ', ' .
348                 $section->{'location'}{'city'} . ', ' .
349                 $section->{'location'}{'state'} . '~' .
350                 $section->{'location'}{'zip'};
351       } elsif ( $section->{'description'} ) {
352         $OUT .= ($section->{'description'});
353       } else {
354         $OUT .= emt('Charges');
355       }
356       $OUT .= '}\\\\';
357       if ($section->{header_generator}) {
358         $OUT .= &{$section->{header_generator}}();
359       } elsif ( $section->{usage_section} ) {
360         $OUT .= '\FSusagehead';
361       } else {
362         $OUT .= '\FShead';
363       }
364       $OUT .= '\endfirsthead';
365       $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}'.emt('Continued from previous page').'}\\\\';
366       if ($section->{header_generator}) {
367         $OUT .= &{$section->{header_generator}}();
368       } elsif ( $section->{usage_section} ) {
369         $OUT .= '\FSusagehead';
370       } else {
371         $OUT .= '\FShead';
372       }
373       $OUT .= '\endhead';
374       $OUT .= '\multicolumn{7}{r}{\rule{0pt}{2.5ex}'.emt('Continued on next page...').'}\\\\';
375       $OUT .= '\endfoot';
376       $OUT .= '\hline';
377
378       if (scalar(@sections) > 1 and !$section->{no_subtotal}) {
379         if ($section->{total_generator}) {
380           $OUT .= &{$section->{total_generator}}($section);
381         } else {
382           $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .
383                   '{' . $section->{'subtotal'} . '}' . "\n";
384         }
385       }
386
387       #if ($section == $sections[$#sections]) {
388         foreach my $line (grep {$_->{section}->{description} eq $section->{description}} @total_items) {
389           if ($section->{total_line_generator}) {
390             $OUT .= &{$section->{total_line_generator}}($line);
391           } else {
392             $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .
393                     '{' . $line->{'total_amount'} . '}' . "\n";
394           }
395         }
396       #}
397
398       $OUT .= '\hline';
399       $OUT .= '\endlastfoot';
400
401       my $lastref = 0;
402       foreach my $line (
403         grep { ( scalar( @sections ) > 1
404                ? $section->{'description'} eq $_->{'section'}->{'description'}
405                : 1
406              ) }
407         @detail_items )
408       {
409         my $ext_description = $line->{'ext_description'};
410
411         # Don't break-up small packages.
412         my $rowbreak = @$ext_description < 5 ? '*' : '';
413
414         $OUT .= "\\hline\n" if (($line->{'ref'} || 0) ne $lastref);
415         if ($section->{description_generator}) {
416           $OUT .= &{$section->{description_generator}}($line);
417         } elsif ($section->{usage_section}) {
418           my $minutes = sprintf('%d', $line->{'duration'} / 60);
419           my $seconds = $line->{'duration'} % 60;
420           $OUT .= '\FSusagedesc
421             {' . $line->{'description'} . '}
422             {' . $line->{'quantity'} . '}
423             {' . $minutes . 'm ' . $seconds . 's' . '}
424             {' . $line->{'amount'} . '}';
425         } else {
426           $OUT .= '\FSdesc'.
427                   '{}'.
428                   '{' . $line->{'description'} . '}' ;
429           if ( $unitprices and length($line->{'unit_amount'}) ) {
430             # then show the unit amount and quantity
431             $OUT .=
432                 '{\\dollar' . $line->{'unit_amount'} . '}'.
433                 '{'         . $line->{'quantity'}    . '}';
434           } else {
435             # leave those columns blank
436             $OUT .= '{}{}';
437           }
438           $OUT .= '{\\dollar' . $line->{'amount'} . "}${rowbreak}\n";
439         }
440         $lastref = $line->{'ref'} || 0;
441
442         foreach my $ext_desc (@$ext_description) {
443           if ($section->{extended_description_generator}) {
444             $OUT .= &{$section->{extended_description_generator}}($ext_desc);
445           } elsif ( $ext_desc !~ /[^\\]&/ ) {
446             $OUT .= '\FSextdesc{' . $ext_desc . "}$rowbreak\n";
447           } else { # call detail
448             $OUT .= '\FScalldetail{' . $ext_desc . "}$rowbreak\n";
449           }
450         }
451
452       }
453
454       $OUT .= '\end{longtable}';
455     }
456     if ($section->{'posttotal'}) {
457       $OUT .= '\begin{flushright}';
458       $OUT .= '\normalfont\large\bfseries\textsc{'. $section->{'posttotal'}. '}\\\\';
459       $OUT .= '\\end{flushright}';
460     }
461   }
462
463 --@]
464 [@--
465
466   my @location_summary_sections =
467     grep {
468       ref $_->{location}
469       && $_->{locationnum}
470       && $_->{description}
471       && $_->{description} ne $finance_section
472     } @sections;
473   if ( $multisection eq 'location' && scalar(@location_summary_sections) > 1 ) {
474
475 $OUT .= '
476       \hline
477       \section*{}
478       \captionsetup{singlelinecheck=false,justification=raggedright,font={Large,sc,bf}}
479       \ifthenelse{\equal{\thepage}{1}}{\setlength{\LTextracouponspace}{\extracouponspace}}{\setlength{\LTextracouponspace}{0pt}}
480
481       \begin{longtable}{cllllllr}
482       \caption*{ '. emt('Summary of New Charges by Location') .' }
483       \\\\
484
485       \hline
486       \rule{0pt}{2.5ex}
487       \makebox[1.4cm]{} &
488       \multicolumn{6}{l}{
489         \truncate{13.0cm}{\textbf{'. emt('Location') .'}}
490       } &
491       \makebox[1.6cm][r]{\textbf{'. emt('Amount') .'}} \\\\
492       \hline
493
494       \endfirsthead
495       \multicolumn{7}{r}{\rule{0pt}{2.5ex}'. emt('Continued from previous page') .'}
496       \\
497       \FShead
498       \endhead
499       \multicolumn{7}{r}{\rule{0pt}{2.5ex}'. emt('Continued on next page...') .'}
500       \\
501       \endfoot
502       \hline
503       \endlastfoot
504       \hline
505     ';
506
507     for my $section (@location_summary_sections) {
508       $OUT.= '
509         \rule{0pt}{2.5ex}
510         \makebox[1.4cm]{} &
511         \multicolumn{6}{l}{
512           \truncate{12.0cm}{\textbf{'. $section->{description} .'}}
513         } &
514         \makebox[1.6cm][r]{\textbf{'. $section->{subtotal} .'}} \\\\
515       ';
516     }
517
518     $OUT .= '\end{longtable}';
519   }
520 --@]
521
522 \vfill
523 \begin{minipage}[t]{\textwidth}
524   [@-- length($summary)
525          ? ''
526         : ( $smallernotes
527               ? '\scriptsize{ '.$notes.' }'
528               : $notes
529           )
530   --@]
531   [@-- $coupon ? '\ifthenelse{\equal{\thepage}{1}}{\rule{0pt}{\extracouponspace}}{}' : '' --@]
532 \end{minipage}
533 \end{document}