update batch payment for multiple payment methods, etc., #17878 and #23741
[freeside.git] / httemplate / search / sqlradius_usage.html
1 % if ( @include_agents ) {
2 %   # jumbo report
3 <& /elements/header.html, $title &>
4 %   foreach my $agent ( @include_agents ) {
5 % $cgi->param('agentnum', $agent->agentnum); #for download links
6 <DIV WIDTH="100%" STYLE="page-break-after: always">
7 <FONT SIZE=6><% $agent->agent %></FONT><BR><BR>
8   <& sqlradius_usage.html, 
9       export            => $export,
10       agentnum          => $agent->agentnum,
11       nohtmlheader      => 1,
12       usage_by_username => \%usage_by_username,
13       download_label    => 'Download this section',
14       &>
15 </DIV>
16 <BR><BR>
17 %  }
18 <& /elements/footer.html &>
19 % } else {
20 <& elements/search.html,
21   'title'       => $title,
22   'name'        => 'services',
23   'query'       => $sql_query,
24   'count_query' => $sql_query->{'count_query'},
25   'header'      => [ #FS::UI::Web::cust_header(),
26                      '#',
27                      'Customer',
28                      'Package',
29                      @svc_header,
30                      'Upload (GB)',
31                      'Download (GB)',
32                      'Total (GB)',
33                    ],
34   'footer'      => \@footer,
35   'fields'      => [ #\&FS::UI::Web::cust_fields,
36                      'display_custnum',
37                      'name',
38                      'pkg',
39                      @svc_fields,
40                      @svc_usage,
41                    ],
42   'links'       => [ #( map { $_ ne 'Cust. Status' ? $link_cust : '' }
43                      #  FS::UI::Web::cust_header() ),
44                      $link_cust,
45                      $link_cust,
46                      '', #package
47                      ( map { $link_svc } @svc_header ),
48                      '',
49                      '',
50                      '',
51                    ],
52   'align'       => #FS::UI::Web::cust_aligns() .
53                    'rlc' . ('l' x scalar(@svc_header)) . 'rrr' ,
54   'nohtmlheader'    => ($opt{'nohtmlheader'} || 0),
55   'download_label'  => $opt{'download_label'},
56 &>
57 % }
58 <%init>
59
60 my %opt = @_;
61
62 die "access denied" unless
63   $FS::CurrentUser::CurrentUser->access_right('List services');
64
65 my $title = 'Data Usage Report - '; 
66 my $agentnum;
67 my @include_agents;
68
69 if ( $opt{'agentnum'} ) {
70   $agentnum = $opt{'agentnum'};
71 } elsif ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
72   $agentnum = $1;
73 }
74
75 if ( $agentnum ) {
76   my $agent = FS::agent->by_key($agentnum);
77   $title = $agent->agent." $title";
78 } else {
79   @include_agents = qsearch('agent', {});
80 }
81
82 # usage query params
83 my( $beginning, $ending ) = FS::UI::Web::parse_beginning_ending($cgi);
84
85 if ( $beginning ) {
86   $title .= time2str('%h %o %Y ', $beginning);
87 }
88 $title .= 'through ';
89 if ( $ending == 4294967295 ) {
90   $title .= 'now';
91 } else {
92   $title .= time2str('%h %o %Y', $ending);
93 }
94
95 my $export;
96 my %usage_by_username;
97 if ( exists($opt{usage_by_username}) ) {
98   # There's no agent separation in the radacct data.  So in the jumbo report
99   # do this procedure once, and pass the hash into all the per-agent sections.
100   %usage_by_username = %{ $opt{usage_by_username} };
101   $export  = $opt{export};
102 } else {
103
104   $cgi->param('exportnum') =~ /^(\d+)$/
105     or die "illegal export: '".$cgi->param('exportnum')."'";
106   $export = FS::part_export->by_key($1)
107     or die "exportnum $1 not found";
108   $export->exporttype =~ /sqlradius/
109     or die "exportnum ".$export->exportnum." is type ".$export->exporttype.
110            ", not sqlradius";
111
112   my $usage = $export->usage_sessions( {
113       stoptime_start  => $beginning,
114       stoptime_end    => $ending,
115       summarize       => 1
116   } );
117   # arrayref of hashrefs of
118   # (username, acctsessiontime, acctinputoctets, acctoutputoctets)
119   # (XXX needs to include 'realm' for sqlradius_withdomain)
120   # rearrange to be indexed by username.
121
122   foreach (@$usage) {
123     my $username = $_->{'username'};
124     my @row = (
125       $_->{'acctinputoctets'},
126       $_->{'acctoutputoctets'},
127       $_->{'acctinputoctets'} + $_->{'acctoutputoctets'}
128     );
129     $usage_by_username{$username} = \@row;
130   }
131 }
132
133 #warn Dumper(\%usage_by_username);
134 my @total_usage = (0, 0, 0, 0); # session time, input, output, input + output
135 my @svc_usage = map {
136   my $i = $_;
137   sub {
138     my $username = $export->export_username(shift);
139     return '' if !exists($usage_by_username{$username});
140     my $value = $usage_by_username{ $username }->[$i];
141     $total_usage[$i] += $value;
142     # for now, always show in GB, rounded to 3 digits
143     bytes_to_gb($value);
144   }
145 } (0,1,2);
146
147 # set up svcdb-specific stuff
148 my $export_username = sub {
149   $export->export_username(shift); # countrycode + phone, formatted MAC, etc.
150 };
151
152 my %svc_header = (
153   svc_acct      => [ 'Username' ],
154   svc_broadband => [ 'MAC address', 'IP address' ],
155 #  svc_phone     => [ 'Phone' ], #not yet supported, no search method
156                                  # (not sure input/output octets is relevant)
157 );
158 my %svc_fields = (
159   svc_acct      => [ $export_username ],
160   svc_broadband => [ $export_username, 'ip_addr' ],
161 #  svc_phone     => [ $export_username ],
162 );
163
164 # what kind of service we're operating on
165 my $svcdb = FS::part_export::export_info()->{$export->exporttype}->{'svc'};
166 my $class = "FS::$svcdb";
167 my @svc_header = @{ $svc_header{$svcdb} };
168 my @svc_fields = @{ $svc_fields{$svcdb} };
169
170 # svc_x search params
171 my %search_hash = ( 'agentnum' => $agentnum,
172                     'exportnum' => $export->exportnum );
173
174 my $sql_query = $class->search(\%search_hash);
175 $sql_query->{'select'}    .= ', part_pkg.pkg';
176 $sql_query->{'addl_from'} .= ' LEFT JOIN part_pkg USING (pkgpart)';
177
178 my $link_svc = [ $p.'view/cust_svc.cgi?', 'svcnum' ];
179
180 my $link_cust = [ $p.'view/cust_main.cgi?', 'custnum' ];
181
182 # columns between the customer name and the usage fields
183 my $skip_cols = 1 + scalar(@svc_header);
184
185 my @footer = (
186   '',
187   FS::Record->scalar_sql($sql_query->{count_query}) . ' services',
188   ('') x $skip_cols,
189   map {
190     my $i = $_;
191     sub { # defer this until the rows have been processed
192       bytes_to_gb($total_usage[$i])
193     }
194   } (0,1,2)
195 );
196
197 sub bytes_to_gb {
198   $_[0] ?  sprintf('%.3f', $_[0] / (1024*1024*1024.0)) : '';
199 }
200
201 </%init>