totals time used on svc_acct report
[freeside.git] / httemplate / search / svc_acct.cgi
1 <% include( 'elements/search.html',
2                  'title'       => 'Account Search Results',
3                  'name'        => 'accounts',
4                  'query'       => $sql_query,
5                  'count_query' => $count_query,
6                  'redirect'    => $link,
7                  'header'      => \@header,
8                  'fields'      => \@fields,
9                  'links'       => \@links,
10                  'align'       => $align,
11                  'color'       => \@color,
12                  'style'       => \@style,
13                  'footer'      => \@footer,
14              )
15 %>
16 <%once>
17
18 #false laziness w/ClientAPI/MyAccount.pm
19 sub format_time { 
20   my $support = shift;
21   (($support < 0) ? '-' : '' ). int(abs($support)/3600)."h".sprintf("%02d",(abs($support)%3600)/60)."m";
22 }
23
24 sub timelast {
25   my( $svc_acct, $last, $permonth ) = @_;
26
27   my $sql = "
28     SELECT SUM(support) FROM acct_rt_transaction
29       LEFT JOIN Transactions
30         ON Transactions.Id = acct_rt_transaction.transaction_id
31     WHERE svcnum = ? 
32       AND Transactions.Created >= ?
33   ";
34
35   my $sth = dbh->prepare($sql) or die dbh->errstr;
36   $sth->execute( $svc_acct->svcnum,
37                  time2str('%Y-%m-%d %X', time - $last*86400 ) 
38                )
39     or die $sth->errstr;
40
41   my $seconds = $sth->fetchrow_arrayref->[0];
42
43   #my $return = (($seconds < 0) ? '-' : '') . concise(duration($seconds));
44   my $return = (($seconds < 0) ? '-' : '') . format_time($seconds);
45
46   $return .= sprintf(' (%.2fx)', $seconds / $permonth ) if $permonth;
47
48   $return;
49
50 }
51
52 </%once>
53 <%init>
54
55 die "access denied"
56   unless $FS::CurrentUser::CurrentUser->access_right('List services');
57
58 my $link      = [ "${p}view/svc_acct.cgi?",   'svcnum'  ];
59 my $link_cust = sub {
60   my $svc_acct = shift;
61   if ( $svc_acct->custnum ) {
62     [ "${p}view/cust_main.cgi?", 'custnum' ];
63   } else {
64     '';
65   }
66 };
67
68 my %search_hash = ();
69 my @extra_sql = ();
70
71 my @header = ( '#', 'Service', 'Account', 'UID', 'Last Login' );
72 my @fields = ( 'svcnum', 'svc', 'email', 'uid', 'last_login_text' );
73 my @links = ( $link, $link, $link, $link, $link );
74 my $align = 'rlllr';
75 my @color = ( '', '', '', '', '' );
76 my @style = ( '', '', '', '', '' );
77 my @footer = ();
78
79 for (qw( domain domsvc agentnum custnum popnum svcpart cust_fields )) {
80   $search_hash{$_} = $cgi->param($_) if length($cgi->param($_));
81 }
82
83 my $timepermonth = '';
84
85 my $orderby = 'ORDER BY svcnum';
86 if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
87
88   $search_hash{'unlinked'} = 1
89     if $cgi->param('magic') eq 'unlinked';
90
91   my $sortby = '';
92   if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
93     $sortby = $1;
94     $sortby = "LOWER($sortby)"
95       if $sortby eq 'username';
96     push @extra_sql, "$sortby IS NOT NULL" #XXX search_hash
97       if $sortby eq 'uid' || $sortby eq 'seconds' || $sortby eq 'last_login';
98     $orderby = "ORDER BY $sortby";
99   }
100
101   if ( $sortby eq 'seconds' ) {
102     my $tot_time = 0;
103     #push @header, 'Time remaining';
104     push @header, 'Time';
105     push @fields, sub { my $svc_acct = shift;
106                         $tot_time += $svc_acct->seconds;
107                         format_time($svc_acct->seconds);
108                       };
109     push @links, '';
110     $align .= 'r';
111     push @color, '';
112     push @style, '';
113
114     @footer = ( '', 'Total', '', '', '',
115                 sub { format_time($tot_time) }, #time
116               );
117
118     my $conf = new FS::Conf;
119     if ( $conf->exists('svc_acct-display_paid_time_remaining') ) {
120       my $tot_paid_time = 0;
121       my %tot = ( '30'=>0, '60'=>0, '90'=>0 );
122       push @header, 'Paid time', 'Last 30', 'Last 60', 'Last 90';
123       push @fields,
124         sub {
125           my $svc_acct = shift;
126           my $seconds = $svc_acct->seconds;
127           my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
128           my $part_pkg = $cust_pkg->part_pkg;
129
130           #my $timepermonth = $part_pkg->option('seconds');
131           $timepermonth = $part_pkg->option('seconds');
132           $timepermonth = $timepermonth / $part_pkg->freq
133             if $part_pkg->freq =~ /^\d+$/ && $part_pkg->freq != 0;
134
135           #my $recur = $part_pkg->calc_recur($cust_pkg);
136           my $recur = $part_pkg->base_recur($cust_pkg);
137
138           return format_time($seconds) unless $timepermonth && $recur;
139
140           my $balance = $cust_pkg->cust_main->balance;
141           my $periods_unpaid = $balance / $recur;
142           my $time_unpaid = $periods_unpaid * $timepermonth;
143           $time_unpaid *= $part_pkg->freq
144             if $part_pkg->freq =~ /^\d+$/ && $part_pkg->freq != 0;
145           $tot_paid_time += $seconds-$time_unpaid;
146           format_time($seconds-$time_unpaid).
147             sprintf(' (%.2fx monthly)', ( $seconds-$time_unpaid ) / $timepermonth );
148         },
149         sub { timelast( shift, 30, $timepermonth ); },
150         sub { timelast( shift, 60, $timepermonth ); },
151         sub { timelast( shift, 90, $timepermonth ); },
152       ;
153       push @links, '', '', '', '';
154       $align .= 'rrrr';
155       push @color, '', '', '', '';
156       push @style, '', '', '', '';
157       push @footer, 
158         sub { format_time($tot_paid_time) }, #paid time
159         '', #XXX sub { $tot{'30'} }, #30
160         '', #XXX sub { $tot{'60'} }, #60
161         '', #XXX sub { $tot{'90'} }, #90
162       ;
163     }
164
165   }
166
167 } elsif ( $cgi->param('magic') =~ /^nologin$/ ) {
168
169   if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
170     my $sortby = $1;
171     $sortby = "LOWER($sortby)"
172       if $sortby eq 'username';
173     push @extra_sql, "last_login IS NULL";
174     $orderby = "ORDER BY $sortby";
175   }
176
177 } elsif ( $cgi->param('magic') =~ /^advanced$/ ) {
178
179   $orderby = "";
180
181   $search_hash{'pkgpart'} = [ $cgi->param('pkgpart') ];
182
183   foreach my $field (qw( last_login last_logout )) {
184
185     my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
186
187     next if $beginning == 0 && $ending == 4294967295;
188
189     if ($cgi->param($field."_invert")) {
190       push @extra_sql,
191         "(svc_acct.$field IS NULL OR ".
192         "svc_acct.$field < $beginning AND ".
193         "svc_acct.$field > $ending)";
194     } else {
195       push @extra_sql,
196         "svc_acct.$field IS NOT NULL",
197         "svc_acct.$field >= $beginning",
198         "svc_acct.$field <= $ending";
199     }
200   
201     $orderby ||= "ORDER BY svc_acct.$field" .
202       ($cgi->param($field."_invert") ? ' DESC' : '');
203
204   }
205
206   $orderby ||= "ORDER BY svcnum";
207
208 } elsif ( $cgi->param('popnum') ) {
209   $orderby = "ORDER BY LOWER(username)";
210 } elsif ( $cgi->param('svcpart') ) {
211   $orderby = "ORDER BY uid";
212   #$orderby = "ORDER BY svcnum";
213 } else {
214   $orderby = "ORDER BY uid";
215
216   my @username_sql;
217
218   my %username_type;
219   foreach ( $cgi->param('username_type') ) {
220     $username_type{$_}++;
221   }
222
223   $cgi->param('username') =~ /^([\w\-\.\&]+)$/; #untaint username_text
224   my $username = $1;
225
226   push @username_sql, "username ILIKE '$username'"
227     if $username_type{'Exact'}
228     || $username_type{'Fuzzy'};
229
230   push @username_sql, "username ILIKE '\%$username\%'"
231     if $username_type{'Substring'}
232     || $username_type{'All'};
233
234   if ( $username_type{'Fuzzy'} || $username_type{'All'} ) {
235     &FS::svc_acct::check_and_rebuild_fuzzyfiles;
236     my $all_username = &FS::svc_acct::all_username;
237
238     my %username;
239     if ( $username_type{'Fuzzy'} || $username_type{'All'} ) { 
240       foreach ( amatch($username, [ qw(i) ], @$all_username) ) {
241         $username{$_}++; 
242       }
243     }
244
245     #if ($username_type{'Sound-alike'}) {
246     #}
247
248     push @username_sql, "username = '$_'"
249       foreach (keys %username);
250
251   }
252
253   push @extra_sql, '( '. join( ' OR ', @username_sql). ' )';
254
255 }
256
257 push @header, FS::UI::Web::cust_header($cgi->param('cust_fields'));
258 push @fields, \&FS::UI::Web::cust_fields,
259 push @links, map { $_ ne 'Cust. Status' ? $link_cust : '' }
260                  FS::UI::Web::cust_header($cgi->param('cust_fields'));
261 $align .= FS::UI::Web::cust_aligns();
262 push @color, FS::UI::Web::cust_colors();
263 push @style, FS::UI::Web::cust_styles();
264
265 $search_hash{'order_by'} = $orderby;
266 $search_hash{'where'} = \@extra_sql;
267
268 my $sql_query = FS::svc_acct->search(\%search_hash);
269 my $count_query = delete($sql_query->{'count_query'});
270
271 </%init>