allow svc_broadband to link directly to a router, #14698
authorMark Wells <mark@freeside.biz>
Mon, 27 Feb 2012 18:31:47 +0000 (10:31 -0800)
committerMark Wells <mark@freeside.biz>
Mon, 27 Feb 2012 18:31:47 +0000 (10:31 -0800)
14 files changed:
FS/FS/Schema.pm
FS/FS/Upgrade.pm
FS/FS/addr_block.pm
FS/FS/part_export/router.pm
FS/FS/router.pm
FS/FS/svc_broadband.pm
httemplate/browse/router.cgi
httemplate/edit/router.cgi
httemplate/edit/svc_broadband.cgi
httemplate/elements/select-tiered.html
httemplate/elements/tr-select-router_block_ip.html [new file with mode: 0644]
httemplate/search/report_svc_broadband.html
httemplate/search/svc_broadband.cgi
httemplate/view/svc_broadband.cgi

index 483c5e0..b2dddca 100644 (file)
@@ -2484,6 +2484,7 @@ sub tables_hashref {
         'routername', 'varchar', '', $char_d, '', '', 
         'svcnum', 'int', 'NULL', '', '', '', 
         'agentnum',   'int', 'NULL', '', '', '', 
+        'auto_addr', 'char', 'NULL', 1, '', '',
       ],
       'primary_key' => 'routernum',
       'unique'      => [],
