don't generate invoices for COMP customers
[freeside.git] / FS / FS / cust_svc.pm
index 26e6274..65f8d58 100644 (file)
@@ -1,8 +1,9 @@
 package FS::cust_svc;
 
 use strict;
-use vars qw( @ISA );
+use vars qw( @ISA $ignore_quantity );
 use Carp qw( cluck );
+use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::cust_pkg;
 use FS::part_pkg;
@@ -11,11 +12,15 @@ use FS::pkg_svc;
 use FS::svc_acct;
 use FS::svc_domain;
 use FS::svc_forward;
+use FS::svc_broadband;
+use FS::svc_external;
 use FS::domain_record;
 use FS::part_export;
 
 @ISA = qw( FS::Record );
 
+$ignore_quantity = 0;
+
 sub _cache {
   my $self = shift;
   my ( $hashref, $cache ) = @_;
@@ -168,15 +173,9 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $error = $new->SUPER::replace($old);
-  if ( $error ) {
-    $dbh->rollback if $oldAutoCommit;
-    return $error if $error;
-  }
-
   if ( $new->svcpart != $old->svcpart ) {
     my $svc_x = $new->svc_x;
-    my $new_svc_x = ref($svc_x)->new({$svc_x->hash});
+    my $new_svc_x = ref($svc_x)->new({$svc_x->hash, svcpart=>$new->svcpart });
     my $error = $new_svc_x->replace($svc_x);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
@@ -184,6 +183,12 @@ sub replace {
     }
   }
 
+  my $error = $new->SUPER::replace($old);
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error if $error;
+  }
+
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   ''; #no error
 
@@ -228,10 +233,10 @@ sub check {
     });
     return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
            " services for pkgnum ". $self->pkgnum
