dd85ada9bf9c336d6fb8673dd857b7b648eda888
[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 package(s) (monthly recurring only)"; }
7
8 sub eventtable_hashref {
9   { 'cust_main' => 1,
10     'cust_pkg'  => 1,
11   };
12 }
13
14 sub event_stage { 'pre-bill'; }
15
16 sub option_fields {
17   (
18     'if_pkgpart'  => { 'label'    => 'Only packages',
19                        'type'     => 'select-table',
20                        'table'    => 'part_pkg',
21                        'name_col' => 'pkg',
22                        #can tweak after fixing discount bug with non-monthly recurring pkgs 
23                        'extra_sql' => q(AND freq NOT LIKE '0%' AND freq NOT LIKE '%d' AND freq NOT LIKE '%h' AND freq NOT LIKE '%w'), 
24                        'multiple' => 1,
25                      },
26     'if_pkg_class'    => { label    => 'Only package class',
27                            type     => 'select-pkg_class',
28                            multiple => 1,
29                          },
30     'discountnum' => { 'label'    => 'Discount',
31                        'type'     => 'select-table', #we don't handle the select-discount create a discount case
32                        'table'    => 'discount',
33                        #(well, since 2013 it winds up calling select-discount
34                        # anyway (but not tr-select-discount)
35                        #'name_col' => 'description', #well, method
36                        #'order_by' => 'ORDER BY discountnum', #requied because name_col is a method
37                        'disable_empty' => 1,
38                        'hashref'  => { 'disabled' => '',
39                                        'months'   => { op=>'!=', value=>'0' },
40                                      },
41                        'disable_custom_discount' => 1,
42                      },
43     'once_percust' => { 'label' => 'Only discount one package per customer',
44                         'type'  => 'checkbox',
45                         'value' => 'Y',
46                       },
47   );
48 }
49
50 #lots of false laziness with referral_pkg_discount
51 #but also lots of doing it differently...and better???
52 sub do_action {
53   my( $self, $object, $cust_event ) = @_;
54
55   my $cust_main = $self->cust_main($object);
56   my %if_pkgpart = map { $_=>1 } split(/\s*,\s*/, $self->option('if_pkgpart') );
57   my $if_pkg_class = $self->option('if_pkg_class') || {};
58
59   my $allpkgs = (keys %if_pkgpart) ? 0 : 1;
60
61   my @cust_pkg = ();
62   if ( $object->table eq 'cust_pkg' ) {
63
64     return 'Package is suspended' if $object->susp;
65     return 'Package not selected'
66       if ! $allpkgs && ! $if_pkgpart{ $object->pkgpart };
67     return 'Package not of selected class'
68       if keys %$if_pkg_class
69       && ! $if_pkg_class->{ $object->part_pkg->classnum };
70     return 'Package frequency not monthly or a multiple'
71       if $object->part_pkg->freq !~ /^\d+$/;
72
73     @cust_pkg = ( $object );
74
75   } else {
76
77     @cust_pkg = grep {
78          ( $allpkgs || $if_pkgpart{ $_->pkgpart } ) 
79       && ( ! keys %$if_pkg_class || $if_pkg_class->{ $_->part_pkg->classnum } )
80       && $_->part_pkg->freq
81       #remove after fixing discount bug with non-monthly pkgs
82       && ( $_->part_pkg->freq =~ /^\d+$/)
83     }
84       $cust_main->unsuspended_pkgs;
85
86     return 'No qualifying packages' unless @cust_pkg;
87
88   }
89
90   my $gotit = 0;
91   foreach my $cust_pkg (@cust_pkg) {
92
93     my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
94
95     #our logic here only makes sense insomuch as you can't have multiple discounts
96     die "Unexpected multiple discounts, contact developers"
97       if scalar(@cust_pkg_discount) > 1;
98
99     my @my_cust_pkg_discount =
100       grep { $_->discountnum == $self->option('discountnum') } @cust_pkg_discount;
101
102     if ( @my_cust_pkg_discount ) { #reset the existing one instead
103
104       $gotit = 1;
105
106       #it's already got this discount and discount never expires--great, move on
107       unless ( $cust_pkg_discount[0]->discount->months ) {
108         if ( $self->option('once_percust') ) {
109           last;
110         } else {
111           next;
112         }
113       };
114         
115       #reset the discount
116       my $error = $cust_pkg_discount[0]->decrement_months_used( $cust_pkg_discount[0]->months_used );
117       die "Error extending discount: $error\n" if $error;
118
119       last if $self->option('once_percust');
120
121     } elsif ( @cust_pkg_discount ) {
122
123       #can't currently discount an already discounted package,
124       #but maybe we can discount a different package
125       next;
126
127     } else { #normal case, create a new one
128
129       $gotit = 1;
130       my $cust_pkg_discount = new FS::cust_pkg_discount {
131         'pkgnum'      => $cust_pkg->pkgnum,
132         'discountnum' => $self->option('discountnum'),
133         'months_used' => 0
134       };
135       my $error = $cust_pkg_discount->insert;
136       die "Error discounting package: $error\n" if $error;
137
138       last if $self->option('once_percust');
139
140     }
141   }
142
143   return $gotit ? '' : 'Discount not applied due to existing discounts';
144
145 }
146
147 1;