svc_hardware: better error messages for bad hw_addr when not validating as a MAC...
[freeside.git] / FS / FS / part_export.pm
index 7819a7c..96fb85f 100644 (file)
@@ -10,6 +10,7 @@ use FS::part_svc;
 use FS::part_export_option;
 use FS::part_export_machine;
 use FS::svc_export_machine;
 use FS::part_export_option;
 use FS::part_export_machine;
 use FS::svc_export_machine;
+use FS::export_cust_svc;
 
 #for export modules, though they should probably just use it themselves
 use FS::queue;
 
 #for export modules, though they should probably just use it themselves
 use FS::queue;
@@ -58,6 +59,12 @@ fields are currently supported:
 
 =item nodomain - blank or "Y" : usernames are exported to this service with no domain
 
 
 =item nodomain - blank or "Y" : usernames are exported to this service with no domain
 
+=item default_machine - For exports that require a machine to be selected for
+each service (see L<FS::svc_export_machine>), the one to use as the default.
+
+=item no_suspend - Don't export service suspensions. In the future there may
+be "no_*" options for the other service actions.
+
 =back
 
 =head1 METHODS
 =back
 
 =head1 METHODS
@@ -156,6 +163,17 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  # delete associated export_cust_svc
+  foreach my $export_cust_svc ( 
+    qsearch('export_cust_svc',{ 'exportnum' => $self->exportnum })
+  ) {
+    my $error = $export_cust_svc->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
   # clean up export_nas records
   my $error = $self->process_m2m(
     'link_table'    => 'export_nas',
   # clean up export_nas records
   my $error = $self->process_m2m(
     'link_table'    => 'export_nas',
@@ -337,6 +355,7 @@ sub check {
     || $self->ut_textn('exportname')
     || $self->ut_domainn('machine')
     || $self->ut_alpha('exporttype')
     || $self->ut_textn('exportname')
     || $self->ut_domainn('machine')
     || $self->ut_alpha('exporttype')
+    || $self->ut_flag('no_suspend')
   ;
 
   if ( $self->machine eq '_SVC_MACHINE' ) {
   ;
 
   if ( $self->machine eq '_SVC_MACHINE' ) {
@@ -535,32 +554,19 @@ sub default_export_machine {
   die "no default export hostname for export ".$self->exportnum;
 }
 
   die "no default export hostname for export ".$self->exportnum;
 }
 
-=item svc_role SVC_X
-
-Returns the role that SVC_X occupies with respect to this export, if any.
-This is part of the part_svc's export configuration.
-
-=cut
-
-sub svc_role {
-  my $self = shift;
-  my $svc_x = shift;
-  my $cust_svc = $svc_x->cust_svc or return '';
-  my $export_svc = qsearchs('export_svc', { exportnum => $self->exportnum,
-                                            svcpart   => $cust_svc->svcpart })
-                   or return '';
-  $export_svc->role;
-} 
-
-#these should probably all go away, just let the subclasses define em
-
 =item export_insert SVC_OBJECT
 
 =cut
 
 =item export_insert SVC_OBJECT
 
 =cut
 
+# Do not overload!  Overload _export_insert instead
+
 sub export_insert {
   my $self = shift;
   #$self->rebless;
 sub export_insert {
   my $self = shift;
   #$self->rebless;
+  if ( $FS::svc_Common::noexport_hack ) {
+    carp "export_insert() suppressed by noexport_hack" if $DEBUG;
+    return;
+  }
   $self->_export_insert(@_);
 }
 
   $self->_export_insert(@_);
 }
 
@@ -577,9 +583,15 @@ sub export_insert {
 
 =cut
 
 
 =cut
 
+# Do not overload!  Overload _export_replace instead
+
 sub export_replace {
   my $self = shift;
   #$self->rebless;
 sub export_replace {
   my $self = shift;
   #$self->rebless;
+  if ( $FS::svc_Common::noexport_hack ) {
+    carp "export_replace() suppressed by noexport_hack" if $DEBUG;
+    return;
+  }
   $self->_export_replace(@_);
 }
 
   $self->_export_replace(@_);
 }
 
@@ -587,9 +599,15 @@ sub export_replace {
 
 =cut
 
 
 =cut
 
+# Do not overload!  Overload _export_delete instead
+
 sub export_delete {
   my $self = shift;
   #$self->rebless;
 sub export_delete {
   my $self = shift;
   #$self->rebless;
+  if ( $FS::svc_Common::noexport_hack ) {
+    carp "export_delete() suppressed by noexport_hack" if $DEBUG;
+    return;
+  }
   $self->_export_delete(@_);
 }
 
   $self->_export_delete(@_);
 }
 
@@ -597,9 +615,15 @@ sub export_delete {
 
 =cut
 
 
 =cut
 
+# Do not overload!  Overload _export_suspend instead
+
 sub export_suspend {
   my $self = shift;
   #$self->rebless;
 sub export_suspend {
   my $self = shift;
   #$self->rebless;
+  if ( $FS::svc_Common::noexport_hack ) {
+    carp "export_suspend() suppressed by noexport_hack" if $DEBUG;
+    return;
+  }
   $self->_export_suspend(@_);
 }
 
   $self->_export_suspend(@_);
 }
 
@@ -607,9 +631,15 @@ sub export_suspend {
 
 =cut
 
 
 =cut
 
+# Do not overload!  Overload _export_unsuspend instead
+
 sub export_unsuspend {
   my $self = shift;
   #$self->rebless;
 sub export_unsuspend {
   my $self = shift;
   #$self->rebless;
+  if ( $FS::svc_Common::noexport_hack ) {
+    carp "export_unsuspend() suppressed by noexport_hack" if $DEBUG;
+    return;
+  }
   $self->_export_unsuspend(@_);
 }
 
   $self->_export_unsuspend(@_);
 }
 
@@ -636,7 +666,7 @@ sub _export_suspend {
   #warn "warning: _export_suspened unimplemented for". ref($self);
   my $svc_x = shift;
   my $new = $svc_x->clone_suspended;
   #warn "warning: _export_suspened unimplemented for". ref($self);
   my $svc_x = shift;
   my $new = $svc_x->clone_suspended;
-  $self->_export_replace( $new, $svc_x );
+  $self->export_replace( $new, $svc_x );
 }
 
 sub _export_unsuspend {
 }
 
 sub _export_unsuspend {
@@ -644,7 +674,82 @@ sub _export_unsuspend {
   #warn "warning: _export_unsuspend unimplemented for ". ref($self);
   my $svc_x = shift;
   my $old = $svc_x->clone_kludge_unsuspend;
   #warn "warning: _export_unsuspend unimplemented for ". ref($self);
   my $svc_x = shift;
   my $old = $svc_x->clone_kludge_unsuspend;
-  $self->_export_replace( $svc_x, $old );
+  $self->export_replace( $svc_x, $old );
+}
+
+=item get_remoteid SVC
+
+Returns the remote id for this export for the given service.
+
+=cut
+
+sub get_remoteid {
+  my ($self, $svc_x) = @_;
+
+  my $export_cust_svc = qsearchs('export_cust_svc',{
+    'exportnum' => $self->exportnum,
+    'svcnum' => $svc_x->svcnum
+  });
+
+  return $export_cust_svc ? $export_cust_svc->remoteid : '';
+}
+
+=item set_remoteid SVC VALUE
+
+Sets the remote id for this export for the given service.
+See L<FS::export_cust_svc>.
+
+If value is true, inserts or updates export_cust_svc record.
+If value is false, deletes any existing record.
+
+Returns error message, blank on success.
+
+=cut
+
+sub set_remoteid {
+  my ($self, $svc_x, $value) = @_;
+
+  my $export_cust_svc = qsearchs('export_cust_svc',{
+    'exportnum' => $self->exportnum,
+    'svcnum' => $svc_x->svcnum
+  });
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $error = '';
+  if ($value) {
+    if ($export_cust_svc) {
+      $export_cust_svc->set('remoteid',$value);
+      $error = $export_cust_svc->replace;
+    } else {
+      $export_cust_svc = new FS::export_cust_svc {
+        'exportnum' => $self->exportnum,
+        'svcnum' => $svc_x->svcnum,
+        'remoteid' => $value
+      };
+      $error = $export_cust_svc->insert;
+    }
+  } else {
+    if ($export_cust_svc) {
+      $error = $export_cust_svc->delete;
+    } #otherwise, it already doesn't exist
+  }
+
+  if ($oldAutoCommit) {
+    $dbh->rollback if $error;
+    $dbh->commit unless $error;
+  }
+
+  return $error;  
 }
 
 =item export_links SVC_OBJECT ARRAYREF
 }
 
 =item export_links SVC_OBJECT ARRAYREF
@@ -714,8 +819,12 @@ actual DIDs except at the lowest level.
 Generally, 'state' alone will return an array of area codes or region names
 in the state.
 
 Generally, 'state' alone will return an array of area codes or region names
 in the state.
 
-'state' and 'areacode' together will return an array of exchanges (NXX
-prefixes), or for some exports, an array of ratecenter names.
+'state' and 'areacode' together will return an array of either:
+- exchange strings of the form "New York (212-555-XXXX)"
+- ratecenter names of the form "New York, NY"
+
+These strings are sent back to the UI and offered as options so that the user
+can choose the local calling area they like.
 
 'areacode' and 'exchange', or 'state' and 'ratecenter', or 'region' by itself
 will return an array of actual DID numbers.
 
 'areacode' and 'exchange', or 'state' and 'ratecenter', or 'region' by itself
 will return an array of actual DID numbers.
@@ -723,6 +832,8 @@ will return an array of actual DID numbers.
 Passing 'tollfree' with a true value will override the whole hierarchy and
 return an array of tollfree numbers.
 
 Passing 'tollfree' with a true value will override the whole hierarchy and
 return an array of tollfree numbers.
 
+C<get_dids> methods should report errors via die().
+
 =cut
 
 # no stub; can('get_dids') should return false by default
 =cut
 
 # no stub; can('get_dids') should return false by default
@@ -753,6 +864,61 @@ sub get_dids_npa_select   { 1; }
 # change the phone number for a service. if false, then they can't (have to
 # reprovision completely).
 
 # change the phone number for a service. if false, then they can't (have to
 # reprovision completely).
 
+=item svc_role SVC
+
+Returns the role that SVC occupies with respect to this export, if any.
+This is part of the part_svc's export configuration.
+
+=cut
+
+sub svc_role {
+  my $self = shift;
+  my $svc_x = shift;
+  my $cust_svc = $svc_x->cust_svc or return '';
+  my $export_svc = qsearchs('export_svc', { exportnum => $self->exportnum,
+                                            svcpart   => $cust_svc->svcpart })
+                   or return '';
+  $export_svc->role;
+} 
+
+=item svc_with_role { SVC | PKGNUM }, ROLE
+
+Given a svc_* object SVC or pkgnum PKG, and a role name ROLE, finds the
+service(s) in the same package that are linked to this export with ROLE.
+
+=cut
+
+sub svc_with_role {
+  my $self = shift;
+  my $svc_or_pkgnum = shift;
+  my $role = shift; 
+  my $pkgnum;
+  if ( ref $svc_or_pkgnum ) {
+    $pkgnum = $svc_or_pkgnum->cust_svc->pkgnum or return '';
+  } else {
+    $pkgnum = $svc_or_pkgnum;
+  }
+  my $role_info = $self->info->{roles}->{$role}
+    or die "role '$role' does not exist for export '".$self->exporttype."'\n";
+  my $svcdb = $role_info->{svcdb};
+
+  my @svcs = qsearch({
+    'table'     =>  $svcdb,
+    'addl_from' =>  ' JOIN cust_svc USING (svcnum)' .
+                    ' JOIN export_svc USING (svcpart)',
+    'extra_sql' =>  " WHERE cust_svc.pkgnum = $pkgnum" .
+                    " AND export_svc.exportnum = ".$self->exportnum .
+                    " AND export_svc.role = '$role'",
+  });               
+  if ( $role_info->{multiple} ) {
+    return @svcs;
+  } else {
+    if ( @svcs > 1 ) {
+      warn "multiple $role services in pkgnum $pkgnum; returning the first one.\n";
+    }
+    return $svcs[0];
+  }
+}
 
 =back
 
 
 =back