Removed depriciated FS::Bill (now in FS::cust_main)
[freeside.git] / bin / bill
1 #!/usr/local/bin/perl -Tw
2 #
3 # bill: Bill customer(s)
4 #
5 # Usage: bill [ -c [ i ] ] [ -d 'date' ] [ -b ]
6 #
7 # Bills all customers.
8 #
9 # Adds record to /dbin/cust_bill and /dbin/cust_pay (if payment made -
10 # CARD & COMP), prints invoice / charges card etc.
11 #
12 # -c: Turn on collecting (you probably want this).
13 #
14 # -i: real-time billing (as opposed to batch billing).  only relevant
15 #     for credit cards.
16 #
17 # -d: Pretent it's 'date'.  Date is in any format Date::Parse is happy with,
18 #     but be careful.
19 #
20 # ## n/a ## -b: send batch when done billing
21 #
22 # ivan@voicenet.com sep/oct 96
23 #
24 # separated billing and collections, cleaned up code.
25 # ivan@voicenet.com 96-nov-11
26 #
27 # added -d option
28 # ivan@voicenet.com 96-nov-13
29 #
30 # added -v option and started to implement it, added 'd:' to getopts call
31 #  (oops!)
32 # ivan@voicenet.com 97-jan-2
33 #
34 # added more debug messages, moved some searches to fssearch.pl library (for 
35 # speed)
36 # rewrote "all customer" finder to know about bill dates, for speed.
37 # ivan@voicenet.com 97-jan-8
38 #
39 # thought about it a while, and removed passing of the -d option to collect...?
40 # ivan@voicenet.com 97-jan-14
41 #
42 # make all -v stuff STDERR 
43 # ivan@voicenet.com 97-feb-4
44 #
45 # added pkgnum as argument to program from /db/part_pkg, with kludge for the
46 # "/bin/echo XX" 's already there.
47 # ivan@voicenet.com 97-feb-23
48 #
49 # - general cleanup
50 # - customers who are suspended can still be billed for the setup fee
51 # - cust_pkg record is re-read after the package setup fee program is run.
52 #   this way,
53 #   that program can modify the record (for example, to start accounts off
54 #   suspended)
55 #   (best to think four or five times before modifying anything else!)
56 # ivan@voicenet.com 97-feb-26
57 #
58 # don't bill recurring fee if its not time! (was removed)
59 # ivan@voicenet.com 97-mar-6
60 #
61 # added -b option, send batch when done billing.
62 # ivan@voicenet.com 97-apr-4
63 #
64 #insecure dependency on line 179ish below needs to be fixed before bill is
65 #used setuid
66 # ivan@voicenet.com 97-jun-2
67 #
68 # removed running of setup program (depriciated)
69 # ivan@voicenet.com 97-jul-21
70 #
71 # rewrote for new API, removed option to specify custnums (use FS::Bill 
72 # instead), removed -v option (?)
73 # ivan@voicenet.com 97-jul-22 - 23 - 25 -28
74 # (need to add back in email stuff, look in /home/ivan/old/dbin/collect)
75 #
76 # s/suidsetup/adminsuidsetup/, s/FS::Search/FS::Record/, added some batch
77 # exporting stuff (which still needs to be generalized) and removed &idiot
78 # ivan@sisd.com 98-may-27
79 #
80 # $Log: bill,v $
81 # Revision 1.2  1998-11-07 08:08:12  ivan
82 #
83 # Removed depriciated FS::Bill (now in FS::cust_main)
84 #
85
86 # setup
87
88 use strict;
89 use Fcntl qw(:flock);
90 use Date::Parse;
91 use Getopt::Std;
92 use FS::UID qw(adminsuidsetup swapuid);
93 use FS::Record qw(qsearch qsearchs);
94
95 my($batchfile)="/var/spool/freeside/batch";
96 my($batchlock)="/var/spool/freeside/batch.lock";
97
98 adminsuidsetup;
99
100 &untaint_argv;  #what it sounds like  (eww)
101 use vars qw($opt_b $opt_c $opt_i $opt_d);
102 getopts("bcid:");       #switches
103
104 #we're at now now (and later).
105 my($time)= $main::opt_d ? str2time($main::opt_d) : $^T;
106
107 # find packages w/ bill < time && cancel != '', and create corresponding
108 # customer objects
109
110 my($cust_main,%saw);
111 foreach $cust_main (
112   map {
113     if ( ( $_->getfield('bill') || 0 ) <= $time &&
114          !$saw{ $_->getfield('custnum') }++ ) {
115       qsearchs('cust_main',{'custnum'=> $_->getfield('custnum') } );
116     } else {
117       ();
118     }
119   } qsearch('cust_pkg',{'cancel'=>''})
120 ) {
121
122   # and bill them
123
124   print "Billing customer #" . $cust_main->getfield('custnum') . "\n";
125
126   my($error);
127
128   $error=$cust_main->bill('time'=>$time);
129   warn "Error billing,  customer #" . $cust_main->getfield('custnum') . 
130     ":" . $error if $error;
131
132   if ($main::opt_c) {
133     $error=$cust_main->collect('invoice_time'=>$time,
134                                'batch_card' => $main::opt_i ? 'no' : 'yes',
135                               );
136     warn "Error collecting customer #" . $cust_main->getfield('custnum') .
137       ":" . $error if $error;
138
139   #sleep 1;
140
141   }
142
143 }
144
145 #if ($main::opt_b) {
146 #
147 #  die "Batch still waiting for reply? ($batchlock exists)\n" if -e $batchlock;
148 #  open(BATCHLOCK,"+>>$batchlock") or die "Can't open $batchlock: $!";
149 #  select(BATCHLOCK); $|=1; select(STDOUT);
150 #  unless ( flock(BATCHLOCK,,LOCK_EX|LOCK_NB) ) {
151 #    seek(BATCHLOCK,0,0);
152 #    my($pid)=<BATCHLOCK>;
153 #    chop($pid);
154 #    die "Is a batch running? (pid $pid)\n";
155 #  }
156 #  seek(BATCHLOCK,0,0);
157 #  print BATCHLOCK $$,"\n";
158 #
159 #  ( open(BATCH,">$batchfile")
160 #    and flock(BATCH,LOCK_EX|LOCK_NB)
161 #  ) or die "Can't open $batchfile: $!";
162 #
163 #  my($cust_pay_batch);
164 #  foreach $cust_pay_batch (qsearch('cust_pay_batch',{})) {
165 #    print BATCH join(':',
166 #      $_->getfield('cardnum'),
167 #      $_->getfield('exp'),
168 #      $_->getfield('amount'),
169 #      $_->getfield('payname')
170 #        || $_->getfield('first'). ' '. $_->getfield('last'),
171 #      "Description",
172 #      $_->getfield('zip'),
173 #    ),"\n";
174 #  }
175 #
176 #  flock(BATCH,LOCK_UN);
177 #  close BATCH;
178 #
179 #  flock(BATCHLOCK,LOCK_UN);
180 #  close BATCHLOCK;
181 #}
182
183 # subroutines
184
185 sub untaint_argv {
186   foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
187     $ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
188     $ARGV[$_]=$1;
189   }
190 }
191