12 use FS::UID qw(adminsuidsetup);
13 use FS::Record qw(qsearch);
16 use vars qw($smtpmachine @body);
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 = "";
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;
33 &untaint_argv; #what it sounds like (eww)
35 #we're at now now (and later).
38 # Get the current month
39 my ($sec,$min,$hour,$mday,$mon,$year) =
40 (localtime($_date) )[0,1,2,3,4,5];
43 # Login to the database
44 my $user = shift or die &usage;
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');
56 my(@customers)=qsearch('cust_main',{});
57 if (scalar(@customers) == 0)
62 # Prepare for sending email
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",
73 my @alerter_template = $conf->config('alerter_template')
74 or die "cannot load config file alerter_template";
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";
80 # Now I can start looping
81 foreach my $customer (@customers)
83 my $paydate = $customer->getfield('paydate');
84 next if $paydate =~ /^\s*$/; #skip empty expiration dates
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');
95 my ($payyear,$paymonth,$payday) = split (/-/,$paydate);
97 my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
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);
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)) {
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",
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";
137 $FS::alerter::_template::payby = "current method";
139 $FS::alerter::_template::expdate = $expire_time;
141 $FS::alerter::_template::company_name = $conf->config('company_name');
142 $FS::alerter::_template::company_address =
143 join("\n", $conf->config('company_address') ). "\n";
145 my $message = new Mail::Internet (
147 'Body' => [ $alerter->fill_in( PACKAGE => 'FS::alerter::_template' ) ],
150 $message->smtpsend( Host => $smtpmachine )
151 or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
152 or die "Can't send expiration email: $!";
154 } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) {
155 push @body, sprintf(qq{%5d %-32.32s %4s %10s %12s %12s},
157 $first . " " . $last . " " . $company,
167 # Now I need to send EMAIL
169 my $message = new Mail::Internet (
171 'Body' => [ (@body) ],
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: $!";
182 foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
183 $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal argument \"$ARGV[$_]\"";
189 die "Usage:\n\n freeside-expiration-alerter user\n";
194 freeside-expiration-alerter - Emails notifications of credit card expirations.
198 freeside-expiration-alerter user
202 Emails customers notice that their credit card or other billing arrangement
203 is about to expire. Usually run as a cron job.
205 user: From the mapsecrets file - see config.html from the base documentation
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.
213 This is released under the GNU Public License. See www.gnu.org
214 for more information regarding this license.
218 L<FS::cust_main>, config.html from the base documentation
222 Jeff Finucane <jeff@cmh.net>