-      if scalar(@cust_svc) >= $quantity;
+      if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_svc
@@ -278,10 +283,15 @@ sub label {
   if ( $svcdb eq 'svc_acct' ) {
     $tag = $svc_x->email;
   } elsif ( $svcdb eq 'svc_forward' ) {
-    my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->srcsvc } );
-    $tag = $svc_acct->email. '->';
+    if ( $svc_x->srcsvc ) {
+      my $svc_acct = $svc_x->srcsvc_acct;
+      $tag = $svc_acct->email;
+    } else {
+      $tag = $svc_x->src;
+    }
+    $tag .= '->';
     if ( $svc_x->dstsvc ) {
-      $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->dstsvc } );
+      my $svc_acct = $svc_x->dstsvc_acct;
       $tag .= $svc_acct->email;
     } else {
       $tag .= $svc_x->dst;
@@ -293,6 +303,13 @@ sub label {
     $tag = $domain->zone;
   } elsif ( $svcdb eq 'svc_broadband' ) {
     $tag = $svc_x->ip_addr;
+  } elsif ( $svcdb eq 'svc_external' ) {
+    my $conf = new FS::Conf;
+    if ( $conf->config('svc_external-display_type') eq 'artera_turbo' ) {
+      $tag = sprintf('%010d', $svc_x->id). '-'. $svc_x->title;
+    } else {
+      $tag = $svc_x->id. ': '. $svc_x->title;
+    }
   } else {
     cluck "warning: asked for label of unsupported svcdb; using svcnum";
     $tag = $svc_x->getfield('svcnum');
@@ -313,6 +330,7 @@ sub svc_x {
   if ( $svcdb eq 'svc_acct' && $self->{'_svc_acct'} ) {
     $self->{'_svc_acct'};
   } else {
+    #require "FS/$svcdb.pm";
     qsearchs( $svcdb, { 'svcnum' => $self->svcnum } );
   }
 }
@@ -350,22 +368,27 @@ for records where B<svcdb> is not "svc_acct".
 sub seconds_since_sqlradacct {
   my($self, $start, $end) = @_;
 
-  my $username = $self->svc_x->username;
+  my $svc_x = $self->svc_x;
 
-  my @part_export = $self->part_svc->part_export('sqlradius')
-    or die "no sqlradius export configured for this service type";
+  my @part_export = $self->part_svc->part_export('sqlradius');
+  push @part_export, $self->part_svc->part_export('sqlradius_withdomain');
+  die "no sqlradius or sqlradius_withdomain export configured for this".
+      "service type"
+    unless @part_export;
     #or return undef;
 
   my $seconds = 0;
   foreach my $part_export ( @part_export ) {
 
+    next if $part_export->option('ignore_accounting');
+
     my $dbh = DBI->connect( map { $part_export->option($_) }
                             qw(datasrc username password)    )
       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} eq 'mysql' ) {
+    if ( $dbh->{Driver}->{Name} =~ /^mysql(PP)?$/ ) {
       $str2time = 'UNIX_TIMESTAMP(';
     } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) {
       $str2time = 'EXTRACT( EPOCH FROM ';
@@ -375,6 +398,15 @@ sub seconds_since_sqlradacct {
       $str2time = 'extract(epoch from ';
     }
 
+    my $username;
+    if ( $part_export->exporttype eq 'sqlradius' ) {
+      $username = $svc_x->username;
+    } elsif ( $part_export->exporttype eq 'sqlradius_withdomain' ) {
+      $username = $svc_x->email;
+    } else {
+      die 'unknown exporttype '. $part_export->exporttype;
+    }
+
     my $query;
   
     #find closed sessions completely within the given range
@@ -453,23 +485,28 @@ for records where B<svcdb> is not "svc_acct".
 sub attribute_since_sqlradacct {
   my($self, $start, $end, $attrib) = @_;
 
-  my $username = $self->svc_x->username;
+  my $svc_x = $self->svc_x;
 
-  my @part_export = $self->part_svc->part_export('sqlradius')
-    or die "no sqlradius export configured for this service type";
+  my @part_export = $self->part_svc->part_export('sqlradius');
+  push @part_export, $self->part_svc->part_export('sqlradius_withdomain');
+  die "no sqlradius or sqlradius_withdomain export configured for this".
+      "service type"
+    unless @part_export;
     #or return undef;
 
   my $sum = 0;
 
   foreach my $part_export ( @part_export ) {
 
+    next if $part_export->option('ignore_accounting');
+
     my $dbh = DBI->connect( map { $part_export->option($_) }
                             qw(datasrc username password)    )
       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} eq 'mysql' ) {
+    if ( $dbh->{Driver}->{Name} =~ /^mysql(PP)?$/ ) {
       $str2time = 'UNIX_TIMESTAMP(';
     } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) {
       $str2time = 'EXTRACT( EPOCH FROM ';
@@ -479,6 +516,15 @@ sub attribute_since_sqlradacct {
       $str2time = 'extract(epoch from ';
     }
 
+    my $username;
+    if ( $part_export->exporttype eq 'sqlradius' ) {
+      $username = $svc_x->username;
+    } elsif ( $part_export->exporttype eq 'sqlradius_withdomain' ) {
+      $username = $svc_x->email;
+    } else {
+      die 'unknown exporttype '. $part_export->exporttype;
+    }
+
     my $sth = $dbh->prepare("SELECT SUM($attrib)
                                FROM radacct
                                WHERE UserName = ?
@@ -496,6 +542,53 @@ sub attribute_since_sqlradacct {
 
 }
 
+=item get_session_history_sqlradacct TIMESTAMP_START TIMESTAMP_END
+
+See L<FS::svc_acct/get_session_history_sqlradacct>.  Equivalent to
+$cust_svc->svc_x->get_session_history_sqlradacct, but more efficient.
+Meaningless for records where B<svcdb> is not "svc_acct".
+
+=cut
+
+sub get_session_history {
+  my($self, $start, $end, $attrib) = @_;
+
+  #$attrib ???
+
+  my @part_export = $self->part_svc->part_export('sqlradius');
+  push @part_export, $self->part_svc->part_export('sqlradius_withdomain');
+  die "no sqlradius or sqlradius_withdomain export configured for this".
+      "service type"
+    unless @part_export;
+    #or return undef;
+                     
+  my @sessions = ();
+
+  foreach my $part_export ( @part_export ) {
+    push @sessions, $part_export->usage_sessions( $self->svc_x, $start, $end );
+  }
+
+  \@sessions;
+
+}
+
+=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,
+                       }
+          );
+}
+
 =back
 
 =head1 BUGS