From 3e3c9fe57cd2b6dd0467b37b470892f0e448f300 Mon Sep 17 00:00:00 2001 From: jeff Date: Sat, 22 May 2010 19:49:20 +0000 Subject: [PATCH] improved fcc 477 report #7783 --- FS/FS/Conf.pm | 7 + FS/FS/Mason.pm | 1 + FS/FS/Report/FCC_477.pm | 90 ++++++++++ FS/FS/Schema.pm | 1 + FS/FS/cust_pkg.pm | 39 +++++ FS/FS/part_pkg.pm | 3 + FS/MANIFEST | 1 + FS/t/Report-FCC_477.t | 5 + httemplate/edit/part_pkg.cgi | 11 ++ httemplate/search/477.html | 257 +++++++++------------------- httemplate/search/477partIA_detail.html | 125 ++++++++++++++ httemplate/search/477partIA_summary.html | 80 +++++++++ httemplate/search/477partIIA.html | 113 ++++++++++++ httemplate/search/477partIIB.html | 102 +++++++++++ httemplate/search/477partIV.html | 17 ++ httemplate/search/477partV.html | 53 ++++++ httemplate/search/477partVI.html | 130 ++++++++++++++ httemplate/search/elements/metasearch.html | 71 ++++++++ httemplate/search/elements/search-html.html | 39 +++-- httemplate/search/elements/search-xml.html | 88 ++++++++++ httemplate/search/elements/search.html | 10 +- httemplate/search/report_477.html | 171 ++++++++++++++++-- 22 files changed, 1213 insertions(+), 201 deletions(-) create mode 100644 FS/FS/Report/FCC_477.pm create mode 100644 FS/t/Report-FCC_477.t create mode 100755 httemplate/search/477partIA_detail.html create mode 100755 httemplate/search/477partIA_summary.html create mode 100755 httemplate/search/477partIIA.html create mode 100755 httemplate/search/477partIIB.html create mode 100755 httemplate/search/477partIV.html create mode 100755 httemplate/search/477partV.html create mode 100755 httemplate/search/477partVI.html create mode 100644 httemplate/search/elements/metasearch.html create mode 100644 httemplate/search/elements/search-xml.html diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 7f914901d..6658704cc 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2369,6 +2369,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.', diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index 155a80966..a0a5b4b34 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -121,6 +121,7 @@ if ( -e $addl_handler_use_file ) { use FS::Msgcat qw(gettext geterror); use FS::Misc qw( send_email send_fax states_hash counties 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 index 000000000..518b9f0e1 --- /dev/null +++ b/FS/FS/Report/FCC_477.pm @@ -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; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 78f3e44a9..3cc853f20 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1298,6 +1298,7 @@ sub tables_hashref { 'pay_weight', 'real', 'NULL', '', '', '', 'credit_weight', 'real', 'NULL', '', '', '', 'agentnum', 'int', 'NULL', '', '', '', + 'fcc_ds0s', 'int', 'NULL', '', '', '', ], 'primary_key' => 'pkgpart', diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 89eadd599..80c8a32fb 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -2381,6 +2381,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 @@ -2502,6 +2506,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 ### @@ -2648,6 +2658,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 diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 276889d62..95bc56a13 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -98,6 +98,8 @@ inherits from FS::Record. The following fields are currently supported: =item agentnum - Optional agentnum (see L) +=item fcc_ds0s - Optional DS0 equivalency number for FCC form 477 + =back =head1 METHODS @@ -477,6 +479,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; diff --git a/FS/MANIFEST b/FS/MANIFEST index 5bcb8534f..e348b8bf7 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -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 index 000000000..b7359aa83 --- /dev/null +++ b/FS/t/Report-FCC_477.t @@ -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"; diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi index 9ee8d4d0d..52f70c5cb 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -47,6 +47,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' => [ @@ -159,6 +160,16 @@ { 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' }, diff --git a/httemplate/search/477.html b/httemplate/search/477.html index 0f1502f2a..06e544c0b 100755 --- a/httemplate/search/477.html +++ b/httemplate/search/477.html @@ -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{ + + +%} +% if ( $type eq 'html' || $type eq 'html-print' ) { + + +%}elsif ( $type eq 'xml' ) { +%} +% unless ( $type eq 'html-print' || $type eq 'xml' ) { + + +% $cgi->param('_type', $type ); +% } +% if ( $type eq 'html' || $type eq 'html-print' ) { + +
+ + Download full results
+% $cgi->param('_type', 'xml'); + as XML file
+ +% $cgi->param('_type', 'html-print'); + as printable copy + +
+%}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' ) { +> +% } +% } +% } 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' ) { +> +% } +% } +% } +% } +% +% if ( $type eq 'html' || $type eq 'html-print' ) { +<% include( '/elements/footer.html') %> +%}elsif ( $type eq 'xml' ) { +
+%} <%init> my $curuser = $FS::CurrentUser::CurrentUser; @@ -46,137 +78,16 @@ 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); + warn "mangling $url with $part\n"; + $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 .= '

