service refactor!
[freeside.git] / FS / FS / cust_svc.pm
index aa0ac5a..cdb34cd 100644 (file)
@@ -1,25 +1,25 @@
 package FS::cust_svc;
 
 use strict;
-use vars qw( @ISA $DEBUG $ignore_quantity );
-use Carp qw( carp cluck );
+use vars qw( @ISA $DEBUG $me $ignore_quantity );
+use Carp;
 use FS::Conf;
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::cust_pkg;
 use FS::part_pkg;
 use FS::part_svc;
 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;
+use FS::cdr;
 
-@ISA = qw( FS::Record );
+#most FS::svc_ classes are autoloaded in svc_x emthod
+use FS::svc_acct;  #this one is used in the cache stuff
+
+@ISA = qw( FS::cust_main_Mixin FS::Record );
 
 $DEBUG = 0;
+$me = '[cust_svc]';
 
 $ignore_quantity = 0;
 
@@ -178,6 +178,7 @@ sub replace {
   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 });
+    local($FS::Record::nowarn_identical) = 1;
     my $error = $new_svc_x->replace($svc_x);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
@@ -199,7 +200,7 @@ sub replace {
 =item check
 
 Checks all fields to make sure this is a valid service.  If there is an error,
-returns the error, otehrwise returns false.  Called by the insert and
+returns the error, otherwise returns false.  Called by the insert and
 replace methods.
 
 =cut
@@ -257,8 +258,8 @@ sub part_svc {
 
 =item cust_pkg
 
-Returns the definition for this service, as a FS::part_svc object (see
-L<FS::part_svc>).
+Returns the package this service belongs to, as a FS::cust_pkg object (see
+L<FS::cust_pkg>).
 
 =cut
 
@@ -273,6 +274,11 @@ Returns a list consisting of:
 - The name of this service (from part_svc)
 - A meaningful identifier (username, domain, or mail alias)
 - The table name (i.e. svc_domain) for this service
+- svcnum
+
+Usage example:
+
+  my($label, $value, $svcdb) = $cust_svc->label;
 
 =cut
 
@@ -280,52 +286,20 @@ sub label {
   my $self = shift;
   carp "FS::cust_svc::label called on $self" if $DEBUG;
   my $svc_x = $self->svc_x
-    or die "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+    or return "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+
   $self->_svc_label($svc_x);
 }
 
 sub _svc_label {
   my( $self, $svc_x ) = ( shift, shift );
-  my $svcdb = $self->part_svc->svcdb;
 
-  my $tag;
-  if ( $svcdb eq 'svc_acct' ) {
-    $tag = $svc_x->email(@_);
-  } elsif ( $svcdb eq 'svc_forward' ) {
-    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 ) {
-      my $svc_acct = $svc_x->dstsvc_acct(@_);
-      $tag .= $svc_acct->email(@_);
-    } else {
-      $tag .= $svc_x->dst;
-    }
-  } elsif ( $svcdb eq 'svc_domain' ) {
-    $tag = $svc_x->getfield('domain');
-  } elsif ( $svcdb eq 'svc_www' ) {
-    my $domain_record = $svc_x->domain_record;
-    $tag = $domain_record->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). '-'.
-             uc( sprintf('%010x', hex($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');
-  }
-
-  $self->part_svc->svc, $tag, $svcdb;
+  (
+    $self->part_svc->svc,
+    $svc_x->label(@_),
+    $self->part_svc->svcdb,
+    $self->svcnum
+  );
 
 }
 
@@ -342,7 +316,10 @@ sub svc_x {
   if ( $svcdb eq 'svc_acct' && $self->{'_svc_acct'} ) {
     $self->{'_svc_acct'};
   } else {
-    #require "FS/$svcdb.pm";
+    require "FS/$svcdb.pm";
+    warn "$me svc_x: part_svc.svcpart ". $self->part_svc->svcpart.
+         ", so searching for $svcdb.svcnum ". $self->svcnum. "\n"
+      if $DEBUG;
     qsearchs( $svcdb, { 'svcnum' => $self->svcnum } );
   }
 }
@@ -382,10 +359,9 @@ sub seconds_since_sqlradacct {
 
   my $svc_x = $self->svc_x;
 
-  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"
+  my @part_export = $self->part_svc->part_export_usage;
+  die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+      " service definition"
     unless @part_export;
     #or return undef;
 
@@ -410,14 +386,7 @@ 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 $username = $part_export->export_username($svc_x);
 
     my $query;
   
@@ -499,10 +468,9 @@ sub attribute_since_sqlradacct {
 
   my $svc_x = $self->svc_x;
 
-  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"
+  my @part_export = $self->part_svc->part_export_usage;
+  die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+      " service definition"
     unless @part_export;
     #or return undef;
 
@@ -528,14 +496,7 @@ 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 $username = $part_export->export_username($svc_x);
 
     my $sth = $dbh->prepare("SELECT SUM($attrib)
                                FROM radacct
@@ -567,11 +528,9 @@ sub get_session_history {
 
   #$attrib ???
 
-  #my @part_export = $cust_svc->part_svc->part_export->can('usage_sessions');
-  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"
+  my @part_export = $self->part_svc->part_export_usage;
+  die "no accounting-capable exports are enabled for ". $self->part_svc->svc.
+      " service definition"
     unless @part_export;
     #or return undef;
                      
@@ -586,6 +545,49 @@ sub get_session_history {
 
 }
 
+=item get_cdrs_for_update
+
+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...
+
+=cut
+
+sub get_cdrs_for_update {
+  my($self, %options) = @_;
+
+  my $default_prefix = $options{'default_prefix'};
+
+  #CDRs are now associated with svc_phone services via svc_phone.phonenum
+  #return () unless $self->svc_x->isa('FS::svc_phone');
+  return () unless $self->part_svc->svcdb eq 'svc_phone';
+  my $number = $self->svc_x->phonenum;
+
+  my @cdrs = 
+    qsearch( {
+      'table'      => 'cdr',
+      'hashref'    => { 'freesidestatus' => '',
+                        'charged_party'  => $number
+                      },
+      'extra_sql'  => 'FOR UPDATE',
+    } );
+
+  if ( length($default_prefix) ) {
+    push @cdrs,
+      qsearch( {
+        'table'      => 'cdr',
+        'hashref'    => { 'freesidestatus' => '',
+                          'charged_party'  => "$default_prefix$number",
+                        },
+        'extra_sql'  => 'FOR UPDATE',
+      } );
+  }
+
+  @cdrs;
+}
+
 =item pkg_svc
 
 Returns the pkg_svc record for for this service, if applicable.