for new installs, centralize some stuff that was spread around different config files.
[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 @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 $mail_sender = $conf->config('invoice_from')
51   if $conf->exists('invoice_from');
52 $failure_recipient = $conf->config('invoice_from')
53   if $conf->exists('invoice_from');
54
55
56 my(@customers)=qsearch('cust_main',{});
57 if (scalar(@customers) == 0)
58 {
59   exit 1;
60 }
61
62 # Prepare for sending email
63
64 $ENV{MAILADDRESS} = $mail_sender;
65 my $header = new Mail::Header ( [
66   "From: Account Processor",
67   "To: $failure_recipient",
68   "Sender: $mail_sender",
69   "Reply-To: $mail_sender",
70   "Subject: Unnotified Billing Arrangement Expirations",
71 ] );
72
73 my @alerter_template = $conf->config('alerter_template')
74   or die "cannot load config file alerter_template";
75
76 my $alerter = new Text::Template (TYPE => 'ARRAY', SOURCE => [ map "$_\n", @alerter_template ])
77   or die "can't create new Text::Template object:  Text::Template::ERROR";
78 $alerter->compile() or die "can't compile template:  Text::Template::ERROR";
79
80 # Now I can start looping
81 foreach my $customer (@customers)
82 {
83   my $paydate = $customer->getfield('paydate');
84   next if $paydate =~ /^\s*$/; #skip empty expiration dates
85
86   my $custnum = $customer->getfield('custnum');
87   my $first = $customer->getfield('first');
88   my $last = $customer->getfield('last');
89   my $company = $customer->getfield('company');
90   my $payby = $customer->getfield('payby');
91   my $payinfo = $customer->getfield('payinfo');
92   my $daytime = $customer->getfield('daytime');
93   my $night = $customer->getfield('night');
94
95   my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
96
97   my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
98
99   #credit cards expire at the end of the month/year of their exp date
100   if ($payby eq 'CARD' || $payby eq 'DCRD') {
101     ($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
102     $expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
103     $expire_time--;
104   }
105
106   if (($expire_time < $_date + $warning_time &&
107     $expire_time > $_date + $warning_time - $window_time) ||
108       ($expire_time < $_date + $urgent_time &&
109        $expire_time > $_date + $urgent_time - $window_time) ||
110       ($expire_time < $_date + $panic_time &&
111        $expire_time > $_date + $panic_time - $window_time)) {
112
113
114
115     my @packages = $customer->ncancelled_pkgs;
116     if (scalar(@packages) != 0) {
117       my @invoicing_list = $customer->invoicing_list;
118       if ( grep { $_ ne 'POST' } @invoicing_list ) { 
119         my $header = new Mail::Header ( [
120           "From: $mail_sender",
121           "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
122           "Sender: $mail_sender",
123           "Reply-To: $mail_sender",
124           "Date: ". time2str("%a, %d %b %Y %X %z", time),
125           "Subject: Billing Arrangement Expiration",
126         ] );
127         $FS::alerter::_template::first = $first;
128         $FS::alerter::_template::last = $last;
129         $FS::alerter::_template::company = $company;
130         if ($payby eq 'CARD' || $payby eq 'DCRD') {
131           $FS::alerter::_template::payby = "credit card (" .
132             substr($payinfo, 0, 2) . "xxxxxxxxxx" .
133             substr($payinfo, -4) . ")";
134         }elsif ($payby eq 'COMP') {
135           $FS::alerter::_template::payby = "complimentary account";
136         }else{
137           $FS::alerter::_template::payby = "current method";
138         }
139         $FS::alerter::_template::expdate = $expire_time;
140
141         $FS::alerter::_template::company_name = $conf->config('company_name');
142         $FS::alerter::_template::company_address =
143           join("\n", $conf->config('company_address') ). "\n";
144
145         my $message = new Mail::Internet (
146           'Header' => $header,
147           'Body' => [ $alerter->fill_in( PACKAGE => 'FS::alerter::_template' ) ],
148         );
149         $!=0;
150         $message->smtpsend( Host => $smtpmachine )
151           or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
152             or die "Can't send expiration email: $!";
153
154       } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) { 
155         push @body, 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     }
164   }
165 }
166
167 # Now I need to send EMAIL
168 if (scalar(@body)) {
169   my $message = new Mail::Internet (
170     'Header' => $header,
171     'Body' => [ (@body) ],
172   );
173   $!=0;
174   $message->smtpsend( Host => $smtpmachine )
175     or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
176       or die "can't send alerter failure email to $failure_recipient".
177              " via server $smtpmachine with SMTP: $!";
178 }
179
180 # subroutines
181 sub untaint_argv {
182   foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
183     $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal argument \"$ARGV[$_]\"";
184     $ARGV[$_]=$1;
185   }
186 }
187
188 sub usage {
189   die "Usage:\n\n  freeside-expiration-alerter user\n";
190 }
191
192 =head1 NAME
193
194 freeside-expiration-alerter - Emails notifications of credit card expirations.
195
196 =head1 SYNOPSIS
197
198   freeside-expiration-alerter user
199
200 =head1 DESCRIPTION
201
202 Emails customers notice that their credit card or other billing arrangement
203 is about to expire.  Usually run as a cron job.
204
205 user: From the mapsecrets file - see config.html from the base documentation
206
207 =head1 BUGS
208
209 Yes..... Use at your own risk. No guarantees or warrantees of any
210 kind apply to this program. Parts of this program are hacked from
211 other GNU licensed software created mainly by Ivan Kohler.
212
213 This is released under the GNU Public License. See www.gnu.org
214 for more information regarding this license.
215
216 =head1 SEE ALSO
217
218 L<FS::cust_main>, config.html from the base documentation
219
220 =head1 AUTHOR
221
222 Jeff Finucane <jeff@cmh.net>
223
224 =cut
225
226