Summary

'. include('/elements/table.html'); -$summary .= ''; -foreach my $column ( sort keys %column_option_name ) { - $summary .= "$column"; -} -$summary .= ""; - -my $total_count = 0; -my $total_residential = 0; -my $rowcount = 1; -foreach my $row ( sort keys %row_option_name ) { - - $summary .= "$row"; - - 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 .= "$count
$percentage% residential"; - push @sql_query, $sql_query; - $columncount++; - } - - $summary .= ""; - $rowcount++; -} - -my $total_percentage = - sprintf("%.2f", $total_count ? 100-100*$total_residential/$total_count : 0); - -my $html_init = '

Totals

'. include('/elements/table.html'). ""; -$html_init .= "Total Connections"; -$html_init .= "% owned loop"; -$html_init .= "% billed to end users"; -$html_init .= "% residential"; -$html_init .= "% residential > 200kbps"; -$html_init .= ""; -$html_init .= "$total_count"; -$html_init .= "100.00"; -$html_init .= "100.00"; -$html_init .= "$total_percentage"; -$html_init .= "$total_percentage"; -$html_init .= "
"; -$html_init .= $summary; -$html_init .= "

Details

"; - -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; - }; diff --git a/httemplate/search/477partIA_detail.html b/httemplate/search/477partIA_detail.html new file mode 100755 index 000000000..546d56c7f --- /dev/null +++ b/httemplate/search/477partIA_detail.html @@ -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 = "

Part IA $technology breakdown by speeds

"; +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 .= "
$percentage% residential" + unless $cgi->param('_type') eq 'xml'; + return $return; + } else { + return 'Bad call to column_value'; + } +}; + +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') }, +); + + diff --git a/httemplate/search/477partIA_summary.html b/httemplate/search/477partIA_summary.html new file mode 100755 index 000000000..269f2caf2 --- /dev/null +++ b/httemplate/search/477partIA_summary.html @@ -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 > 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 = "

Part IA $technology totals

"; +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); + + + diff --git a/httemplate/search/477partIIA.html b/httemplate/search/477partIIA.html new file mode 100755 index 000000000..64f773a21 --- /dev/null +++ b/httemplate/search/477partIIA.html @@ -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 = '

Part IIA

'; +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'; + diff --git a/httemplate/search/477partIIB.html b/httemplate/search/477partIIB.html new file mode 100755 index 000000000..278dfdc8b --- /dev/null +++ b/httemplate/search/477partIIB.html @@ -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 = '

Part IIB

'; +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'; + diff --git a/httemplate/search/477partIV.html b/httemplate/search/477partIV.html new file mode 100755 index 000000000..269a925dc --- /dev/null +++ b/httemplate/search/477partIV.html @@ -0,0 +1,17 @@ +%if ( $cgi->param('_type') eq 'html' || $cgi->param('_type') eq 'html-print' ) { +

Part IV

+%} elsif ( $cgi->param('_type') eq 'xml' ) { + +%} +<% $cgi->param('notes') |h %> +%if ( $cgi->param('_type') eq 'xml' ) { + +%} +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('List packages'); + + diff --git a/httemplate/search/477partV.html b/httemplate/search/477partV.html new file mode 100755 index 000000000..c6ceac4db --- /dev/null +++ b/httemplate/search/477partV.html @@ -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 = '

Part V

'; +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); + + diff --git a/httemplate/search/477partVI.html b/httemplate/search/477partVI.html new file mode 100755 index 000000000..dbd17032c --- /dev/null +++ b/httemplate/search/477partVI.html @@ -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 = '

Part VI

