Virtual field merge
[freeside.git] / FS / FS / svc_acct.pm
index b33f3ae..0ee7a72 100644 (file)
@@ -1,7 +1,7 @@
 package FS::svc_acct;
 
 use strict;
-use vars qw( @ISA $noexport_hack $conf
+use vars qw( @ISA $DEBUG $me $conf
              $dir_prefix @shells $usernamemin
              $usernamemax $passwordmin $passwordmax
              $username_ampersand $username_letter $username_letterfirst
@@ -9,7 +9,7 @@ use vars qw( @ISA $noexport_hack $conf
              $username_uppercase
              $welcome_template $welcome_from $welcome_subject $welcome_mimetype
              $smtpmachine
-             $radius_password
+             $radius_password $radius_ip
              $dirhash
              @saltset @pw_set );
 use Carp;
@@ -32,6 +32,9 @@ use FS::Msgcat qw(gettext);
 
 @ISA = qw( FS::svc_Common );
 
+$DEBUG = 0;
+$me = '[FS::svc_acct]';
+
 #ask FS::UID to run this stuff for us later
 $FS::UID::callback{'FS::svc_acct'} = sub { 
   $conf = new FS::Conf;
@@ -59,9 +62,13 @@ $FS::UID::callback{'FS::svc_acct'} = sub {
     $welcome_mimetype = $conf->config('welcome_email-mimetype') || 'text/plain';
   } else {
     $welcome_template = '';
+    $welcome_from = '';
+    $welcome_subject = '';
+    $welcome_mimetype = '';
   }
   $smtpmachine = $conf->config('smtpmachine');
   $radius_password = $conf->config('radius-password') || 'Password';
+  $radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address';
 };
 
 @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
@@ -183,7 +190,7 @@ sqlradius export only)
 
 (TODOC: L<FS::queue> and L<freeside-queued>)
 
-(TODOC: new exports! $noexport_hack)
+(TODOC: new exports!)
 
 =cut
 
@@ -263,7 +270,11 @@ sub insert {
       #    qsearch('export_svc', { 'exportnum' => $part_export->exportnum });
       #}
 
-      my $nodomain = $exports->{$part_export->exporttype}{'nodomain'};
+      #my $nodomain = $exports->{$part_export->exporttype}{'nodomain'};
+      #silly kludge to avoid uninitialized value errors
+      my $nodomain = exists( $exports->{$part_export->exporttype}{'nodomain'} )
+                     ? $exports->{$part_export->exporttype}{'nodomain'}
+                     : '';
       if ( $nodomain =~ /^Y/i ) {
         $conflict_user_svcpart{$_} = $part_export->exportnum
           foreach @svcparts;
@@ -359,13 +370,13 @@ sub insert {
           'svcnum' => $self->svcnum,
           'job'    => 'FS::svc_acct::send_email'
         };
-        warn "attempting to queue email to $to";
         my $error = $wqueue->insert(
           'to'       => $to,
           'from'     => $welcome_from,
           'subject'  => $welcome_subject,
           'mimetype' => $welcome_mimetype,
           'body'     => $welcome_template->fill_in( HASH => {
+                          'custnum'  => $self->custnum,
                           'username' => $self->username,
                           'password' => $self->_password,
                           'first'    => $cust_main->first,
@@ -375,14 +386,14 @@ sub insert {
         );
         if ( $error ) {
           $dbh->rollback if $oldAutoCommit;
-          return "queuing welcome email: $error";
+          return "error queuing welcome email: $error";
         }
 
         foreach my $jobnum ( @jobnums ) {
           my $error = $wqueue->depend_insert($jobnum);
           if ( $error ) {
             $dbh->rollback if $oldAutoCommit;
-            return "queuing welcome email job dependancy: $error";
+            return "error queuing welcome email job dependancy: $error";
           }
         }
 
@@ -403,7 +414,7 @@ error, otherwise returns false.
 
 The corresponding FS::cust_svc record will be deleted as well.
 
-(TODOC: new exports! $noexport_hack)
+(TODOC: new exports!)
 
 =cut
 
@@ -496,6 +507,7 @@ sqlradius export only)
 sub replace {
   my ( $new, $old ) = ( shift, shift );
   my $error;
+  warn "$me replacing $old with $new\n" if $DEBUG;
 
   return "Username in use"
     if $old->username ne $new->username &&
@@ -522,7 +534,13 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  # redundant, but so $new->usergroup gets set
+  $error = $new->check;
+  return $error if $error;
+
   $old->usergroup( [ $old->radius_groups ] );
+  warn "old groups: ". join(' ',@{$old->usergroup}). "\n" if $DEBUG;
+  warn "new groups: ". join(' ',@{$new->usergroup}). "\n" if $DEBUG;
   if ( $new->usergroup ) {
     #(sorta) false laziness with FS::part_export::sqlradius::_export_replace
     my @newgroups = @{$new->usergroup};
@@ -586,6 +604,8 @@ error, returns the error, otherwise returns false.
 
 Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
 
+Calls any export-specific suspend hooks.
+
 =cut
 
 sub suspend {
@@ -610,6 +630,8 @@ an error, returns the error, otherwise returns false.
 
 Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
 
+Calls any export-specific unsuspend hooks.
+
 =cut
 
 sub unsuspend {
@@ -656,7 +678,8 @@ sub check {
   }
 
   my $error = $self->ut_numbern('svcnum')
-              || $self->ut_number('domsvc')
+              #|| $self->ut_number('domsvc')
+              || $self->ut_foreign_key('domsvc', 'svc_domain', 'svcnum' )
               || $self->ut_textn('sec_phrase')
   ;
   return $error if $error;
@@ -765,12 +788,14 @@ sub check {
   $recref->{quota} = $1;
 
   unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) {
-    unless ( $recref->{slipip} eq '0e0' ) {
+    if ( $recref->{slipip} eq '' ) {
+      $recref->{slipip} = '';
+    } elsif ( $recref->{slipip} eq '0e0' ) {
+      $recref->{slipip} = '0e0';
+    } else {
       $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
         or return "Illegal slipip: ". $self->slipip;
       $recref->{slipip} = $1;
-    } else {
-      $recref->{slipip} = '0e0';
     }
 
   }
@@ -807,7 +832,7 @@ sub check {
            ": ". $recref->{_password};
   }
 
-  ''; #no error
+  $self->SUPER::check;
 }
 
 =item radius
@@ -842,7 +867,7 @@ sub radius_reply {
       ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
     } grep { /^radius_/ && $self->getfield($_) } fields( $self->table );
   if ( $self->slipip && $self->slipip ne '0e0' ) {
-    $reply{'Framed-IP-Address'} = $self->slipip;
+    $reply{$radius_ip} = $self->slipip;
   }
   %reply;
 }
@@ -904,6 +929,8 @@ sub svc_domain {
 
 Returns the FS::cust_svc record for this account (see L<FS::cust_svc>).
 
+=cut
+
 sub cust_svc {
   my $self = shift;
   qsearchs( 'cust_svc', { 'svcnum' => $self->svcnum } );
@@ -944,7 +971,7 @@ external SQL radacct table, specified via sqlradius export.  Sessions which
 started in the specified range but are still open are counted from session
 start to the end of the range (unless they are over 1 day old, in which case
 they are presumed missing their stop record and not counted).  Also, sessions
-which end in therange but started earlier are counted from the start of the
+which end in the range but started earlier are counted from the start of the
 range to session end.  Finally, sessions which start before the range but end
 after are counted for the entire range.
 
@@ -960,6 +987,24 @@ sub seconds_since_sqlradacct {
   $self->cust_svc->seconds_since_sqlradacct(@_);
 }
 
+=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE
+
+Returns the sum of the given attribute for all accounts (see L<FS::svc_acct>)
+in this package for sessions ending between TIMESTAMP_START (inclusive) and
+TIMESTAMP_END (exclusive).
+
+TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
+L<perlfunc/"time">.  Also see L<Time::Local> and L<Date::Parse> for conversion
+functions.
+
+=cut
+
+#note: POD here, implementation in FS::cust_svc
+sub attribute_since_sqlradacct {
+  my $self = shift;
+  $self->cust_svc->attribute_since_sqlradacct(@_);
+}
+
 =item radius_groups
 
 Returns all RADIUS groups for this account (see L<FS::radius_usergroup>).
@@ -986,36 +1031,28 @@ sub radius_groups {
 
 =item send_email
 
+This is the FS::svc_acct job-queue-able version.  It still uses
+FS::Misc::send_email under-the-hood.
+
 =cut
 
 sub send_email {
   my %opt = @_;
 
-  use Date::Format;
-  use Mail::Internet 1.44;
-  use Mail::Header;
+  eval "use FS::Misc qw(send_email)";
+  die $@ if $@;
 
   $opt{mimetype} ||= 'text/plain';
   $opt{mimetype} .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
 
-  $ENV{MAILADDRESS} = $opt{from};
-  my $header = new Mail::Header ( [
-    "From: $opt{from}",
-    "To: $opt{to}",
-    "Sender: $opt{from}",
-    "Reply-To: $opt{from}",
-    "Date: ". time2str("%a, %d %b %Y %X %z", time),
-    "Subject: $opt{subject}",
-    "Content-Type: $opt{mimetype}",
-  ] );
-  my $message = new Mail::Internet (
-    'Header' => $header,
-    'Body' => [ map "$_\n", split("\n", $opt{body}) ],
+  my $error = send_email(
+    'from'         => $opt{from},
+    'to'           => $opt{to},
+    'subject'      => $opt{subject},
+    'content-type' => $opt{mimetype},
+    'body'         => [ map "$_\n", split("\n", $opt{body}) ],
   );
-  $!=0;
-  $message->smtpsend( Host => $smtpmachine )
-    or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-      or die "can't send email to $opt{to} via $smtpmachine with SMTP: $!";
+  die $error if $error;
 }
 
 =item check_and_rebuild_fuzzyfiles