pass correct arguments to msg_template::prepare, #24659
[freeside.git] / FS / FS / Cron / alert_expiration.pm
1 package FS::Cron::alert_expiration;
2
3 use vars qw( @ISA @EXPORT_OK);
4 use Exporter;
5 use FS::Record qw(qsearch qsearchs);
6 use FS::Conf;
7 use FS::cust_main;
8 use FS::Misc;
9 use Time::Local;
10 use Date::Parse qw(str2time);
11
12
13 @ISA = qw( Exporter );
14 @EXPORT_OK = qw( alert_expiration );
15
16 my $warning_time = 30 * 24 * 60 * 60;
17 my $urgent_time = 15 * 24 * 60 * 60;
18 my $panic_time = 5 * 24 * 60 * 60;
19 my $window_time = 24 * 60 * 60;
20
21 sub alert_expiration {
22   my $conf = new FS::Conf;
23   my $smtpmachine = $conf->config('smtpmachine');
24   
25   my %opt = @_;
26   my ($_date) = $opt{'d'} ? str2time($opt{'d'}) : $^T;
27   $_date += $opt{'y'} * 86400 if $opt{'y'};
28   my ($sec, $min, $hour, $mday, $mon, $year) = (localtime($_date)) [0..5];
29   $mon++;
30
31   my $debug = 0;
32   $debug = 1 if $opt{'v'};
33   $debug = $opt{'l'} if $opt{'l'};
34
35   $FS::cust_main::DEBUG = $debug;
36
37   # Get a list of customers.
38  
39   my %limit;
40   $limit{'agentnum'} = $opt{'a'} if $opt{'a'};
41   $limit{'payby'}    = $opt{'p'} if $opt{'p'};
42
43   my @customers;
44
45   if(my @custnums = @ARGV) {
46     # We're given an explicit list of custnums, so select those.  Then check against 
47     # -a and -p to avoid doing anything unexpected.
48     foreach (@custnums) {
49       my $customer = FS::cust_main->by_key($_);
50       if($customer and (!$opt{'a'} or $customer->agentnum == $opt{'a'})
51                    and (!$opt{'p'} or $customer->payby eq $opt{'p'}) ) {
52         push @customers, $customer;
53       }
54     }
55   }
56   else { # no @ARGV
57     @customers = qsearch('cust_main', \%limit);
58   }
59   return if(!@customers);
60   foreach my $customer (@customers) {
61     next if !($customer->ncancelled_pkgs); # skip inactive customers
62     my $paydate = $customer->paydate;
63     next if $paydate =~ /^\s*$/; # skip empty expiration dates
64     
65     my $custnum = $customer->custnum;
66     my $first   = $customer->first;
67     my $last    = $customer->last;
68     my $company = $customer->company;
69     my $payby   = $customer->payby;
70     my $payinfo = $customer->payinfo;
71     my $daytime = $customer->daytime;
72     my $night   = $customer->night;
73
74     my ($paymonth, $payyear) = $customer->paydate_monthyear;
75     $paymonth--; # localtime() convention
76     $payday = 1; # This is enforced by FS::cust_main::check.
77     my $expire_time;
78     if($payby eq 'CARD' || $payby eq 'DCRD') {
79       # Credit cards expire at the end of the month/year.
80       if($paymonth == 11) {
81         $payyear++;
82         $paymonth = 0;
83       } else {
84         $paymonth++;
85       }
86       $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear) - 1;
87     }
88     else {
89       $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
90     }
91     
92     if (grep { $expire_time < $_date + $_ &&
93                $expire_time > $_date + $_ - $window_time } 
94                ($warning_time, $urgent_time, $panic_time) ) {
95       # Send an expiration notice.
96       my $agentnum = $customer->agentnum;
97       my $error = '';
98
99       my $msgnum = $conf->config('alerter_msgnum', $agentnum);
100       if ( $msgnum ) { # new hotness
101         my $msg_template = qsearchs('msg_template', { msgnum => $msgnum } );
102         $customer->setfield('expdate', $expire_time);
103         $error = $msg_template->send('cust_main' => $customer,
104                                      'object'    => $customer);
105       }
106       else { #!$msgnum, the hard way
107         $mail_sender = $conf->config('invoice_from', $agentnum);
108         $failure_recipient = $conf->config('invoice_from', $agentnum) 
109           || 'postmaster';
110        
111         my @alerter_template = $conf->config('alerter_template', $agentnum)
112           or die 'cannot load config file alerter_template';
113
114         my $alerter = new Text::Template(TYPE   => 'ARRAY',
115                                          SOURCE => [ 
116                                            map "$_\n", @alerter_template
117                                            ])
118           or die "can't create Text::Template object: $Text::Template::ERROR";
119
120         $alerter->compile()
121           or die "can't compile template: $Text::Template::ERROR";
122         
123         my @invoicing_list = $customer->invoicing_list;
124         my @to_addrs = grep { $_ ne 'POST' } @invoicing_list;
125         if(@to_addrs) {
126           # Set up template fields.
127           my %fill_in;
128           $fill_in{$_} = $customer->getfield($_) 
129             foreach(qw(first last company));
130           $fill_in{'expdate'} = $expire_time;
131           $fill_in{'company_name'} = $conf->config('company_name', $agentnum);
132           $fill_in{'company_address'} =
133             join("\n",$conf->config('company_address',$agentnum))."\n";
134           if($payby eq 'CARD' || $payby eq 'DCRD') {
135             $fill_in{'payby'} = "credit card (".
136               substr($customer->payinfo, 0, 2) . "xxxxxxxxxx" .
137               substr($payinfo, -4) . ")";
138           }
139           elsif($payby eq 'COMP') {
140             $fill_in{'payby'} = 'complimentary account';
141           }
142           else {
143             $fill_in{'payby'} = 'current method';
144           }
145           # Send it already!
146           $error = FS::Misc::send_email ( 
147             from    =>  $mail_sender,
148             to      =>  [ @to_addrs ],
149             subject =>  'Billing Arrangement Expiration',
150             body    =>  [ $alerter->fill_in( HASH => \%fill_in ) ],
151           );
152       } 
153       else { # if(@to_addrs)
154         push @{$agent_failure_body{$customer->agentnum}},
155           sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
156             $custnum,
157             $first . " " . $last . "   " . $company,
158             $payby,
159             $paydate,
160             $daytime,
161             $night );
162       }
163     } # if($msgnum)
164     
165 # should we die here rather than report failure as below?
166     die "can't send expiration alert: $error"
167       if $error;
168     
169     } # if(expired)
170   } # foreach(@customers)
171
172   # Failure notification
173   foreach my $agentnum (keys %agent_failure_body) {
174     $mail_sender = $conf->config('invoice_from', $agentnum)
175       if($conf->exists('invoice_from', $agentnum));
176     $failure_recipient = $conf->config('invoice_from', $agentnum)
177       if($conf->exists('invoice_from', $agentnum));
178     my $error = FS::Misc::send_email (
179       from    =>  $mail_sender,
180       to      =>  $failure_recipient,
181       subject =>  'Unnotified Billing Arrangement Expirations',
182       body    =>  [ @{$agent_failure_body{$agentnum}} ],
183       );
184     die "can't send alerter failure email to $failure_recipient: $error"
185       if $error;
186   }
187
188 }
189
190 1;