improved fcc 477 report #7783
authorjeff <jeff>
Sat, 22 May 2010 19:57:53 +0000 (19:57 +0000)
committerjeff <jeff>
Sat, 22 May 2010 19:57:53 +0000 (19:57 +0000)
22 files changed:
FS/FS/Conf.pm
FS/FS/Mason.pm
FS/FS/Report/FCC_477.pm [new file with mode: 0644]
FS/FS/Schema.pm
FS/FS/cust_pkg.pm
FS/FS/part_pkg.pm
FS/MANIFEST
FS/t/Report-FCC_477.t [new file with mode: 0644]
httemplate/edit/part_pkg.cgi
httemplate/search/477.html
httemplate/search/477partIA_detail.html [new file with mode: 0755]
httemplate/search/477partIA_summary.html [new file with mode: 0755]
httemplate/search/477partIIA.html [new file with mode: 0755]
httemplate/search/477partIIB.html [new file with mode: 0755]
httemplate/search/477partIV.html [new file with mode: 0755]
httemplate/search/477partV.html [new file with mode: 0755]
httemplate/search/477partVI.html [new file with mode: 0755]
httemplate/search/elements/metasearch.html [new file with mode: 0644]
httemplate/search/elements/search-html.html
httemplate/search/elements/search-xml.html [new file with mode: 0644]
httemplate/search/elements/search.html
httemplate/search/report_477.html

