rest of per-agent config for company_name, company_address, logo, etc.. RT#3989
[freeside.git] / FS / bin / freeside-expiration-alerter
1 #!/usr/bin/perl -Tw
2
3 use strict;
4 use Date::Format;
5 use Time::Local;
6 use Text::Template;
7 use Getopt::Std;
8 use Net::SMTP;
9 use Mail::Header;
10 use Mail::Internet;
11 use FS::Conf;
12 use FS::UID qw(adminsuidsetup);
13 use FS::Record qw(qsearch);
14 use FS::cust_main;
15
16 use vars qw($smtpmachine %agent_failure_body);
17
18 #hush, perl!
19 $FS::alerter::_template::first = "";
20 $FS::alerter::_template::last = "";
21 $FS::alerter::_template::company = "";
22 $FS::alerter::_template::payby = "";
23 $FS::alerter::_template::expdate = "";
24
25 # Set the mail program  and other variables
26 my $mail_sender = "billing\@mydomain.tld";  # or invoice_from if available
27 my $failure_recipient = "postmaster";       # or invoice_from if available
28 my $warning_time = 30 * 24 * 60 * 60;
29 my $urgent_time = 15 * 24 * 60 * 60;
30 my $panic_time = 5 * 24 * 60 * 60;
31 my $window_time = 24 * 60 * 60;
32
33 &untaint_argv;  #what it sounds like  (eww)
34
35 #we're at now now (and later).
36 my($_date)= $^T;
37
38 # Get the current month
39 my ($sec,$min,$hour,$mday,$mon,$year) =
40         (localtime($_date) )[0,1,2,3,4,5]; 
41 $mon++;
42
43 # Login to the database
44 my $user = shift or die &usage;
45 adminsuidsetup $user;
46
47 # Get the needed configuration files
48 my $conf = new FS::Conf;
49 $smtpmachine = $conf->config('smtpmachine');
50
51 my(@customers)=qsearch('cust_main',{});
52 if (scalar(@customers) == 0)
53 {
54   exit 1;
55 }
56
57 # Now I can start looping
58 foreach my $customer (@customers)
59 {
60   my $paydate = $customer->getfield('paydate');
61   next if $paydate =~ /^\s*$/; #skip empty expiration dates
62
63   my $custnum = $customer->getfield('custnum');
64   my $first = $customer->getfield('first');
65   my $last = $customer->getfield('last');
66   my $company = $customer->getfield('company');
67   my $payby = $customer->getfield('payby');
68   my $payinfo = $customer->getfield('payinfo');
69   my $daytime = $customer->getfield('daytime');
70   my $night = $customer->getfield('night');
71
72   my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
73
74   my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
75
76   #credit cards expire at the end of the month/year of their exp date
77   if ($payby eq 'CARD' || $payby eq 'DCRD') {
78     ($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
79     $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
80     $expire_time--;
81   }
82
83   if (($expire_time < $_date + $warning_time &&
84     $expire_time > $_date + $warning_time - $window_time) ||
85       ($expire_time < $_date + $urgent_time &&
86        $expire_time > $_date + $urgent_time - $window_time) ||
87       ($expire_time < $_date + $panic_time &&
88        $expire_time > $_date + $panic_time - $window_time)) {
89
90     # Prepare for sending email, now inside the customer loop so i can be agent
91     # virtualized
92
93     my $agentnum = $customer->agentnum;
94
95     $mail_sender = $conf->config('invoice_from', $agentnum )
96       if $conf->exists('invoice_from', $agentnum);
97     $failure_recipient = $conf->config('invoice_from', $agentnum)
98       if $conf->exists('invoice_from', $agentnum);
99
100     $ENV{MAILADDRESS} = $mail_sender;
101
102     my @alerter_template = $conf->config('alerter_template', $agentnum)
103       or die "cannot load config file alerter_template";
104
105     my $alerter = new Text::Template TYPE   => 'ARRAY',
106                                      SOURCE => [ map "$_\n", @alerter_template ]
107       or die "can't create new Text::Template object: $Text::Template::ERROR";
108
109     $alerter->compile() or die "can't compile template: $Text::Template::ERROR";
110
111     my @packages = $customer->ncancelled_pkgs;
112     if (scalar(@packages) != 0) {
113       my @invoicing_list = $customer->invoicing_list;
114       if ( grep { $_ ne 'POST' } @invoicing_list ) { 
115         my $header = new Mail::Header ( [
116           "From: $mail_sender",
117           "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
118           "Sender: $mail_sender",
119           "Reply-To: $mail_sender",
120           "Date: ". time2str("%a, %d %b %Y %X %z", time),
121           "Subject: Billing Arrangement Expiration",
122         ] );
123         $FS::alerter::_template::first = $first;
124         $FS::alerter::_template::last = $last;
125         $FS::alerter::_template::company = $company;
126         if ($payby eq 'CARD' || $payby eq 'DCRD') {
127           $FS::alerter::_template::payby = "credit card (" .
128             substr($payinfo, 0, 2) . "xxxxxxxxxx" .
129             substr($payinfo, -4) . ")";
130         }elsif ($payby eq 'COMP') {
131           $FS::alerter::_template::payby = "complimentary account";
132         }else{
133           $FS::alerter::_template::payby = "current method";
134         }
135         $FS::alerter::_template::expdate = $expire_time;
136
137         $FS::alerter::_template::company_name =
138           $conf->config('company_name', $agentnum);
139         $FS::alerter::_template::company_address =
140           join("\n", $conf->config('company_address', $agentnum) ). "\n";
141
142         my $message = new Mail::Internet (
143           'Header' => $header,
144           'Body' => [ $alerter->fill_in( PACKAGE => 'FS::alerter::_template' ) ],
145         );
146         $!=0;
147         $message->smtpsend( Host => $smtpmachine )
148           or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
149             or die "Can't send expiration email: $!";
150
151       } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) { 
152         push @{$agent_failure_body{$customer->agentnum}},
153           sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
154             $custnum,
155             $first . " " . $last . "   " . $company,
156             $payby,
157             $paydate,
158             $daytime,
159             $night
160           );
161       }
162     }
163   }
164 }
165
166 # Now I need to send failure EMAIL
167
168 foreach my $agentnum ( keys %agent_failure_body ) {
169
170   $mail_sender = $conf->config('invoice_from', $agentnum )
171     if $conf->exists('invoice_from', $agentnum);
172   $failure_recipient = $conf->config('invoice_from', $agentnum)
173     if $conf->exists('invoice_from', $agentnum);
174
175   $ENV{MAILADDRESS} = $mail_sender;
176   my $header = new Mail::Header ( [
177     "From: Account Processor",
178     "To: $failure_recipient",
179     "Sender: $mail_sender",
180     "Reply-To: $mail_sender",
181     "Subject: Unnotified Billing Arrangement Expirations",
182   ] );
183
184   my $message = new Mail::Internet (
185     'Header' => $header,
186     'Body' => [ @{$agent_failure_body{$agentnum}} ],
187   );
188   $!=0;
189   $message->smtpsend( Host => $smtpmachine )
190     or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
191       or die "can't send alerter failure email to $failure_recipient".
192              " via server $smtpmachine with SMTP: $!";
193 }
194
195 # subroutines
196 sub untaint_argv {
197   foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
198     $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal argument \"$ARGV[$_]\"";
199     $ARGV[$_]=$1;
200   }
201 }
202
203 sub usage {
204   die "Usage:\n\n  freeside-expiration-alerter user\n";
205 }
206
207 =head1 NAME
208
209 freeside-expiration-alerter - Emails notifications of credit card expirations.
210
211 =head1 SYNOPSIS
212
213   freeside-expiration-alerter user
214
215 =head1 DESCRIPTION
216
217 Emails customers notice that their credit card or other billing arrangement
218 is about to expire.  Usually run as a cron job.
219
220 user: From the mapsecrets file - see config.html from the base documentation
221
222 =head1 BUGS
223
224 Yes..... Use at your own risk. No guarantees or warrantees of any
225 kind apply to this program. Parts of this program are hacked from
226 other GNU licensed software created mainly by Ivan Kohler.
227
228 This is released under the GNU Public License. See www.gnu.org
229 for more information regarding this license.
230
231 =head1 SEE ALSO
232
233 L<FS::cust_main>, config.html from the base documentation
234
235 =head1 AUTHOR
236
237 Jeff Finucane <jeff@cmh.net>
238
239 =cut
240
241