fix up forward editing for new svc_forward.src field
[freeside.git] / FS / FS / cust_svc.pm
index 50d9445..0d8a121 100644 (file)
@@ -1,7 +1,7 @@
 package FS::cust_svc;
 
 use strict;
-use vars qw( @ISA );
+use vars qw( @ISA $ignore_quantity );
 use Carp qw( cluck );
 use FS::Record qw( qsearch qsearchs dbh );
 use FS::cust_pkg;
@@ -11,11 +11,14 @@ use FS::pkg_svc;
 use FS::svc_acct;
 use FS::svc_domain;
 use FS::svc_forward;
+use FS::svc_broadband;
 use FS::domain_record;
 use FS::part_export;
 
 @ISA = qw( FS::Record );
 
+$ignore_quantity = 0;
+
 sub _cache {
   my $self = shift;
   my ( $hashref, $cache ) = @_;
@@ -220,6 +223,7 @@ sub check {
     # or new FS::pkg_svc ( { 'pkgpart'  => $cust_pkg->pkgpart,
     #                        'svcpart'  => $self->svcpart,
     #                        'quantity' => 0                   } );
+    my $quantity = $pkg_svc ? $pkg_svc->quantity : 0;
 
     my @cust_svc = qsearch('cust_svc', {
       'pkgnum'  => $self->pkgnum,
@@ -227,10 +231,10 @@ sub check {
     });
     return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
            " services for pkgnum ". $self->pkgnum
-      if scalar(@cust_svc) >= $pkg_svc->quantity;
+      if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item part_svc
@@ -277,10 +281,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;
@@ -289,9 +298,11 @@ sub label {
     $tag = $svc_x->getfield('domain');
   } elsif ( $svcdb eq 'svc_www' ) {
     my $domain = qsearchs( 'domain_record', { 'recnum' => $svc_x->recnum } );
-    $tag = $domain->reczone;
+    $tag = $domain->zone;
   } elsif ( $svcdb eq 'svc_broadband' ) {
-    $tag = $svc_x->ip_addr . '/' . $svc_x->ip_netmask;
+    $tag = $svc_x->ip_addr;
+  } elsif ( $svcdb eq 'svc_external' ) {
+    $tag = $svc_x->id. ': '. $svc_x->title;
   } else {
     cluck "warning: asked for label of unsupported svcdb; using svcnum";
     $tag = $svc_x->getfield('svcnum');
@@ -337,11 +348,11 @@ sub seconds_since {
   $sth->fetchrow_arrayref->[0];
 }
 
-=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END 
+=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END
 
 See L<FS::svc_acct/seconds_since_sqlradacct>.  Equivalent to
-$cust_svc->svc_x->seconds_since, but more efficient.  Meaningless for records
-where B<svcdb> is not "svc_acct".
+$cust_svc->svc_x->seconds_since_sqlradacct, but more efficient.  Meaningless
+for records where B<svcdb> is not "svc_acct".
 
 =cut
 
@@ -349,19 +360,24 @@ 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' ) {
@@ -373,6 +389,17 @@ sub seconds_since_sqlradacct {
            "; guessing how to convert to UNIX timestamps";
       $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
     my $sth = $dbh->prepare("SELECT SUM(acctsessiontime)
@@ -380,23 +407,24 @@ sub seconds_since_sqlradacct {
                                WHERE UserName = ?
                                  AND $str2time AcctStartTime) >= ?
                                  AND $str2time AcctStopTime ) <  ?
-                                 AND AcctStopTime =! 0
+                                 AND $str2time AcctStopTime ) > 0
                                  AND AcctStopTime IS NOT NULL"
     ) or die $dbh->errstr;
     $sth->execute($username, $start, $end) or die $sth->errstr;
     my $regular = $sth->fetchrow_arrayref->[0];
   
     #find open sessions which start in the range, count session start->range end
-    # don't count them if they are over 1 day old (probably missing stop record)
-    $sth = $dbh->prepare("SELECT SUM( ? - $str2time AcctStartTime ) )
-                            FROM radacct
-                            WHERE UserName = ?
-                              AND $str2time AcctStartTime ) >= ?
-                              AND ( ? - $str2time AcctStartTime ) < 86400
-                              AND (    AcctStopTime = 0
-                                    OR AcctStopTime IS NULL )"
-    ) or die $dbh->errstr;
-    $sth->execute($end, $username, $start, $end) or die $sth->errstr;
+    $query = "SELECT SUM( ? - $str2time AcctStartTime ) )
+                FROM radacct
+                WHERE UserName = ?
+                  AND $str2time AcctStartTime ) >= ?
+                  AND $str2time AcctStartTime ) <  ?
+                  AND ( ? - $str2time AcctStartTime ) ) < 86400
+                  AND (    $str2time AcctStopTime ) = 0
+                                    OR AcctStopTime IS NULL )";
+    $sth = $dbh->prepare($query) or die $dbh->errstr;
+    $sth->execute($end, $username, $start, $end, $end)
+      or die $sth->errstr. " executing query $query";
     my $start_during = $sth->fetchrow_arrayref->[0];
   
     #find closed sessions which start before the range but stop during,
