1 package FS::part_event::Action::pkg_discount;
4 use base qw( FS::part_event::Action );
6 sub description { "Discount unsuspended package(s) (monthly recurring only)"; }
8 sub eventtable_hashref {
14 sub event_stage { 'pre-bill'; }
18 'if_pkgpart' => { 'label' => 'Only packages',
19 'type' => 'select-table',
20 'table' => 'part_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'),
26 'if_pkg_class' => { 'label' => 'Only package class',
27 'type' => 'select-pkg_class',
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
38 'hashref' => { 'disabled' => '',
39 'months' => { op=>'!=', value=>'0' },
41 'disable_custom_discount' => 1,
43 'once_percust' => { 'label' => 'Only discount one package per customer',
50 #lots of false laziness with referral_pkg_discount
51 #but also lots of doing it differently...and better???
53 my( $self, $object, $cust_event ) = @_;
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 = map { $_=>1 } split(/\s*,\s*/, $self->option('if_pkg_class') );
59 my $allpkgs = (keys %if_pkgpart) ? 0 : 1;
62 if ( $object->table eq 'cust_pkg' ) {
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'
69 && ! $if_pkg_class{ $object->part_pkg->classnum };
70 return 'Package frequency not monthly or a multiple'
71 if $object->part_pkg->freq !~ /^\d+$/;
73 @cust_pkg = ( $object );
78 ( $allpkgs || $if_pkgpart{ $_->pkgpart } )
79 && ( ! keys %if_pkg_class || $if_pkg_class{ $_->part_pkg->classnum } )
81 #remove after fixing discount bug with non-monthly pkgs
82 && ( $_->part_pkg->freq =~ /^\d+$/)
84 $cust_main->unsuspended_pkgs;
86 return 'No qualifying packages' unless @cust_pkg;
91 foreach my $cust_pkg (@cust_pkg) {
93 my @cust_pkg_discount = $cust_pkg->cust_pkg_discount_active;
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;
99 my @my_cust_pkg_discount =
100 grep { $_->discountnum == $self->option('discountnum') } @cust_pkg_discount;
102 if ( @my_cust_pkg_discount ) { #reset the existing one instead
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') ) {
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;
119 last if $self->option('once_percust');
121 } elsif ( @cust_pkg_discount ) {
123 #can't currently discount an already discounted package,
124 #but maybe we can discount a different package
127 } else { #normal case, create a new one
130 my $cust_pkg_discount = new FS::cust_pkg_discount {
131 'pkgnum' => $cust_pkg->pkgnum,
132 'discountnum' => $self->option('discountnum'),
135 my $error = $cust_pkg_discount->insert;
136 die "Error discounting package: $error\n" if $error;
138 last if $self->option('once_percust');
143 return $gotit ? '' : 'Discount not applied due to existing discounts';