1 package FS::cust_main::Billing_Discount;
4 use vars qw( $DEBUG $me );
5 use FS::Record qw( qsearch ); #qsearchs );
8 # 1 is mostly method/subroutine entry and options
9 # 2 traces progress of some operations
10 # 3 is even more information including possibly sensitive data
12 $me = '[FS::cust_main::Billing_Discount]';
16 FS::cust_main::Billing_Discount - Billing discount mixin for cust_main
22 These methods are available on FS::cust_main objects.
28 =item _discount_pkg_and_bill
32 sub _discount_pkgs_and_bill {
35 my @cust_bill = $self->cust_bill;
36 my $cust_bill = pop @cust_bill;
37 return () unless $cust_bill && $cust_bill->owed;
40 push @where, "cust_bill_pkg.invnum = ". $cust_bill->invnum;
41 push @where, "cust_bill_pkg.pkgpart_override IS NULL";
42 push @where, "part_pkg.freq = '1'";
43 push @where, "(cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0)";
44 push @where, "(cust_pkg.susp IS NULL OR cust_pkg.susp = 0)";
45 push @where, "EXISTS( SELECT 1 FROM part_pkg_discount
46 WHERE part_pkg.pkgpart = part_pkg_discount.pkgpart )";
49 SELECT 1 FROM cust_bill_pkg_discount
50 WHERE cust_bill_pkg.billpkgnum = cust_bill_pkg_discount.billpkgnum
53 my $extra_sql = 'WHERE '. join(' AND ', @where);
57 'table' => 'cust_pkg',
58 'select' => "DISTINCT cust_pkg.*",
59 'addl_from' => 'JOIN cust_bill_pkg USING(pkgnum) '.
60 'JOIN part_pkg USING(pkgpart)',
62 'extra_sql' => $extra_sql,
65 ($cust_bill, @cust_pkg);
68 =item _discountable_pkgs_at_term
72 #this isn't even a method
73 sub _discountable_pkgs_at_term {
74 my ($term, @pkgs) = @_;
75 my $part_pkg = new FS::part_pkg { freq => $term - 1 };
76 grep { ( !$_->adjourn || $_->adjourn > $part_pkg->add_freq($_->bill) ) &&
77 ( !$_->expire || $_->expire > $part_pkg->add_freq($_->bill) )
84 Returns a list of lengths for term discounts
93 my @discount_pkgs = $self->_discount_pkgs_and_bill;
94 shift @discount_pkgs; #discard bill;
96 # convert @discount_pkgs (the list of packages that have available discounts)
97 # to a list of distinct term lengths in months, and strip any decimal places
98 # from the number of months, not that it should have any
99 map { $terms{sprintf('%.0f', $_->months)} = 1 }
100 grep { $_->months && $_->months > 1 }
102 map { $_->part_pkg->part_pkg_discount }
105 return sort { $a <=> $b } keys %terms;
109 =item discount_term_values MONTHS
111 Returns a list with credit, dollar amount saved, and total bill acheived
112 by prepaying the most recent invoice for MONTHS.
116 # XXX this should work by creating a quotation; then we can finally retire
117 # the "no_commit" option, which doesn't work with modern tax calculation
119 sub discount_term_values {
123 local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
125 warn "$me discount_term_values called with $term\n" if $DEBUG;
129 my @packages = $self->_discount_pkgs_and_bill;
130 my $cust_bill = shift(@packages);
131 @packages = _discountable_pkgs_at_term( $term, @packages );
132 return () unless scalar(@packages);
134 $_->bill($_->last_bill) foreach @packages;
135 my @final = map { new FS::cust_pkg { $_->hash } } @packages;
138 'recurring_only' => 1,
139 'no_usage_reset' => 1,
145 'pkg_list' => \@packages,
146 'time' => $cust_bill->_date,
149 my $error = $self->bill(%options, %params);
150 die $error if $error; # XXX think about this a bit more
153 $credit += $_->charged foreach @{$params{return_bill}};
154 $credit = sprintf('%.2f', $credit);
155 warn "$me discount_term_values $term credit: $credit\n" if $DEBUG;
159 'pkg_list' => \@packages,
160 'time' => $packages[0]->part_pkg->add_freq($cust_bill->_date)
163 $error = $self->bill(%options, %params);
164 die $error if $error; # XXX think about this a bit more
167 $next += $_->charged foreach @{$params{return_bill}};
168 warn "$me discount_term_values $term next: $next\n" if $DEBUG;
172 'pkg_list' => \@final,
173 'time' => $cust_bill->_date,
174 'freq_override' => $term,
177 $error = $self->bill(%options, %params);
178 die $error if $error; # XXX think about this a bit more
180 my $final = $self->balance - $credit;
181 $final += $_->charged foreach @{$params{return_bill}};
182 $final = sprintf('%.2f', $final);
183 warn "$me discount_term_values $term final: $final\n" if $DEBUG;
185 my $savings = sprintf('%.2f', $self->balance + ($term - 1) * $next - $final);
187 ( $credit, $savings, $final );
191 sub discount_terms_hash {
195 my @terms = $self->discount_terms;
196 foreach my $term (@terms) {
197 my @result = $self->discount_term_values($term);
198 $result{$term} = [ @result ] if scalar(@result);
211 L<FS::cust_main>, L<FS::cust_main::Billing>