refactor and fix customer accounting summary report, #24112 and #40103
[freeside.git] / httemplate / search / elements / grid-report.html
1 <%doc>
2
3 Simple display front-end for reports that produce some kind of data table,
4 which the user can request as an Excel spreadsheet. /elements/header.html
5 and /elements/footer.html are included automatically, so don't include them
6 again.
7
8 Usage:
9
10 <& elements/grid-report.html,
11   title => 'My Report',
12   rows => [
13     { header => 1, },
14     ...
15   ],
16   cells => [
17     [ # row 0
18       { value => '123.45',
19         # optional
20         format => 'money',
21         header => 1,
22         rowspan => 2,
23         colspan => 3,
24         class => 'highlight',
25       },
26       ...
27     ],
28   ],
29   head => q[<div>Thing to insert before the table</div>],
30   foot => q[<span>That's all folks!</span>].
31 &>
32 </%doc>
33 % if ( $cgi->param('_type') =~ /(xls)$/ ) {
34 <%perl>
35   # egregious false laziness w/ search/report_tax-xls.cgi
36   # and search/customer_cdr_profit.html
37   my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format;
38   my $filename = $cgi->url(-relative => 1);
39   $filename =~ s/\.html$//;
40   $filename .= $format->{extension};
41   http_header('Content-Type' => $format->{mime_type});
42   http_header('Content-Disposition' => qq!attachment;filename="$filename"!);
43
44   my $output = '';
45   my $XLS = IO::String->new($output);
46   my $workbook = $format->{class}->new($XLS)
47     or die "Error opening .xls file: $!";
48
49   my $worksheet = $workbook->add_worksheet('Summary');
50
51   my %format = (
52     header => {
53       size      => 11,
54       bold      => 1,
55       align     => 'center',
56       valign    => 'vcenter',
57       text_wrap => 1,
58     },
59     money => {
60       size      => 11,
61       align     => 'right',
62       valign    => 'bottom',
63       num_format=> 8,
64     },
65     '' => {},
66   );
67   my %default = (
68       font      => 'Calibri',
69       border    => 1,
70   );
71   foreach (keys %format) {
72     my %f = (%default, %{$format{$_}});
73     $format{$_} = $workbook->add_format(%f);
74     $format{"m_$_"} = $workbook->add_format(%f);
75   }
76
77   my ($r, $c) = (0, 0);
78   # indices in these correspond to column positions
79   my @rowspans;
80   my @widths;
81   
82   for my $row (@rows) {
83     $c = 0;
84     my $thisrow = shift @cells;
85     for my $cell (@$thisrow) {
86       # skip over cells that are occupied by rowspans above them
87       while ($rowspans[$c]) {
88         $rowspans[$c]--;
89         $c++;
90       }
91
92       # skip this cell if it's empty, also
93       next if !ref($cell);
94       # format name
95       my $f = '';
96       $f = 'header' if $row->{header} or $cell->{header};
97       $f = 'money' if $cell->{format} eq 'money';
98       if ( $cell->{rowspan} > 1 or $cell->{colspan} > 1 ) {
99         my $range = xl_range_formula(
100           'Summary',
101           $r, $r - 1 + ($cell->{rowspan} || 1),
102           $c, $c - 1 + ($cell->{colspan} || 1)
103         );
104         #warn "merging $range\n";
105         $worksheet->merge_range($range, $cell->{value}, $format{"m_$f"});
106       } else {
107       #warn "writing ".xl_rowcol_to_cell($r, $c)."\n";
108         $worksheet->write( $r, $c, $cell->{value}, $format{$f} );
109       }
110
111       # estimate column width, as in search-xls, but without date formats
112       my $width = length($cell->{value}) / ($cell->{colspan} || 1);
113       $width *= 1.1 if $f eq 'header';
114       $width++ if $f eq 'money'; # for money symbol
115       $width += 2; # pad it
116
117       for (1 .. ($cell->{colspan} || 1)) {
118         # adjust minimum widths to allow for this cell's contents
119         $widths[$c] = $width if $width > ($widths[$c] || 0);
120
121         # and if this cell has a rowspan, block off that many rows below it
122         if ( $cell->{rowspan} > 1 ) {
123           $rowspans[$c] = $cell->{rowspan} - 1;
124         }
125         $c++;
126       }
127     } #$cell
128   $r++;
129   } #$row
130
131   $c = 0;
132   for my $c (0 .. scalar(@widths) - 1) {
133     $worksheet->set_column($c, $c, $widths[$c]);
134   }
135   $workbook->close;
136
137   http_header('Content-Length' => length($output));
138   $m->print($output);
139 </%perl>
140 % } else {
141 <& /elements/header.html, $title &>
142 <% $head %>
143 % my $myself = $cgi->self_url;
144 <P ALIGN="right" CLASS="noprint">
145 Download full reports<BR>
146 as <A HREF="<% "$myself;_type=xls" %>">Excel spreadsheet</A><BR>
147 % # as <A HREF="<% "$myself;_type=csv" %>">CSV file</A> # is this still needed?
148 </P>
149 <style type="text/css">
150 .report * {
151   background-color: #f8f8f8;
152   border: 1px solid #999999;
153   padding: 2px;
154 }
155 .report td {
156   text-align: right;
157 }
158 .total * { background-color: #f5f6be; }
159 .shaded * { background-color: #c8c8c8; }
160 .totalshaded * { background-color: #bfc094; }
161 </style>
162 <table class="report" width="100%" cellspacing=0>
163 % foreach my $rowinfo (@rows) {
164   <tr<% $rowinfo->{class} ? ' class="'.$rowinfo->{class}.'"' : ''%>>
165 %   my $thisrow = shift @cells;
166 %   foreach my $cell (@$thisrow) {
167 %     next if !ref($cell); # placeholders
168 %     my $td = $cell->{header} ? 'th' : 'td';
169 %     my $style = '';
170 %     $style .= " rowspan=".$cell->{rowspan} if $cell->{rowspan} > 1;
171 %     $style .= " colspan=".$cell->{colspan} if $cell->{colspan} > 1;
172 %     $style .= ' class="' . $cell->{class} . '"' if $cell->{class};
173       <<%$td%><%$style%>><% $cell->{value} |h %></<%$td%>>
174 %   }
175   </tr>
176 % }
177 </table>
178 <% $foot %>
179 <& /elements/footer.html &>
180 % }
181 <%args>
182 $title
183 @rows
184 @cells
185 $head => ''
186 $foot => ''
187 </%args>