take the package-def defined action here, like freeside-prepaidd
[freeside.git] / FS / FS / Cron / bill.pm
1 package FS::Cron::bill;
2
3 use strict;
4 use vars qw( @ISA @EXPORT_OK );
5 use Exporter;
6 use Date::Parse;
7 use FS::Record qw(qsearch qsearchs);
8 use FS::cust_main;
9
10 @ISA = qw( Exporter );
11 @EXPORT_OK = qw ( bill );
12
13 sub bill {
14
15   my %opt = @_;
16
17   $FS::cust_main::DEBUG = 1 if $opt{'v'};
18   
19   my %search = ();
20   $search{'payby'}    = $opt{'p'} if $opt{'p'};
21   $search{'agentnum'} = $opt{'a'} if $opt{'a'};
22   
23   #we're at now now (and later).
24   my($time)= $opt{'d'} ? str2time($opt{'d'}) : $^T;
25   $time += $opt{'y'} * 86400 if $opt{'y'};
26
27   # select * from cust_main where
28   my $where_pkg = <<"END";
29     0 < ( select count(*) from cust_pkg
30             where cust_main.custnum = cust_pkg.custnum
31               and ( cancel is null or cancel = 0 )
32               and (    setup is null or setup =  0
33                     or bill  is null or bill  <= $time 
34                     or ( expire is not null and expire <= $^T )
35                   )
36         )
37 END
38   
39   # or
40   my $where_bill_event = <<"END";
41     0 < ( select count(*) from cust_bill
42             where cust_main.custnum = cust_bill.custnum
43               and 0 < charged
44                       - coalesce(
45                                   ( select sum(amount) from cust_bill_pay
46                                       where cust_bill.invnum = cust_bill_pay.invnum )
47                                   ,0
48                                 )
49                       - coalesce(
50                                   ( select sum(amount) from cust_credit_bill
51                                       where cust_bill.invnum = cust_credit_bill.invnum )
52                                   ,0
53                                 )
54               and 0 < ( select count(*) from part_bill_event
55                           where payby = cust_main.payby
56                             and ( disabled is null or disabled = '' )
57                             and seconds <= $time - cust_bill._date
58                             and 0 = ( select count(*) from cust_bill_event
59                                        where cust_bill.invnum = cust_bill_event.invnum
60                                          and part_bill_event.eventpart = cust_bill_event.eventpart
61                                          and status = 'done'
62                                     )
63   
64                       )
65         )
66 END
67   
68   my $extra_sql = ( scalar(%search) ? ' AND ' : ' WHERE ' ). "( $where_pkg OR $where_bill_event )";
69   
70   my @cust_main;
71   if ( @ARGV ) {
72     @cust_main = map { qsearchs('cust_main', { custnum => $_, %search } ) } @ARGV
73   } else {
74     @cust_main = qsearch('cust_main', \%search, '', $extra_sql );
75   }
76   ;
77   
78   my($cust_main,%saw);
79   foreach $cust_main ( @cust_main ) {
80   
81     # $^T not $time because -d is for pre-printing invoices
82     foreach my $cust_pkg (
83       grep { $_->expire && $_->expire <= $^T } $cust_main->ncancelled_pkgs
84     ) {
85       my $error = $cust_pkg->cancel;
86       warn "Error cancelling expired pkg ". $cust_pkg->pkgnum. " for custnum ".
87            $cust_main->custnum. ": $error"
88         if $error;
89     }
90     # $^T not $time because -d is for pre-printing invoices
91     foreach my $cust_pkg (
92       grep { $_->part_pkg->is_prepaid
93              && $_->bill && $_->bill < $^T && ! $_->susp
94            }
95            $cust_main->ncancelled_pkgs
96     ) {
97       my $action = $cust_pkg->part_pkg->option('recur_action') || 'suspend';
98       my $error = $cust_pkg->$action();
99       warn "Error suspending package ". $cust_pkg->pkgnum.
100            " for custnum ". $cust_main->custnum.
101            ": $error"
102         if $error;
103     }
104   
105     my $error = $cust_main->bill( 'time'    => $time,
106                                   'resetup' => $opt{'s'},
107                                 );
108     warn "Error billing, custnum ". $cust_main->custnum. ": $error" if $error;
109   
110     $cust_main->apply_payments_and_credits;
111   
112     $error = $cust_main->collect( 'invoice_time' => $time,
113                                   'freq'         => $opt{'freq'},
114                                 );
115     warn "Error collecting, custnum". $cust_main->custnum. ": $error" if $error;
116   
117   }
118
119 }