index 693d1f5..a1ee23c 100644 (file)
@@ -2370,6 +2370,13 @@ worry that config_items is freeside-specific and icky.
   },
 
   {
+    'key'         => 'cust_pkg-show_fcc_voice_grade_equivalent',
+    'section'     => 'UI',
+    'description' => "Show a field on package definitions for assigning a DSO equivalency number suitable for use on FCC form 477.",
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'svc_acct-edit_uid',
     'section'     => 'shell',
     'description' => 'Allow UID editing.',
index 9c8157a..10edd6d 100644 (file)
@@ -125,6 +125,7 @@ if ( -e $addl_handler_use_file ) {
                    states_hash counties cities state_label
                  );
   use FS::Misc::eps2png qw( eps2png );
+  use FS::Report::FCC_477;
   use FS::Report::Table::Monthly;
   use FS::TicketSystem;
   use FS::Tron qw( tron_lint );
diff --git a/FS/FS/Report/FCC_477.pm b/FS/FS/Report/FCC_477.pm
new file mode 100644 (file)
index 0000000..518b9f0
--- /dev/null
@@ -0,0 +1,90 @@
+package FS::Report::FCC_477;
+
+use strict;
+use vars qw( @ISA @upload @download @technology @part2aoption @part2boption );
+use FS::Report;
+
+@ISA = qw( FS::Report );
+
+=head1 NAME
+
+FS::Report::FCC_477 - Routines for FCC Form 477 reports
+
+=head1 SYNOPSIS
+
+=head1 BUGS
+
+Documentation.
+
+=head1 SEE ALSO
+
+=cut
+
+@upload = qw(
+ <200kpbs
+ 200-768kpbs
+ 768kbps-1.5mbps
+ 1.5-3mpbs
+ 3-6mbps
+ 6-10mbps
+ 10-25mbps
+ 25-100mbps
+ >100bmps
+);
+
+@download = qw(
+ 200-768kpbs
+ 768kbps-1.5mbps
+ 1.5-3mpbs
+ 3-6mbps
+ 6-10mbps
+ 10-25mbps
+ 25-100mbps
+ >100bmps
+);
+
+@technology = (
+  'Asymetric xDSL',
+  'Symetric xDSL',
+  'Other Wireline',
+  'Cable Modem',
+  'Optical Carrier',
+  'Satellite',
+  'Terrestrial Fixed Wireless',
+  'Terrestrial Mobile Wireless',
+  'Electric Power Line',
+  'Other Technology',
+);
+
+@part2aoption = (
+ 'LD carrier',
+ 'owned loops',
+ 'unswitched UNE loops',
+ 'UNE-P',
+ 'UNE-P replacement',
+ 'FTTP',
+ 'coax',
+ 'wireless',
+);
+
+@part2boption = (
+ 'nomadic',
+ 'copper',
+ 'FTTP',
+ 'coax',
+ 'wireless',
+ 'other broadband',
+);
+
+sub parse_technology_option {
+  my $cgi = shift;
+  my @result = ();
+  my $i = 0;
+  for (my $i = 0; $i < scalar(@technology); $i++) {
+    my $value = $cgi->param("part1_technology_option_$i"); #lame
+    push @result, $value =~ /^\d+$/ ? $value : 0;
+  }
+  return (@result);
+}
+
+1;
index ab80a08..ebe1195 100644 (file)
@@ -1461,6 +1461,7 @@ sub tables_hashref {
         'pay_weight',    'real',    'NULL', '', '', '',
         'credit_weight', 'real',    'NULL', '', '', '',
         'agentnum',      'int',     'NULL', '', '', '', 
+        'fcc_ds0s',      'int',     'NULL', '', '', '', 
         'no_auto',      'char',     'NULL',  1, '', '', 
       ],
       'primary_key' => 'pkgpart',
index cd62816..b851ac7 100644 (file)
@@ -2563,6 +2563,10 @@ a value suited to passing to FS::UI::Web::cust_header
 
 specifies the user for agent virtualization
 
+=item fcc_line
+
+ boolean selects packages containing fcc form 477 telco lines
+
 =back
 
 =cut
@@ -2684,6 +2688,12 @@ sub search {
   push @where,  "part_pkg.custom = 'Y'" if $params->{custom};
 
   ###
+  # parse fcc_line
+  ###
+
+  push @where,  "part_pkg.fcc_ds0s > 0" if $params->{fcc_line};
+
+  ###
   # parse censustract
   ###
 
@@ -2841,6 +2851,35 @@ sub search {
 
 }
 
+=item fcc_477_count
+
+Returns a list of two package counts.  The first is a count of packages
+based on the supplied criteria and the second is the count of residential
+packages with those same criteria.  Criteria are specified as in the search
+method.
+
+=cut
+
+sub fcc_477_count {
+  my ($class, $params) = @_;
+
+  my $sql_query = $class->search( $params );
+
+  my $count_sql = delete($sql_query->{'count_query'});
+  $count_sql =~ s/ FROM/,count(CASE WHEN cust_main.company IS NULL OR cust_main.company = '' THEN 1 END) FROM/
+    or die "couldn't parse count_sql";
+
+  my $count_sth = dbh->prepare($count_sql)
+    or die "Error preparing $count_sql: ". dbh->errstr;
+  $count_sth->execute
+    or die "Error executing $count_sql: ". $count_sth->errstr;
+  my $count_arrayref = $count_sth->fetchrow_arrayref;
+
+  return ( @$count_arrayref );
+
+}
+
+
 =item location_sql
 
 Returns a list: the first item is an SQL fragment identifying matching 
index d2f6596..ef7f557 100644 (file)
@@ -98,6 +98,8 @@ inherits from FS::Record.  The following fields are currently supported:
 
 =item agentnum - Optional agentnum (see L<FS::agent>)
 
+=item fcc_ds0s - Optional DS0 equivalency number for FCC form 477
+
 =back
 
 =head1 METHODS
@@ -480,6 +482,7 @@ sub check {
            ? $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum' )
            : $self->ut_agentnum_acl('agentnum', \@null_agentnum_right)
        )
+    || $self->ut_numbern('fcc_ds0s')
     || $self->SUPER::check
   ;
   return $error if $error;
index b140765..5537c2f 100644 (file)
@@ -39,6 +39,7 @@ FS/Daemon.pm
 FS/Misc.pm
 FS/Record.pm
 FS/Report.pm
+FS/Report/FCC_477.pm
 FS/Report/Table.pm
 FS/Report/Table/Monthly.pm
 FS/SearchCache.pm
diff --git a/FS/t/Report-FCC_477.t b/FS/t/Report-FCC_477.t
new file mode 100644 (file)
index 0000000..b7359aa
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Report::FCC_477;
+$loaded=1;
+print "ok 1\n";
index 6ef8dd8..c8b7ecb 100755 (executable)
@@ -48,6 +48,7 @@
                             'bill_dst_pkgpart' => 'Include line item(s) from package',
                             'svc_dst_pkgpart'  => 'Include services of package',
                             'report_option'    => 'Report classes',
+                            'fcc_ds0s'         => 'Voice-grade eqivalents',
                           },
 
               'fields' => [
                               { field=>'pay_weight',    type=>'text', size=>6 },
                               { field=>'credit_weight', type=>'text', size=>6 },
 
+                              ( $conf->exists('cust_pkg-show_fcc_voice_grade_equivalent')
+                                ? ( 
+                                    { type  => 'tablebreak-tr-title',
+                                      value => 'FCC Form 477 information',
+                                    },
+                                    { field=>'fcc_ds0s', type=>'text', size=>6 },
+                                  )
+                                 : ()
+                              ),
+
 
                             { type => 'columnend' },
 
index 0f1502f..bd7fb2d 100755 (executable)
@@ -1,44 +1,76 @@
-<% include( 'elements/search.html',
-                  'title'         => 'FCC Form 477 Results', 
-                  'html_init'     => $html_init,
-                  'name'          => 'regions',
-                  'query'         => [ @sql_query ],
-                  'count_query'   =>  $count_query,
-                  'order_by'      => 'ORDER BY censustract',
-                  'avoid_quote'   => 1,
-                  'no_csv_header' => 1,
-                  'header'        => [
-                                       'County code',
-                                       'Census tract code',
-                                       'Upload rate',
-                                       'Download rate',
-                                       'Technology code',
-                                       'Technology code other',
-                                       'Quantity',
-                                       'Percentage residential',
-                                     ],
-                  'fields'        => [
-                    sub { my $row = shift; substr($row->censustract, 2, 3) },
-                    sub { my $row = shift; substr($row->censustract, 5) },
-                    'upload',
-                    'download',
-                    sub { 7 },
-                    sub { '' },
-                    'quantity',
-                    sub { my $row = shift; sprintf "%.2f", $row->residential },
-                  ],
-                  'links'        => [
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                    [ $link, $link_suffix ],
-                  ],
-              )
-%>
+% unless ( $type eq 'xml' ) {
+<% include( '/elements/header.html', 'FCC Form 477 Results') %>
+%}else{
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<Form_477_submission xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://specialreports.fcc.gov/wcb/Form477/XMLSchema-instance/form_477_upload_Schema.xsd" >
+%}
+% if ( $type eq 'html' || $type eq 'html-print' ) {
+<TABLE WIDTH="100%">
+  <TR><TD></TD>
+%}elsif ( $type eq 'xml' ) {
+%}
+%           unless ( $type eq 'html-print' || $type eq 'xml' ) {
+
+              <TD ALIGN="right">
+
+                Download full results<BR>
+%               $cgi->param('_type', 'xml');
+                as <A HREF="<% $cgi->self_url %>">XML file</A><BR>
+
+%               $cgi->param('_type', 'html-print');
+                as <A HREF="<% $cgi->self_url %>">printable copy</A>
+
+              </TD>
+%             $cgi->param('_type', $type );
+%           }
+% if ( $type eq 'html' || $type eq 'html-print' ) {
+  </TR>
+</TABLE>
+%}elsif ( $type eq 'xml' ) {
+%}
+% foreach my $part ( @parts ) {
+%   if ( $part{$part} ) {
+%
+%     if ( $part eq 'V' ) {
+%       next unless ( $part{'IIA'} || $part{'IIB'} );
+%     }
+%
+%     if ( $part eq 'VI' ) {
+%       next unless $part{'IA'};
+%     }
+%
+%     my @reports = ();
+%     if ( $part eq 'IA' ) {
+%       for ( my $tech = 0; $tech < scalar(@technology_option); $tech++ ) {
+%         next unless $technology_option[$tech];
+%         my $url = &{$url_mangler}($cgi->self_url, $part);
+%         if ( $type eq 'xml' ) {
+<<% 'Part_IA_'. chr(65 + $tech) %>>
+%         }
+<% include( "477part${part}_summary.html", 'tech_code' => $tech, 'url' => $url ) %>
+<% include( "477part${part}_detail.html", 'tech_code' => $tech, 'url' => $url ) %>
+%         if ( $type eq 'xml' ) {
+</<% 'Part_IA_'. chr(65 + $tech) %>>
+%         }
+%       }
+%     } else {
+%       if ( $type eq 'xml' ) {
+<<% 'Part_'. uc($part) %>>
+%       }
+%       my $url = &{$url_mangler}($cgi->self_url, $part);
+<% include( "477part${part}.html", 'url' => $url ) %>
+%       if ( $type eq 'xml' ) {
+</<% 'Part_'. uc($part) %>>
+%       }
+%     }
+%   }
+% }
+%
+% if ( $type eq 'html' || $type eq 'html-print' ) {
+<% include( '/elements/footer.html') %>
+%}elsif ( $type eq 'xml' ) {
+</Form_477_submission>
+%}
 <%init>
 
 my $curuser = $FS::CurrentUser::CurrentUser;
@@ -46,137 +78,15 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 die "access denied"
   unless $curuser->access_right('List packages');
 
-my %search_hash = ();
-my @sql_query = ();
-  
-for ( qw(agentnum magic classnum) ) {
-  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
-}
+my %part = map { $_ => 1 } grep { /^\w+$/ } $cgi->param('part');
+my $type = $cgi->param('_type') || 'html';
+my $xlsname = '477report';
+my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
+my $url_mangler = sub {
+  my ($url, $part) = (shift, shift);
+  $url =~ s/477\./477part$part./;
+  $url;
+};
+my @parts = qw( IA IIA IIB IV V VI );
 
-my @column_option = $cgi->param('column_option')
-  if $cgi->param('column_option');
-
-my @row_option = $cgi->param('row_option')
-  if $cgi->param('row_option');
-
-my $where = join(' OR ', map { "num = $_" } grep { /^\d+$/ } @column_option );
-my %column_option_name = $where ?
-                           ( map { $_->name => $_->num }
-                             qsearch({ 'table' => 'part_pkg_report_option',
-                                       'hashref' => {},
-                                       'extra_sql' => "WHERE $where",
-                                    })
-                           ) :
-                           ( 'all packages' => '' );
-
-$where = join(' OR ', map { "num = $_" } grep { /^\d+$/ } @row_option );
-my %row_option_name = $where ?
-                        ( map { $_->name => $_->num }
-                          qsearch({ 'table' => 'part_pkg_report_option',
-                                    'hashref' => {},
-                                    'extra_sql' => "WHERE $where",
-                                 })
-                        ) :
-                        ( 'all packages' => '' );
-
-@row_option = map { $row_option_name{$_} } sort keys %row_option_name;
-@column_option = map { $column_option_name{$_} } sort keys %column_option_name;
-
-#$search_hash{row_option} = join(',', @row_option) if @row_option;
-my $summary .= '<H2>Summary</H2>'. include('/elements/table.html');
-$summary .= '<TR><TH></TH>';
-foreach my $column ( sort keys %column_option_name ) {
-  $summary .= "<TH>$column</TH>";
-}
-$summary .= "</TR>";
-
-my $total_count = 0;
-my $total_residential = 0;
-my $rowcount = 1;
-foreach my $row ( sort keys %row_option_name ) {
-
-  $summary .= "<TR><TH>$row</TH>";
-
-  my $columncount = 2;
-  foreach my $column ( sort keys %column_option_name ) {
-    my @report_option = ();
-    push @report_option, $row_option_name{$row}
-      if $row_option_name{$row};
-    push @report_option, $column_option_name{$column}
-      if $column_option_name{$column};
-    my $report_option = join(',', @report_option) if @report_option;
-
-    my $sql_query = FS::cust_pkg->search(
-      { %search_hash,
-        ($report_option ? ( 'report_option' => $report_option ) : () ),
-      }
-    );
-    my $extracolumns = "$rowcount AS upload, $columncount AS download";
-    my $percent = "CASE WHEN count(*) > 0 THEN 100-100*cast(count(cust_main.company) as numeric)/cast(count(*) as numeric) ELSE cast(0 as numeric) END AS residential";
-    $sql_query->{select} = "count(*) AS quantity, $extracolumns, censustract, $percent";
-    $sql_query->{extra_sql} =~ /^(.*)(ORDER BY pkgnum)(.*)$/s
-      or die "couldn't parse extra_sql";
-    $sql_query->{extra_sql} = "$1 GROUP BY censustract $3"; 
-
-    my $count_sql = delete($sql_query->{'count_query'});
-    $count_sql =~ s/ FROM/,count(cust_main.company) FROM/
-      or die "couldn't parse count_sql";
-
-    my $count_sth = dbh->prepare($count_sql)
-      or die "Error preparing $count_sql: ". dbh->errstr;
-    $count_sth->execute
-      or die "Error executing $count_sql: ". $count_sth->errstr;
-    my $count_arrayref = $count_sth->fetchrow_arrayref;
-    my $count = $count_arrayref->[0];
-    $total_count += $count;
-    my $residential = $count_arrayref->[1];
-    $total_residential += $residential;
-    my $percentage = sprintf('%.2f', $count ? 100-100*$residential/$count : 0);
-
-    $summary .= "<TD>$count<BR>$percentage% residential</TD>";
-    push @sql_query, $sql_query;
-    $columncount++;
-  }
-
-  $summary .= "</TR>";
-  $rowcount++;
-}
-
-my $total_percentage =
-  sprintf("%.2f", $total_count ? 100-100*$total_residential/$total_count : 0);
-
-my $html_init = '<H2>Totals</H2>'. include('/elements/table.html'). "<TR>";
-$html_init .= "<TH>Total Connections</TH>";
-$html_init .= "<TH>% owned loop</TH>";
-$html_init .= "<TH>% billed to end users</TH>";
-$html_init .= "<TH>% residential</TH>";
-$html_init .= "<TH>% residential &gt; 200kbps</TH>";
-$html_init .= "</TR><TR>";
-$html_init .= "<TD>$total_count</TD>";
-$html_init .= "<TD>100.00</TD>";
-$html_init .= "<TD>100.00</TD>";
-$html_init .= "<TD>$total_percentage</TD>";
-$html_init .= "<TD>$total_percentage</TD>";
-$html_init .= "</TR></TABLE><BR>";
-$html_init .= $summary;
-$html_init .= "</TABLE><BR><H2>Details</H2>";
-
-my $count_query = 'SELECT count(*) FROM ( ('.
-   join( ') UNION (',
-          map { my $extra = $_->{extra_sql};  my $addl = $_->{addl_from};
-                "SELECT censustract from cust_pkg $addl $extra";
-              }
-          @sql_query
-       ). ') ) AS foo';
-
-my $link = 'cust_pkg.cgi?'.
-           join(';', map{ "$_=". $search_hash{$_} } keys %search_hash). ';';
-my $link_suffix = sub { my $row = shift;
-                        my $result = 'censustract='. $row->censustract. ';';
-                        $result .= 'report_option='. @row_option[$row->upload - 1]
-                          if @row_option[$row->upload - 1];
-                        $result .= 'report_option='. @column_option[$row->download - 1]
-                          if @column_option[$row->download - 1];
-                        $result;
-                      };
 </%init>
diff --git a/httemplate/search/477partIA_detail.html b/httemplate/search/477partIA_detail.html
new file mode 100755 (executable)
index 0000000..546d56c
--- /dev/null
@@ -0,0 +1,125 @@
+<% include( 'elements/search.html',
+                  'html_init'        => $html_init,
+                  'name'             => 'lines',
+                  'query'            => $query,
+                  'count_query'      => $count_query,
+                  'really_disable_download' => 1,
+                  'disable_download' => 1,
+                  'nohtmlheader'     => 1,
+                  'disable_total'    => 1,
+                  'header'           => [ '', @column_option_name ],
+                  'xml_elements'     => [ @xml_elements ],
+                  'fields'           => [  @fields ],
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my %opt = @_;
+my %search_hash = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my @column_option = grep { /^\d+/ } $cgi->param('part1_column_option')
+  if $cgi->param('part1_column_option');
+
+my @row_option = grep { /^\d+/ } $cgi->param('part1_row_option')
+  if $cgi->param('part1_row_option');
+
+my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
+
+my @column_option_name = scalar(@column_option)
+  ? ( map { my $part_pkg_report_option = 
+              qsearchs({ 'table' => 'part_pkg_report_option',
+                         'hashref' => { num => $_ },
+                      });
+            $part_pkg_report_option ? $part_pkg_report_option->name
+                                    : 'no such report option';
+          } @column_option
+    )
+  : ( 'all packages' );
+
+my $where = join(' OR ', map { "num = $_" } @row_option );
+my %row_option_name = $where ?
+                        ( map { $_->num => $_->name }
+                          qsearch({ 'table' => 'part_pkg_report_option',
+                                    'hashref' => {},
+                                    'extra_sql' => "WHERE $where",
+                                 })
+                        ) :
+                        ();
+
+my $tech_code = $opt{tech_code};
+my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown';
+my $html_init = "<H2>Part IA $technology breakdown by speeds</H2>";
+my $xml_prefix = 'PartIA_'. chr(65 + $tech_code);
+
+my $query = 'SELECT '. join(' UNION ALL SELECT ',@row_option);
+my $count_query = 'SELECT '. scalar(@row_option);
+
+my $value = sub {
+  my ($rowref, $column) = (shift, shift);
+  my $row = $rowref->[0];
+
+  if ($column eq 'name') {
+    return $row_option_name{$row} || 'no such report option';
+  } elsif ( $column =~ /^(\d+)$/ ) {
+    my @report_option = ( $row || '',
+                          $column_option[$1 - 2] || '',
+                          $technology_option[$tech_code] || '',
+                        );
+
+    my ( $count, $residential ) = FS::cust_pkg->fcc_477_count(
+      { %search_hash, 'report_option' => join(',', @report_option) }
+    );
+
+    my $percentage = sprintf('%.2f', $count ? 100 * $residential / $count : 0);
+    my $return = $count;
+    $return .= "<BR>$percentage% residential"
+      unless $cgi->param('_type') eq 'xml';
+    return $return;
+  } else {
+    return '<FONT SIZE="+1" COLOR="#ff0000">Bad call to column_value</FONT>';
+  }
+};
+
+my @fields = (
+  sub { &{$value}(shift, 'name');},
+  sub { &{$value}(shift, 2);},
+  sub { &{$value}(shift, 3);},
+  sub { &{$value}(shift, 4);},
+  sub { &{$value}(shift, 5);},
+  sub { &{$value}(shift, 6);},
+  sub { &{$value}(shift, 7);},
+  sub { &{$value}(shift, 8);},
+  sub { &{$value}(shift, 9);},
+ );
+shift @fields if $cgi->param('_type') eq 'xml';
+
+my $xml_element = sub {
+  my ($rowref, $column) = (shift, shift);
+  my $row = $rowref->[0];
+
+  $row++;
+  $xml_prefix. $column. $row;
+
+};
+
+my @xml_elements = (
+  sub { &{$xml_element}(shift, 'f') },
+  sub { &{$xml_element}(shift, 'g') },
+  sub { &{$xml_element}(shift, 'h') },
+  sub { &{$xml_element}(shift, 'i') },
+  sub { &{$xml_element}(shift, 'j') },
+  sub { &{$xml_element}(shift, 'k') },
+  sub { &{$xml_element}(shift, 'l') },
+  sub { &{$xml_element}(shift, 'm') },
+);
+
+</%init>
diff --git a/httemplate/search/477partIA_summary.html b/httemplate/search/477partIA_summary.html
new file mode 100755 (executable)
index 0000000..269f2ca
--- /dev/null
@@ -0,0 +1,80 @@
+<% include( 'elements/search.html',
+                  'html_init'        => $html_init,
+                  'name'             => 'lines',
+                  'query'            => 'SELECT 1',
+                  'count_query'      => 'SELECT 1',
+                  'really_disable_download' => 1,
+                  'disable_download' => 1,
+                  'nohtmlheader'     => 1,
+                  'disable_total'    => 1,
+                  'header'           => [
+                                          'Total Connections',
+                                          '% owned loop',
+                                          '% billed to end users',
+                                          '% residential',
+                                          '% residential &gt; 200kbps',
+                                        ],
+                  'xml_elements'     => [
+                                          $xml_prefix. 'a1',
+                                          $xml_prefix. 'b1',
+                                          $xml_prefix. 'c1',
+                                          $xml_prefix. 'd1',
+                                          $xml_prefix. 'e1',
+                                        ],
+                  'fields'           => [
+                                          sub { $total_count },
+                                          sub { '100.00' },
+                                          sub { '100.00' },
+                                          sub { $total_percentage },
+                                          sub { $total_percentage },
+                                        ],
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my %opt = @_;
+my %search_hash = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my @column_option = grep { /^\d+$/ } $cgi->param('part1_column_option')
+  if $cgi->param('part1_column_option');
+
+my @row_option = grep { /^\d+$/ } $cgi->param('part1_row_option')
+  if $cgi->param('part1_row_option');
+
+my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
+
+my $total_count = 0;
+my $total_residential = 0;
+my $tech_code = $opt{tech_code};
+my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown';
+my $html_init = "<H2>Part IA $technology totals</H2>";
+my $xml_prefix = 'PartIA_'. chr(65 + $tech_code);
+
+foreach my $row ( @row_option ) {
+  foreach my $column ( @column_option ) {
+
+    my @report_option = ( $row || '-1', $column || '-1', $technology_option[$tech_code] );
+
+    my ( $count, $residential ) = FS::cust_pkg->fcc_477_count(
+      { %search_hash, 'report_option' => join(',', @report_option) }
+    );
+
+    $total_count += $count;
+    $total_residential += $residential;
+  }
+}
+
+my $total_percentage =
+  sprintf("%.2f", $total_count ? 100*$total_residential/$total_count : 0);
+
+
+</%init>
diff --git a/httemplate/search/477partIIA.html b/httemplate/search/477partIIA.html
new file mode 100755 (executable)
index 0000000..64f773a
--- /dev/null
@@ -0,0 +1,113 @@
+<% include( 'elements/search.html',
+                  'html_init'        => $html_init,
+                  'name'             => 'lines',
+                  'query'            => $query,
+                  'count_query'      => 'SELECT 11',
+                  'really_disable_download' => 1,
+                  'disable_download' => 1,
+                  'nohtmlheader'     => 1,
+                  'disable_total'    => 1,
+                  'header'           => [ @headers ],
+                  'xml_elements'     => [ @xml_elements ],
+                  'fields'           => [ @fields ],
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my $html_init = '<H2>Part IIA</H2>';
+my %search_hash = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my @row_option = grep { /^\d+$/ } $cgi->param('part2a_row_option')
+  if $cgi->param('part2a_row_option');
+
+# fudge in two rows of LD carrier
+unshift @row_option, $row_option[0];
+
+# fudge in the first pair of rows
+unshift @row_option, '';
+unshift @row_option, '';
+
+my $query = 'SELECT '. join(' UNION SELECT ', 1..11);
+
+my $total_count = 0;
+my $column_value = sub {
+  my $row = shift;
+
+  my @report_option = ( $row_option[$row - 1] || '' );
+
+  my $sql_query = FS::cust_pkg->search(
+    { %search_hash, 'report_option' => join(',', @report_option) }
+  );
+
+  my $count_sql = delete($sql_query->{'count_query'});
+  if ( $row == 2 || $row == 4 ) {
+    $count_sql =~ s/COUNT\(\*\) FROM/COALESCE( sum(CASE WHEN cust_main.company IS NULL OR cust_main.company = '' THEN fcc_ds0s END), 0 ) FROM/
+      or die "couldn't parse count_sql";
+  } else {
+    $count_sql =~ s/COUNT\(\*\) FROM/COALESCE( sum(fcc_ds0s), 0 ) FROM/
+      or die "couldn't parse count_sql";
+  }
+
+  my $count_sth = dbh->prepare($count_sql)
+    or die "Error preparing $count_sql: ". dbh->errstr;
+  $count_sth->execute
+    or die "Error executing $count_sql: ". $count_sth->errstr;
+  my $count_arrayref = $count_sth->fetchrow_arrayref;
+  my $count = $count_arrayref->[0];
+
+  $total_count = $count if $row == 1;
+  $count = sprintf('%.2f', $total_count ? 100*$count/$total_count : 0)
+    if $row != 1;
+
+  return "$count";
+};
+
+my @headers = (
+  '',
+  'End user lines',
+  'UNE-P replacement',
+  'UNE (unswitched)',
+  'UNE-P',
+);
+
+my @xml_elements = (
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}a" },
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}b" },
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}c" },
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}d" },
+);
+
+
+my @rows = (
+  'lines',
+  '% residential',
+  '% LD carrier',
+  '% residential and LD carrier',
+  '% own loops',
+  '% obtained unswitched UNE loops',
+  '% UNE-P',
+  '% UNE-P replacement',
+  '% FTTP',
+  '% coax',
+  '% wireless',
+);
+
+my @fields = (
+  sub { my $row = shift; $rows[$row->[0] - 1]; },
+  sub { my $row = shift; &{$column_value}($row->[0]); },
+  sub { 0; },
+  sub { 0; },
+  sub { 0; },
+);
+
+shift @fields if $cgi->param('_type') eq 'xml';
+</%init>
diff --git a/httemplate/search/477partIIB.html b/httemplate/search/477partIIB.html
new file mode 100755 (executable)
index 0000000..278dfdc
--- /dev/null
@@ -0,0 +1,102 @@
+<% include( 'elements/search.html',
+                  'html_init'        => $html_init,
+                  'name'             => 'lines',
+                  'query'            => $query,
+                  'count_query'      => 'SELECT 11',
+                  'really_disable_download' => 1,
+                  'disable_download' => 1,
+                  'nohtmlheader'     => 1,
+                  'disable_total'    => 1,
+                  'header'           => [ @headers ],
+                  'xml_elements'     => [ @xml_elements ],
+                  'fields'           => [ @fields ],
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my $html_init = '<H2>Part IIB</H2>';
+my %search_hash = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my @row_option = grep { /^\d+$/ } $cgi->param('part2b_row_option')
+  if $cgi->param('part2b_row_option');
+
+# fudge in 2nd row
+unshift @row_option, $row_option[0];
+
+my $query = 'SELECT '. join(' UNION SELECT ', 1..8);
+
+my $total_count = 0;
+my $column_value = sub {
+  my $row = shift;
+
+  my @report_option = ( $row_option[$row - 1] || '' );
+
+  my $sql_query = FS::cust_pkg->search(
+    { %search_hash, 'report_option' => join(',', @report_option) }
+  );
+
+  my $count_sql = delete($sql_query->{'count_query'});
+  if ( $row == 2 ) {
+    $count_sql =~ s/COUNT\(\*\) FROM/COALESCE( sum(CASE WHEN cust_main.company IS NULL OR cust_main.company = '' THEN fcc_ds0s END), 0 ) FROM/
+      or die "couldn't parse count_sql";
+  } else {
+    $count_sql =~ s/COUNT\(\*\) FROM/COALESCE( sum(fcc_ds0s), 0 ) FROM/
+      or die "couldn't parse count_sql";
+  }
+
+  my $count_sth = dbh->prepare($count_sql)
+    or die "Error preparing $count_sql: ". dbh->errstr;
+  $count_sth->execute
+    or die "Error executing $count_sql: ". $count_sth->errstr;
+  my $count_arrayref = $count_sth->fetchrow_arrayref;
+  my $count = $count_arrayref->[0];
+
+  $total_count = $count if $row == 1;
+  $count = sprintf('%.2f', $total_count ? 100*$count/$total_count : 0)
+    if $row != 1;
+
+  return "$count";
+};
+
+my @headers = (
+  '',
+  'with broadband',
+  'without broadband',
+  'wholesale',
+);
+
+my @xml_elements = (
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}a" },
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}b" },
+  sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}c" },
+);
+
+my @rows = (
+  'total number',
+  '% residential',
+  '% nomadic',
+  '% copper',
+  '% FTTP',
+  '% coax',
+  '% wireless',
+  '% other broadband',
+);
+
+my @fields = (
+  sub { my $row = shift; $rows[$row->[0] - 1]; },
+  sub { 0; },
+  sub { my $row = shift; &{$column_value}($row->[0]); },
+  sub { 0; },
+);
+
+shift @fields if $cgi->param('_type') eq 'xml';
+</%init>
diff --git a/httemplate/search/477partIV.html b/httemplate/search/477partIV.html
new file mode 100755 (executable)
index 0000000..269a925
--- /dev/null
@@ -0,0 +1,17 @@
+%if ( $cgi->param('_type') eq 'html' || $cgi->param('_type') eq 'html-print' ) {
+<H2>Part IV</H2>
+%} elsif ( $cgi->param('_type') eq 'xml' ) {
+<notes>
+%}
+<% $cgi->param('notes') |h %>
+%if ( $cgi->param('_type') eq 'xml' ) {
+</notes>
+%}
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+  
+</%init>
diff --git a/httemplate/search/477partV.html b/httemplate/search/477partV.html
new file mode 100755 (executable)
index 0000000..c6ceac4
--- /dev/null
@@ -0,0 +1,53 @@
+<% include( 'elements/search.html',
+                  'html_init'         => $html_init,
+                  'name'              => 'zip code',
+                  'query'             => [ @sql_query ],
+                  'count_query'       => $count_query,
+                  'nohtmlheader'      => 1,
+                  'disable_total'     => 1,
+                  'header'            => [ 'zip code' ],
+                  'xml_elements'      => [ 'zip codes' ],
+                  'no_field_elements' => 1,
+                  'fields'            => [ 'zip' ],
+                  'url'               => $opt{url} || $cgi->self_url,
+
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my %opt = @_;
+my $html_init = '<H2>Part V</H2>';
+my %search_hash = ();
+my @sql_query = ();
+my @count_query = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my $sql_query = FS::cust_pkg->search( { %search_hash, 'fcc_line' > 1 });
+$sql_query->{select} = 'DISTINCT zip';
+$sql_query->{extra_sql} =~ s/ORDER BY [.\w]+//;
+push @sql_query, $sql_query;
+push @count_query, delete($sql_query->{'count_query'});
+$count_query[0] =~ s/count\(\*\)/count(DISTINCT zip)/;
+$count_query[0] =~ s/ORDER BY [.\w]+//;
+
+$search_hash{classnum} = $cgi->param('part2a_classnum')
+  if $cgi->param('part2a_classnum');
+
+$sql_query = FS::cust_pkg->search( { %search_hash } );
+$sql_query->{select} = 'DISTINCT zip';
+$sql_query->{extra_sql} =~ s/ORDER BY [.\w]+//;
+push @sql_query, $sql_query;
+push @count_query, delete($sql_query->{'count_query'});
+$count_query[1] =~ s/count\(\*\)/count(DISTINCT zip)/;
+$count_query[1] =~ s/ORDER BY [.\w]+//;
+my $count_query = join(' UNION ', @count_query);
+
+</%init>
diff --git a/httemplate/search/477partVI.html b/httemplate/search/477partVI.html
new file mode 100755 (executable)
index 0000000..dbd1703
--- /dev/null
@@ -0,0 +1,130 @@
+<% include( 'elements/search.html',
+                  'html_init'     => $html_init,
+                  'name'          => 'regions',
+                  'query'         => [ @sql_query ],
+                  'count_query'   =>  $count_query,
+                  'order_by'      => 'ORDER BY censustract',
+                  'avoid_quote'   => 1,
+                  'no_csv_header' => 1,
+                  'nohtmlheader'  => 1,
+                  'header'        => [
+                                       'County code',
+                                       'Census tract code',
+                                       'Upload rate',
+                                       'Download rate',
+                                       'Technology code',
+                                       'Technology code other',
+                                       'Quantity',
+                                       'Percentage residential',
+                                     ],
+                  'xml_elements'  => [
+                                       'county_fips',
+                                       'census_tract',
+                                       'upload_rate_code',
+                                       'download_rate_code',
+                                       'technology_code',
+                                       'technology_code_other',
+                                       'value',
+                                       'percentage',
+                                     ],
+                  'fields'        => [
+                    sub { my $row = shift; substr($row->censustract, 2, 3) },
+                    sub { my $row = shift; substr($row->censustract, 5) },
+                    'upload',
+                    'download',
+                    'technology_code',
+                    sub { '' },  # doesn't really work
+                    'quantity',
+                    sub { my $row = shift; sprintf "%.2f", $row->residential },
+                  ],
+                  'links'        => [
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                    [ $link, $link_suffix ],
+                  ],
+                  'url' => $opt{url} || $cgi->self_url,
+                  'xml_row_element' => 'Datarow',
+              )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('List packages');
+
+my %opt = @_;
+
+my $html_init = '<H2>Part VI</H2>';
+
+my %search_hash = ();
+my @sql_query = ();
+  
+for ( qw(agentnum magic classnum) ) {
+  $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+}
+
+my @column_option = grep { /^\d+$/ } $cgi->param('part1_column_option')
+  if $cgi->param('part1_column_option');
+
+my @row_option = grep { /^\d+$/ } $cgi->param('part1_row_option')
+  if $cgi->param('part1_row_option');
+
+my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
+
+my $rowcount = 1;
+foreach my $row ( @row_option ) {
+  my $columncount = 2;
+  foreach my $column ( @column_option ) {
+    my $tech_code = 0;
+    foreach my $technology ( @technology_option ) {
+      $tech_code++;
+      next unless $technology;
+      my @report_option = ();
+      push @report_option, $row if $row;
+      push @report_option, $column if $column;
+      push @report_option, $technology;
+      my $report_option = join(',', @report_option) if @report_option;
+      my $sql_query = FS::cust_pkg->search(
+        { %search_hash,
+          ($report_option ? ( 'report_option' => $report_option ) : () ),
+        }
+      );
+      my $extracolumns = "$rowcount AS upload, $columncount AS download, $tech_code as technology_code";
+      my $percent = "CASE WHEN count(*) > 0 THEN 100-100*cast(count(cust_main.company) as numeric)/cast(count(*) as numeric) ELSE cast(0 as numeric) END AS residential";
+      $sql_query->{select} = "count(*) AS quantity, $extracolumns, censustract, $percent";
+      $sql_query->{extra_sql} =~ /^(.*)(ORDER BY pkgnum)(.*)$/s
+        or die "couldn't parse extra_sql";
+      $sql_query->{extra_sql} = "$1 GROUP BY censustract $3";
+      push @sql_query, $sql_query;
+    }
+    $columncount++;
+  }
+  $rowcount++;
+}
+
+my $count_query = 'SELECT count(*) FROM ( ('.
+   join( ') UNION ALL (',
+          map { my $extra = $_->{extra_sql};  my $addl = $_->{addl_from};
+                "SELECT censustract from cust_pkg $addl $extra";
+              }
+          @sql_query
+       ). ') ) AS foo';
+
+my $link = 'cust_pkg.cgi?'.
+           join(';', map{ "$_=". $search_hash{$_} } keys %search_hash). ';';
+my $link_suffix = sub { my $row = shift;
+                        my $result = 'censustract='. $row->censustract. ';';
+                        $result .= 'report_option='. @row_option[$row->upload - 1]
+                          if @row_option[$row->upload - 1];
+                        $result .= 'report_option='. @column_option[$row->download - 1]
+                          if @column_option[$row->download - 1];
+                        $result;
+                      };
+</%init>
diff --git a/httemplate/search/elements/metasearch.html b/httemplate/search/elements/metasearch.html
new file mode 100644 (file)
index 0000000..b9d3e3c
--- /dev/null
@@ -0,0 +1,71 @@
+<%doc>
+
+Example:
+
+  include( 'elements/metasearch.html',
+
+    ###
+    # required
+    ###
+
+    'title'         => 'Page title',
+    
+    #arrayref of hashrefs suited for passing to elements/search.html
+    #see that documentation
+    'search'      => [
+                       {
+                         query => { 'table'     => 'tablename',
+                                    #everything else is optional...
+                                    'hashref'   => { 'f1' => 'value',
+                                                     'f2' => { 'op'   => '<',
+                                                               'value' => '54',
+                                                             },
+                                                    },
+                                    'select'    => '*',
+                                    'order_by'  => 'ORDER BY something',
+   
+                                  },
+                         count_query => 'SELECT COUNT(*) FROM tablename',
+                       },
+                       {
+                         query => 'table'     => 'anothertablename',
+                         count_query => 'SELECT COUNT(*) FROM anothertablename',
+                       },
+                     ],
+   
+    ###
+    # optional
+    ###
+   
+    # some HTML callbacks...
+    'menubar'          => '', #menubar arrayref
+    'html_init'        => '', #after the header/menubar and before the pager
+    'html_form'        => '', #after the pager, right before the results
+                              # (only shown if there are results)
+                              # (use this for any form-opening tag rather than
+                              #  html_init, to avoid a nested form)
+    'html_foot'        => '', #at the bottom
+    'html_posttotal'   => '', #at the bottom
+                              # (these three can be strings or coderefs)
+   
+  );
+
+</%doc>
+% foreach my $search ( @{$opt{search}} ) {
+<% include('search.html',
+             %$search,
+             'type'           => $type,
+             'nohtmlheader'   => 1,
+          )
+%>
+%
+% } 
+<%init>
+
+my(%opt) = @_;
+#warn join(' / ', map { "$_ => $opt{$_}" } keys %opt ). "\n";
+
+my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|select|html(-print)?)$/
+           ? $1 : 'html' ;
+
+</%init>
index 37178dc..e5e6ca9 100644 (file)
 %
 %     if ( $type eq 'html-print' ) {
 
-        <% include( '/elements/header-popup.html', $opt{'title'} ) %>
+        <% $opt{nohtmlheader}
+             ? ''
+             : include( '/elements/header-popup.html', $opt{'title'} )
+        %>
 
 %     } elsif ( $type eq 'select' ) {
 
-        <% include( '/elements/header-popup.html', $opt{'title'} ) %>
+        <% $opt{nohtmlheader}
+             ? ''
+             : include( '/elements/header-popup.html', $opt{'title'} )
+        %>
         <% defined($opt{'html_init'}) 
               ? ( ref($opt{'html_init'})
                     ? &{$opt{'html_init'}}()
 %       #  @menubar = ( 'Main menu' => $p );
 %       }
 
-        <% include( '/elements/header.html', $opt{'title'},
-                      include( '/elements/menubar.html', @menubar )
-                  )
+        <% $opt{nohtmlheader}
+             ? ''
+             : include( '/elements/header.html', $opt{'title'},
+                          include( '/elements/menubar.html', @menubar )
+                      )
         %>
 
         <% defined($opt{'html_init'}) 
 %                 $cgi->delete('maxrecords');
 %                 $cgi->param('_dummy', 1);
 
-                  ( show <SELECT NAME="maxrecords" onChange="window.location = '<% $cgi->self_url %>;maxrecords=' + this.options[this.selectedIndex].value;">
+                  ( show <SELECT NAME="maxrecords" onChange="window.location = '<% $self_url %>;maxrecords=' + this.options[this.selectedIndex].value;">
 
 %                   foreach my $max ( map { $_ * $confmax } qw( 1 5 10 25 ) ) {
                   <OPTION VALUE="<% $max %>" <% ( $maxrecords == $max ) ? 'SELECTED' : '' %>><% $max %></OPTION>
                 Download full results<BR>
 
 %               $cgi->param('_type', "$xlsname.xls" ); 
-                as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR>
+                as <A HREF="<% $self_url %>">Excel spreadsheet</A><BR>
 
 %               $cgi->param('_type', 'csv'); 
-                as <A HREF="<% $cgi->self_url %>">CSV file</A><BR>
+                as <A HREF="<% $self_url %>">CSV file</A><BR>
+
+%             if ( defined($opt{xml_elements}) ) {
+%               $cgi->param('_type', 'xml'); 
+                as <A HREF="<% $self_url %>">XML file</A><BR>
+%             }
 
 %               $cgi->param('_type', 'html-print'); 
-                as <A HREF="<% $cgi->self_url %>">printable copy</A>
+                as <A HREF="<% $self_url %>">printable copy</A>
 
               </TD>
 %             $cgi->param('_type', "html" ); 
 %     }
 
 %     if ( $type eq 'html-print' ) {
+%       unless ( $opt{nohtmlheader} ) {
 
         </BODY></HTML>
       
+%       }
 %     } else {
 
         <% defined($opt{'html_foot'}) 
               : ''
         %>
 
-        <% include( '/elements/footer.html' ) %>
+        <% $opt{nohtmlheader}
+             ? ''
+             : include( '/elements/footer.html' )
+        %>
 
 %     }
 
@@ -447,6 +465,7 @@ my $confmax        = $args{'confmax'};
 my $maxrecords     = $args{'maxrecords'};
 my $offset         = $args{'offset'};
 my %opt            = %{ $args{'opt'} };
+my $self_url       = $opt{'url'} || $cgi->self_url;
 
 my $count_sth = dbh->prepare($opt{'count_query'})
   or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
diff --git a/httemplate/search/elements/search-xml.html b/httemplate/search/elements/search-xml.html
new file mode 100644 (file)
index 0000000..9f5e9b6
--- /dev/null
@@ -0,0 +1,88 @@
+% foreach my $row ( @$rows ) {
+%
+% if (&{$beginrow}($row)){
+<% &{$beginrow}($row) %>
+% }
+%
+%   foreach my $i ( 0 .. scalar( @{$opt{'fields'}} ) - 1 ) {
+%     my $field = $opt{'fields'}->[$i];
+%     my $value = '';
+%     if ( ref($field) eq 'CODE' ) {
+%       $value = &{$field}($row);
+%       $value = '(N/A)' #unimplemented
+%         if ref($value) eq 'ARRAY';
+%     } else {
+%       $value = $row->$field();
+%     }
+%
+<% &{$beginfield}($row, $i) %><% $value |h %><% &{$endfield}($row, $i) %>
+%
+%   }
+%
+% if (&{$endrow}($row)) {
+<% &{$endrow}($row) %>
+% }
+%
+% }
+<%init>
+
+my %args = @_;
+my $header = $args{'header'};
+my $rows   = $args{'rows'};
+my %opt    = %{ $args{'opt'} };
+
+http_header('Content-Type' => 'application/XML' ); # So saith RFC 4180
+http_header('Content-Disposition' => 
+  'attachment;filename="'.($opt{'name'} || PL($opt{'name_singular'}) ).'.xml"');
+
+unless ( $opt{'fields'} ) {
+  foreach my $i ( 0 .. ( $#{ @$rows[0] } ) ) {
+    $opt{'fields'}->[$i] = sub { my $row = shift; $row->[$i]; };
+  }
+}
+
+my $beginrow = sub { return ''; };
+my $endrow = sub { return ''; };
+if ($opt{xml_row_element}) {
+  $beginrow = sub { my ($row, $index) = @_;
+                    my $value;
+                    if ( ref($opt{xml_row_element}) eq 'CODE' ) {
+                      $value = &{$opt{xml_row_element}}($row);
+                    } else {
+                      $value = $opt{xml_row_element};
+                    }
+                    return "<$value>";
+                  };
+  $endrow = sub { my ($row, $index) = @_;
+                  my $value;
+                  if ( ref($opt{xml_row_element}) eq 'CODE' ) {
+                    $value = &{$opt{xml_row_element}}($row);
+                  } else {
+                    $value = $opt{xml_row_element};
+                  }
+                  return "</$value>";
+                };
+}
+my $beginfield = sub { my ($row, $index) = @_;
+                       my $value;
+                       if ( ref($opt{xml_elements}->[$index]) eq 'CODE' ) {
+                         $value = &{$opt{xml_elements}->[$index]}($row);
+                       } else {
+                         $value = $opt{xml_elements}->[$index];
+                       }
+                       return "<$value>";
+                     };
+my $endfield =   sub { my ($row, $index) = @_;
+                       my $value;
+                       if ( ref($opt{xml_elements}->[$index]) eq 'CODE' ) {
+                         $value = &{$opt{xml_elements}->[$index]}($row);
+                       } else {
+                         $value = $opt{xml_elements}->[$index];
+                       }
+                       return "</$value>";
+                     };
+
+$beginfield = sub { return ''; } if $opt{no_field_elements};  #hmm
+$endfield = sub { return ''; } if $opt{no_field_elements};  #hmm
+
+</%init>
index 636885e..a258f17 100644 (file)
@@ -172,6 +172,10 @@ Example:
 %
 <% include('search-xls.html',  header=>$header, rows=>$rows, opt=>\%opt ) %>
 %
+% } elsif ( $type eq 'xml' ) {
+%
+<% include('search-xml.html',  rows=>$rows, opt=>\%opt ) %>
+%
 % } else { # regular HTML
 %
 <% include('search-html.html',
@@ -195,7 +199,7 @@ my(%opt) = @_;
 
 my $curuser = $FS::CurrentUser::CurrentUser;
 
-my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|select|html(-print)?)$/
+my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|xml|select|html(-print)?)$/
            ? $1 : 'html' ;
 
 my %align = (
@@ -224,9 +228,13 @@ if($type =~ /csv|xls/) {
   } while ( exists($h->[$i]) );
 }
 
+# wtf?
 $opt{disable_download} = 0
   if $opt{disable_download} && $curuser->access_right('Configuration download');
 
+$opt{disable_download} = 1
+  if $opt{really_disable_download};
+
 my @link_agentnums = ();
 my $null_link = '';
 if ( $opt{'agent_virt'} ) {
index 2b51112..bc2a958 100755 (executable)
                )
     %>
 
-%   if ( scalar( qsearch( 'part_pkg_report_option', { 'disabled' => '' } ) ) ) {
-%   # the m2 javascript magic in edit/elements/edit.html would be better here
-
-    <% include( '/elements/tr-select-table.html',
-                   'label'        => 'Column report classes',
-                   'table'        => 'part_pkg_report_option',
-                   'name_col'     => 'name',
-                   'hashref'      => { 'disabled' => '' },
-                   'element_name' => 'column_option',
-                   'multiple'     => 'multiple',
+    <SCRIPT type="text/javascript">
+      function partchange(what) {
+        var id = 'part' + what.value;
+        var element = document.getElementById(id);
+        if (what.checked) {
+          element.style.display = '';
+        } else {
+          element.style.display = 'none';
+        }
+      }
+    </SCRIPT>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part IA?',
+                   'field' => 'part',
+                   'value' => 'IA',
+                   'onchange' => 'partchange(this)',
+               )
+    %>
+
+    <TR id='partIA' style="display:none"><TD>Part IA</TD><TD><TABLE>
+      <TR><TD>Download speeds</TD><TD>
+        <TABLE>
+%       foreach my $speed ( @FS::Report::FCC_477::download ) {
+          <TR>
+            <TH><% $speed %></TH>
+            <TD>
+            <% include( '/elements/select-table.html',
+                           'table'        => 'part_pkg_report_option',
+                           'name_col'     => 'name',
+                           'hashref'      => { 'disabled' => '' },
+                           'element_name' => 'part1_column_option',
+                           'disable_empty' => 1,
+                       )
+            %>
+            </TD>
+          </TR>
+%       }
+        </TABLE></TD>
+      <TD>Upload speeds</TD><TD>
+        <TABLE>
+%       foreach my $speed ( @FS::Report::FCC_477::upload ) {
+          <TR>
+            <TH><% $speed %></TH>
+            <TD>
+            <% include( '/elements/select-table.html',
+                           'table'        => 'part_pkg_report_option',
+                           'name_col'     => 'name',
+                           'hashref'      => { 'disabled' => '' },
+                           'element_name' => 'part1_row_option',
+                           'disable_empty' => 1,
+                       )
+            %>
+            </TD>
+          </TR>
+%       }
+        </TABLE></TD></TR>
+      <TR><TD>Technologies</TD><TD>
+        <TABLE>
+%       my $i = 0;
+%       foreach my $tech ( @FS::Report::FCC_477::technology ) {
+          <TR>
+            <TH><% $tech %></TH>
+            <TD>
+            <% include( '/elements/select-table.html',
+                           'table'        => 'part_pkg_report_option',
+                           'name_col'     => 'name',
+                           'hashref'      => { 'disabled' => '' },
+                           'element_name' => "part1_technology_option_$i",
+                           'empty_label'  => '(omit)',
+                       )
+            %>
+            </TD>
+          </TR>
+%       $i++
+%       }
+        </TABLE></TD></TR>
+    </TABLE></TD></TR>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part IIA?',
+                   'field' => 'part',
+                   'value' => 'IIA',
+                   'onchange' => 'partchange(this)',
                )
     %>
 
-    <% include( '/elements/tr-select-table.html',
-                   'label'        => 'Row report classes',
-                   'table'        => 'part_pkg_report_option',
-                   'name_col'     => 'name',
-                   'hashref'      => { 'disabled' => '' },
-                   'element_name' => 'row_option',
-                   'multiple'     => 'multiple',
+    <TR id='partIIA' style="display:none"><TD>Part IIA</TD><TD><TABLE>
+%   foreach my $option ( @FS::Report::FCC_477::part2aoption ) {
+    <TR>
+      <TH><% $option %></TH>
+      <TD>
+      <% include( '/elements/select-table.html',
+                     'table'        => 'part_pkg_report_option',
+                     'name_col'     => 'name',
+                     'hashref'      => { 'disabled' => '' },
+                     'element_name' => 'part2a_row_option',
+                 )
+      %>
+      </TD>
+    </TR>
+%   }
+  </TABLE></TD></TR>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part IIB?',
+                   'field' => 'part',
+                   'value' => 'IIB',
+                   'onchange' => 'partchange(this)',
                )
     %>
 
+    <TR id='partIIB' style="display:none"><TD>Part IIB</TD><TD><TABLE>
+%   foreach my $option ( @FS::Report::FCC_477::part2boption ) {
+    <TR>
+      <TH><% $option %></TH>
+      <TD>
+      <% include( '/elements/select-table.html',
+                     'table'        => 'part_pkg_report_option',
+                     'name_col'     => 'name',
+                     'hashref'      => { 'disabled' => '' },
+                     'element_name' => 'part2b_row_option',
+                 )
+      %>
+      </TD>
+    </TR>
 %   }
+  </TABLE></TD></TR>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part IV?',
+                   'field' => 'part',
+                   'value' => 'IV',
+                   'onchange' => 'partchange(this)',
+               )
+    %>
+
+    <TR id='partIV' style="display:none"><TD>Part IV</TD><TD><TABLE>
+    <% include( '/elements/tr-textarea.html',
+                   'label'        => 'Explanatory notes',
+                   'id'           => 'partIV',
+                   'field'         => 'notes',
+                   'rows'         => 15,
+                   'cols'         => 80,
+               )
+    %>
+  </TABLE></TD></TR>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part V?',
+                   'field' => 'part',
+                   'value' => 'V',
+               )
+    %>
+
+    <% include( '/elements/tr-checkbox.html',
+                   'label' => 'Enable part VI?',
+                   'field' => 'part',
+                   'value' => 'VI',
+               )
+    %>
 
   </TABLE>