suspend later just like expire (#1487)
[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   my $invoice_time = $opt{'n'} ? $^T : $time;
28
29   # select * from cust_main where
30   my $where_pkg = <<"END";
31     0 < ( select count(*) from cust_pkg
32             where cust_main.custnum = cust_pkg.custnum
33               and ( cancel is null or cancel = 0 )
34               and (    setup is null or setup =  0
35                     or bill  is null or bill  <= $time 
36                     or ( expire is not null and expire <= $^T )
37                     or ( adjourn is not null and adjourn <= $^T )
38                   )
39         )
40 END
41   
42   # or
43   my $where_bill_event = <<"END";
44     0 < ( select count(*) from cust_bill
45             where cust_main.custnum = cust_bill.custnum
46               and 0 < charged
47                       - coalesce(
48                                   ( select sum(amount) from cust_bill_pay
49                                       where cust_bill.invnum = cust_bill_pay.invnum )
50                                   ,0
51                                 )
52                       - coalesce(
53                                   ( select sum(amount) from cust_credit_bill
54                                       where cust_bill.invnum = cust_credit_bill.invnum )
55                                   ,0
56                                 )
57               and 0 < ( select count(*) from part_bill_event
58                           where payby = cust_main.payby
59                             and ( disabled is null or disabled = '' )
60                             and seconds <= $time - cust_bill._date
61                             and 0 = ( select count(*) from cust_bill_event
62                                        where cust_bill.invnum = cust_bill_event.invnum
63                                          and part_bill_event.eventpart = cust_bill_event.eventpart
64                                          and status = 'done'
65                                     )
66   
67                       )
68         )
69 END
70   
71   my $extra_sql = ( scalar(%search) ? ' AND ' : ' WHERE ' ). "( $where_pkg OR $where_bill_event )";
72   
73   my @cust_main;
74   if ( @ARGV ) {
75     @cust_main = map { qsearchs('cust_main', { custnum => $_, %search } ) } @ARGV
76   } else {
77     @cust_main = qsearch('cust_main', \%search, '', $extra_sql );
78   }
79   ;
80   
81   my($cust_main,%saw);
82   foreach $cust_main ( @cust_main ) {
83   
84     # $^T not $time because -d is for pre-printing invoices
85     foreach my $cust_pkg (
86       grep { $_->expire && $_->expire <= $^T } $cust_main->ncancelled_pkgs
87     ) {
88       my $error = $cust_pkg->cancel;
89       warn "Error cancelling expired pkg ". $cust_pkg->pkgnum. " for custnum ".
90            $cust_main->custnum. ": $error"
91         if $error;
92     }
93     # $^T not $time because -d is for pre-printing invoices
94     foreach my $cust_pkg (
95       grep { (    $_->part_pkg->is_prepaid && $_->bill && $_->bill < $^T
96                || $_->adjourn && $_->adjourn <= $^T
97              )
98              && ! $_->susp
99            }
100            $cust_main->ncancelled_pkgs
101     ) {
102       my $action = $cust_pkg->part_pkg->option('recur_action') || 'suspend';
103       my $error = $cust_pkg->$action();
104       warn "Error suspending package ". $cust_pkg->pkgnum.
105            " for custnum ". $cust_main->custnum.
106            ": $error"
107         if $error;
108     }
109   
110     my $error = $cust_main->bill( 'time'         => $time,
111                                   'invoice_time' => $invoice_time,
112                                   'resetup'      => $opt{'s'},
113                                 );
114     warn "Error billing, custnum ". $cust_main->custnum. ": $error" if $error;
115   
116     $cust_main->apply_payments_and_credits;
117   
118     $error = $cust_main->collect( 'invoice_time' => $time,
119                                   'freq'         => $opt{'freq'},
120                                 );
121     warn "Error collecting, custnum". $cust_main->custnum. ": $error" if $error;
122   
123   }
124
125 }