From 0f359d5480aa1621d73ee802f420e8951abc620d Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 31 Jul 2014 22:54:08 -0700 Subject: [PATCH] new 477 report: deployment info, combined browse-edit UI, #24047 --- FS/FS/AccessRight.pm | 3 + FS/FS/Mason.pm | 3 + FS/FS/Record.pm | 31 ++++ FS/FS/Report/FCC_477.pm | 160 ++++++++++-------- FS/FS/Schema.pm | 69 ++++++++ FS/FS/deploy_zone.pm | 221 +++++++++++++++++++++++++ FS/FS/deploy_zone_block.pm | 126 ++++++++++++++ FS/FS/deploy_zone_vertex.pm | 125 ++++++++++++++ FS/FS/part_pkg_fcc_option.pm | 52 +++--- FS/MANIFEST | 6 + FS/t/deploy_zone.t | 5 + FS/t/deploy_zone_block.t | 5 + FS/t/deploy_zone_vertex.t | 5 + bin/convert-477-options | 2 +- httemplate/browse/deploy_zone.html | 72 ++++++++ httemplate/browse/part_pkg-fcc.html | 215 ++++++++++++++++++++++++ httemplate/edit/deploy_zone-fixed.html | 87 ++++++++++ httemplate/edit/part_pkg.cgi | 37 +++-- httemplate/edit/process/deploy_zone-fixed.html | 9 + httemplate/elements/deploy_zone_block.html | 47 ++++++ httemplate/elements/input-fcc_options.html | 108 ++++++++++++ httemplate/elements/tr-input-fcc_options.html | 4 +- httemplate/misc/part_pkg_fcc_options.html | 9 +- httemplate/search/477.html | 76 ++++----- httemplate/search/report_477.html | 8 +- 25 files changed, 1331 insertions(+), 154 deletions(-) create mode 100644 FS/FS/deploy_zone.pm create mode 100644 FS/FS/deploy_zone_block.pm create mode 100644 FS/FS/deploy_zone_vertex.pm create mode 100644 FS/t/deploy_zone.t create mode 100644 FS/t/deploy_zone_block.t create mode 100644 FS/t/deploy_zone_vertex.t create mode 100644 httemplate/browse/deploy_zone.html create mode 100755 httemplate/browse/part_pkg-fcc.html create mode 100644 httemplate/edit/deploy_zone-fixed.html create mode 100644 httemplate/edit/process/deploy_zone-fixed.html create mode 100644 httemplate/elements/deploy_zone_block.html create mode 100644 httemplate/elements/input-fcc_options.html diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm index 9b9642ed1..8d4d67ba6 100644 --- a/FS/FS/AccessRight.pm +++ b/FS/FS/AccessRight.pm @@ -359,6 +359,9 @@ tie my %rights, 'Tie::IxHash', 'Bulk edit package definitions', + 'Edit FCC report configuration', + { rightname => 'Edit FCC report configuration for all agents', global=>1 }, + 'Edit CDR rates', #{ rightname=>'Edit global CDR rates', global=>1, }, diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index 4f43c6baa..93eca5e48 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -387,6 +387,9 @@ if ( -e $addl_handler_use_file ) { use FS::state; use FS::state; use FS::queue_stat; + use FS::deploy_zone; + use FS::deploy_zone_block; + use FS::deploy_zone_vertex; # Sammath Naur if ( $FS::Mason::addl_handler_use ) { diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 24f2a25dd..200575612 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -124,6 +124,8 @@ FS::Record - Database record objects $error = $record->ut_floatn('column'); $error = $record->ut_number('column'); $error = $record->ut_numbern('column'); + $error = $record->ut_decimal('column'); + $error = $record->ut_decimaln('column'); $error = $record->ut_snumber('column'); $error = $record->ut_snumbern('column'); $error = $record->ut_money('column'); @@ -2434,6 +2436,35 @@ sub ut_numbern { ''; } +=item ut_decimal COLUMN[, DIGITS] + +Check/untaint decimal numbers (up to DIGITS decimal places. If there is an +error, returns the error, otherwise returns false. + +=item ut_decimaln COLUMN[, DIGITS] + +Check/untaint decimal numbers. May be null. If there is an error, returns +the error, otherwise returns false. + +=cut + +sub ut_decimal { + my($self, $field, $digits) = @_; + $digits ||= ''; + $self->getfield($field) =~ /^\s*(\d+(\.\d{0,$digits})?)\s*$/ + or return "Illegal or empty (decimal) $field: ".$self->getfield($field); + $self->setfield($field, $1); + ''; +} + +sub ut_decimaln { + my($self, $field, $digits) = @_; + $self->getfield($field) =~ /^\s*(\d*(\.\d{0,$digits})?)\s*$/ + or return "Illegal (decimal) $field: ".$self->getfield($field); + $self->setfield($field, $1); + ''; +} + =item ut_money COLUMN Check/untaint monetary numbers. May be negative. Set to 0 if null. If there diff --git a/FS/FS/Report/FCC_477.pm b/FS/FS/Report/FCC_477.pm index 79f00e371..bf4754d0e 100644 --- a/FS/FS/Report/FCC_477.pm +++ b/FS/FS/Report/FCC_477.pm @@ -165,8 +165,6 @@ sub save_fcc477map { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - # lame (should be normal FS::Record access) - my $sql = "delete from fcc477map where formkey = ?"; my $sth = dbh->prepare($sql) or die dbh->errstr; $sth->execute($key) or do { @@ -204,6 +202,8 @@ sub statenum2state { my $num = shift; $states{$num}; } +### everything above this point is unmaintained ### + =head1 THE "NEW" REPORT (October 2014 and later) @@ -259,15 +259,14 @@ sub is_fixed_broadband { ).")"; } -=item part6 OPTIONS +=item report_fixed_broadband OPTIONS -Returns Part 6 of the 2014 FCC 477 data, as an arrayref of arrayrefs. -OPTIONS may contain: +Returns the Fixed Broadband Subscription report (section 5.4), as an arrayref +of an arrayrefs. OPTIONS may contain: - date: a timestamp value to count active packages as of that date - agentnum: limit to customers of that agent -Part 6 is the broadband subscription detail report. Columns of the -report are: +Columns of this report are: - census tract - technology code - downstream speed @@ -278,7 +277,7 @@ report are: =cut -sub part6 { +sub report_fixed_broadband { my $class = shift; my %opt = shift; my $date = $opt{date} || time; @@ -323,10 +322,67 @@ sub part6 { dbh->selectall_arrayref($statement); } -=item part9 OPTIONS +=item report_fixed_voice OPTIONS + +Returns the Fixed Voice Subscription Detail report (section 5.5). OPTIONS +are as above. Columns are: + +- census tract +- service type (0 for non-VoIP, 1 for VoIP) +(the above columns form a key) +- VGE lines/VoIP subscriptions in service +- consumer grade VGE lines/VoIP subscriptions + +=cut + +sub report_fixed_voice { + my $class = shift; + my %opt = shift; + my $date = $opt{date} || time; + my $agentnum = $opt{agentnum}; + + my @select = ( + 'cust_location.censustract', + # VoIP indicator (0 for non-VoIP, 1 for VoIP) + 'COALESCE(is_voip, 0)', + # number of lines/subscriptions + 'SUM(CASE WHEN is_voip = 1 THEN 1 ELSE phone_lines END)', + # consumer grade lines/subscriptions + 'SUM(CASE WHEN is_consumer = 1 THEN ( CASE WHEN is_voip = 1 THEN voip_sessions ELSE phone_lines END) ELSE 0 END)' + ); + + my $from = 'cust_pkg + JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum) + JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum) + JOIN part_pkg USING (pkgpart) '. + join_optionnames_int(qw( + is_phone is_voip is_consumer phone_lines voip_sessions + )) + ; + + my @where = ( + active_on($date), + "(is_voip = 1 OR is_phone = 1)", + ); + push @where, "cust_main.agentnum = $agentnum" if $agentnum; + my $group_by = 'cust_location.censustract, COALESCE(is_voip, 0)'; + my $order_by = $group_by; + + my $statement = "SELECT ".join(', ', @select) . " + FROM $from + WHERE ".join(' AND ', @where)." + GROUP BY $group_by + ORDER BY $order_by + "; + + warn $statement if $DEBUG; + dbh->selectall_arrayref($statement); +} + +=item report_local_phone OPTIONS -Returns Part 9 of the 2014 FCC 477 data. Part 9 is the Local Exchange -Telephone Subscription report. Columns are: +Returns the Local Exchange Telephone Subscription report (section 5.6). +OPTIONS are as above. Each row is data for one state. Columns are: - state FIPS code (key) - wholesale switched voice lines @@ -346,7 +402,7 @@ Telephone Subscription report. Columns are: =cut -sub part9 { +sub report_local_phone { my $class = shift; my %opt = shift; my $date = $opt{date} || time; @@ -358,10 +414,10 @@ sub part9 { "SUM(phone_circuits)", "SUM(phone_lines)", "SUM(CASE WHEN is_broadband = 1 THEN phone_lines ELSE 0 END)", - "SUM(CASE WHEN is_consumer = 1 AND is_longdistance IS NULL THEN phone_lines ELSE 0 END)", - "SUM(CASE WHEN is_consumer = 1 AND is_longdistance = 1 THEN phone_lines ELSE 0 END)", - "SUM(CASE WHEN is_consumer IS NULL AND is_longdistance IS NULL THEN phone_lines ELSE 0 END)", - "SUM(CASE WHEN is_consumer IS NULL AND is_longdistance = 1 THEN phone_lines ELSE 0 END)", + "SUM(CASE WHEN is_consumer = 1 AND phone_longdistance IS NULL THEN phone_lines ELSE 0 END)", + "SUM(CASE WHEN is_consumer = 1 AND phone_longdistance = 1 THEN phone_lines ELSE 0 END)", + "SUM(CASE WHEN is_consumer IS NULL AND phone_longdistance IS NULL THEN phone_lines ELSE 0 END)", + "SUM(CASE WHEN is_consumer IS NULL AND phone_longdistance = 1 THEN phone_lines ELSE 0 END)", "SUM(CASE WHEN phone_localloop = 'owned' THEN phone_lines ELSE 0 END)", "SUM(CASE WHEN phone_localloop = 'leased' THEN phone_lines ELSE 0 END)", "SUM(CASE WHEN phone_localloop = 'resale' THEN phone_lines ELSE 0 END)", @@ -378,7 +434,7 @@ sub part9 { join_optionnames_int(qw( is_phone is_broadband phone_vges phone_circuits phone_lines - is_consumer is_longdistance + is_consumer phone_longdistance )). join_optionnames('media', 'phone_localloop') ; @@ -401,7 +457,26 @@ sub part9 { dbh->selectall_arrayref($statement); } -sub part10 { +=item report_voip OPTIONS + +Returns the Interconnected VoIP Subscription report (section 5.7). +OPTIONS are as above. Columns are: + +- state FIPS code (key) +- OTT subscriptions (non-last-mile) +- OTT subscriptions sold to consumers +- last-mile subscriptions +- last-mile subscriptions sold to consumers +- last-mile subscriptions bundled with broadband Internet +- last-mile subscriptions over copper pairs +- last-mile subscriptions over coaxial +- last-mile subscriptions over fiber +- last-mile subscriptions over fixed wireless +- last-mile subscriptions over other media + +=cut + +sub report_voip { my $class = shift; my %opt = shift; my $date = $opt{date} || time; @@ -453,54 +528,5 @@ sub part10 { dbh->selectall_arrayref($statement); } -=item part11 OPTIONS - -Returns part 11 (voice subscription detail), as above. - -=cut - -sub part11 { - my $class = shift; - my %opt = shift; - my $date = $opt{date} || time; - my $agentnum = $opt{agentnum}; - - my @select = ( - 'cust_location.censustract', - # VoIP indicator (0 for non-VoIP, 1 for VoIP) - 'COALESCE(is_voip, 0)', - # number of lines/subscriptions - 'SUM(CASE WHEN is_voip = 1 THEN 1 ELSE phone_lines END)', - # consumer grade lines/subscriptions - 'SUM(CASE WHEN is_consumer = 1 THEN ( CASE WHEN is_voip = 1 THEN 1 ELSE phone_lines END) ELSE 0 END)' - ); - - my $from = 'cust_pkg - JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum) - JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum) - JOIN part_pkg USING (pkgpart) '. - join_optionnames_int(qw( - is_phone is_voip is_consumer phone_lines - )) - ; - - my @where = ( - active_on($date), - "(is_voip = 1 OR is_phone = 1)", - ); - push @where, "cust_main.agentnum = $agentnum" if $agentnum; - my $group_by = 'cust_location.censustract, COALESCE(is_voip, 0)'; - my $order_by = $group_by; - - my $statement = "SELECT ".join(', ', @select) . " - FROM $from - WHERE ".join(' AND ', @where)." - GROUP BY $group_by - ORDER BY $order_by - "; - - warn $statement if $DEBUG; - dbh->selectall_arrayref($statement); -} 1; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 27bd81374..830b39a84 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -6672,6 +6672,75 @@ sub tables_hashref { 'index' => [], }, + # eventually link to tower/sector? + 'deploy_zone' => { + 'columns' => [ + 'zonenum', 'serial', '', '', '', '', + 'description', 'char', 'NULL', $char_d, '', '', + 'agentnum', 'int', '', '', '', '', + 'dbaname', 'char', 'NULL', $char_d, '', '', + 'zonetype', 'char', '', 1, '', '', + 'technology', 'int', '', '', '', '', + 'spectrum', 'int', 'NULL', '', '', '', + 'servicetype', 'char', '', '12', '', '', + 'adv_speed_up', 'decimal', 'NULL', '10,3', '', '', + 'adv_speed_down', 'decimal', 'NULL', '10,3', '', '', + 'cir_speed_up', 'decimal', 'NULL', '10,3', '', '', + 'cir_speed_down', 'decimal', 'NULL', '10,3', '', '', + 'is_consumer', 'char', 'NULL', 1, '', '', + 'is_business', 'char', 'NULL', 1, '', '', + ], + 'primary_key' => 'zonenum', + 'unique' => [], + 'index' => [ [ 'agentnum' ] ], + 'foreign_keys' => [ + { columns => [ 'agentnum' ], + table => 'agent', + references => [ 'agentnum' ], + }, + ], + }, + + 'deploy_zone_block' => { + 'columns' => [ + 'blocknum', 'serial', '', '', '', '', + 'zonenum', 'int', '', '', '', '', + 'censusblock', 'char', '', 15, '', '', + 'censusyear', 'char', '', 4, '', '', + ], + 'primary_key' => 'blocknum', + 'unique' => [], + 'index' => [ [ 'zonenum' ] ], + 'foreign_keys' => [ + { columns => [ 'zonenum' ], + table => 'deploy_zone', + references => [ 'zonenum' ], + }, + ], + }, + + 'deploy_zone_vertex' => { + 'columns' => [ + 'vertexnum', 'serial', '', '', '', '', + 'zonenum', 'int', '', '', '', '', + 'latitude', 'decimal', '', '10,7', '', '', + 'longitude', 'decimal', '', '10,7', '', '', + 'sequence', 'int', '', '', '', '', + ], + 'primary_key' => 'vertexnum', + 'unique' => [ [ 'zonenum', 'sequence' ] ], + 'index' => [ ], + 'foreign_keys' => [ + { columns => [ 'zonenum' ], + table => 'deploy_zone', + references => [ 'zonenum' ], + }, + ], + }, + + + + # name type nullability length default local diff --git a/FS/FS/deploy_zone.pm b/FS/FS/deploy_zone.pm new file mode 100644 index 000000000..3caeda24b --- /dev/null +++ b/FS/FS/deploy_zone.pm @@ -0,0 +1,221 @@ +package FS::deploy_zone; + +use strict; +use base qw( FS::o2m_Common FS::Record ); +use FS::Record qw( qsearch qsearchs dbh ); + +=head1 NAME + +FS::deploy_zone - Object methods for deploy_zone records + +=head1 SYNOPSIS + + use FS::deploy_zone; + + $record = new FS::deploy_zone \%hash; + $record = new FS::deploy_zone { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::deploy_zone object represents a geographic zone where a certain kind +of service is available. Currently we store this information to generate +the FCC Form 477 deployment reports, but it may find other uses later. + +FS::deploy_zone inherits from FS::Record. The following fields are currently +supported: + +=over 4 + +=item zonenum + +primary key + +=item description + +Optional text describing the zone. + +=item agentnum + +The agent that serves this zone. + +=item dbaname + +The name under which service is marketed in this zone. If null, will +default to the agent name. + +=item zonetype + +The way the zone geography is defined: "B" for a list of census blocks +(used by the FCC for fixed broadband service), "P" for a polygon (for +mobile services). See L and L. + +=item technology + +The FCC technology code for the type of service available. + +=item spectrum + +For mobile service zones, the FCC code for the RF band. + +=item servicetype + +"broadband" or "voice" + +=item adv_speed_up + +For broadband, the advertised upstream bandwidth in the zone. If multiple +speed tiers are advertised, use the highest. + +=item adv_speed_down + +For broadband, the advertised downstream bandwidth in the zone. + +=item cir_speed_up + +For broadband, the contractually guaranteed upstream bandwidth, if that type +of service is sold. + +=item cir_speed_down + +For broadband, the contractually guaranteed downstream bandwidth, if that +type of service is sold. + +=item is_consumer + +'Y' if this service is sold for consumer/household use. + +=item is_business + +'Y' if this service is sold to business or institutional use. Not mutually +exclusive with is_consumer. + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new zone. To add the zone to the database, see L<"insert">. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'deploy_zone'; } + +=item insert ELEMENTS + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +sub delete { + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + # clean up linked records + my $self = shift; + my $error = $self->process_o2m( + 'table' => $self->element_table, + 'num_col' => 'zonenum', + 'fields' => 'zonenum', + 'params' => {}, + ) || $self->SUPER::delete(@_); + + if ($error) { + dbh->rollback if $oldAutoCommit; + return $error; + } + ''; +} + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid zone record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('zonenum') + || $self->ut_textn('description') + || $self->ut_number('agentnum') + || $self->ut_foreign_key('agentnum', 'agent', 'agentnum') + || $self->ut_alphan('dbaname') + || $self->ut_enum('zonetype', [ 'B', 'P' ]) + || $self->ut_number('technology') + || $self->ut_numbern('spectrum') + || $self->ut_enum('servicetype', [ 'broadband', 'voice' ]) + || $self->ut_decimaln('adv_speed_up', 3) + || $self->ut_decimaln('adv_speed_down', 3) + || $self->ut_decimaln('cir_speed_up', 3) + || $self->ut_decimaln('cir_speed_down', 3) + || $self->ut_flag('is_consumer') + || $self->ut_flag('is_business') + ; + return $error if $error; + + $self->SUPER::check; +} + +=item element_table + +Returns the name of the table that contains the zone's elements (blocks or +vertices). + +=cut + +sub element_table { + my $self = shift; + if ($self->zonetype eq 'B') { + return 'deploy_zone_block'; + } elsif ( $self->zonetype eq 'P') { + return 'deploy_zone_vertex'; + } else { + die 'unknown zonetype'; + } +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L + +=cut + +1; + diff --git a/FS/FS/deploy_zone_block.pm b/FS/FS/deploy_zone_block.pm new file mode 100644 index 000000000..58234b924 --- /dev/null +++ b/FS/FS/deploy_zone_block.pm @@ -0,0 +1,126 @@ +package FS::deploy_zone_block; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::deploy_zone_block - Object methods for deploy_zone_block records + +=head1 SYNOPSIS + + use FS::deploy_zone_block; + + $record = new FS::deploy_zone_block \%hash; + $record = new FS::deploy_zone_block { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::deploy_zone_block object represents a census block that's part of +a deployment zone. FS::deploy_zone_block inherits from FS::Record. The +following fields are currently supported: + +=over 4 + +=item blocknum + +primary key + +=item zonenum + +L foreign key for the zone. + +=item censusblock + +U.S. census block number (15 digits). + +=item censusyear + +The year of the census map where the block appeared or was last verified. + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new block entry. To add the recordto the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I method. + +=cut + +# the new method can be inherited from FS::Record, if a table method is defined + +sub table { 'deploy_zone_block'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +# the insert method can be inherited from FS::Record + +=item delete + +Delete this record from the database. + +=cut + +# the delete method can be inherited from FS::Record + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +# the replace method can be inherited from FS::Record + +=item check + +Checks all fields to make sure this is a valid record. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('blocknum') + || $self->ut_number('zonenum') + || $self->ut_number('censusblock') + || $self->ut_number('censusyear') + ; + return $error if $error; + + $self->SUPER::check; +} + +=back + +=head1 SEE ALSO + +L + +=cut + +1; + diff --git a/FS/FS/deploy_zone_vertex.pm b/FS/FS/deploy_zone_vertex.pm new file mode 100644 index 000000000..a25bfde23 --- /dev/null +++ b/FS/FS/deploy_zone_vertex.pm @@ -0,0 +1,125 @@ +package FS::deploy_zone_vertex; + +use strict; +use base qw( FS::Record ); +use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::deploy_zone_vertex - Object methods for deploy_zone_vertex records + +=head1 SYNOPSIS + + use FS::deploy_zone_vertex; + + $record = new FS::deploy_zone_vertex \%hash; + $record = new FS::deploy_zone_vertex { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::deploy_zone_vertex object represents a vertex of a polygonal +deployment zone (L). FS::deploy_zone_vertex inherits from +FS::Record. The following fields are currently supported: + +=over 4 + +=item vertexnum + +primary key + +=item zonenum + +Foreign key to L. + +=item latitude + +Latitude, as a decimal; positive values are north of the Equator. + +=item longitude + +Longitude, as a decimal; positive values are east of Greenwich. + +=item sequence + +The ordinal position of this vertex, starting with zero. + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new vertex record. To add the record to the database, see L<"insert">. + +=cut + +sub table { 'deploy_zone_vertex'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=cut + +=item delete + +Delete this record from the database. + +=cut + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=cut + +=item check + +Checks all fields to make sure this is a valid vertex. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +# the check method should currently be supplied - FS::Record contains some +# data checking routines + +sub check { + my $self = shift; + + my $error = + $self->ut_numbern('vertexnum') + || $self->ut_number('zonenum') + || $self->ut_coord('latitude') + || $self->ut_coord('longitude') + || $self->ut_number('sequence') + ; + return $error if $error; + + $self->SUPER::check; +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L + +=cut + +1; + diff --git a/FS/FS/part_pkg_fcc_option.pm b/FS/FS/part_pkg_fcc_option.pm index 0a288def5..a090b96ae 100644 --- a/FS/FS/part_pkg_fcc_option.pm +++ b/FS/FS/part_pkg_fcc_option.pm @@ -112,31 +112,31 @@ tie our %media_types, 'Tie::IxHash', ( 'Other' => [ 90, 0 ], ); -our %technology_labels = ( - 10 => 'Other ADSL', - 11 => 'ADSL2', - 12 => 'VDSL', - 20 => 'SDSL', - 30 => 'Other Copper Wireline', - 40 => 'Other Cable Modem', - 41 => 'Cable - DOCSIS 1, 1.1, 2.0', - 42 => 'Cable - DOCSIS 3.0', - 50 => 'Fiber', - 60 => 'Satellite', - 70 => 'Terrestrial Fixed Wireless', - # mobile wireless - 80 => 'Mobile - WCDMA/UMTS/HSPA', - 81 => 'Mobile - HSPA+', - 82 => 'Mobile - EVDO/EVDO Rev A', - 83 => 'Mobile - LTE', - 84 => 'Mobile - WiMAX', - 85 => 'Mobile - CDMA', - 86 => 'Mobile - GSM', - 87 => 'Mobile - Analog', - 88 => 'Other Mobile', - - 90 => 'Electric Power Line', - 0 => 'Other' +tie our %technology_labels, 'Tie::IxHash', ( + 10 => 'Other ADSL', + 11 => 'ADSL2', + 12 => 'VDSL', + 20 => 'SDSL', + 30 => 'Other Copper Wireline', + 40 => 'Other Cable Modem', + 41 => 'Cable - DOCSIS 1, 1.1, 2.0', + 42 => 'Cable - DOCSIS 3.0', + 50 => 'Fiber', + 60 => 'Satellite', + 70 => 'Terrestrial Fixed Wireless', + # mobile wireless + 80 => 'Mobile - WCDMA/UMTS/HSPA', + 81 => 'Mobile - HSPA+', + 82 => 'Mobile - EVDO/EVDO Rev A', + 83 => 'Mobile - LTE', + 84 => 'Mobile - WiMAX', + 85 => 'Mobile - CDMA', + 86 => 'Mobile - GSM', + 87 => 'Mobile - Analog', + 88 => 'Other Mobile', + + 90 => 'Electric Power Line', + 0 => 'Other' ); sub media_types { @@ -144,7 +144,7 @@ sub media_types { } sub technology_labels { - +{ %technology_labels }; + Storable::dclone(\%technology_labels); } =head1 BUGS diff --git a/FS/MANIFEST b/FS/MANIFEST index 693904d71..9a9573dbd 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -812,3 +812,9 @@ FS/state.pm t/state.t FS/queue_stat.pm t/queue_stat.t +FS/deploy_zone.pm +t/deploy_zone.t +FS/deploy_zone_block.pm +t/deploy_zone_block.t +FS/deploy_zone_vertex.pm +t/deploy_zone_vertex.t diff --git a/FS/t/deploy_zone.t b/FS/t/deploy_zone.t new file mode 100644 index 000000000..d220e81c7 --- /dev/null +++ b/FS/t/deploy_zone.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::deploy_zone; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/deploy_zone_block.t b/FS/t/deploy_zone_block.t new file mode 100644 index 000000000..c3241b158 --- /dev/null +++ b/FS/t/deploy_zone_block.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::deploy_zone_block; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/deploy_zone_vertex.t b/FS/t/deploy_zone_vertex.t new file mode 100644 index 000000000..78c079ffd --- /dev/null +++ b/FS/t/deploy_zone_vertex.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::deploy_zone_vertex; +$loaded=1; +print "ok 1\n"; diff --git a/bin/convert-477-options b/bin/convert-477-options index 2b8970aae..99a6ea5e0 100755 --- a/bin/convert-477-options +++ b/bin/convert-477-options @@ -121,7 +121,7 @@ for my $part_pkg (qsearch('part_pkg', { freq => {op => '!=', value => '0'}})) { if ($part_pkg->fcc_voip_class) { # there's no such thing as a VoIP DS0 equivalent, but this is # what we used the field for - push @fcc_opts, 'voip_lines' => $part_pkg->fcc_ds0s; + push @fcc_opts, 'voip_sessions' => $part_pkg->fcc_ds0s; } else { push @fcc_opts, 'phone_lines' => $part_pkg->fcc_ds0s, 'is_phone' => 1; } diff --git a/httemplate/browse/deploy_zone.html b/httemplate/browse/deploy_zone.html new file mode 100644 index 000000000..489a22658 --- /dev/null +++ b/httemplate/browse/deploy_zone.html @@ -0,0 +1,72 @@ +<& /elements/header.html, 'Deployment zones' &> +<& /elements/menubar.html, + 'Add a new fixed broadband zone' => $p.'edit/deploy_zone-fixed.html', + 'Add a new mobile zone' => $p.'edit/deploy_zone-mobile.html', +&> +

