signups with snarf info!
[freeside.git] / FS / FS / svc_Common.pm
index 5bea5b0..2fed5dd 100644 (file)
@@ -1,10 +1,11 @@
 package FS::svc_Common;
 
 use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs fields );
+use vars qw( @ISA $noexport_hack );
+use FS::Record qw( qsearch qsearchs fields dbh );
 use FS::cust_svc;
 use FS::part_svc;
+use FS::queue;
 
 @ISA = qw( FS::Record );
 
@@ -27,7 +28,59 @@ inherit from, i.e. FS::svc_acct.  FS::svc_Common inherits from FS::Record.
 
 =over 4
 
-=item insert
+=cut
+
+sub virtual_fields {
+
+  # This restricts the fields based on part_svc_column and the svcpart of 
+  # the service.  There are four possible cases:
+  # 1.  svcpart passed as part of the svc_x hash.
+  # 2.  svcpart fetched via cust_svc based on svcnum.
+  # 3.  No svcnum or svcpart.  In this case, return ALL the fields with 
+  #     dbtable eq $self->table.
+  # 4.  Called via "fields('svc_acct')" or something similar.  In this case
+  #     there is no $self object.
+
+  my $self = shift;
+  my $svcpart;
+  my @vfields = $self->SUPER::virtual_fields;
+
+  return @vfields unless (ref $self); # Case 4
+
+  if ($self->svcpart) { # Case 1
+    $svcpart = $self->svcpart;
+  } elsif (my $cust_svc = $self->cust_svc) { # Case 2
+    $svcpart = $cust_svc->svcpart;
+  } else { # Case 3
+    $svcpart = '';
+  }
+
+  if ($svcpart) { #Cases 1 and 2
+    my %flags = map { $_->columnname, $_->columnflag } (
+        qsearch ('part_svc_column', { svcpart => $svcpart } )
+      );
+    return grep { not ($flags{$_} eq 'X') } @vfields;
+  } else { # Case 3
+    return @vfields;
+  } 
+  return ();
+}
+
+=item check
+
+Checks the validity of fields in this record.
+
+At present, this does nothing but call FS::Record::check (which, in turn, 
+does nothing but run virtual field checks).
+
+=cut
+
+sub check {
+  my $self = shift;
+  $self->SUPER::check;
+}
+
+=item insert [ JOBNUM_ARRAYREF [ OBJECTS_ARRAYREF ] ]
 
 Adds this record to the database.  If there is an error, returns the error,
 otherwise returns false.
@@ -35,10 +88,19 @@ otherwise returns false.
 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be 
 defined.  An FS::cust_svc record will be created and inserted.
 
