c0e2b807e1847b6bc8557a4c08e6a1955cb107b3
[freeside.git] / httemplate / search / report_prepaid_income.cgi
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 longer-than monthly terms.
62
63 % if ( $legacy ) {
64   <BR><BR>
65   Legacy unearned revenue is the amount of unearned revenue represented by 
66   customer packages.  This number may be larger than actual unearned 
67   revenue if you have imported longer-than monthly customer packages from
68   a previous billing system.
69 % }
70
71 <% include('/elements/footer.html') %>
72 <%init>
73
74 die "access denied"
75   unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
76
77 my $conf = new FS::Conf;
78 my $money_char = $conf->config('money_char') || '$';
79
80 my $legacy = $conf->exists('enable_legacy_prepaid_income');
81 my $actual_label = $legacy ? 'Actual ' : '';
82
83 #doesn't yet deal with daily/weekly packages
84
85 my $time = time;
86
87 my $now = $cgi->param('date') && str2time($cgi->param('date')) || $time;
88 $now =~ /^(\d+)$/ or die "unparsable date?";
89 $now = $1;
90
91 my $link = "cust_bill_pkg.cgi?nottax=1;unearned_now=$now";
92
93 my $curuser = $FS::CurrentUser::CurrentUser;
94
95 my $agentnum = '';
96 my @agentnums = ();
97 $agentnum ? ($agentnum) : $curuser->agentnums;
98 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
99   @agentnums = ($1);
100   #XXX#push @where, "agentnum = $agentnum";
101   #XXX#$link .= ";agentnum=$agentnum";
102 } else {
103   @agentnums = $curuser->agentnums;
104 }
105
106 my @where = ();
107
108 #here is the agent virtualization
109 push @where, $curuser->agentnums_sql( 'table'=>'cust_main' );
110
111 #well, because cust_bill_pkg.cgi has it and without it the numbers don't match..
112 push @where , " payby != 'COMP' "
113   unless $cgi->param('include_comp_cust');
114
115 my %total = ();
116 my %total_legacy = ();
117 foreach my $agentnum (@agentnums) {
118   
119   my $where = join(' AND ', @where, "cust_main.agentnum = $agentnum");
120   $where = "AND $where" if $where;
121
122   my( $total, $total_legacy ) = ( 0, 0 );
123
124   # my @cust_bill_pkg =
125   #   grep { $_->cust_pkg && $_->cust_pkg->part_pkg->freq !~ /^([01]|\d+[hdw])$/ }
126   #     qsearch({
127   #       'select'    => 'cust_bill_pkg.*',
128   #       'table'     => 'cust_bill_pkg',
129   #       'addl_from' => ' LEFT JOIN cust_bill USING ( invnum  ) '.
130   #                      ' LEFT JOIN cust_main USING ( custnum ) ',
131   #       'hashref'   => {
132   #                        'recur' => { op=>'!=', value=>0    },
133   #                        'sdate' => { op=>'<',  value=>$now },
134   #                        'edate' => { op=>'>',  value=>$now },
135   #                      },
136   #       'extra_sql' => $where,
137   #     });
138   #
139   #    foreach my $cust_bill_pkg ( @cust_bill_pkg) { 
140   #      my $period = $cust_bill_pkg->edate - $cust_bill_pkg->sdate;
141   #   
142   #      my $elapsed = $now - $cust_bill_pkg->sdate;
143   #      $elapsed = 0 if $elapsed < 0;
144   #   
145   #      my $remaining = 1 - $elapsed/$period;
146   #   
147   #      my $unearned = $remaining * $cust_bill_pkg->recur;
148   #      $total += $unearned;
149   #   
150   #    }
151
152   #re-written in sql:
153
154   #false laziness w/cust_bill_pkg.cgi
155
156   my $float = 'REAL'; #'DOUBLE PRECISION';
157
158   my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS $float)";
159   my $elapsed = "(CASE WHEN cust_bill_pkg.sdate > $now
160                    THEN 0
161                    ELSE ($now - cust_bill_pkg.sdate)
162                  END)";
163   #my $elapsed = "CAST($unearned - cust_bill_pkg.sdate AS $float)";
164
165   my $remaining = "(1 - $elapsed/$period)";
166
167   my $select = "SUM($remaining * cust_bill_pkg.recur)";
168
169   #[...]
170
171   my $sql = "SELECT $select FROM cust_bill_pkg
172                             LEFT JOIN cust_pkg  USING ( pkgnum )
173                             LEFT JOIN part_pkg  USING ( pkgpart )
174                             LEFT JOIN cust_main USING ( custnum )
175                WHERE pkgpart > 0
176                  AND sdate < $now
177                  AND edate > $now
178                  AND cust_bill_pkg.recur != 0
179                  AND part_pkg.freq != '0'
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                  $where
185              ";
186
187   my $sth = dbh->prepare($sql) or die dbh->errstr;
188   $sth->execute or die $sth->errstr;
189   my $total = $sth->fetchrow_arrayref->[0];
190
191   $total = sprintf('%.2f', $total);
192   $total{$agentnum} = $total;
193   $total{0} += $total;
194
195   if ( $legacy ) {
196
197     #not yet rewritten in sql, but now not enabled by default
198
199     my @cust_pkg = 
200       grep { $_->part_pkg->recur != 0
201              && $_->part_pkg->freq !~ /^([01]|\d+[dw])$/
202            }
203         qsearch({
204           'select'    => 'cust_pkg.*',
205           'table'     => 'cust_pkg',
206           'addl_from' => ' LEFT JOIN cust_main USING ( custnum ) ',
207           'hashref'   => { 'bill' => { op=>'>', value=>$now } },
208           'extra_sql' => $where,
209         });
210
211     foreach my $cust_pkg ( @cust_pkg ) {
212       my $period = $cust_pkg->bill - $cust_pkg->last_bill;
213    
214       my $elapsed = $now - $cust_pkg->last_bill;
215       $elapsed = 0 if $elapsed < 0;
216    
217       my $remaining = 1 - $elapsed/$period;
218    
219       my $unearned = $remaining * $cust_pkg->part_pkg->recur; #!! only works for flat/legacy
220       $total_legacy += $unearned;
221    
222     }
223
224     $total_legacy = sprintf('%.2f', $total_legacy);
225     $total_legacy{$agentnum} = $total_legacy;
226     $total_legacy{0} += $total_legacy;
227
228   }
229
230 }
231
232 $total{0} = sprintf('%.2f', $total{0});
233 $total_legacy{0} = sprintf('%.2f', $total_legacy{0});
234   
235 </%init>