agent-virtualize credit card surcharge percentage, RT#72961
[freeside.git] / FS / FS / router.pm
index 7a9fda3..c0c93dd 100755 (executable)
@@ -1,12 +1,10 @@
 package FS::router;
 package FS::router;
+use base qw( FS::m2m_Common FS::Record );
 
 use strict;
 
 use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch );
+use FS::Record qw( qsearchs qsearch dbh );
 use FS::addr_block;
 
 use FS::addr_block;
 
-@ISA = qw( FS::Record FS::m2m_Common );
-
 =head1 NAME
 
 FS::router - Object methods for router records
 =head1 NAME
 
 FS::router - Object methods for router records
@@ -40,6 +38,10 @@ fields are currently supported:
 
 =item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
 
 
 =item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
 
+=item manual_addr - set to 'Y' to allow services linked to this router 
+to have any IP address, rather than one in an address block belonging 
+to the router.
+
 =back
 
 =head1 METHODS
 =back
 
 =head1 METHODS
@@ -59,16 +61,87 @@ sub table { 'router'; }
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
 
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
 
-=item delete
+If the pseudo-field 'blocknum' is set to an L<FS::addr_block> number, then 
+that address block will be assigned to this router.  Currently only one
+block can be assigned this way.
+
+=cut
 
 
-Deletes this record from the database.  If there is an error, returns the
-error, otherwise returns false.
+sub insert {
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $self = shift;
+  my $error = $self->SUPER::insert(@_);
+  return $error if $error;
+  if ( $self->blocknum ) {
+    my $block = FS::addr_block->by_key($self->blocknum);
+    if ($block) {
+      if ($block->routernum) {
+        $error = "block ".$block->cidr." is already assigned to a router";
+      } else {
+        $block->set('routernum', $self->routernum);
+        $block->set('manual_flag', 'Y');
+        $error = $block->replace;
+      }
+    } else {
+      $error = "blocknum ".$self->blocknum." not found";
+    }
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+  $dbh->commit if $oldAutoCommit;
+  return $error;
+}
 
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
 returns the error, otherwise returns false.
 
 
 =item replace OLD_RECORD
 
 Replaces OLD_RECORD with this one in the database.  If there is an error,
 returns the error, otherwise returns false.
 
+=cut
+
+sub replace {
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $self = shift;
+  my $old = shift || $self->replace_old;
+  my $error = $self->SUPER::replace($old, @_);
+  return $error if $error;
+
+  if ( length($self->blocknum) ) {
+    #warn "FS::router::replace: blocknum = ".$self->blocknum."\n";
+    # then release any blocks we're already holding
+    foreach my $block ($self->addr_block) {
+      $block->set('routernum', 0);
+      $block->set('manual_flag', '');
+      $error ||= $block->replace;
+    }
+    if ( !$error and $self->blocknum > 0 ) {
+      # and, if the new blocknum is a real blocknum, assign it
+      my $block = FS::addr_block->by_key($self->blocknum);
+      if ( $block ) {
+        $block->set('routernum', $self->routernum);
+        $block->set('manual_flag', '');
+        $error ||= $block->replace;
+      } else {
+        $error = "blocknum ".$self->blocknum." not found";
+      }
+    }
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+  $dbh->commit if $oldAutoCommit;
+  return $error;
+}
+
 =item check
 
 Checks all fields to make sure this is a valid record.  If there is an error,
 =item check
 
 Checks all fields to make sure this is a valid record.  If there is an error,
@@ -83,13 +156,45 @@ sub check {
   my $error =
     $self->ut_numbern('routernum')
     || $self->ut_text('routername')
   my $error =
     $self->ut_numbern('routernum')
     || $self->ut_text('routername')
+    || $self->ut_enum('manual_addr', [ '', 'Y' ])
     || $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
     || $self->ut_agentnum_acl('agentnum', 'Broadband global configuration')
+    || $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum')
   ;
   return $error if $error;
 
   $self->SUPER::check;
 }
 
   ;
   return $error if $error;
 
   $self->SUPER::check;
 }
 
+=item delete
+
+Deallocate all address blocks from this router and delete it.
+
+=cut
+
+sub delete {
+    my $self = shift;
+
+    my $oldAutoCommit = $FS::UID::AutoCommit;
+    local $FS::UID::AutoCommit = 0;
+    my $dbh = dbh;
+    my $error;
+    foreach my $block ($self->addr_block) {
+      $block->set('manual_flag', '');
+      $block->set('routernum', 0);
+      $error ||= $block->replace;
+    }
+
+    $error ||= $self->SUPER::delete;
+    if ( $error ) {
+       $dbh->rollback if $oldAutoCommit;
+       return $error;
+    }
+  
+    $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+    '';
+}
+
 =item addr_block
 
 Returns a list of FS::addr_block objects (address blocks) associated
 =item addr_block
 
 Returns a list of FS::addr_block objects (address blocks) associated
@@ -99,22 +204,29 @@ with this object.
 
 sub addr_block {
   my $self = shift;
 
 sub addr_block {
   my $self = shift;
-  return qsearch('addr_block', { routernum => $self->routernum });
+  qsearch('addr_block', { routernum => $self->routernum });
 }
 
 }
 
-=item part_svc_router
+=item auto_addr_block
 
 
-Returns a list of FS::part_svc_router objects associated with this 
-object.  This is unlikely to be useful for any purpose other than retrieving 
-the associated FS::part_svc objects.  See below.
+Returns a list of address blocks on which auto-assignment of IP addresses
+is enabled.
 
 =cut
 
 
 =cut
 
-sub part_svc_router {
+sub auto_addr_block {
   my $self = shift;
   my $self = shift;
-  return qsearch('part_svc_router', { routernum => $self->routernum });
+  return () if $self->manual_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 
+object.  This is unlikely to be useful for any purpose other than retrieving 
+the associated FS::part_svc objects.  See below.
+
 =item part_svc
 
 Returns a list of FS::part_svc objects associated with this object.
 =item part_svc
 
 Returns a list of FS::part_svc objects associated with this object.
@@ -131,16 +243,14 @@ sub part_svc {
 
 Returns the agent associated with this router, if any.
 
 
 Returns the agent associated with this router, if any.
 
-=cut
+=item cust_svc
 
 
-sub agent {
-  qsearchs('agent', { 'agentnum' => shift->agentnum });
-}
+Returns the cust_svc associated with this router, if any.  This should be
+the service that I<provides connectivity to the router>, not any service 
+connected I<through> the router.
 
 =back
 
 
 =back
 
-=head1 BUGS
-
 =head1 SEE ALSO
 
 FS::svc_broadband, FS::router, FS::addr_block, FS::part_svc,
 =head1 SEE ALSO
 
 FS::svc_broadband, FS::router, FS::addr_block, FS::part_svc,