allow exports to add links to customer view (#1407)
[freeside.git] / FS / FS / cust_svc.pm
index cdb34cd..51fc186 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 use vars qw( @ISA $DEBUG $me $ignore_quantity );
 use Carp;
 use FS::Conf;
-use FS::Record qw( qsearch qsearchs dbh );
+use FS::Record qw( qsearch qsearchs dbh str2time_sql );
 use FS::cust_pkg;
 use FS::part_pkg;
 use FS::part_svc;
@@ -68,6 +68,8 @@ The following fields are currently supported:
 
 =item svcpart - Service definition (see L<FS::part_svc>)
 
+=item overlimit - date the service exceeded its usage limit
+
 =back
 
 =head1 METHODS
@@ -130,22 +132,78 @@ sub cancel {
 
   my $svc = $self->svc_x;
   if ($svc) {
+
     my $error = $svc->cancel;
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "Error canceling service: $error";
     }
-    $error = $svc->delete;
+    $error = $svc->delete; #this deletes this cust_svc record as well
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "Error deleting service: $error";
     }
+
+  } else {
+
+    #huh?
+    warn "WARNING: no svc_ record found for svcnum ". $self->svcnum.
+         "; deleting cust_svc only\n"; 
+
+    my $error = $self->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Error deleting cust_svc: $error";
+    }
+
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  ''; #no errors
+
+}
+
+=item overlimit [ ACTION ]
+
+Retrieves or sets the overlimit date.  If ACTION is absent, return
+the present value of overlimit.  If ACTION is present, it can
+have the value 'suspend' or 'unsuspend'.  In the case of 'suspend' overlimit
+is set to the current time if it is not already set.  The 'unsuspend' value
+causes the time to be cleared.  
+
+If there is an error on setting, returns the error, otherwise returns false.
+
+=cut
+
+sub overlimit {
+  my $self = shift;
+  my $action = shift or return $self->getfield('overlimit');
+
+  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;
+
+  if ( $action eq 'suspend' ) {
+    $self->setfield('overlimit', time) unless $self->getfield('overlimit');
+  }elsif ( $action eq 'unsuspend' ) {
+    $self->setfield('overlimit', '');
+  }else{
+    die "unexpected action value: $action";
   }
 
-  my $error = $self->delete;
+  local $ignore_quantity = 1;
+  my $error = $self->replace;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
-    return "Error deleting cust_svc: $error";
+    return "Error setting overlimit: $error";
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -175,6 +233,8 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  $old = $new->replace_old unless defined($old);
+  
   if ( $new->svcpart != $old->svcpart ) {
     my $svc_x = $new->svc_x;
     my $new_svc_x = ref($svc_x)->new({$svc_x->hash, svcpart=>$new->svcpart });
@@ -212,6 +272,7 @@ sub check {
     $self->ut_numbern('svcnum')
     || $self->ut_numbern('pkgnum')
     || $self->ut_number('svcpart')
+    || $self->ut_numbern('overlimit')
   ;
   return $error if $error;
 
@@ -268,6 +329,34 @@ sub cust_pkg {
   qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
 }
 
+=item pkg_svc
+
+Returns the pkg_svc record for for this service, if applicable.
+
+=cut
+
+sub pkg_svc {
+  my $self = shift;
+  my $cust_pkg = $self->cust_pkg;
+  return undef unless $cust_pkg;
+
+  qsearchs( 'pkg_svc', { 'svcpart' => $self->svcpart,
+                         'pkgpart' => $cust_pkg->pkgpart,
+                       }
+          );
+}
+
+=item date_inserted
+
+Returns the date this service was inserted.
+
+=cut
+
+sub date_inserted {
+  my $self = shift;
+  $self->h_date('insert');
+}
+
 =item label
 
 Returns a list consisting of:
@@ -303,6 +392,20 @@ sub _svc_label {
 
 }
 
+=item export_links
+
+Returns a list of html elements associated with this services exports.
+
+=cut
+
+sub export_links {
+  my $self = shift;
+  my $svc_x = $self->svc_x
+    or return "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+
+  $svc_x->export_links;
+}
+
 =item svc_x
 
 Returns the FS::svc_XXX object for this service (i.e. an FS::svc_acct object or
@@ -375,17 +478,8 @@ sub seconds_since_sqlradacct {
       or die "can't connect to sqlradius database: ". $DBI::errstr;
 
     #select a unix time conversion function based on database type
-    my $str2time;
-    if ( $dbh->{Driver}->{Name} =~ /^mysql(PP)?$/ ) {
-      $str2time = 'UNIX_TIMESTAMP(';
-    } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) {
-      $str2time = 'EXTRACT( EPOCH FROM ';
-    } else {
-      warn "warning: unknown database type ". $dbh->{Driver}->{Name}.
-           "; guessing how to convert to UNIX timestamps";
-      $str2time = 'extract(epoch from ';
-    }
-
+    my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
+    
     my $username = $part_export->export_username($svc_x);
 
     my $query;
@@ -485,16 +579,7 @@ sub attribute_since_sqlradacct {
       or die "can't connect to sqlradius database: ". $DBI::errstr;
 
     #select a unix time conversion function based on database type
-    my $str2time;
-    if ( $dbh->{Driver}->{Name} =~ /^mysql(PP)?$/ ) {
-      $str2time = 'UNIX_TIMESTAMP(';
-    } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) {
-      $str2time = 'EXTRACT( EPOCH FROM ';
-    } else {
-      warn "warning: unknown database type ". $dbh->{Driver}->{Name}.
-           "; guessing how to convert to UNIX timestamps";
-      $str2time = 'extract(epoch from ';
-    }
+    my $str2time = str2time_sql( $dbh->{Driver}->{Name} );
 
     my $username = $part_export->export_username($svc_x);
 
@@ -550,8 +635,7 @@ sub get_session_history {
 Returns (and SELECTs "FOR UPDATE") all unprocessed (freesidestatus NULL) CDR
 objects (see L<FS::cdr>) associated with this service.
 
-Currently CDRs are associated with svc_acct services via a DID in the
-username.  This part is rather tenative and still subject to change...
+CDRs are associated with svc_phone services via svc_phone.phonenum
 
 =cut
 
@@ -585,24 +669,28 @@ sub get_cdrs_for_update {
       } );
   }
 
-  @cdrs;
-}
-
-=item pkg_svc
-
-Returns the pkg_svc record for for this service, if applicable.
-
-=cut
+  #astricon hack?  config option?
+  push @cdrs,
+    qsearch( {
+      'table'        => 'cdr',
+      'hashref'      => { 'freesidestatus' => '',
+                          'src'            => $number,
+                       },
+      'extra_sql'    => 'FOR UPDATE',
+     } );
 
-sub pkg_svc {
-  my $self = shift;
-  my $cust_pkg = $self->cust_pkg;
-  return undef unless $cust_pkg;
+  if ( length($default_prefix) ) {
+    push @cdrs,
+      qsearch( {
+        'table'        => 'cdr',
+        'hashref'      => { 'freesidestatus' => '',
+                            'src'            => "$default_prefix$number",
+                       },
+        'extra_sql'    => 'FOR UPDATE',
+       } );
+  }
 
-  qsearchs( 'pkg_svc', { 'svcpart' => $self->svcpart,
-                         'pkgpart' => $cust_pkg->pkgpart,
-                       }
-          );
+  @cdrs;
 }
 
 =back