+If an arrayref is passed as parameter, the B<jobnum>s of any export jobs will
+be added to the array.
+
+If an arrayref of FS::tablename objects (for example, FS::acct_snarf objects)
+is passed as the optional second parameter, they will have their svcnum fields
+set and will be inserted after this record, but before any exports are run.
+
 =cut
 
 sub insert {
   my $self = shift;
+  local $FS::queue::jobnums = shift if @_;
+  my $objects = scalar(@_) ? shift : [];
   my $error;
 
   local $SIG{HUP} = 'IGNORE';
@@ -48,6 +110,10 @@ sub insert {
   local $SIG{TSTP} = 'IGNORE';
   local $SIG{PIPE} = 'IGNORE';
 
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
   $error = $self->check;
   return $error if $error;
 
@@ -55,21 +121,55 @@ sub insert {
   my $cust_svc;
   unless ( $svcnum ) {
     $cust_svc = new FS::cust_svc ( {
-      'svcnum'  => $svcnum,
+      #hua?# 'svcnum'  => $svcnum,
       'pkgnum'  => $self->pkgnum,
       'svcpart' => $self->svcpart,
     } );
     $error = $cust_svc->insert;
-    return $error if $error;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
     $svcnum = $self->svcnum($cust_svc->svcnum);
+  } else {
+    $cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
+    unless ( $cust_svc ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "no cust_svc record found for svcnum ". $self->svcnum;
+    }
+    $self->pkgnum($cust_svc->pkgnum);
+    $self->svcpart($cust_svc->svcpart);
   }
 
   $error = $self->SUPER::insert;
   if ( $error ) {
-    $cust_svc->delete if $cust_svc;
+    $dbh->rollback if $oldAutoCommit;
     return $error;
   }
 
+  foreach my $object ( @$objects ) {
+    $object->svcnum($self->svcnum);
+    $error = $object->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  #new-style exports!
+  unless ( $noexport_hack ) {
+    foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+      my $error = $part_export->export_insert($self);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
   '';
 }
 
@@ -95,16 +195,80 @@ sub delete {
 
   my $svcnum = $self->svcnum;
 
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
   $error = $self->SUPER::delete;
   return $error if $error;
 
-  my $cust_svc = qsearchs( 'cust_svc' , { 'svcnum' => $svcnum } );  
+  #new-style exports!
+  unless ( $noexport_hack ) {
+    foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+      my $error = $part_export->export_delete($self);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
+  return $error if $error;
+
+  my $cust_svc = $self->cust_svc;
   $error = $cust_svc->delete;
   return $error if $error;
 
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
   '';
 }
 
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub replace {
+  my ($new, $old) = (shift, shift);
+
+  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 = $new->SUPER::replace($old);
+  if ($error) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  #new-style exports!
+  unless ( $noexport_hack ) {
+    foreach my $part_export ( $new->cust_svc->part_svc->part_export ) {
+      my $error = $part_export->export_replace($new,$old);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+}
+
+
 =item setfixed
 
 Sets any fixed fields for this service (see L<FS::part_svc>).  If there is an
@@ -145,7 +309,7 @@ sub setx {
   #get part_svc
   my $svcpart;
   if ( $self->svcnum ) {
-    my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $self->svcnum } );
+    my $cust_svc = $self->cust_svc;
     return "Unknown svcnum" unless $cust_svc; 
     $svcpart = $cust_svc->svcpart;
   } else {
@@ -155,9 +319,11 @@ sub setx {
   return "Unkonwn svcpart" unless $part_svc;
 
   #set default/fixed/whatever fields from part_svc
-  foreach my $field ( fields('svc_acct') ) {
-    if ( $part_svc->getfield('svc_acct__'. $field. '_flag') eq $x ) {
-      $self->setfield( $field, $part_svc->getfield('svc_acct__'. $field) );
+  my $table = $self->table;
+  foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
+    my $part_svc_column = $part_svc->part_svc_column($field);
+    if ( $part_svc_column->columnflag eq $x ) {
+      $self->setfield( $field, $part_svc_column->columnvalue );
     }
   }
 
@@ -165,34 +331,111 @@ sub setx {
 
 }
 
+=item cust_svc
+
+Returns the cust_svc record associated with this svc_ record, as a FS::cust_svc
+object (see L<FS::cust_svc>).
+
+=cut
+
+sub cust_svc {
+  my $self = shift;
+  qsearchs('cust_svc', { 'svcnum' => $self->svcnum } );
+}
+
 =item suspend
 
+Runs export_suspend callbacks.
+
+=cut
+
+sub suspend {
+  my $self = shift;
+
+  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;
+
+  #new-style exports!
+  unless ( $noexport_hack ) {
+    foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+      my $error = $part_export->export_suspend($self);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
 =item unsuspend
 
+Runs export_unsuspend callbacks.
+
+=cut
+
+sub unsuspend {
+  my $self = shift;
+
+  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;
+
+  #new-style exports!
+  unless ( $noexport_hack ) {
+    foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
+      my $error = $part_export->export_unsuspend($self);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
 =item cancel
 
-Stubs - return false (no error) so derived classes don't need to define these
+Stub - returns false (no error) so derived classes don't need to define these
 methods.  Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
 
 =cut
 
-sub suspend { ''; }
-sub unsuspend { ''; }
 sub cancel { ''; }
 
 =back
 
 =head1 VERSION
 
-$Id: svc_Common.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
+$Id: svc_Common.pm,v 1.14 2003-10-25 02:05:44 ivan Exp $
 
 =head1 BUGS
 
 The setfixed method return value.
 
-The new method should set defaults from part_svc (like the check method
-sets fixed values)?
-
 =head1 SEE ALSO
 
 L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, schema.html