multi-select for agent on RADIUS usage report, RT#73050
[freeside.git] / httemplate / search / prepaid_income.html
1 <% include("/elements/header.html", 'Prepaid Income (Unearned Revenue) Report') %>
2
3 <% include( '/elements/table-grid.html' ) %>
4
5   <TR>
6 %   if ( scalar(@agentnums) > 1 ) {
7       <TH CLASS="grid" BGCOLOR="#cccccc">Agent</TH>
8 %   }
9     <TH CLASS="grid" BGCOLOR="#cccccc"><% $actual_label %>Unearned Revenue</TH>
10 %   if ( $legacy ) {
11       <TH CLASS="grid" BGCOLOR="#cccccc">Legacy Unearned Revenue</TH>
12 %   }
13   </TR>
14
15 % my $bgcolor1 = '#eeeeee';
16 % my $bgcolor2 = '#ffffff';
17 % my $bgcolor;
18 %
19 % push @agentnums, 0 unless scalar(@agentnums) < 2;
20 % foreach my $agentnum (@agentnums) {  
21 %
22 %   if ( $bgcolor eq $bgcolor1 ) {
23 %     $bgcolor = $bgcolor2;
24 %   } else {
25 %     $bgcolor = $bgcolor1;
26 %   }
27 %
28 %   my $alink = $agentnum ? "$link;agentnum=$agentnum" : $link;
29 %
30 %   my $agent_name = 'Total';
31 %   if ( $agentnum ) {
32 %     my $agent = qsearchs('agent', { 'agentnum' => $agentnum })
33 %       or die "unknown agentnum $agentnum";
34 %     $agent_name = $agent->agent;
35 %   }
36
37     <TR>
38
39 %     if ( scalar(@agentnums) > 1 ) {
40         <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $agent_name |h %></TD>
41 %     }
42
43       <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $alink %>"><% $money_char %><% $total{$agentnum} %></A></TD>
44
45 %     if ( $legacy ) {
46         <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
47           <% $now == $time ? $money_char.$total_legacy{$agentnum} : '<i>N/A</i>'%>
48         </TD>
49 %     }
50
51     </TR>
52
53 %  }
54
55 </TABLE>
56
57 <BR>
58 <% $actual_label %><% $actual_label ? 'u' : 'U' %>nearned revenue
59 is the as-yet-unearned portion of revenue
60 <% $actual_label ? 'Freeside has actually' : '' %>
61 invoiced for packages with 
62 <% $cgi->param('include_monthly') ? 'terms extending into the future.'
63                                   : 'longer-than monthly terms.' %>
64
65 % if ( $legacy ) {
66   <BR><BR>
67   Legacy unearned revenue is the amount of unearned revenue represented by 
68   customer packages.  This number may be larger than actual unearned 
69   revenue if you have imported longer-than monthly customer packages from
70   a previous billing system.
71 % }
72
73 <% include('/elements/footer.html') %>
74 <%init>
75
76 die "access denied"
77   unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
78
79 my $conf = new FS::Conf;
80 my $money_char = $conf->config('money_char') || '$';
81
82 my $legacy = $conf->exists('enable_legacy_prepaid_income');
83 my $actual_label = $legacy ? 'Actual ' : '';
84
85 #doesn't yet deal with daily/weekly packages
86
87 my $mode = $cgi->param('mode');
88
89 my $time = time;
90
91 my $now = $cgi->param('date') && parse_datetime($cgi->param('date')) || $time;
92 $now =~ /^(\d+)$/ or die "unparsable date?";
93 $now = $1;
94
95 my $dt = DateTime->from_epoch(epoch => $now, time_zone => 'local');
96 $dt->truncate(to => 'day'); # local midnight on the report day
97 $dt->add(days => 1); # the day after that
98 $dt->subtract(seconds => 1); # the last second of the report day
99 $now = $dt->epoch;
100
101 my $link = "unearned_detail.html?date=$now;mode=$mode";
102
103 if ( $cgi->param('include_monthly') ) {
104   $link .= ';include_monthly=1';
105 }
106
107 my $curuser = $FS::CurrentUser::CurrentUser;
108
109 my $agentnum = '';
110 my @agentnums = ();
111 $agentnum ? ($agentnum) : $curuser->agentnums;
112 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
113   @agentnums = ($1);
114   #XXX#push @where, "agentnum = $agentnum";
115   #XXX#$link .= ";agentnum=$agentnum";
116 } else {
117   @agentnums = $curuser->agentnums;
118 }
119
120 my @where = ();
121
122 #here is the agent virtualization
123 push @where, $curuser->agentnums_sql( 'table'=>'cust_main' );
124
125 my $status = '';
126 if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
127   $status = $1;
128   $link .= ";status=$status";
129   push @where, FS::cust_main->cust_status_sql . " = '$status'";
130 }
131
132 # cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql)
133 if ( grep { $_ eq 'cust_classnum' } $cgi->param ) {
134   my @classnums = grep /^\d*$/, $cgi->param('cust_classnum');
135   $link .= ";cust_classnum=$_" foreach @classnums;
136   push @where, 'COALESCE( cust_main.classnum, 0) IN ( '.
137                    join(',', map { $_ || '0' } @classnums ).
138                ' )'
139     if @classnums;
140 }
141
142 my %total = ();
143 my %total_legacy = ();
144 foreach my $agentnum (@agentnums) {
145   
146   my $where = join(' AND ', @where, "cust_main.agentnum = $agentnum");
147   $where = "AND $where" if $where;
148
149   my( $total, $total_legacy ) = ( 0, 0 );
150
151   my @opt = ($now, '', setuprecur => 'recur', no_usage => 1);
152   # balance owed, recurring only, not including usage charges
153   my $unearned_base;
154   if ( $mode eq 'billed' ) {
155     $unearned_base = '( ' . 
156                      FS::cust_bill_pkg->charged_sql(@opt) . ' - ' .
157                      FS::cust_bill_pkg->credited_sql(@opt) . ' )';
158   } elsif ( $mode eq 'paid' ) {
159     $unearned_base = FS::cust_bill_pkg->paid_sql(@opt);
160   }
161   
162   my $edate_zero = midnight_sql('edate');
163   my $sdate_zero = midnight_sql('sdate');
164   my $period = "CAST( ($edate_zero - $sdate_zero) / 86400.0 AS DECIMAL(10,0) )";
165   my $remaining = "GREATEST(
166     CAST( ($edate_zero - $now) / 86400.0 AS DECIMAL(10,0) ),
167     0)";
168   my $fraction = "$remaining / $period";
169   
170   my $unearned_sql = "CAST(
171   GREATEST( $unearned_base * $fraction, 0 )
172     AS DECIMAL(10,2)
173   )";
174
175   my $select = "SUM( $unearned_sql )";
176
177   if ( !$cgi->param('include_monthly') ) {
178     # all except freq != 0; one-time charges should never be included
179     $where .= "
180                  AND part_pkg.freq != '1'
181                  AND part_pkg.freq NOT LIKE '%h'
182                  AND part_pkg.freq NOT LIKE '%d'
183                  AND part_pkg.freq NOT LIKE '%w'";
184   }
185
186   # $mode actually doesn't matter here, since unpaid invoices have zero
187   # unearned revenue
188
189   my $sql = 
190   "SELECT $select FROM cust_bill_pkg
191                   LEFT JOIN cust_pkg  ON (cust_bill_pkg.pkgnum = cust_pkg.pkgnum)
192                   LEFT JOIN part_pkg  USING ( pkgpart )
193                   LEFT JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
194                WHERE pkgpart > 0
195                  AND cust_bill_pkg.sdate < $now
196                  AND cust_bill_pkg.edate > $now
197                  AND cust_bill_pkg.recur != 0
198                  AND part_pkg.freq != '0'
199                  $where
200              ";
201
202   my $sth = dbh->prepare($sql) or die dbh->errstr;
203   $sth->execute or die $sth->errstr;
204   my $total = $sth->fetchrow_arrayref->[0];
205
206   $total = sprintf('%.2f', $total);
207   $total{$agentnum} = $total;
208   $total{0} += $total;
209
210   if ( $legacy ) {
211
212     #not yet rewritten in sql, but now not enabled by default
213
214     my @cust_pkg = 
215       grep { $_->part_pkg->recur != 0
216              && $_->part_pkg->freq !~ /^([01]|\d+[dw])$/
217            }
218         qsearch({
219           'select'    => 'cust_pkg.*',
220           'table'     => 'cust_pkg',
221           'addl_from' => ' LEFT JOIN cust_main USING ( custnum ) ',
222           'hashref'   => { 'bill' => { op=>'>', value=>$now } },
223           'extra_sql' => $where,
224         });
225
226     foreach my $cust_pkg ( @cust_pkg ) {
227       my $period = $cust_pkg->bill - $cust_pkg->last_bill;
228    
229       my $elapsed = $now - $cust_pkg->last_bill;
230       $elapsed = 0 if $elapsed < 0;
231    
232       my $remaining = 1 - $elapsed/$period;
233    
234       my $unearned = $remaining * $cust_pkg->part_pkg->recur; #!! only works for flat/legacy
235       $total_legacy += $unearned;
236    
237     }
238
239     $total_legacy = sprintf('%.2f', $total_legacy);
240     $total_legacy{$agentnum} = $total_legacy;
241     $total_legacy{0} += $total_legacy;
242
243   }
244
245 }
246
247 $total{0} = sprintf('%.2f', $total{0});
248 $total_legacy{0} = sprintf('%.2f', $total_legacy{0});
249   
250 </%init>