@@ -407,27 +435,27 @@ sub seconds_since_sqlradacct {
                               AND $str2time AcctStartTime ) < ?
                               AND $str2time AcctStopTime  ) >= ?
                               AND $str2time AcctStopTime  ) <  ?
-                              AND AcctStopTime != 0
+                              AND $str2time AcctStopTime ) > 0
                               AND AcctStopTime IS NOT NULL"
     ) or die $dbh->errstr;
     $sth->execute($start, $username, $start, $start, $end ) or die $sth->errstr;
     my $end_during = $sth->fetchrow_arrayref->[0];
   
     #find closed (not anymore - or open) sessions which start before the range
-    # but stop after, or are still open, count range start->range end
+    # but stop after, or are still open, count range start->range end
     # don't count open sessions (probably missing stop record)
     $sth = $dbh->prepare("SELECT COUNT(*)
                             FROM radacct
                             WHERE UserName = ?
                               AND $str2time AcctStartTime ) < ?
                               AND ( $str2time AcctStopTime ) >= ?
-                                                            )"
+                                                                  )"
                               #      OR AcctStopTime =  0
-                              #      OR AcctStopTime IS NULL )"
+                              #      OR AcctStopTime IS NULL       )"
     ) or die $dbh->errstr;
     $sth->execute($username, $start, $end ) or die $sth->errstr;
     my $entire_range = ($end-$start) * $sth->fetchrow_arrayref->[0];
-  
+
     $seconds += $regular + $end_during + $start_during + $entire_range;
 
   }
@@ -436,11 +464,133 @@ sub seconds_since_sqlradacct {
 
 }
 
-=back
+=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE
+
+See L<FS::svc_acct/attribute_since_sqlradacct>.  Equivalent to
+$cust_svc->svc_x->attribute_since_sqlradacct, but more efficient.  Meaningless
+for records where B<svcdb> is not "svc_acct".
+
+=cut
+
+#note: implementation here, POD in FS::svc_acct
+#(false laziness w/seconds_since_sqlradacct above)
+sub attribute_since_sqlradacct {
+  my($self, $start, $end, $attrib) = @_;
+
+  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"
+    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' ) {
+      $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 $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;
+    }
 
-=head1 VERSION
+    my $sth = $dbh->prepare("SELECT SUM($attrib)
+                               FROM radacct
+                               WHERE UserName = ?
+                                 AND $str2time AcctStopTime ) >= ?
+                                 AND $str2time AcctStopTime ) <  ?
+                                 AND AcctStopTime IS NOT NULL"
+    ) or die $dbh->errstr;
+    $sth->execute($username, $start, $end) or die $sth->errstr;
+
+    $sum += $sth->fetchrow_arrayref->[0];
+
+  }
 
-$Id: cust_svc.pm,v 1.19 2002-10-17 14:16:17 ivan Exp $
+  $sum;
+
+}
+
+=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) = @_;
+
+  my $username = $self->svc_x->username;
+
+  my @part_export = $self->part_svc->part_export('sqlradius')
+    or die "no sqlradius export configured for this service type";
+    #or return undef;
+                     
+  my @sessions = ();
+
+  foreach my $part_export ( @part_export ) {
+                                            
+    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' ) {
+      $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 @fields = qw( acctstarttime acctstoptime acctsessiontime
+                     acctinputoctets acctoutputoctets framedipaddress );
+     
+    my $sth = $dbh->prepare('SELECT '. join(', ', @fields).
+                            "  FROM radacct
+                               WHERE UserName = ?
+                                 AND $str2time AcctStopTime ) >= ?
+                                 AND $str2time AcctStopTime ) <=  ?
+                                 ORDER BY AcctStartTime DESC
+    ") or die $dbh->errstr;                                 
+    $sth->execute($username, $start, $end) or die $sth->errstr;
+
+    push @sessions, map { { %$_ } } @{ $sth->fetchall_arrayref({}) };
+
+  }
+  \@sessions
+
+}
+
+=back
 
 =head1 BUGS