summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2012-02-27 10:31:47 -0800
committerMark Wells <mark@freeside.biz>2012-02-27 10:31:47 -0800
commiteb9d1063e1203231ee0c6922ea5638370f7b5ece (patch)
treee2d6781d7b0a9239c5e3deb98b27c694426f86c5
parentcaba365bbebc7e73ad0c25f9a3a9c75a48ed6140 (diff)
allow svc_broadband to link directly to a router, #14698
-rw-r--r--FS/FS/Schema.pm2
-rw-r--r--FS/FS/Upgrade.pm5
-rwxr-xr-xFS/FS/addr_block.pm39
-rw-r--r--FS/FS/part_export/router.pm4
-rwxr-xr-xFS/FS/router.pm16
-rwxr-xr-xFS/FS/svc_broadband.pm192
-rw-r--r--httemplate/browse/router.cgi4
-rwxr-xr-xhttemplate/edit/router.cgi3
-rw-r--r--httemplate/edit/svc_broadband.cgi5
-rw-r--r--httemplate/elements/select-tiered.html12
-rw-r--r--httemplate/elements/tr-select-router_block_ip.html94
-rwxr-xr-xhttemplate/search/report_svc_broadband.html4
-rwxr-xr-xhttemplate/search/svc_broadband.cgi4
-rw-r--r--httemplate/view/svc_broadband.cgi9
14 files changed, 297 insertions, 96 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 483c5e0..b2dddca 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -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', '', '', '',
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index d00bb5c..8f66c66 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -264,7 +264,10 @@ sub upgrade_data {
'part_export' => [],
#insert default tower_sector if not present
- 'tower',
+ 'tower' => [],
+
+ #routernum/blocknum
+ 'svc_broadband' => [],
;
\%hash;
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index be42cb5..e00f587 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -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
diff --git a/FS/FS/part_export/router.pm b/FS/FS/part_export/router.pm
index 42aa51c..6a1d676 100644
--- a/FS/FS/part_export/router.pm
+++ b/FS/FS/part_export/router.pm
@@ -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);
diff --git a/FS/FS/router.pm b/FS/FS/router.pm
index f66f2ce..99373e5 100755
--- a/FS/FS/router.pm
+++ b/FS/FS/router.pm
@@ -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
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
index f5aa2fe..ac463bc 100755
--- a/FS/FS/svc_broadband.pm
+++ b/FS/FS/svc_broadband.pm
@@ -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,
diff --git a/httemplate/browse/router.cgi b/httemplate/browse/router.cgi
index 069ca9b..21047d7 100644
--- a/httemplate/browse/router.cgi
+++ b/httemplate/browse/router.cgi
@@ -9,6 +9,7 @@
'count_query' => "SELECT count(*) from router $count_sql",
'header' => [ 'Router name',
'Address block(s)',
+ 'IP addressing',
'Action',
],
'fields' => [ 'routername',
@@ -16,10 +17,12 @@
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>
diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi
index 70eaa45..6672d5d 100755
--- a/httemplate/edit/router.cgi
+++ b/httemplate/edit/router.cgi
@@ -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,
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
index ff57ac0..8fccb1f 100644
--- a/httemplate/edit/svc_broadband.cgi
+++ b/httemplate/edit/svc_broadband.cgi
@@ -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 ),
);
diff --git a/httemplate/elements/select-tiered.html b/httemplate/elements/select-tiered.html
index 35f9e5a..99b2852 100644
--- a/httemplate/elements/select-tiered.html
+++ b/httemplate/elements/select-tiered.html
@@ -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
index 0000000..e6b7bfe
--- /dev/null
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -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>
diff --git a/httemplate/search/report_svc_broadband.html b/httemplate/search/report_svc_broadband.html
index ee4dfce..37f21b7 100755
--- a/httemplate/search/report_svc_broadband.html
+++ b/httemplate/search/report_svc_broadband.html
@@ -26,6 +26,10 @@
'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,
diff --git a/httemplate/search/svc_broadband.cgi b/httemplate/search/svc_broadband.cgi
index 605b829..ee62e90 100755
--- a/httemplate/search/svc_broadband.cgi
+++ b/httemplate/search/svc_broadband.cgi
@@ -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',
diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi
index 2e93d42..05ae632 100644
--- a/httemplate/view/svc_broadband.cgi
+++ b/httemplate/view/svc_broadband.cgi
@@ -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 {