Fixed Broadband Zones

+<& elements/browse.html, + name_singular => 'zone', + query => { table => 'deploy_zone', + hashref => { zonetype => 'B' }, + }, + count_query => "SELECT COUNT(*) FROM deploy_zone WHERE zonetype = 'B'", + agent_virt => 1, + header => [ '#', + 'Description', + 'Technology', + 'Market', + 'Advertised Mbps', + 'Contractual Mbps', + 'Census blocks', + ], + fields => [ 'zonenum', + 'description', + sub { my $self = shift; + $tech_label->{$self->technology} }, + sub { my $self = shift; + join( ' / ', + $self->is_consumer ? 'consumer' : (), + $self->is_business ? 'business' : () + ) + }, + sub { my $self = shift; + join( ' / ', grep $_, + $self->adv_speed_down, + $self->adv_speed_up + ) + }, + sub { my $self = shift; + join( ' / ', grep $_, + $self->cir_speed_down, + $self->cir_speed_up + ) + }, + sub { my $self = shift; + FS::deploy_zone_block->count('zonenum = '.$self->zonenum) + }, + ], + sort_fields => [ 'zonenum', + 'description', + 'technology', + 'is_consumer is not null, is_business is not null', + 'adv_speed_down, adv_speed_up', + 'cir_speed_down, cir_speed_up', + ], + links => [ '', $link_fixed, ], + align => 'clllllr', + nohtmlheader => 1, + disable_maxselect => 1, + disable_total => 1, +&> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +my $acl_edit = $curuser->access_right('Edit FCC report configuration'); +my $acl_edit_global = $curuser->access_right('Edit FCC report configuration for all agents'); +die "access denied" + unless $acl_edit or $acl_edit_global; + +my $link_fixed = [ $p.'edit/deploy_zone-fixed.html?', 'zonenum' ]; +my $link_mobile= [ $p.'edit/deploy_zone-mobile.html', 'zonenum' ]; + +my $tech_label = FS::part_pkg_fcc_option->technology_labels; + diff --git a/httemplate/browse/part_pkg-fcc.html b/httemplate/browse/part_pkg-fcc.html new file mode 100755 index 000000000..9462c3248 --- /dev/null +++ b/httemplate/browse/part_pkg-fcc.html @@ -0,0 +1,215 @@ +<& elements/browse.html, + 'title' => 'Package Definitions - FCC Options', + 'menubar' => \@menubar, + 'html_init' => $html_init, + 'html_form' => $html_form, + 'html_posttotal' => $html_posttotal, + 'name' => 'package definitions', + 'disableable' => 1, + 'disabled_statuspos' => 4, + 'agent_virt' => 1, + 'agent_null_right' => [ $edit, $edit_global ], + 'agent_null_right_link' => $edit_global, + 'agent_pos' => 6, + 'query' => + { 'select' => $select, + 'table' => 'part_pkg', + 'addl_from' => $addl_from, + 'hashref' => \%hash, + 'extra_sql' => $extra_sql, + 'order_by' => "ORDER BY $orderby" + }, + 'count_query' => $count_query, + 'header' => \@header, + 'fields' => \@fields, + 'links' => \@links, + 'align' => $align, + 'link_field' => 'pkgpart', + 'html_foot' => $html_foot, +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $edit = 'Edit package definitions'; +my $edit_global = 'Edit global package definitions'; +my $acl_edit = $curuser->access_right($edit); +my $acl_edit_global = $curuser->access_right($edit_global); + +die "access denied" + unless $acl_edit || $acl_edit_global; + +my $conf = new FS::Conf; + +my $orderby = 'pkgpart'; +my %hash = (); +my $extra_count = ''; + +my @where = (); + +# only ever show recurring packages here +$hash{'freq'} = { op=>'!=', value=>'0' }; +$extra_count = " freq != '0' "; + +# filter by classnum +my $classnum = ''; +if ( $cgi->param('classnum') =~ /^(\d+)$/ ) { + $classnum = $1; + push @where, $classnum ? "classnum = $classnum" + : "classnum IS NULL"; +} +$cgi->delete('classnum'); + +# filter by agent permissions +push @where, FS::part_pkg->curuser_pkgs_sql + unless $acl_edit_global; + +my $extra_sql = scalar(@where) + ? ( scalar(keys %hash) ? ' AND ' : ' WHERE ' ). + join( 'AND ', @where) + : ''; + +# pull option values into the select +my @optionnames = ( qw( + media + is_consumer + is_broadband technology broadband_upstream broadband_downstream + is_phone phone_wholesale phone_vges phone_circuits + phone_lines phone_longdistance phone_localloop + is_voip voip_lastmile voip_sessions +) ); + +my $select = join(',', + 'part_pkg.*', + '(SELECT classname FROM pkg_class WHERE pkg_class.classnum = part_pkg.classnum) AS classname', # grr, disableable... + @optionnames +); + +my $addl_from = + FS::Report::FCC_477::join_optionnames(@optionnames); + +#restore this so pagination works +$cgi->param('classnum', $classnum) if length($classnum); + +#should hide this if there aren't any classes +my $html_posttotal = + "
( show class: ". + include('/elements/select-pkg_class.html', + #'curr_value' => $classnum, + 'value' => $classnum, #insist on 0 :/ + 'onchange' => 'filter_change()', + 'pre_options' => [ '-1' => 'all', + '0' => '(none)', ], + 'disable_empty' => 1, + ). + ' )'; + +my $link = [ $p.'edit/part_pkg.cgi?', 'pkgpart' ]; + +my @header = ( '#', 'Package', 'Comment' ); +my @fields = ( 'pkgpart', 'pkg', 'comment' ,); +my $align = 'rll'; +my @links = ( $link, $link, '', ); + +unless ( length($classnum) ) { + push @header, 'Class'; + push @fields, 'classname'; + $align .= 'l'; +} + +# still include the report_option classes, to help with migration +# but not other plan options + +my %report_optionname_name = map { 'report_option_'.$_->num, $_->name } + qsearch('part_pkg_report_option', { disabled => '' }); + +push @header, 'Report classes'; + +push @fields, + sub { + my $part_pkg = shift; + my %options = $part_pkg->options; + # gather any options that are really report options, + # convert them to their user-friendly names, + # and sort them (I think?) + my @report_options = + sort { $a cmp $b } + map { $report_optionname_name{$_} } + grep { $options{$_} + and exists($report_optionname_name{$_}) } + keys %options; + + my @rows; + foreach (@report_options) { + push @rows, [ + { 'data' => $_, + 'align' => 'center', + 'colspan' => 2 + } + ]; + } # foreach @report_options + \@rows; + }; + +$align .= 'cr'; + +# -------- +# now the FCC option part +# -------- + +my @pkgparts; +push @header, 'FCC report parameters'; +push @fields, sub { + my $part_pkg = shift; + my %hash = $part_pkg->fcc_options; + include('/elements/input-fcc_options.html', + id => 'pkgpart'.$part_pkg->pkgpart, + curr_value => encode_json(\%hash), + html_only => 1 + ); +}; +$align .= 'l'; + +my $count_extra_sql = $extra_sql; +$count_extra_sql =~ s/^\s*AND /WHERE /i; +$extra_count = ( $count_extra_sql ? ' AND ' : ' WHERE ' ). $extra_count + if $extra_count; +my $count_query = "SELECT COUNT(*) FROM part_pkg $count_extra_sql $extra_count"; + +my $html_init = + include('/elements/init_overlib.html') . + include('/elements/input-fcc_options.html', js_only => 1) . + include('.style'); + +my $html_form = ''; +my $html_foot = ''; +# insert a checkbox column +unshift @header, ''; +unshift @fields, sub { + ''; +}; +unshift @links, ''; +$align = 'c'.$align; + + +$html_form = qq!
!; +$html_foot = qq! + +
!; + +my @menubar = + ( 'Package definitions' => $p.'browse/part_pkg.cgi' ); + + +<%def .style> + + diff --git a/httemplate/edit/deploy_zone-fixed.html b/httemplate/edit/deploy_zone-fixed.html new file mode 100644 index 000000000..ecec9c434 --- /dev/null +++ b/httemplate/edit/deploy_zone-fixed.html @@ -0,0 +1,87 @@ +<& elements/edit.html, + 'name_singular' => 'deployment zone', + 'table' => 'deploy_zone', + 'post_url' => popurl(1).'process/deploy_zone-fixed.html', + 'labels' => { + 'description' => 'Description', + 'agentnum' => 'Agent', + 'dbaname' => 'Business name (if different from agent)', + 'technology' => 'Technology', + 'adv_speed_up' => 'Upstream', + 'adv_speed_down' => 'Downstream', + 'cir_speed_up' => 'Upstream', + 'cir_speed_down' => 'Downstream', + 'is_consumer' => 'Consumer/mass market', + 'is_business' => 'Business/government', + 'blocknum' => '', + }, + 'fields' => [ + { field => 'zonetype', + type => 'hidden', + value => 'B' + }, + { field => 'servicetype', + type => 'hidden', + value => 'broadband' + }, + 'description', + { field => 'agentnum', + type => 'select-agent', + disable_empty => 1, + viewall_right => 'Edit FCC report configuration for all agents', + }, + 'dbaname', + { field => 'technology', + type => 'select', + options => [ keys(%$technology_labels) ], + labels => $technology_labels, + }, + { field => 'is_consumer', type => 'checkbox', value=>'Y' }, + { field => 'is_business', type => 'checkbox', value=>'Y' }, + { type => 'tablebreak-tr-title', + value => 'Advertised maximum speed (Mbps)' }, + 'adv_speed_down', + 'adv_speed_up', + { type => 'tablebreak-tr-title', + value => 'Contractually guaranteed speed (Mbps)' }, + 'cir_speed_down', + 'cir_speed_up', + + { type => 'tablebreak-tr-title', value => 'Census blocks'}, + { field => 'blocknum', + type => 'deploy_zone_block', + o2m_table => 'deploy_zone_block', + m2_label => ' ', + m2_error_callback => $m2_error_callback, + }, + ], + +&> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right([ + 'Edit FCC report configuration', + 'Edit FCC report configuration for all agents', + ]); + +my $technology_labels = FS::part_pkg_fcc_option->technology_labels; + +my $m2_error_callback = sub { + my ($cgi, $deploy_zone) = @_; + my @blocknums = grep { + /^blocknum\d+/ and length($cgi->param($_.'_censusblock')) + } $cgi->param; + + map { + my $k = $_; + FS::deploy_zone_block->new({ + blocknum => scalar($cgi->param($k)), + zonenum => $deploy_zone->zonenum, + censusblock => scalar($cgi->param($k.'_censusblock')), + censusyear => scalar($cgi->param($k.'_censusyear')), + }) + } @blocknums; +}; + + diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi index 9e55d9f42..65eca6cf4 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -232,23 +232,26 @@ }, }, - { type => 'tablebreak-tr-title', - value => 'FCC Form 477 information', - }, - { field => 'fcc_options_string', - type => 'input-fcc_options', - curr_value_callback => sub { - my ($cgi, $part_pkg, $fref) = @_; - if ( $cgi->param('fcc_options_string') ) { - # error redirect - return $cgi->param('fcc_options_string'); - } - my %hash; - %hash = $part_pkg->fcc_options - if ($part_pkg->pkgpart); - return encode_json(\%hash); + ($fcc_opts ? ( + { type => 'tablebreak-tr-title', + value => 'FCC Form 477 information', }, - }, + { field => 'fcc_options_string', + type => 'input-fcc_options', + curr_value_callback => sub { + my ($cgi, $part_pkg, $fref) = @_; + if ( $cgi->param('fcc_options_string') ) { + # error redirect + return $cgi->param('fcc_options_string'); + } + my %hash; + %hash = $part_pkg->fcc_options + if ($part_pkg->pkgpart); + return encode_json(\%hash); + }, + }, + ) : () + ), { type => 'tablebreak-tr-title', value => 'External Links', #better name? @@ -405,6 +408,8 @@ my $agent_clone_extra_sql = my $conf = new FS::Conf; my $taxproducts = $conf->exists('enable_taxproducts'); +my $fcc_opts = $conf->exists('part_pkg-show_fcc_options'); + my @locales = grep { ! /^en_/i } $conf->config('available-locales'); #should filter from the default locale lang instead of en_ my %locale_labels = map { ( $_ => 'Package -- '. FS::Locales->description($_) ) diff --git a/httemplate/edit/process/deploy_zone-fixed.html b/httemplate/edit/process/deploy_zone-fixed.html new file mode 100644 index 000000000..c14c81c58 --- /dev/null +++ b/httemplate/edit/process/deploy_zone-fixed.html @@ -0,0 +1,9 @@ +<& elements/process.html, + error_redirect => popurl(2).'deploy_zone-fixed.html?', + table => 'deploy_zone', + viewall_dir => 'browse', + process_o2m => + { 'table' => 'deploy_zone_block', + 'fields' => [qw( censusblock censusyear )] + }, +&> diff --git a/httemplate/elements/deploy_zone_block.html b/httemplate/elements/deploy_zone_block.html new file mode 100644 index 000000000..9985944bd --- /dev/null +++ b/httemplate/elements/deploy_zone_block.html @@ -0,0 +1,47 @@ +% unless ( $opt{'js_only'} ) { + + + Block + + > +   + Year + + > +% } +<%init> + +my( %opt ) = @_; + +my $name = $opt{'element_name'} || $opt{'field'} || 'blocknum'; +my $id = $opt{'id'} || 'blocknum'; + +my $curr_value = $opt{'curr_value'} || $opt{'value'}; + +my $onchange = $opt{'onchange'}; +if ( $onchange ) { + $onchange =~ s/\(what\);/(this);/; + $onchange = 'onchange="'.$onchange.'"'; +} + +my $deploy_zone_block = $curr_value + ? FS::deploy_zone_block->by_key($curr_value) + : FS::deploy_zone_block->new; + + diff --git a/httemplate/elements/input-fcc_options.html b/httemplate/elements/input-fcc_options.html new file mode 100644 index 000000000..b191e1c07 --- /dev/null +++ b/httemplate/elements/input-fcc_options.html @@ -0,0 +1,108 @@ +% unless ($opt{js_only}) { +<& hidden.html, 'field' => $id, @_ &> +%# <& input-text.html, 'id' => $id, @_ &> # XXX debugging +
    +
+ +% } +% unless ($opt{html_only}) { +% my $popup = $fsurl.'misc/part_pkg_fcc_options.html?id='; +% my $popup_name = 'popup-'.time. "-$$-". rand() * 2**32; + +% } +<%init> +my %opt = @_; +my $id = $opt{id} || $opt{field}; + diff --git a/httemplate/elements/tr-input-fcc_options.html b/httemplate/elements/tr-input-fcc_options.html index 11cb4a962..58f7247c4 100644 --- a/httemplate/elements/tr-input-fcc_options.html +++ b/httemplate/elements/tr-input-fcc_options.html @@ -41,9 +41,9 @@ function show_fcc_options() { } var media = String.toLowerCase(curr_values['media'] || 'unknown media'); if ( curr_values['is_consumer'] ) { - out += '
  • Consumer-grade service
  • >'; + out += '
  • Consumer-grade
  • >'; } else { - out += '
  • Business-grade service
  • '; + out += '
  • Business-grade
  • '; } if ( curr_values['is_broadband'] ) { out += '
  • Broadband via ' + tech + '' diff --git a/httemplate/misc/part_pkg_fcc_options.html b/httemplate/misc/part_pkg_fcc_options.html index f74328446..a5ecb12bc 100644 --- a/httemplate/misc/part_pkg_fcc_options.html +++ b/httemplate/misc/part_pkg_fcc_options.html @@ -90,6 +90,8 @@ <& .checkbox, 'is_voip' &>
    + + <& .checkbox, 'voip_lastmile' &>
    @@ -145,7 +147,7 @@ function save_changes() { } parent_input.value = JSON.stringify(data); // update the display - parent.show_fcc_options(); + parent.show_fcc_options(parent_input.id); parent.cClick(); //overlib } @@ -179,6 +181,11 @@ function enable_fieldset(fieldset_id) { form.elements['phone1'].disabled = (this.value == ''); } ); + addEventListener(form.elements['is_phone'], 'change', + function() { + form.elements['phone_wholesale'].dispatchEvent( new Event('change') ); + } + ); // load data from the parent form and trigger handlers for(var i = 0; i < form.elements.length; i++) { diff --git a/httemplate/search/477.html b/httemplate/search/477.html index 68493377d..26bd9f33b 100644 --- a/httemplate/search/477.html +++ b/httemplate/search/477.html @@ -36,20 +36,19 @@ a.download { float: right; } -% foreach my $partnum (@partnums) { -% $cgi->param('parts', $partnum); +% foreach my $partname (@partnames) { +% $cgi->param('parts', $partname); % $cgi->param('type', 'csv'); -% my $header = ".header$partnum"; -% my $data = $parts{$partnum}; +% my $header = ".header_$partname"; +% my $data = $parts{$partname}; <& $header &> -% #XXX column headings % foreach my $row (@$data) { % foreach my $item (@$row) { @@ -58,7 +57,7 @@ a.download { % }
    - Part <% $partnum %> + <% $parttitle{$partname} %> Download
    -% } # foreach $partnum +% } # foreach $partname <& /elements/footer.html &> <%init> die "access denied" @@ -80,10 +79,10 @@ if ($cgi->param('agentnum') =~ /^(\d+)$/ ) { $agentnum = $1; } my $date = parse_datetime($cgi->param('date')) || time; -my @partnums = grep /^\d+$/, $cgi->param('parts'); -foreach my $partnum (@partnums) { - my $method = "part$partnum"; - $parts{$partnum} ||= FS::Report::FCC_477->$method( +my @partnames = grep /^\w+$/, $cgi->param('parts'); +foreach my $partname (@partnames) { + my $method = "report_$partname"; + $parts{$partname} ||= FS::Report::FCC_477->$method( date => $date, agentnum => $agentnum ); @@ -93,11 +92,11 @@ $m->cache->set($session, \%parts, '1h'); my $title = 'FCC Form 477 Data - ' . time2str('%b %o, %Y', $date); if ( $cgi->param('type') eq 'csv' ) { - my $partnum = $partnums[0]; # ignore any beyond the first - my $data = $parts{$partnum}; + my $partname = $partnames[0]; # ignore any beyond the first + my $data = $parts{$partname}; my $csv = Text::CSV_XS->new({ eol => "\r\n" }); # i think - my $filename = time2str('%Y-%m-%d', $date) . '-part' . $partnum . '.csv'; + my $filename = time2str('%Y-%m-%d', $date) . '-'. $partname . '.csv'; http_header('Content-Type' => 'text/csv'); http_header('Content-Disposition' => qq(attachment;filename="$filename")); @@ -111,7 +110,7 @@ if ( $cgi->param('type') eq 'csv' ) { } -<%def .header6> +<%def .header_fixed_broadband> Census Tract Technology @@ -125,30 +124,18 @@ if ( $cgi->param('type') eq 'csv' ) { Consumer -<%def .header7> +<%def .header_fixed_voice> - State - Speed (Mbps) - Subscriptions + Census Tract + VoIP? + Lines/Subscriptions - Down - Up Total Consumer -<%def .header8> - - State - Subscriptions - - - Total - Direct - - -<%def .header9> +<%def .header_local_phone> State Wholesale @@ -183,7 +170,7 @@ if ( $cgi->param('type') eq 'csv' ) { Wireless -<%def .header10> +<%def .header_voip> State VoIP OTT @@ -206,14 +193,29 @@ if ( $cgi->param('type') eq 'csv' ) { Other -<%def .header11> +<%def .header_mobile_broadband> +%# unimplemented - Census Tract - VoIP? - Lines/Subscriptions + State + Speed (Mbps) + Subscriptions + Down + Up Total Consumer +<%def .header_mobile_voice> +%# unimplemented + + State + Subscriptions + + + Total + Direct + + + diff --git a/httemplate/search/report_477.html b/httemplate/search/report_477.html index e3ae69e6f..2a6878ef4 100755 --- a/httemplate/search/report_477.html +++ b/httemplate/search/report_477.html @@ -31,12 +31,12 @@ 'label' => 'Enable parts', 'field' => 'parts', 'labels' => { - 6 => 'Part 6 (Fixed Broadband Subscription)', + fixed_broadband => 'Fixed Broadband Subscription', #7 => 'Part 7 (Mobile Wireless Broadband Subscription), #8 => 'Part 8 (Mobile Local Telephone Subscription), - 9 => 'Part 9 (Local Exchange Telephone Subscription)', - 10 => 'Part 10 (Interconnected VoIP Subscription)', - 11 => 'Part 11 (Voice Telephone Subscription Detail)', + fixed_voice => 'Voice Telephone Subscription', + local_phone => 'Local Exchange Telephone Subscription', + voip => 'Interconnected VoIP Subscription', }, options => [ 6, 9, 10, 11 ], &> -- 2.11.0