'; + +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; + }; + diff --git a/httemplate/search/elements/metasearch.html b/httemplate/search/elements/metasearch.html new file mode 100644 index 000000000..b9d3e3ce2 --- /dev/null +++ b/httemplate/search/elements/metasearch.html @@ -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) + + ); + + +% 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' ; + + diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html index 3d4a4b039..67f0b0d03 100644 --- a/httemplate/search/elements/search-html.html +++ b/httemplate/search/elements/search-html.html @@ -32,11 +32,17 @@ % % 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'}}() @@ -54,9 +60,11 @@ % # @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'}) @@ -95,7 +103,7 @@ % $cgi->delete('maxrecords'); % $cgi->param('_dummy', 1); - ( show % foreach my $max ( map { $_ * $confmax } qw( 1 5 10 25 ) ) { @@ -136,13 +144,18 @@ Download full results
% $cgi->param('_type', "$xlsname.xls" ); - as Excel spreadsheet
+ as Excel spreadsheet
% $cgi->param('_type', 'csv'); - as CSV file
+ as CSV file
+ +% if ( defined($opt{xml_elements}) ) { +% $cgi->param('_type', 'xml'); + as XML file
+% } % $cgi->param('_type', 'html-print'); - as printable copy + as printable copy % $cgi->param('_type', "html" ); @@ -416,9 +429,11 @@ % } % if ( $type eq 'html-print' ) { +% unless ( $opt{nohtmlheader} ) { +% } % } else { <% defined($opt{'html_foot'}) @@ -429,7 +444,10 @@ : '' %> - <% include( '/elements/footer.html' ) %> + <% $opt{nohtmlheader} + ? '' + : include( '/elements/footer.html' ) + %> % } @@ -446,6 +464,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 index 000000000..9f5e9b6c1 --- /dev/null +++ b/httemplate/search/elements/search-xml.html @@ -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 ""; + }; +} +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 ""; + }; + +$beginfield = sub { return ''; } if $opt{no_field_elements}; #hmm +$endfield = sub { return ''; } if $opt{no_field_elements}; #hmm + + diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html index 1312cac41..b97a9b321 100644 --- a/httemplate/search/elements/search.html +++ b/httemplate/search/elements/search.html @@ -171,6 +171,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', @@ -194,7 +198,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 = ( @@ -223,9 +227,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'} ) { diff --git a/httemplate/search/report_477.html b/httemplate/search/report_477.html index 7b85c137c..206d49056 100755 --- a/httemplate/search/report_477.html +++ b/httemplate/search/report_477.html @@ -23,30 +23,167 @@ ) %> -% 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', + + + <% include( '/elements/tr-checkbox.html', + 'label' => 'Enable part IA?', + 'field' => 'part', + 'value' => 'IA', + 'onchange' => 'partchange(this)', + ) + %> + + Part IA + + + +
Download speeds + +% foreach my $speed ( @FS::Report::FCC_477::download ) { + + + + +% } +
<% $speed %> + <% include( '/elements/select-table.html', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'element_name' => 'part1_column_option', + 'disable_empty' => 1, + ) + %> +
Upload speeds + +% foreach my $speed ( @FS::Report::FCC_477::upload ) { + + + + +% } +
<% $speed %> + <% include( '/elements/select-table.html', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'element_name' => 'part1_row_option', + 'disable_empty' => 1, + ) + %> +
Technologies + +% my $i = 0; +% foreach my $tech ( @FS::Report::FCC_477::technology ) { + + + + +% $i++ +% } +
<% $tech %> + <% include( '/elements/select-table.html', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'element_name' => "part1_technology_option_$i", + 'empty_label' => '(omit)', + ) + %> +
+ + <% 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', + Part IIA +% foreach my $option ( @FS::Report::FCC_477::part2aoption ) { + + + + +% } +
<% $option %> + <% include( '/elements/select-table.html', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'element_name' => 'part2a_row_option', + ) + %> +
+ + <% include( '/elements/tr-checkbox.html', + 'label' => 'Enable part IIB?', + 'field' => 'part', + 'value' => 'IIB', + 'onchange' => 'partchange(this)', ) %> + Part IIB +% foreach my $option ( @FS::Report::FCC_477::part2boption ) { + + + + % } +
<% $option %> + <% include( '/elements/select-table.html', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'element_name' => 'part2b_row_option', + ) + %> +
+ + <% include( '/elements/tr-checkbox.html', + 'label' => 'Enable part IV?', + 'field' => 'part', + 'value' => 'IV', + 'onchange' => 'partchange(this)', + ) + %> + + Part IV + <% include( '/elements/tr-textarea.html', + 'label' => 'Explanatory notes', + 'id' => 'partIV', + 'field' => 'notes', + 'rows' => 15, + 'cols' => 80, + ) + %> +
+ + <% 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', + ) + %> -- 2.11.0