diff options
author | Ivan Kohler <ivan@freeside.biz> | 2014-08-06 15:36:56 -0700 |
---|---|---|
committer | Ivan Kohler <ivan@freeside.biz> | 2014-08-06 15:36:56 -0700 |
commit | 7c370a4579d1ed8b81dba2fb5fae9978b283b49e (patch) | |
tree | 26f9c1bedfc28ed4fffb14d35a98388b0536cd87 /FS | |
parent | 5583e0b1788d9e307f676f21827e9920f5bf0677 (diff) | |
parent | 1ebcca94aba75c5901c6eefaf373f39e94b03cf0 (diff) |
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Diffstat (limited to '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 | 289 | ||||
-rw-r--r-- | FS/FS/Schema.pm | 71 | ||||
-rw-r--r-- | FS/FS/deploy_zone.pm | 277 | ||||
-rw-r--r-- | FS/FS/deploy_zone_block.pm | 126 | ||||
-rw-r--r-- | FS/FS/deploy_zone_vertex.pm | 120 | ||||
-rw-r--r-- | FS/FS/part_pkg.pm | 8 | ||||
-rw-r--r-- | FS/FS/part_pkg_fcc_option.pm | 73 | ||||
-rw-r--r-- | FS/MANIFEST | 6 | ||||
-rw-r--r-- | FS/t/deploy_zone.t | 5 | ||||
-rw-r--r-- | FS/t/deploy_zone_block.t | 5 | ||||
-rw-r--r-- | FS/t/deploy_zone_vertex.t | 5 |
14 files changed, 903 insertions, 119 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..0f3dfb143 100644 --- a/FS/FS/Report/FCC_477.pm +++ b/FS/FS/Report/FCC_477.pm @@ -8,7 +8,10 @@ use vars qw( @upload @download @technology @part2aoption @part2boption ); use FS::Record qw( dbh ); -$DEBUG = 1; +use Tie::IxHash; +use Storable; + +$DEBUG = 0; =head1 NAME @@ -165,8 +168,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 +205,8 @@ sub statenum2state { my $num = shift; $states{$num}; } +### everything above this point is unmaintained ### + =head1 THE "NEW" REPORT (October 2014 and later) @@ -213,6 +216,8 @@ sub statenum2state { =cut +# functions for internal use + sub join_optionnames { join(' ', map { join_optionname($_) } @_); } @@ -254,33 +259,78 @@ sub active_on { } sub is_fixed_broadband { - "is_broadband::int = 1 AND technology::int IN(".join(',', - 10, 11, 12, 20, 30, 40, 41, 42, 50, 60, 70, 90, 0 - ).")"; + "is_broadband::int = 1 AND technology::int IN( 10, 11, 12, 20, 30, 40, 41, 42, 50, 60, 70, 90, 0 )" } -=item part6 OPTIONS +sub is_mobile_broadband { + "is_broadband::int = 1 AND technology::int IN( 80, 81, 82, 83, 84, 85, 86, 87, 88)" +} -Returns Part 6 of the 2014 FCC 477 data, as an arrayref of arrayrefs. -OPTIONS may contain: -- date: a timestamp value to count active packages as of that date -- agentnum: limit to customers of that agent +=item report SECTION, OPTIONS -Part 6 is the broadband subscription detail report. Columns of the -report are: -- census tract -- technology code -- downstream speed -- upstream speed -(the above columns form a key) -- number of subscriptions -- number of consumer-grade subscriptions +Returns the report section SECTION (see the C<parts> method for section +name strings) as an arrayref of arrayrefs. OPTIONS may contain "date" +(a timestamp value to run the report as of this date) and "agentnum" +(to limit to a single agent). =cut -sub part6 { +sub report { my $class = shift; - my %opt = shift; + my $section = shift; + my %opt = @_; + + my $method = $section.'_sql'; + die "Report section '$section' is not implemented\n" + unless $class->can($method); + my $statement = $class->$method(%opt); + + my $sth = dbh->prepare($statement); + $sth->execute or die $sth->errstr; + $sth->fetchall_arrayref; +} + +sub fbd_sql { + my $class = shift; + my %opt = @_; + my $date = $opt{date} || time; + warn $date; + my $agentnum = $opt{agentnum}; + + my @select = ( + 'censusblock', + 'COALESCE(dbaname, agent.agent)', + 'technology', + 'CASE WHEN is_consumer IS NOT NULL THEN 1 ELSE 0 END', + 'adv_speed_down', + 'adv_speed_up', + 'CASE WHEN is_business IS NOT NULL THEN 1 ELSE 0 END', + 'cir_speed_down', + 'cir_speed_up', + ); + my $from = + 'deploy_zone_block + JOIN deploy_zone USING (zonenum) + JOIN agent USING (agentnum)'; + my @where = ( + "zonetype = 'B'", + "active_date < $date", + "(expire_date > $date OR expire_date IS NULL)", + ); + push @where, "agentnum = $agentnum" if $agentnum; + + my $order_by = 'censusblock, dbaname, technology, is_consumer, is_business'; + + "SELECT ".join(', ', @select) . " + FROM $from + WHERE ".join(' AND ', @where)." + ORDER BY $order_by + "; +} + +sub fbs_sql { + my $class = shift; + my %opt = @_; my $date = $opt{date} || time; my $agentnum = $opt{agentnum}; @@ -312,43 +362,60 @@ sub part6 { 'broadband_downstream, broadband_upstream '; my $order_by = $group_by; - my $statement = "SELECT ".join(', ', @select) . " + "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 part9 OPTIONS - -Returns Part 9 of the 2014 FCC 477 data. Part 9 is the Local Exchange -Telephone Subscription report. Columns are: - -- state FIPS code (key) -- wholesale switched voice lines -- wholesale unswitched local loops -- end-user total lines -- end-user lines sold in a package with broadband -- consumer-grade lines where you are not the long-distance carrier -- consumer-grade lines where the carrier IS the long-distance carrier -- business-grade lines where you are not the long-distance carrier -- business-grade lines where the carrier IS the long-distance carrier -- end-user lines where you own the local loop facility -- end-user lines where you lease an unswitched local loop from a LEC -- end-user lines resold from another carrier -- end-user lines provided over fiber to the premises -- end-user lines provided over coaxial -- end-user lines provided over fixed wireless +sub fvs_sql { + my $class = shift; + my %opt = @_; + my $date = $opt{date} || time; + my $agentnum = $opt{agentnum}; -=cut + 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; + + "SELECT ".join(', ', @select) . " + FROM $from + WHERE ".join(' AND ', @where)." + GROUP BY $group_by + ORDER BY $order_by + "; + +} -sub part9 { +sub lts_sql { my $class = shift; - my %opt = shift; + my %opt = @_; my $date = $opt{date} || time; my $agentnum = $opt{agentnum}; @@ -358,10 +425,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 +445,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') ; @@ -390,20 +457,17 @@ sub part9 { my $group_by = 'state.fips'; my $order_by = $group_by; - my $statement = "SELECT ".join(', ', @select) . " + "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); } -sub part10 { +sub voip_sql { my $class = shift; - my %opt = shift; + my %opt = @_; my $date = $opt{date} || time; my $agentnum = $opt{agentnum}; @@ -442,65 +506,112 @@ sub part10 { my $group_by = 'state.fips'; my $order_by = $group_by; - my $statement = "SELECT ".join(', ', @select) . " + "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 part11 OPTIONS +sub mbs_sql { + my $class = shift; + my %opt = @_; + my $date = $opt{date} || time; + my $agentnum = $opt{agentnum}; -Returns part 11 (voice subscription detail), as above. + my @select = ( + 'state.fips', + 'broadband_downstream', + 'broadband_upstream', + 'COUNT(*)', + 'COUNT(is_consumer)', + ); + my $from = + 'cust_pkg + JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum) + JOIN state USING (country, state) + JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum) + JOIN part_pkg USING (pkgpart) '. + join_optionnames_int(qw( + is_broadband technology + is_consumer + )). + join_optionnames(qw(broadband_downstream broadband_upstream)) + ; + my @where = ( + active_on($date), + is_mobile_broadband() + ); + push @where, "cust_main.agentnum = $agentnum" if $agentnum; + my $group_by = 'state.fips, broadband_downstream, broadband_upstream '; + my $order_by = $group_by; -=cut + "SELECT ".join(', ', @select) . " + FROM $from + WHERE ".join(' AND ', @where)." + GROUP BY $group_by + ORDER BY $order_by + "; +} -sub part11 { +sub mvs_sql { my $class = shift; - my %opt = shift; + my %opt = @_; 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)' + 'state.fips', + 'COUNT(*)', + 'COUNT(mobile_direct)', ); - - 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 $from = + 'cust_pkg + JOIN cust_location ON (cust_pkg.locationnum = cust_location.locationnum) + JOIN state USING (country, state) + JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum) + JOIN part_pkg USING (pkgpart) '. + join_optionnames_int(qw( is_mobile mobile_direct) ) ; - my @where = ( active_on($date), - "(is_voip = 1 OR is_phone = 1)", + 'is_mobile = 1' ); push @where, "cust_main.agentnum = $agentnum" if $agentnum; - my $group_by = 'cust_location.censustract, COALESCE(is_voip, 0)'; + my $group_by = 'state.fips'; my $order_by = $group_by; - my $statement = "SELECT ".join(', ', @select) . " + "SELECT ".join(', ', @select) . " FROM $from WHERE ".join(' AND ', @where)." GROUP BY $group_by ORDER BY $order_by "; +} + +=item parts + +Returns a Tie::IxHash reference of the internal short names used for the +report sections ('fbd', 'mbs', etc.) to the full names. + +=cut + +tie our %parts, 'Tie::IxHash', ( + fbd => 'Fixed Broadband Deployment', + fbs => 'Fixed Broadband Subscription', + fvs => 'Fixed Voice Subscription', + lts => 'Local Exchange Telephone Subscription', + voip => 'Interconnected VoIP Subscription', + mbd => 'Mobile Broadband Deployment', + mbsa => 'Mobile Broadband Service Availability', + mbs => 'Mobile Broadband Subscription', + mvd => 'Mobile Voice Deployment', + mvs => 'Mobile Voice Subscription', +); - warn $statement if $DEBUG; - dbh->selectall_arrayref($statement); +sub parts { + Storable::dclone(\%parts); } 1; diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 27bd81374..1b82e0ec7 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -6672,6 +6672,77 @@ 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', '', '', '', + 'adv_speed_up', 'decimal', '', '10,3', '0', '', + 'adv_speed_down', 'decimal', '', '10,3', '0', '', + 'cir_speed_up', 'decimal', '', '10,3', '0', '', + 'cir_speed_down', 'decimal', '', '10,3', '0', '', + 'is_broadband', 'char', 'NULL', 1, '', '', + 'is_voice', 'char', 'NULL', 1, '', '', + 'is_consumer', 'char', 'NULL', 1, '', '', + 'is_business', 'char', 'NULL', 1, '', '', + 'active_date', @date_type, '', '', + 'expire_date', @date_type, '', '', + ], + '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', '', '', + ], + 'primary_key' => 'vertexnum', + 'unique' => [ ], + '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..16f59c81d --- /dev/null +++ b/FS/FS/deploy_zone.pm @@ -0,0 +1,277 @@ +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 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. + +=item is_broadband + +'Y' if this service includes broadband Internet. + +=item is_voice + +'Y' if this service includes voice communication. + +=item active_date + +The date this zone became active. + +=item expire_date + +The date this zone became inactive, if any. + +=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_textn('dbaname') + || $self->ut_enum('zonetype', [ 'B', 'P' ]) + || $self->ut_number('technology') + || $self->ut_numbern('spectrum') + || $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') + || $self->ut_flag('is_broadband') + || $self->ut_flag('is_voice') + || $self->ut_numbern('active_date') + || $self->ut_numbern('expire_date') + ; + return $error if $error; + + foreach(qw(adv_speed_down adv_speed_up cir_speed_down cir_speed_up)) { + if ($self->get('is_broadband')) { + if (!$self->get($_)) { + $self->set($_, 0); + } + } else { + $self->set($_, ''); + } + } + if (!$self->get('active_date')) { + $self->set('active_date', time); + } + + $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'; + } +} + +=item deploy_zone_block + +Returns the census block records in this zone, in order by census block +number. Only appropriate to block-type zones. + +=item deploy_zone_vertex + +Returns the vertex records for this zone, in order by sequence number. Only +appropriate to polygon-type zones. + +=cut + +sub deploy_zone_block { + my $self = shift; + qsearch({ + table => 'deploy_zone_block', + hashref => { zonenum => $self->zonenum }, + order_by => ' ORDER BY censusblock', + }); +} + +sub deploy_zone_vertex { + my $self = shift; + qsearch({ + table => 'deploy_zone_vertex', + hashref => { zonenum => $self->zonenum }, + order_by => ' ORDER BY vertexnum', + }); +} + +=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..078b32640 --- /dev/null +++ b/FS/FS/deploy_zone_vertex.pm @@ -0,0 +1,120 @@ +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. + +=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') + ; + 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.pm b/FS/FS/part_pkg.pm index 741eb8741..06f304a22 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -338,7 +338,7 @@ sub insert { if ( $options{fcc_options} ) { warn " updating fcc options " if $DEBUG; - $self->process_fcc_options( $options{fcc_options} ); + $self->set_fcc_options( $options{fcc_options} ); } warn " committing transaction" if $DEBUG and $oldAutoCommit; @@ -624,7 +624,7 @@ sub replace { if ( $options->{fcc_options} ) { warn " updating fcc options " if $DEBUG; - $new->process_fcc_options( $options->{fcc_options} ); + $new->set_fcc_options( $options->{fcc_options} ); } warn " committing transaction" if $DEBUG and $oldAutoCommit; @@ -787,14 +787,14 @@ sub propagate { join("\n", @error); } -=item process_fcc_options HASHREF +=item set_fcc_options HASHREF Sets the FCC options on this package definition to the values specified in HASHREF. =cut -sub process_fcc_options { +sub set_fcc_options { my $self = shift; my $pkgpart = $self->pkgpart; my $options; diff --git a/FS/FS/part_pkg_fcc_option.pm b/FS/FS/part_pkg_fcc_option.pm index 0a288def5..5c78e5f9e 100644 --- a/FS/FS/part_pkg_fcc_option.pm +++ b/FS/FS/part_pkg_fcc_option.pm @@ -112,31 +112,48 @@ 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' +); + +tie our %spectrum_labels, 'Tie::IxHash', ( + 90 => '700 MHz Band', + 91 => 'Cellular Band', + 92 => 'Specialized Mobile Radio (SMR) Band', + 93 => 'Advanced Wireless Services (AWS) 1 Band', + 94 => 'Broadband Personal Communications Service (PCS) Band', + 95 => 'Wireless Communications Service (WCS) Band', + 96 => 'Broadband Radio Service/Educational Broadband Service Band', + 97 => 'Satellite (e.g. L-band, Big LEO, Little LEO)', + 98 => 'Unlicensed (including broadcast television “white spaces”) Bands', + 99 => '600 MHz', + 100 => 'H Block', + 101 => 'Advanced Wireless Services (AWS) 3 Band', + 102 => 'Advanced Wireless Services (AWS) 4 Band', + 103 => 'Other', ); sub media_types { @@ -144,7 +161,11 @@ sub media_types { } sub technology_labels { - +{ %technology_labels }; + Storable::dclone(\%technology_labels); +} + +sub spectrum_labels { + Storable::dclone(\%spectrum_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"; |