Move expiration alerts into FS::Cron::alert_expiration
authormark <mark>
Wed, 24 Jun 2009 09:07:21 +0000 (09:07 +0000)
committermark <mark>
Wed, 24 Jun 2009 09:07:21 +0000 (09:07 +0000)
FS/FS/Cron/alert_expiration.pm [new file with mode: 0644]
FS/bin/freeside-daily

diff --git a/FS/FS/Cron/alert_expiration.pm b/FS/FS/Cron/alert_expiration.pm
new file mode 100644 (file)
index 0000000..a9b9da9
--- /dev/null
@@ -0,0 +1,177 @@
+package FS::Cron::alert_expiration;
+
+use vars qw( @ISA @EXPORT_OK);
+use Exporter;
+use FS::Record qw(qsearch);
+use FS::Conf;
+use FS::cust_main;
+use FS::Misc;
+use Time::Local;
+use Date::Parse qw(str2time);
+
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( alert_expiration );
+
+my $warning_time = 30 * 24 * 60 * 60;
+my $urgent_time = 15 * 24 * 60 * 60;
+my $panic_time = 5 * 24 * 60 * 60;
+my $window_time = 24 * 60 * 60;
+
+sub alert_expiration {
+  my $conf = new FS::Conf;
+  my $smtpmachine = $conf->config('smtpmachine');
+  
+  my %opt = @_;
+  my ($_date) = $opt{'d'} ? str2time($opt{'d'}) : $^T;
+  $_date += $opt{'y'} * 86400 if $opt{'y'};
+  my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($_date)) [0..5];
+  $mon++;
+
+  my $debug = 0;
+  $debug = 1 if $opt{'v'};
+  $debug = $opt{'l'} if $opt{'l'};
+
+  $FS::cust_main::DEBUG = $debug;
+
+  # Get a list of customers.
+  my %limit;
+  $limit{'agentnum'} = $opt{'a'} if $opt{'a'};
+  $limit{'payby'}    = $opt{'p'} if $opt{'p'};
+
+  my @customers;
+
+  if(my @custnums = @ARGV) {
+    # We're given an explicit list of custnums, so select those.  Then check against 
+    # -a and -p to avoid doing anything unexpected.
+    foreach (@custnums) {
+      my $customer = FS::cust_main->by_key($_);
+      if($customer and (!$opt{'a'} or $customer->agentnum == $opt{'a'})
+                   and (!$opt{'p'} or $customer->payby eq $opt{'p'}) ) {
+        push @customers, $customer;
+      }
+    }
+  }
+  else { # no @ARGV
+    @customers = qsearch('cust_main', \%limit);
+  }
+  return if(!@customers);
+  foreach my $customer (@customers) {
+    my $paydate = $customer->paydate;
+    next if $paydate =~ /^\s*$/; # skip empty expiration dates
+    
+    my $custnum = $customer->custnum;
+    my $first   = $customer->first;
+    my $last    = $customer->last;
+    my $company = $customer->company;
+    my $payby   = $customer->payby;
+    my $payinfo = $customer->payinfo;
+    my $daytime = $customer->daytime;
+    my $night   = $customer->night;
+
+    my ($paymonth, $payyear) = $customer->paydate_monthyear;
+    $paymonth--; # localtime() convention
+    $payday = 1; # This is enforced by FS::cust_main::check.
+    my $expire_time;
+    if($payby eq 'CARD' || $payby eq 'DCRD') {
+      # Credit cards expire at the end of the month/year.
+      if($paymonth == 11) {
+        $payyear++;
+        $paymonth = 0;
+      } else {
+        $paymonth++;
+      }
+      $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear) - 1;
+    }
+    else {
+      $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
+    }
+    
+    if (grep { $expire_time < $_date + $_ &&
+               $expire_time > $_date + $_ - $window_time } 
+               ($warning_time, $urgent_time, $panic_time) ) {
+      my $agentnum = $customer->agentnum;
+      $mail_sender = $conf->config('invoice_from', $agentnum);
+      $failure_recipient = $conf->config('invoice_from', $agentnum) 
+        || 'postmaster';
+      
+      my @alerter_template = $conf->config('alerter_template', $agentnum)
+        or die 'cannot load config file alerter_template';
+
+      my $alerter = new Text::Template(TYPE   => 'ARRAY',
+                                       SOURCE => [ 
+                                         map "$_\n", @alerter_template
+                                         ])
+        or die "can't create Text::Template object: $Text::Template::ERROR";
+
+      $alerter->compile()
+        or die "can't compile template: $Text::Template::ERROR";
+      
+      my @packages = $customer->ncancelled_pkgs;
+      if(@packages) {
+        my @invoicing_list = $customer->invoicing_list;
+        my @to_addrs = grep { $_ ne 'POST' } @invoicing_list;
+        if(@to_addrs) {
+          # Set up template fields.
+          my %fill_in;
+          $fill_in{$_} = $customer->getfield($_) 
+            foreach(qw(first last company));
+          $fill_in{'expdate'} = $expire_time;
+          $fill_in{'company_name'} = $conf->config('company_name', $agentnum);
+          $fill_in{'company_address'} =
+            join("\n",$conf->config('company_address',$agentnum))."\n";
+          if($payby eq 'CARD' || $payby eq 'DCRD') {
+            $fill_in{'payby'} = "credit card (".
+              substr($customer->payinfo, 0, 2) . "xxxxxxxxxx" .
+              substr($payinfo, -4) . ")";
+          }
+          elsif($payby eq 'COMP') {
+            $fill_in{'payby'} = 'complimentary account';
+          }
+          else {
+            $fill_in{'payby'} = 'current method';
+          }
+          # Send it already!
+          my $error = FS::Misc::send_email ( 
+            from    =>  $mail_sender,
+            to      =>  [ @to_addrs ],
+            subject =>  'Billing Arrangement Expiration',
+            body    =>  [ $alerter->fill_in( HASH => \%fill_in ) ],
+          );
+          die "can't send expiration alert: $error"
+            if $error;
+        } 
+        else { # if(@to_addrs)
+          push @{$agent_failure_body{$customer->agentnum}},
+            sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
+              $custnum,
+              $first . " " . $last . "   " . $company,
+              $payby,
+              $paydate,
+              $daytime,
+              $night );
+        }
+      } # if(@packages)
+    } # if(expired)
+  } # foreach(@customers)
+
+  # Failure notification
+  foreach my $agentnum (keys %agent_failure_body) {
+    $mail_sender = $conf->config('invoice_from', $agentnum)
+      if($conf->exists('invoice_from', $agentnum));
+    $failure_recipient = $conf->config('invoice_from', $agentnum)
+      if($conf->exists('invoice_from', $agentnum));
+    my $error = FS::Misc::send_email (
+      from    =>  $mail_sender,
+      to      =>  $failure_recipient,
+      subject =>  'Unnotified Billing Arrangement Expirations',
+      body    =>  [ @{$agent_failure_body{$agentnum}} ],
+      );
+    die "can't send alerter failure email to $failure_recipient: $error"
+      if $error;
+  }
+
+}
+
+1;
index bda42f0..acf7950 100755 (executable)
@@ -14,6 +14,10 @@ adminsuidsetup $user;
 use FS::Cron::bill qw(bill);
 bill(%opt);
 
+# Send alerts about upcoming credit card expiration.
+use FS::Cron::alert_expiration qw(alert_expiration);
+alert_expiration(%opt);
+
 #what to do about the below when using -m?  that is the question.
 
 #you don't want to skip this, besides, it should be cheap