@@ -2519,6 +2520,7 @@ sub tables_hashref {
       'columns' => [
         'svcnum',                  'int',     '',      '', '', '', 
         'description',         'varchar', 'NULL', $char_d, '', '', 
+        'routernum',               'int', 'NULL',      '', '', '',
         'blocknum',                'int', 'NULL',      '', '', '', 
         'sectornum',               'int', 'NULL',      '', '', '',
         'speed_up',                'int', 'NULL',      '', '', '', 
index d00bb5c..8f66c66 100644 (file)
@@ -264,7 +264,10 @@ sub upgrade_data {
     'part_export'      => [],
 
     #insert default tower_sector if not present
-    'tower',
+    'tower' => [],
+
+    #routernum/blocknum
+    'svc_broadband' => [],
   ;
 
   \%hash;
index be42cb5..e00f587 100755 (executable)
@@ -8,6 +8,7 @@ use FS::svc_broadband;
 use FS::Conf;
 use NetAddr::IP;
 use Carp qw( carp );
+use List::Util qw( first );
 
 @ISA = qw( FS::Record );
 
@@ -222,37 +223,43 @@ sub cidr {
   $self->NetAddr->cidr;
 }
 
-=item next_free_addr
+=item free_addrs
 
 Returns a NetAddr::IP object corresponding to the first unassigned address 
 in the block (other than the network, broadcast, or gateway address).  If 
-there are no free addresses, returns false.  There are never free addresses
+there are no free addresses, returns nothing.  There are never free addresses
 when manual_flag is true.
 
+=item next_free_addr
+
+Returns a NetAddr::IP object for the first unassigned address in the block,
+or '' if there are none.
+
 =cut
 
-sub next_free_addr {
+sub free_addrs {
   my $self = shift;
 
-  return '' if $self->manual_flag;
+  return if $self->manual_flag;
 
   my $conf = new FS::Conf;
   my @excludeaddr = $conf->config('exclude_ip_addr');
   
-my @used =
-( (map { $_->NetAddr->addr }
-    ($self,
-     qsearch('svc_broadband', { blocknum => $self->blocknum }))
-  ), @excludeaddr
-);
-
-  my @free = $self->NetAddr->hostenum;
-  while (my $ip = shift @free) {
-    if (not grep {$_ eq $ip->addr;} @used) { return $ip; };
-  }
+  my %used = map { $_ => 1 }
+  (
+    (map { $_->NetAddr->addr }
+      ($self,
+       qsearch('svc_broadband', { blocknum => $self->blocknum }))
+    ), @excludeaddr
+  );
 
-  '';
+  grep { !$used{$_->addr} } $self->NetAddr->hostenum;
+
+}
 
+sub next_free_addr {
+  my $self = shift;
+  ($self->free_addrs, '')[0]
 }
 
 =item allocate -- deprecated
index 42aa51c..6a1d676 100644 (file)
@@ -302,13 +302,13 @@ sub _queue {
 }
 
 sub _get_router {
-  my ($self, $svc_broadband, %args) = (shift, shift, shift, @_);
+  my ($self, $svc_broadband, %args) = (shift, shift, @_);
 
   my $router;
   if ($args{'routernum'}) {
     $router = qsearchs('router', { routernum => $args{'routernum'}});
   } else {
-    $router = $svc_broadband->addr_block->router;
+    $router = $svc_broadband->router;
   }
 
   return($router);
index f66f2ce..99373e5 100755 (executable)
@@ -40,6 +40,9 @@ fields are currently supported:
 
 =item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
 
+=item auto_addr - flag to automatically assign IP addresses to services
+linked to this router ('Y' or null).
+
 =back
 
 =head1 METHODS
@@ -83,6 +86,7 @@ sub check {
   my $error =
     $self->ut_numbern('routernum')
     || $self->ut_text('routername')
+    || $self->ut_enum('auto_addr', [ '', 'Y' ])
     || $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
   ;
   return $error if $error;
@@ -128,6 +132,11 @@ sub delete {
 Returns a list of FS::addr_block objects (address blocks) associated
 with this object.
 
+=item auto_addr_block
+
+Returns a list of address blocks on which auto-assignment of IP addresses
+is enabled.
+
 =cut
 
 sub addr_block {
@@ -135,6 +144,13 @@ sub addr_block {
   return qsearch('addr_block', { routernum => $self->routernum });
 }
 
+sub auto_addr_block {
+  my $self = shift;
+  return () if !$self->auto_addr;
+  return qsearch('addr_block', { routernum => $self->routernum,
+                                 manual_flag => '' });
+}
+
 =item part_svc_router
 
 Returns a list of FS::part_svc_router objects associated with this 
index f5aa2fe..ac463bc 100755 (executable)
@@ -101,15 +101,15 @@ sub table_info {
       'description' => 'Descriptive label for this particular device',
       'speed_down'  => 'Maximum download speed for this service in Kbps.  0 denotes unlimited.',
       'speed_up'    => 'Maximum upload speed for this service in Kbps.  0 denotes unlimited.',
-      'ip_addr'     => 'IP address.  Leave blank for automatic assignment.',
-      'sectornum'   => 'Tower sector',
-      'blocknum'    => { 'label' => 'Address block',
-                         'type'  => 'select',
-                         'select_table' => 'addr_block',
-                         'select_key'   => 'blocknum',
-                         'select_label' => 'cidr',
-                         'disable_inventory' => 1,
-                       },
+      #'ip_addr'     => 'IP address.  Leave blank for automatic assignment.',
+      #'blocknum'    => 
+      #{ 'label' => 'Address block',
+      #                   'type'  => 'select',
+      #                   'select_table' => 'addr_block',
+                         'select_key'   => 'blocknum',
+      #                   'select_label' => 'cidr',
+      #                   'disable_inventory' => 1,
+      #                 },
      'plan_id' => 'Service Plan Id',
      'performance_profile' => 'Peformance Profile',
      'authkey'      => 'Authentication key',
@@ -118,6 +118,8 @@ sub table_info {
      'longitude'    => 'Longitude',
      'altitude'     => 'Altitude',
      'vlan_profile' => 'VLAN profile',
+     'sectornum'    => 'Tower/sector',
+     'routernum'    => 'Router/block',
      'usergroup'    => { 
                          label => 'RADIUS groups',
                          type  => 'select-radius_group.html',
@@ -209,13 +211,20 @@ sub search {
 
   #routernum, can be arrayref
   for my $routernum ( $params->{'routernum'} ) {
-    push @from, 'LEFT JOIN addr_block USING ( blocknum )';
+    # this no longer uses addr_block
     if ( ref $routernum and grep { $_ } @$routernum ) {
-      my $where = join(',', map { /^(\d+)$/ ? $1 : () } @$routernum );
-      push @where, "addr_block.routernum IN ($where)" if $where;
+      my $in = join(',', map { /^(\d+)$/ ? $1 : () } @$routernum );
+      my @orwhere;
+      push @orwhere, "svc_broadband.routernum IN ($in)" if $in;
+      push @orwhere, "svc_broadband.routernum IS NULL" 
+        if grep /^none$/, @$routernum;
+      push @where, '( '.join(' OR ', @orwhere).' )';
     }
     elsif ( $routernum =~ /^(\d+)$/ ) {
-      push @where, "addr_block.routernum = $1";
+      push @where, "svc_broadband.routernum = $1";
+    }
+    elsif ( $routernum eq 'none' ) {
+      push @where, "svc_broadband.routernum IS NULL";
     }
   }
 
@@ -327,8 +336,6 @@ Delete this record from the database.
 Replaces the OLD_RECORD with this one in the database.  If there is an error,
 returns the error, otherwise returns false.
 
-=cut
-
 # Standard FS::svc_Common::replace
 
 =item suspend
@@ -365,6 +372,7 @@ sub check {
   my $error =
     $self->ut_numbern('svcnum')
     || $self->ut_numbern('blocknum')
+    || $self->ut_foreign_keyn('routernum', 'router', 'routernum')
     || $self->ut_foreign_keyn('sectornum', 'tower_sector', 'sectornum')
     || $self->ut_textn('description')
     || $self->ut_numbern('speed_up')
@@ -380,8 +388,8 @@ sub check {
   ;
   return $error if $error;
 
-  if($self->speed_up < 0) { return 'speed_up must be positive'; }
-  if($self->speed_down < 0) { return 'speed_down must be positive'; }
+  if(($self->speed_up || 0) < 0) { return 'speed_up must be positive'; }
+  if(($self->speed_down || 0) < 0) { return 'speed_down must be positive'; }
 
   my $cust_svc = $self->svcnum
                  ? qsearchs('cust_svc', { 'svcnum' => $self->svcnum } )
@@ -393,18 +401,27 @@ sub check {
     $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $self->pkgnum } );
     return "Invalid pkgnum" unless $cust_pkg;
   }
-    
-  if ($self->blocknum) {
-    $error = $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum');
-    return $error if $error;
-  }
+  my $agentnum = $cust_pkg->cust_main->agentnum if $cust_pkg;
+
+  if ($self->routernum) {
+    return "Router ".$self->routernum." does not provide this service"
+      unless qsearchs('part_svc_router', { 
+        svcpart => $self->cust_svc->svcpart,
+        routernum => $self->routernum
+    });
+  
+    my $router = $self->router;
+    return "Router ".$self->routernum." does not serve this customer"
+      if $router->agentnum and $router->agentnum != $agentnum;
 
-  if ($cust_pkg && $self->blocknum) {
-    my $addr_agentnum = $self->addr_block->agentnum;
-    if ($addr_agentnum && $addr_agentnum != $cust_pkg->cust_main->agentnum) {
-      return "Address block does not service this customer";
+    if ( $router->auto_addr ) {
+      my $error = $self->assign_ip_addr;
+      return $error if $error;
     }
-  }
+    else {
+      $self->blocknum('');
+    }
+  } # if $self->routernum
 
   if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) {
     my $l = $cust_pkg->cust_location_or_main;
@@ -423,55 +440,60 @@ sub check {
   $self->SUPER::check;
 }
 
-sub _check_ip_addr {
-  my $self = shift;
+=item assign_ip_addr
 
-  if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
+Assign an address block matching the selected router, and the selected block
+if there is one.
 
-    return '' if $conf->exists('svc_broadband-allow_null_ip_addr'); #&& !$self->blocknum
+=cut
 
-    return "Must supply either address or block"
-      unless $self->blocknum;
-    my $next_addr = $self->addr_block->next_free_addr;
-    if ($next_addr) {
-      $self->ip_addr($next_addr->addr);
-    } else {
-      return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
-    }
+sub assign_ip_addr {
+  my $self = shift;
+  my @blocks;
+  my $ip_addr;
 
+  if ( $self->blocknum and $self->addr_block->routernum == $self->routernum ) {
+    # simple case: user chose a block, find an address in that block
+    # (this overrides an existing IP address if it's not in the block)
+    @blocks = ($self->addr_block);
+  }
+  elsif ( $self->routernum ) {
+    @blocks = $self->router->auto_addr_block;
+  }
+  else { 
+    return '';
   }
 
-  if (not($self->blocknum)) {
-    return "Must supply either address or block"
-      unless ($self->ip_addr and $self->ip_addr ne '0.0.0.0');
-    my @block = grep { $_->NetAddr->contains($self->NetAddr) }
-                 map { $_->addr_block }
-                 $self->allowed_routers;
-    if (scalar(@block)) {
-      $self->blocknum($block[0]->blocknum);
-    }else{
-      return "Address not with available block.";
+  foreach my $block ( @blocks ) {
+    if ( $self->ip_addr and $block->NetAddr->contains($self->NetAddr) ) {
+      # don't change anything
+      return '';
     }
+    $ip_addr = $block->next_free_addr;
+    last if $ip_addr;
   }
-
-  # This should catch errors in the ip_addr.  If it doesn't,
-  # they'll almost certainly not map into the block anyway.
-  my $self_addr = $self->NetAddr; #netmask is /32
-  return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
-
-  my $block_addr = $self->addr_block->NetAddr;
-  unless ($block_addr->contains($self_addr)) {
-    return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
+  if ( $ip_addr ) {
+    $self->set(ip_addr => $ip_addr->addr);
+    return '';
   }
-
-  my $router = $self->addr_block->router 
-    or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
-  if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
-  } # do nothing
   else {
-    return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
+    return 'No IP address available on this router';
   }
+}
 
+sub _check_ip_addr {
+  my $self = shift;
+
+  if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
+    return '' if $conf->exists('svc_broadband-allow_null_ip_addr'); 
+    return 'IP address required';
+  }
+#  if (my $dup = qsearchs('svc_broadband', {
+#        ip_addr => $self->ip_addr,
+#        svcnum  => {op=>'!=', value => $self->svcnum}
+#      }) ) {
+#    return 'IP address conflicts with svcnum '.$dup->svcnum;
+#  }
   '';
 }
 
@@ -510,6 +532,17 @@ sub addr_block {
   qsearchs('addr_block', { blocknum => $self->blocknum });
 }
 
+=item router
+
+Returns the FS::router record for this service.
+
+=cut
+
+sub router {
+  my $self = shift;
+  qsearchs('router', { routernum => $self->routernum });
+}
+
 =back
 
 =item allowed_routers
@@ -520,7 +553,34 @@ Returns a list of allowed FS::router objects.
 
 sub allowed_routers {
   my $self = shift;
-  map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
+  map { $_->router } qsearch('part_svc_router', 
+    { svcpart => $self->cust_svc->svcpart });
+}
+
+
+#class method
+sub _upgrade_data {
+  my $class = shift;
+
+  # set routernum to addr_block.routernum
+  foreach my $self (qsearch('svc_broadband', {
+      blocknum => {op => '!=', value => ''},
+      routernum => ''
+    })) {
+    my $addr_block = $self->addr_block;
+    if ( my $routernum = $addr_block->routernum ) {
+      $self->set(routernum => $routernum);
+      my $error = $self->replace;
+      die "error assigning routernum $routernum to service ".$self->svcnum.
+          ":\n$error\n"
+        if $error;
+    }
+    else {
+      warn "svcnum ".$self->svcnum.
+        ": no routernum in address block ".$addr_block->cidr.", skipped\n";
+    }
+  }
+  '';
 }
 
 =head1 BUGS
@@ -530,6 +590,8 @@ The business with sb_field has been 'fixed', in a manner of speaking.
 allowed_routers isn't agent virtualized because part_svc isn't agent
 virtualized
 
+Having both routernum and blocknum as foreign keys is somewhat dubious.
+
 =head1 SEE ALSO
 
 FS::svc_Common, FS::Record, FS::addr_block,
index 069ca9b..21047d7 100644 (file)
@@ -9,6 +9,7 @@
                 'count_query'     => "SELECT count(*) from router $count_sql",
                 'header'          => [ 'Router name',
                                        'Address block(s)',
+                                       'IP addressing',
                                        'Action',
                                      ],
                 'fields'          => [ 'routername',
                                                                shift->addr_block
                                                  );
                                            },
+                                       sub { shift->auto_addr ? 'Automatic' : 'Manual' },
                                        sub { 'Delete' },
                                      ],
                 'links'           => [ [ "${p2}edit/router.cgi?", 'routernum' ],
                                        '',
+                                       '',
                                        [ "${p}misc/delete-router.html?", 'routernum' ],
                                      ],
                 'agent_virt'      => 1,
@@ -52,4 +55,5 @@ my $count_sql = $extra_sql.  ( $extra_sql =~ /WHERE/ ? ' AND' : 'WHERE' ).
     'null_right' => 'Broadband global configuration',
   );
 
+
 </%init>
index 70eaa45..6672d5d 100755 (executable)
@@ -6,11 +6,14 @@
      'labels'      => { 'routernum'  => 'Router',
                         'routername' => 'Name',
                         'svc_part'   => 'Service',
+                        'agentnum'   => 'Agent',
+                        'auto_addr'  => 'Assign IP addresses automatically',
                       },
      'fields'      => [
                         { 'field'=>'routername', 'type'=>'text', 'size'=>32 },
                         { 'field'=>'agentnum',   'type'=>'select-agent' },
                         { 'field'=>'svcnum',     'type'=>'hidden' },
+                        { 'field'=>'auto_addr','type'=>'checkbox','value'=>'Y'},
                       ],
      'error_callback' => $callback,
      'edit_callback'  => $callback,
index ff57ac0..8fccb1f 100644 (file)
@@ -100,10 +100,9 @@ END
 ;
 
 my @fields = (
-  qw( description ip_addr speed_down speed_up ),
+  qw( description speed_down speed_up ),
   { field=>'sectornum', type=>'select-tower_sector', },
-  qw( blocknum ),
-  { field=>'block_label', type=>'fixed' },
+  { field=>'routernum', type=>'select-router_block_ip', },
   qw( mac_addr latitude longitude altitude vlan_profile 
       performance_profile authkey plan_id ),
 );
index 35f9e5a..99b2852 100644 (file)
@@ -35,6 +35,7 @@ contain the following:
   isn't fully tested.
 - after: an HTML string to be inserted after the select element, before 
   the next one.  By default there's nothing between them.
+- onchange: an additional javascript function to be called on change.
 
 For convenience, "curr_value" and "field" can be passed as part of the 
 main argument list, and will be applied to the last tier.
@@ -43,8 +44,13 @@ main argument list, and will be applied to the last tier.
 % $i = 0;
 % foreach my $tier (@$tiers) {
 %   my $onchange;
-%   $onchange="onchange='${pre}select_change(this, $i)'"
+%   $onchange="${pre}select_change(this, $i)"
 %     if $i < scalar(@$tiers) - 1;
+%
+%   $onchange .= ';'.$tier->{onchange}."(this, $i);"
+%     if $tier->{onchange};
+%
+%   $onchange = "onchange='$onchange'" if $onchange;
 <SELECT 
   NAME="<% $tier->{field} %>"
   ID="<% $pre."select_".$i %>"
@@ -54,7 +60,8 @@ main argument list, and will be applied to the last tier.
 %   if ( $i == 0 ) {
 %     my $options = $tiers_by_key->[0]->{''};
 %     foreach ( sort keys %$options ) {
-  <OPTION VALUE="<%$_ |h%>"><% $options->{$_} |h%></OPTION>
+  <OPTION VALUE="<%$_ |h%>" <% $curr_values->[$i] eq $_ ? 'SELECTED' : ''%>>
+  <% $options->{$_} |h%></OPTION>
 %     }
 %   }
 %   $i++;
@@ -178,7 +185,6 @@ while($i >= 1) {
   foreach my $key ( %{ $tier->{by_key} } ) {
     my $options = $tier->{by_key}->{$key};
     if ( exists( $options->{$curr_value} ) ) {
-      warn "tier $i curr_value ($curr_value) found under key $key\n";
       $tiers->[$i-1]->{curr_value} = $key;
       last;
     }
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
new file mode 100644 (file)
index 0000000..e6b7bfe
--- /dev/null
@@ -0,0 +1,94 @@
+<script type="text/javascript">
+var auto_addr_routernum = <% encode_json(\%auto_addr_routernum) %>;
+function hide_if_auto_addr(obj, i) {
+  var routernum = obj.value;
+  var select_blocknum = document.getElementsByName('blocknum')[0];
+  var label_auto_addr = document.getElementById('label_auto_addr');
+  var input_ip_addr = document.getElementById('input_ip_addr');
+  var auto = ( auto_addr_routernum[routernum] == 'Y' );
+  select_blocknum.style.display = auto ? '' : 'none';
+  label_auto_addr.style.display = auto ? '' : 'none';
+  input_ip_addr.style.display = !auto ? '' : 'none';
+}
+</script>
+<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router') &>
+<td>
+  <& /elements/select-tiered.html, prefix => 'router_', tiers => [
+  {
+    field     => 'routernum',
+    records   => \@routers,
+    name_col  => 'routername',
+    value_col => 'routernum',
+    onchange  => 'hide_if_auto_addr',
+    curr_value=> $opt{'routernum'},
+  },
+  {
+    field     => 'blocknum',
+    table     => 'addr_block',
+    hashref   => (exists($fixed{'blocknum'}) ? 
+                    { blocknum => $fixed{'blocknum'} } : {}
+                 ),
+    name_col  => 'cidr',
+    link_col  => 'routernum',
+    empty_label => '(any)',
+    curr_value  => $opt{'blocknum'},
+  },
+]
+&>
+</td></tr>
+<& /elements/tr-td-label.html, label => 'IP address' &>
+<td>
+% if ( $fixed{'ip_addr'} ) {
+  <input type="hidden" id="input_ip_addr" name="ip_addr" 
+    value="<% $opt{'ip_addr'} |h%>"><% $opt{'ip_addr'} || '' %>
+% }
+% else {
+  <input type="text" id="input_ip_addr" name="ip_addr" 
+    style="display:none" value="<% $opt{'ip_addr'} |h%>">
+% }
+  <span id="label_auto_addr"><% $opt{'ip_addr'} || '' %> 
+  <i>(automatic)</i></span>
+</td> </tr>
+<script type="text/javascript">
+hide_if_auto_addr(document.getElementsByName('routernum')[0],0);
+</script>
+<%init>
+my %opt = @_;
+my @routers;
+
+my $svc_x = $opt{'object'};
+if ( $svc_x ) {
+  $opt{$_} = $svc_x->$_
+    foreach qw(routernum blocknum ip_addr svcpart);
+}
+
+warn Dumper(\%opt);
+
+my $svcpart = $opt{'svcpart'} || '';
+my %fixed; #  which fields are fixed
+$svcpart =~ /^\d*$/ or die "invalid svcpart '$svcpart'";
+if ( $svcpart ) {
+  my $part_svc = FS::part_svc->by_key($svcpart);
+  # Traditionally, columnflag 'F' on IP address means that it MUST 
+  # be auto-assigned (or, if null IP addresses are allowed, that 
+  # it must be null).
+  foreach (qw(routernum blocknum ip_addr)) {
+    my $psc = $part_svc->part_svc_column($_);
+    if ( $psc and $psc->columnflag eq 'F' ) {
+      $fixed{$_} = $psc->columnvalue;
+    }
+  }
+  if ( $fixed{'routernum'} ) {
+    @routers = (FS::router->by_key($fixed{'routernum'}))
+  }
+  else {
+    @routers = map { $_->router } 
+      qsearch('part_svc_router', { svcpart => $svcpart });
+  }
+}
+else {
+  @routers = qsearch('router', {});
+}
+
+my %auto_addr_routernum = map { $_->routernum, $_->auto_addr } @routers;
+</%init>
index ee4dfce..37f21b7 100755 (executable)
                     'multiple'      => 'multiple',
               )
     %>
+    <tr>
+      <td></td>
+      <td><input type="checkbox" name="routernum" value="none" checked> Include services with no router</td>
+    </tr>
 
     <% include( '/elements/tr-selectmultiple-part_pkg.html',
                 %pkg_search,
index 605b829..ee62e90 100755 (executable)
@@ -15,8 +15,8 @@
               'fields'      => [ 'svcnum',
                                  'svc',
                                  sub {
-                                   my $blocknum = shift->blocknum or return '';
-                                   $routerbyblock{$blocknum}->routername;
+                                   my $router = shift->router; 
+                                   $router ? $router->routername : '';
                                  },
                                  @tower_fields,
                                  'ip_addr',
index 2e93d42..05ae632 100644 (file)
@@ -27,7 +27,7 @@ $labels{'coordinates'} = 'Latitude/Longitude';
 
 my @fields = (
   'description',
-  { field => 'router', value => \&router },
+  { field => 'routernum', value => \&router },
   'speed_down',
   'speed_up',
   { field => 'ip_addr', value => \&ip_addr },
@@ -48,9 +48,10 @@ push @fields,
 
 sub router {
   my $svc = shift;
-  my $addr_block = $svc->addr_block or return '';
-  my $router = $addr_block->router or return '';
-  $router->routernum . ': ' . $router->routername;
+  my $router = $svc->router or return '';
+  my $block = $svc->addr_block;
+  $block = '; '.$block->cidr if $block;
+  $router->routernum . ': ' . $router->routername . $block
 }
 
 sub ip_addr {