improvements to prepaid income report, #13289
[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 amount of unearned 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 $link = "cust_bill_pkg.cgi?nottax=1;unearned_now=$now;mode=$mode";
96
97 if ( $cgi->param('include_monthly') ) {
98   $link .= ';include_monthly=1';
99 }
100
101 my $curuser = $FS::CurrentUser::CurrentUser;
102
103 my $agentnum = '';
104 my @agentnums = ();
105 $agentnum ? ($agentnum) : $curuser->agentnums;
106 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
107   @agentnums = ($1);
108   #XXX#push @where, "agentnum = $agentnum";
109   #XXX#$link .= ";agentnum=$agentnum";
110 } else {
111   @agentnums = $curuser->agentnums;
112 }
113
114 my @where = ();
115
116 #here is the agent virtualization
117 push @where, $curuser->agentnums_sql( 'table'=>'cust_main' );
118
119 my $status = '';
120 if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
121   $status = $1;
122   $link .= ";status=$status";
123   push @where, FS::cust_main->cust_status_sql . " = '$status'";
124 }
125
126 my %total = ();
127 my %total_legacy = ();
128 foreach my $agentnum (@agentnums) {
129   
130   my $where = join(' AND ', @where, "cust_main.agentnum = $agentnum");
131   $where = "AND $where" if $where;
132
133   my( $total, $total_legacy ) = ( 0, 0 );
134
135   my $float = 'REAL'; #'DOUBLE PRECISION';
136
137   my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS $float)";
138   my $elapsed = "(CASE WHEN cust_bill_pkg.sdate > $now
139                    THEN 0
140                    ELSE ($now - cust_bill_pkg.sdate)
141                  END)";
142   #my $elapsed = "CAST($unearned - cust_bill_pkg.sdate AS $float)";
143
144   my $remaining = "(1 - $elapsed/$period)";
145
146   my $base;
147   my $from = '';
148   if ( $mode eq 'billed' ) {
149     $base = 'cust_bill_pkg.recur';
150     $from = 'cust_bill_pkg';
151   }
152   elsif ( $mode eq 'paid' ) {
153     $base = 'cust_bill_pay_pkg.amount';
154     $from = 'cust_bill_pay_pkg 
155              LEFT JOIN cust_bill_pkg USING ( billpkgnum )
156              LEFT JOIN cust_bill_pay USING ( billpaynum )
157              LEFT JOIN cust_pay      USING ( paynum )';
158     $where .= ' AND ' if $where;
159     $where .= "cust_bill_pay_pkg.setuprecur = 'recur' 
160                AND cust_pay._date <= $now";
161   }
162   else {
163     die "invalid mode '$mode'";
164   }
165   my $select = "SUM($remaining * $base)";
166
167   if ( !$cgi->param('include_monthly') ) {
168     # all except freq != 0; one-time charges should never be included
169     $where .= "
170                  AND part_pkg.freq != '1'
171                  AND part_pkg.freq NOT LIKE '%h'
172                  AND part_pkg.freq NOT LIKE '%d'
173                  AND part_pkg.freq NOT LIKE '%w'";
174   }
175
176   my $sql = 
177   "SELECT $select FROM $from 
178                   LEFT JOIN cust_pkg  ON (cust_bill_pkg.pkgnum = cust_pkg.pkgnum)
179                   LEFT JOIN part_pkg  USING ( pkgpart )
180                   LEFT JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
181                WHERE pkgpart > 0
182                  AND cust_bill_pkg.sdate < $now
183                  AND cust_bill_pkg.edate > $now
184                  AND cust_bill_pkg.recur != 0
185                  AND part_pkg.freq != '0'
186                  $where
187              ";
188
189   my $sth = dbh->prepare($sql) or die dbh->errstr;
190   $sth->execute or die $sth->errstr;
191   my $total = $sth->fetchrow_arrayref->[0];
192
193   $total = sprintf('%.2f', $total);
194   $total{$agentnum} = $total;
195   $total{0} += $total;
196
197   if ( $legacy ) {
198
199     #not yet rewritten in sql, but now not enabled by default
200
201     my @cust_pkg = 
202       grep { $_->part_pkg->recur != 0
203              && $_->part_pkg->freq !~ /^([01]|\d+[dw])$/
204            }
205         qsearch({
206           'select'    => 'cust_pkg.*',
207           'table'     => 'cust_pkg',
208           'addl_from' => ' LEFT JOIN cust_main USING ( custnum ) ',
209           'hashref'   => { 'bill' => { op=>'>', value=>$now } },
210           'extra_sql' => $where,
211         });
212
213     foreach my $cust_pkg ( @cust_pkg ) {
214       my $period = $cust_pkg->bill - $cust_pkg->last_bill;
215    
216       my $elapsed = $now - $cust_pkg->last_bill;
217       $elapsed = 0 if $elapsed < 0;
218    
219       my $remaining = 1 - $elapsed/$period;
220    
221       my $unearned = $remaining * $cust_pkg->part_pkg->recur; #!! only works for flat/legacy
222       $total_legacy += $unearned;
223    
224     }
225
226     $total_legacy = sprintf('%.2f', $total_legacy);
227     $total_legacy{$agentnum} = $total_legacy;
228     $total_legacy{0} += $total_legacy;
229
230   }
231
232 }
233
234 $total{0} = sprintf('%.2f', $total{0});
235 $total_legacy{0} = sprintf('%.2f', $total_legacy{0});
236   
237 </%init>