optimize billing for customers with lots of existing packages and invoices, RT#30238
[freeside.git] / FS / FS / part_event / Condition / inactive_age.pm
1 package FS::part_event::Condition::inactive_age;
2
3 use strict;
4 use base qw( FS::part_event::Condition );
5 use FS::Record qw( qsearch );
6
7 sub description { 'Days without billing activity' }
8
9 sub option_fields {
10   (
11     'age'  =>  { 'label'   => 'No activity within',
12                  'type'    => 'freq',
13                },
14     'ignore_pkgclass' =>
15                { 'label' => 'Except charges of class',
16                  'type'  => 'select-pkg_class',
17                },
18     # flags to select kinds of activity, 
19     # like if you just want "no payments since"?
20     # not relevant yet
21   );
22 }
23
24 sub condition {
25   my( $self, $obj, %opt ) = @_;
26   my $custnum = $obj->custnum;
27   my $age = $self->option_age_from('age', $opt{'time'} );
28
29   my $ignore_pkgclass = $self->option('ignore_pkgclass');
30
31   my $where = "custnum = $custnum AND _date >= $age";
32
33   foreach my $t (qw(cust_pay cust_credit cust_refund)) {
34     my $class = "FS::$t";
35     return 0 if $class->count($where);
36   }
37
38   # cust_bill: handle the ignore_pkgclass option
39   if ( $ignore_pkgclass =~ /^\d+$/ ) {
40     $where .= " AND EXISTS( ".
41       "SELECT 1 FROM cust_bill_pkg JOIN cust_pkg USING (pkgnum) " .
42       "JOIN part_pkg USING (pkgpart) " .
43       "WHERE cust_bill_pkg.invnum = cust_bill.invnum " .
44       "AND COALESCE(part_pkg.classnum, -1) != $ignore_pkgclass" .
45       " )";
46   }
47   #warn "$where\n";
48   return 0 if FS::cust_bill->count($where);
49
50   1;
51 }
52
53 sub condition_sql {
54   my( $class, $table, %opt ) = @_;
55   my $age   = $class->condition_sql_option_age_from('age', $opt{'time'});
56   my $ignore_pkgclass = $class->condition_sql_option_integer('ignore_pkgclass');
57   # will evaluate to zero if there isn't one
58   my @sql;
59   for my $t (qw(cust_pay cust_credit cust_refund)) {
60     push @sql, "
61       NOT EXISTS( SELECT 1 FROM $t
62                     WHERE $t.custnum = cust_main.custnum AND $t._date >= $age
63                     LIMIT 1
64                 )
65     ";
66   }
67   #cust_bill
68   push @sql, "
69     NOT EXISTS(
70                 SELECT 1 FROM cust_bill JOIN cust_bill_pkg USING (invnum)
71                       JOIN cust_pkg USING (pkgnum) JOIN part_pkg USING (pkgpart)
72                   WHERE cust_bill.custnum = cust_main.custnum
73                     AND cust_bill._date >= $age
74                     AND COALESCE(part_pkg.classnum, -1) != $ignore_pkgclass
75                   LIMIT 1
76               )
77   ";
78   join(' AND ', @sql);
79 }
80
81 1;
82