db99e62b405167cea523a924a49e30edabd2eef8
[freeside.git] / FS / bin / freeside-overdue
1 #!/usr/bin/perl -w
2
3 use strict;
4 use vars qw( $days_to_pay $cust_main $cust_pkg 
5              $cust_svc $svc_acct );
6 use Getopt::Std;
7 use FS::cust_main;
8 use FS::cust_pkg;
9 use FS::cust_svc;
10 use FS::svc_acct;
11 use FS::Record qw(qsearch qsearchs);
12 use FS::UID qw(adminsuidsetup);
13
14 &untaint_argv;
15 my %opt;
16 getopts('ed:qpl:scbyoi', \%opt);
17 my $user = shift or die &usage;
18
19 adminsuidsetup $user;
20
21 my $now = time; #eventually take a time option like freeside-bill
22 my ($sec,$min,$hour,$mday,$mon,$year) =
23   (localtime($now) )[0,1,2,3,4,5];
24 $mon++;
25 $year += 1900;
26
27 foreach $cust_main ( qsearch('cust_main',{} ) ) {
28
29   my ( $eyear, $emon, $eday ) = ( 2037, 12, 31 );
30   if ( $cust_main->paydate =~ /^(\d{4})\-(\d{1,2})\-(\d{1,2})$/
31        && $cust_main->payby eq 'BILL') {
32     ( $eyear, $emon, $eday ) = ( $1, $2, $3 );
33   }
34
35   if ( ( $opt{d}
36            && $cust_main->balance_date(time - $opt{d} * 86400) > 0
37            && qsearchs( 'cust_pkg', { 'custnum' => $cust_main->custnum,
38                                       'susp' => "" } ) )
39        || ( $opt{e}
40             && $cust_main->payby eq 'BILL'
41             && ( $eyear < $year
42                  || ( $eyear == $year && $emon < $mon ) ) )
43   ) { 
44
45     unless ( $opt{q} ) {
46       print $cust_main->custnum, "\t",
47             $cust_main->last, "\t", $cust_main->first, "\t",
48             $cust_main->balance_date(time-$opt{d} * 86400);
49     }
50
51     if ( $opt{p} && ! grep { $_ eq 'POST' } $cust_main->invoicing_list ) {
52       print "\n\tAdding postal invoicing" unless $opt{q};
53       my @invoicing_list = $cust_main->invoicing_list;
54       push @invoicing_list, 'POST';
55       $cust_main->invoicing_list(\@invoicing_list);
56     }
57
58     if ( $opt{l} ) {
59       print "\n\tCharging late fee of \$$opt{l}" unless $opt{q};
60       my $error = $cust_main->charge($opt{l}, 'Late fee');
61       # comment or plandata with info so we don't redo the same late fee every
62       # day
63     }
64
65     foreach $cust_pkg ( qsearch( 'cust_pkg', 
66                                  { 'custnum' => $cust_main->custnum } ) ) {
67
68       if ($opt{s}) {
69         print "\n\tSuspending pkgnum " . $cust_pkg->pkgnum unless $opt{q};
70         $cust_pkg->suspend;
71       }
72
73       if ($opt{c}) {
74         print "\n\tCancelling pkgnum " . $cust_pkg->pkgnum unless $opt{q};
75         $cust_pkg->cancel;
76       }
77       
78     }
79
80     if ( $opt{b} ) {
81       print "\n\tBilling" unless $opt{q};
82       my $error = $cust_main->bill('time'=>$now);
83       warn "Error billing,  customer #" . $cust_main->custnum . 
84         ":" . $error if $error;
85     }
86
87     if ( $opt{y} ) {
88       print "\n\tApplying outstanding payments and credits" unless $opt{q};
89       $cust_main->apply_payments;
90       $cust_main->apply_credits;
91     }
92
93     if ( $opt{o} ) {
94       print "\n\tCollecting" unless $opt{q};
95       my $error = $cust_main->collect(
96         'invoice_time' => $now,
97         'batch_card'   => $opt{i} ? 'no' : 'yes',
98         'force_print'  => 'yes',
99       );
100       warn "Error collecting from customer #" . $cust_main->custnum.  ":$error"
101         if $error;
102     }
103
104     print "\n" unless $opt{q};
105
106   }
107
108 }
109
110 sub untaint_argv {
111   foreach $_ ( $[ .. $#ARGV ) { 
112     $ARGV[$_] =~ /^([\w\-\/\.]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
113     $ARGV[$_]=$1;
114   }
115 }
116
117 sub usage {
118   die "Usage:\n\n    freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user\n";
119 }
120
121
122 =head1 NAME
123
124 freeside-overdue - Perform actions on overdue and/or expired accounts.
125
126 =head1 SYNOPSIS
127
128   freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user
129
130 =head1 DESCRIPTION
131
132 Performs actions on overdue and/or expired accounts.
133
134 Selection options (at least one selection option is required):
135
136   -d:  Customers with a balance due on invoices older than the supplied number
137        of days.  Requires an integer argument.
138
139   -e:  Customers with a billing expiration date in the past.
140
141 Action options: 
142
143   -q:  Be quiet (by default, selected accounts are printed).
144
145   -p:  Add postal invoicing to the relevant customers.
146
147   -l:  Add a charge of the given amount to the relevant customers.
148
149   -s:  Suspend accounts.
150
151   -c:  Cancel accounts.
152
153   -b:  Bill customers (create invoices)
154
155   -y:  Apply unapplied payments and credits
156
157   -o:  Collect from customers (charge cards, print invoices)
158
159     -i:  real-time billing (as opposed to batch billing).  only relevant
160          for credit cards.
161
162   user: From the mapsecrets file - see config.html from the base documentation
163
164 =head1 CRONTAB
165
166 Example crontab entries:
167
168 # suspend expired accounts
169 20 4 * * * freeside-overdue -e -s user
170
171 # quietly add postal invoicing to customers over 30 days past due
172 20 4 * * * freeside-overdue -d 30 -p -q user
173
174 # suspend accounts and charge a $10.23 fee for customers over 60 days past due
175 20 4 * * * freeside-overdue -d 60 -s -l 10.23 user
176
177 # cancel accounts over 90 days past due
178 20 4 * * * freeside-overdue -d 90 -c user
179
180 =head1 ORIGINAL AUTHORS
181
182 Original disable-overdue version by mw/kwh: Mark W.? and Kristian Hoffmann ?
183
184 Ivan seems to be turning it into the "do-everything" CLI.
185
186 =head1 BUGS
187
188 Hell now that this is the do-everything CLI it should have --longoptions
189
190 =cut
191
192 1;
193