RT#30586: Discounts for ACH
[freeside.git] / FS / FS / part_event / Action / pkg_discount.pm
1 package FS::part_event::Action::pkg_discount;
2
3 use strict;
4 use base qw( FS::part_event::Action );
5
6 sub description { "Discount unsuspended customer packages (monthly recurring only)"; }
7
8 sub eventtable_hashref {
9   { 'cust_main' => 1 };
10 }
11
12 sub event_stage { 'pre-bill'; }
13
14 sub option_fields {
15   (
16     'if_pkgpart'  => { 'label'    => 'Only packages',
17                        'type'     => 'select-table',
18                        'table'    => 'part_pkg',
19                        'name_col' => 'pkg',
20                        #can tweak after fixing discount bug with non-monthly recurring pkgs 
21                        'extra_sql' => q(AND freq NOT LIKE '0%' AND freq NOT LIKE '%d' AND freq NOT LIKE '%h' AND freq NOT LIKE '%w'), 
22                        'multiple' => 1,
23                      },
24     'discountnum' => { 'label'    => 'Discount',
25                        'type'     => 'select-table', #we don't handle the select-discount create a discount case
26                        'table'    => 'discount',
27                        'name_col' => 'description', #well, method
28                        'order_by' => 'ORDER BY discountnum', #requied because name_col is a method
29                        'hashref'  => { 'disabled' => '',
30                                        'months'   => { op=>'!=', value=>'0' },
31                                      },
32                        'disable_empty' => 1,
33                      },
34   );
35 }
36
37 #lots of false laziness with referral_pkg_discount
38 #but also lots of doing it differently...and better???
39 sub do_action {
40   my( $self, $object, $cust_event ) = @_;
41
42   my $cust_main = $self->cust_main($object);
43   my %if_pkgpart = map { $_=>1 } split(/\s*,\s*/, $self->option('if_pkgpart') );
44   my $allpkgs = (keys %if_pkgpart) ? 0 : 1;
45   my @cust_pkg = grep { ( $allpkgs || $if_pkgpart{ $_->pkgpart } ) 
46                           && $_->part_pkg->freq
47                           #can remove after fixing discount bug with non-monthly pkgs
48                           && ( $_->part_pkg->freq =~ /^\d+$/) } 
49                       $cust_main->unsuspended_pkgs;
50   return 'No qualifying packages' unless @cust_pkg;
51
52   my $gotit = 0;
53   foreach my $cust_pkg (@cust_pkg) {
54
55     my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
56
57     #our logic here only makes sense insomuch as you can't have multiple discounts
58     die "Unexpected multiple discounts, contact developers"
59       if scalar(@cust_pkg_discount) > 1;
60
61     my @my_cust_pkg_discount =
62       grep { $_->discountnum == $self->option('discountnum') } @cust_pkg_discount;
63
64     if ( @my_cust_pkg_discount ) { #reset the existing one instead
65
66       $gotit = 1;
67
68       #it's already got this discount and discount never expires--great, move on
69       next unless $cust_pkg_discount[0]->discount->months;
70         
71       #reset the discount
72       my $error = $cust_pkg_discount[0]->decrement_months_used( $cust_pkg_discount[0]->months_used );
73       die "Error extending discount: $error\n" if $error;
74
75     } elsif ( @cust_pkg_discount ) {
76
77       #can't currently discount an already discounted package,
78       #but maybe we can discount a different package
79       next;
80
81     } else { #normal case, create a new one
82
83       $gotit = 1;
84       my $cust_pkg_discount = new FS::cust_pkg_discount {
85         'pkgnum'      => $cust_pkg->pkgnum,
86         'discountnum' => $self->option('discountnum'),
87         'months_used' => 0
88       };
89       my $error = $cust_pkg_discount->insert;
90       die "Error discounting package: $error\n" if $error;
91
92     }
93   }
94
95   return $gotit ? '' : 'Discount not applied due to existing discounts';
96
97 }
98
99 1;