summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjeff <jeff>2010-05-22 19:57:53 +0000
committerjeff <jeff>2010-05-22 19:57:53 +0000
commit4ef5b3b917b7802a136551ad567dc9d649700f53 (patch)
tree2d55887d28f88922e5a61eeb802bb1a0fe627437
parent6c08340010e68fa7ca0989f4902761c8d0db941f (diff)
improved fcc 477 report #7783
-rw-r--r--FS/FS/Conf.pm7
-rw-r--r--FS/FS/Mason.pm1
-rw-r--r--FS/FS/Report/FCC_477.pm90
-rw-r--r--FS/FS/Schema.pm1
-rw-r--r--FS/FS/cust_pkg.pm39
-rw-r--r--FS/FS/part_pkg.pm3
-rw-r--r--FS/MANIFEST1
-rw-r--r--FS/t/Report-FCC_477.t5
-rwxr-xr-xhttemplate/edit/part_pkg.cgi11
-rwxr-xr-xhttemplate/search/477.html256
-rwxr-xr-xhttemplate/search/477partIA_detail.html125
-rwxr-xr-xhttemplate/search/477partIA_summary.html80
-rwxr-xr-xhttemplate/search/477partIIA.html113
-rwxr-xr-xhttemplate/search/477partIIB.html102
-rwxr-xr-xhttemplate/search/477partIV.html17
-rwxr-xr-xhttemplate/search/477partV.html53
-rwxr-xr-xhttemplate/search/477partVI.html130
-rw-r--r--httemplate/search/elements/metasearch.html71
-rw-r--r--httemplate/search/elements/search-html.html39
-rw-r--r--httemplate/search/elements/search-xml.html88
-rw-r--r--httemplate/search/elements/search.html10
-rwxr-xr-xhttemplate/search/report_477.html171
22 files changed, 1212 insertions, 201 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 693d1f5..a1ee23c 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -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.',
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 9c8157a..10edd6d 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -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
index 0000000..518b9f0
--- /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 ab80a08..ebe1195 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -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',
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index cd62816..b851ac7 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -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
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index d2f6596..ef7f557 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<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;
diff --git a/FS/MANIFEST b/FS/MANIFEST
index b140765..5537c2f 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 0000000..b7359aa
--- /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 6ef8dd8..c8b7ecb 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -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' => [
@@ -167,6 +168,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 0f1502f..bd7fb2d 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{
+<?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
index 0000000..546d56c
--- /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 = "<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
index 0000000..269f2ca
--- /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 &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
index 0000000..64f773a
--- /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 = '<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
index 0000000..278dfdc
--- /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 = '<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
index 0000000..269a925
--- /dev/null
+++ b/httemplate/search/477partIV.html
@@ -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
index 0000000..c6ceac4
--- /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 = '<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
index 0000000..dbd1703
--- /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 = '<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
index 0000000..b9d3e3c
--- /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)
+
+ );
+
+</%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>
diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html
index 37178dc..e5e6ca9 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 <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>
@@ -136,13 +144,18 @@
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" );
@@ -417,9 +430,11 @@
% }
% if ( $type eq 'html-print' ) {
+% unless ( $opt{nohtmlheader} ) {
</BODY></HTML>
+% }
% } else {
<% defined($opt{'html_foot'})
@@ -430,7 +445,10 @@
: ''
%>
- <% 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
index 0000000..9f5e9b6
--- /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 "</$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>
diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html
index 636885e..a258f17 100644
--- a/httemplate/search/elements/search.html
+++ b/httemplate/search/elements/search.html
@@ -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'} ) {
diff --git a/httemplate/search/report_477.html b/httemplate/search/report_477.html
index 2b51112..bc2a958 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',
+ <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>