42991c4f8a58ee1c214151eaf6557eb56c7a93f5
[freeside.git] / FS / bin / freeside-bill
1 #!/usr/bin/perl -Tw
2
3 use strict;
4 use Fcntl qw(:flock);
5 use Date::Parse;
6 use Getopt::Std;
7 use FS::UID qw(adminsuidsetup swapuid);
8 use FS::Record qw(qsearch qsearchs);
9 use FS::cust_main;
10
11 &untaint_argv;  #what it sounds like  (eww)
12 use vars qw($opt_a $opt_c $opt_i $opt_d);
13 getopts("acid:");
14 my $user = shift or die &usage;
15
16 adminsuidsetup $user;
17
18 my %bill_only = map { $_ => 1 } (
19   @ARGV ? @ARGV : ( map $_->custnum, qsearch('cust_main', {} ) )
20 );
21
22 #we're at now now (and later).
23 my($time)= $main::opt_d ? str2time($main::opt_d) : $^T;
24
25 # find packages w/ bill < time && cancel != '', and create corresponding
26 # customer objects
27
28 my($cust_main,%saw);
29 foreach $cust_main (
30   map {
31     unless ( exists $saw{ $_->custnum } && defined $saw{ $_->custnum} ) {
32       $saw{ $_->custnum } = 0; # to avoid 'use of uninitialized value' errors
33     }
34     if (
35       ( $main::opt_a || ( ( $_->getfield('bill') || 0 ) <= $time ) )
36       && $bill_only{ $_->custnum }
37       && !$saw{ $_->custnum }++
38     ) {
39       qsearchs('cust_main',{'custnum'=> $_->custnum } );
40     } else {
41       ();
42     }
43   } ( qsearch('cust_pkg', { 'cancel' => '' }),
44       qsearch('cust_pkg', { 'cancel' => 0  }),
45     )
46 ) {
47
48   # and bill them
49
50   print "Billing customer #" . $cust_main->getfield('custnum') . "\n";
51
52   my($error);
53
54   $error=$cust_main->bill('time'=>$time);
55   warn "Error billing,  customer #" . $cust_main->getfield('custnum') . 
56     ":" . $error if $error;
57
58   if ($main::opt_c) {
59     $error=$cust_main->collect('invoice_time'=>$time,
60                                'batch_card' => $main::opt_i ? 'no' : 'yes',
61                               );
62     warn "Error collecting customer #" . $cust_main->getfield('custnum') .
63       ":" . $error if $error;
64
65   #sleep 1;
66
67   }
68
69 }
70
71 # subroutines
72
73 sub untaint_argv {
74   foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
75     #$ARGV[$_] =~ /^([\w\-\/]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
76     # Date::Parse
77     $ARGV[$_] =~ /^(.*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
78     $ARGV[$_]=$1;
79   }
80 }
81
82 sub usage {
83   die "Usage:\n\n  freeside-bill [ -c [ i ] ] [ -d 'date' ] [ -b ] user\n";
84 }
85
86 =head1 NAME
87
88 freeside-bill - Command line (crontab, script) interface to customer billing.
89
90 =head1 SYNOPSIS
91
92   freeside-bill [ -c [ -a ] [ -i ] ] [ -d 'date' ] user [ custnum custnum ... ]
93
94 =head1 DESCRIPTION
95
96 Bills customers.  Searches for customers who are due for billing and calls
97 the bill and collect methods of a cust_main object.  See L<FS::cust_main>.
98
99   -c: Turn on collecting (you probably want this).
100
101   -a: Call collect even if there isn't a new invoice (probably a bad idea for
102       daily use)
103
104   -i: real-time billing (as opposed to batch billing).  only relevant
105       for credit cards.
106
107   -d: Pretend it's 'date'.  Date is in any format Date::Parse is happy with,
108       but be careful.
109
110 user: From the mapsecrets file - see config.html from the base documentation
111
112 custnum: if one or more customer numbers are specified, only bills those
113 customers.  Otherwise, bills all customers.
114
115 =head1 VERSION
116
117 $Id: freeside-bill,v 1.6 2000-06-24 00:28:30 ivan Exp $
118
119 =head1 BUGS
120
121 =head1 SEE ALSO
122
123 L<FS::cust_main>, config.html from the base documentation
124
125 =cut
126