9fcc92259b1c3c7b37976abe71510068a22414ca
[freeside.git] / httemplate / search / customer_usage_profit.html
1 % if ( $cgi->param('_type') =~ /(xls)$/ ) {
2 <%perl>
3   # egregious false laziness w/ search/report_tax-xls.cgi
4   my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format;
5   my $filename = $cgi->url(-relative => 1);
6   $filename =~ s/\.html$//;
7   $filename .= $format->{extension};
8   http_header('Content-Type' => $format->{mime_type});
9   http_header('Content-Disposition' => qq!attachment;filename="$filename"!);
10
11   my $output = '';
12   my $XLS = IO::String->new($output);
13   my $workbook = $format->{class}->new($XLS)
14     or die "Error opening .xls file: $!";
15
16   my $worksheet = $workbook->add_worksheet('Summary');
17
18   my %format = (
19     header => {
20       size      => 11,
21       bold      => 1,
22       align     => 'center',
23       valign    => 'vcenter',
24       text_wrap => 1,
25     },
26     money => {
27       size      => 11,
28       align     => 'right',
29       valign    => 'bottom',
30       num_format=> 8,
31     },
32     '' => {},
33   );
34   my %default = (
35       font      => 'Calibri',
36       border    => 1,
37   );
38   foreach (keys %format) {
39     my %f = (%default, %{$format{$_}});
40     $format{$_} = $workbook->add_format(%f);
41     $format{"m_$_"} = $workbook->add_format(%f);
42   }
43
44   my ($r, $c) = (0, 0);
45   for my $row (@rows) {
46     $c = 0;
47     my $thisrow = shift @cells;
48     for my $cell (@$thisrow) {
49       if (!ref($cell)) {
50         # placeholder, so increment $c so that we write to the correct place
51         $c++;
52         next;
53       }
54       # format name
55       my $f = '';
56       $f = 'header' if $row->{header} or $cell->{header};
57       $f = 'money' if $cell->{format} eq 'money';
58       if ( $cell->{rowspan} > 1 or $cell->{colspan} > 1 ) {
59         my $range = xl_range_formula(
60           'Summary',
61           $r, $r - 1 + ($cell->{rowspan} || 1),
62           $c, $c - 1 + ($cell->{colspan} || 1)
63         );
64         #warn "merging $range\n";
65         $worksheet->merge_range($range, $cell->{value}, $format{"m_$f"});
66       } else {
67       #warn "writing ".xl_rowcol_to_cell($r, $c)."\n";
68         $worksheet->write( $r, $c, $cell->{value}, $format{$f} );
69       }
70       $c++;
71     } #$cell
72   $r++;
73   } #$row
74   $workbook->close;
75
76   http_header('Content-Length' => length($output));
77   $m->print($output);
78 </%perl>
79 % } else {
80 <& /elements/header.html, $title &>
81 % my $myself = $cgi->self_url;
82 <P ALIGN="right" CLASS="noprint">
83 Download full reports<BR>
84 as <A HREF="<% "$myself;_type=xls" %>">Excel spreadsheet</A>
85 </P>
86 <style type="text/css">
87 .report * {
88   background-color: #f8f8f8;
89   border: 1px solid #999999;
90   padding: 2px;
91 }
92 .report td {
93   text-align: right;
94 }
95 .total { background-color: #f5f6be; }
96 .shaded { background-color: #c8c8c8; }
97 .totalshaded { background-color: #bfc094; }
98 </style>
99 <table class="report" width="100%" cellspacing=0>
100 % foreach my $rowinfo (@rows) {
101   <tr<% $rowinfo->{class} ? ' class="'.$rowinfo->{class}.'"' : ''%>>
102 %   my $thisrow = shift @cells;
103 %   foreach my $cell (@$thisrow) {
104 %     next if !ref($cell); # placeholders
105 %     my $td = $cell->{header} ? 'th' : 'td';
106 %     my $style = '';
107 %     $style .= ' class="'.$cell->{class}.'"' if $cell->{class};
108 %     $style .= " rowspan=".$cell->{rowspan} if $cell->{rowspan} > 1;
109 %     $style .= " colspan=".$cell->{colspan} if $cell->{colspan} > 1;
110 %     $style .= ' style="color: red"' if $cell->{value} < 0;
111       <<%$td%><%$style%>><% $cell->{value} |h %></<%$td%>>
112 %   }
113   </tr>
114 % }
115 </table>
116
117 <& /elements/footer.html &>
118 % }
119 <%init>
120
121 die "access denied"
122   unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
123
124 my ($agentnum,$sel_agent);
125 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
126   $agentnum = $1;
127   $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } );
128   die "agentnum $agentnum not found!" unless $sel_agent;
129 }
130 my $title = $sel_agent ? $sel_agent->agent.' ' : '';
131
132 $title .= 'Customer Usage Profit/Loss Report';
133
134 my @items  = ('cust_bill_pkg_recur', 'cust_bill_pkg_recur', 'cust_bill_pkg_detail', 'cust_bill_pkg_detail' );
135 my @params = ( [], [ 'cost' => 1 ], [], [ 'cost' => 1 ] );
136
137 my @labels = ();
138 my @cross_params = ();
139
140 my %search_hash;
141 foreach (qw(agentnum)) {
142   if ( defined $cgi->param($_) ) {
143     $search_hash{$_} = $cgi->param($_);
144   }
145 }
146
147 my $query = FS::cust_main::Search->search(\%search_hash);
148 my @cust_main = qsearch($query);
149
150 foreach my $cust_main (@cust_main) {
151   push @cross_params, [ ('custnum' => $cust_main->custnum) ];
152 }
153
154 my %opt = (
155   items         => \@items,
156   params        => \@params,
157   cross_params  => \@cross_params,
158   agentnum      => $agentnum,
159 );
160 for ( qw(start_month start_year end_month end_year) ) {
161   if ( $cgi->param($_) =~ /^(\d+)$/ ) {
162     $opt{$_} = $1;
163   }
164 }
165
166 my $report = FS::Report::Table::Monthly->new(%opt);
167 my $data = $report->data;
168
169 ### False laziness with customer_accounting_summary.html
170 my @total;
171
172 my @rows; # hashes of row info
173 my @cells; # arrayrefs of cell info
174 # We use Excel currency format, but not Excel dates, because
175 # these are whole months and there's no nice way to express that.
176 # This is the historical behavior for monthly reports.
177
178 # header row
179 $rows[0] = {};
180 $cells[0] = [
181   { header => 1, rowspan => 2 },
182   map {
183     { header => 1, colspan => 5, value => time2str('%b %Y', $_) }
184   } @{ $data->{speriod} }
185 ];
186 my $ncols = scalar(@{ $data->{speriod} });
187
188 $rows[1] = {};
189 $cells[1] = [ '',
190   map { 
191   ( 
192     { header => 1, value => mt('Recur Fee') },
193     { header => 1, value => mt('Recur Cost') },
194     { header => 1, value => mt('Usage Fee') },
195     { header => 1, value => mt('Usage Cost') },
196     { header => 1, value => mt('Profit'), class => 'shaded' },
197   ) } (1..$ncols)
198 ];
199
200 my $row = 0;
201 foreach my $cust_main (@cust_main) { # correspond to cross_params
202   my $skip = 1; # skip the customer iff ALL of their values are zero
203   push @rows, {};
204   my @thisrow;
205   # customer name
206   push @thisrow,
207     { value   => $cust_main->name,
208       header  => 1
209     };
210   for my $col (0..$ncols-1) { # the month
211     my $profit = 0;
212     for my $item (0..3) { # recur/recur_cost/usage/usage_cost
213       my $value = $data->{data}[$item][$col][$row];
214       $skip = 0 if abs($value) > 0.005;
215       push @thisrow, { value => sprintf('%0.2f', $value), format => 'money' };
216       $total[$col * 5 + $item] += $value;
217       $profit += (($item % 2) ? -1 : 1) * $value;
218     } #item
219     push @thisrow, { 
220       value => sprintf('%0.2f', $profit), 
221       format => 'money',
222       class => 'shaded',
223     };
224     $total[$col * 5 + 4] += $profit;
225   } #month
226   push @cells, \@thisrow;
227
228   if ( $skip ) {
229     # all values are zero--remove the rows we just added
230     pop @rows;
231     pop @cells;
232   }
233   $row++;
234 }
235
236 push @rows, { class => 'total' };
237 my @thisrow;
238 push @thisrow,
239   { value => mt('Total'),
240     header => 1
241   };
242 for my $col (0..($ncols * 5)-1) { # month and recur/recur_cost/usage/usage_cost/profit
243   my $value = $total[$col];
244   push @thisrow, { 
245     value => sprintf('%0.2f', $value), 
246     format => 'money',
247     class => ($col % 5 == 4) ? 'totalshaded' : 'total',
248   };
249 }
250 push @cells, \@thisrow;
251
252 </%init>