diff options
author | Mark Wells <mark@freeside.biz> | 2014-07-31 22:54:08 -0700 |
---|---|---|
committer | Mark Wells <mark@freeside.biz> | 2014-07-31 22:54:08 -0700 |
commit | 0f359d5480aa1621d73ee802f420e8951abc620d (patch) | |
tree | 4bab32c865f9ef7b2bb03247a6be75215cfebf85 /FS/FS | |
parent | 6c284750de8fe49d7d4cdc6a9a4fb618697780e2 (diff) |
new 477 report: deployment info, combined browse-edit UI, #24047
Diffstat (limited to 'FS/FS')
-rw-r--r-- | FS/FS/AccessRight.pm | 3 | ||||
-rw-r--r-- | FS/FS/Mason.pm | 3 | ||||
-rw-r--r-- | FS/FS/Record.pm | 31 | ||||
-rw-r--r-- | FS/FS/Report/FCC_477.pm | 160 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 69 | ||||
-rw-r--r-- | FS/FS/deploy_zone.pm | 221 | ||||
-rw-r--r-- | FS/FS/deploy_zone_block.pm | 126 | ||||
-rw-r--r-- | FS/FS/deploy_zone_vertex.pm | 125 | ||||
-rw-r--r-- | FS/FS/part_pkg_fcc_option.pm | 52 |
9 files changed, 697 insertions, 93 deletions
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<FS::deploy_zone_block> and L<FS::deploy_zone_vertex>. + +=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<FS::Record> + +=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<FS::deploy_zone> 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<hash> 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<FS::Record> + +=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>). 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<FS::deploy_zone>. + +=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<FS::Record> + +=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 |